I have a simple model consisting of 1000 instances of sphere. I am trying to use instancing to reduce the number of draw calls. However, I am not able to change the transparency/opacity of the individual child geometries.
I already tried the following things:
I am able to change transparency of every sphere using
material.fragmentShader = "varying vec3 vColor;void main() { gl_FragColor = vec4( vColor, 0.2 );}";
However, that changes the opacity to 0.2 for every sphere.
The html file is like:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>A-Frame Instancing component</title>
<meta name="description" content="A-Frame Instancing component">
<script>
/*
var WebVRConfig = {
FORCE_ENABLE_VR: true,
BUFFER_SCALE: 1.0
};
*/
</script>
<script src="https://cdn.rawgit.com/aframevr/aframe/v0.4.0/dist/aframe-master.min.js"></script>
<script src="https://cdn.rawgit.com/donmccurdy/aframe-extras/v3.2.0/dist/aframe-extras.min.js"></script>
<script type="text/javascript" src="build/aframe-instancing.js"></script>
</head>
<body>
<a-scene stats>
<a-assets>
<img id="sky" src="https://cdn.rawgit.com/aframevr/aframe/master/examples/primitives/models/peach-gradient.jpg">
</a-assets>
<a-entity instancing="count:100"></a-entity>
<a-sky src="#sky"></a-sky>
<a-entity light="type:directional;color:#FFFFFF" position="-1 1 1"></a-entity>
</a-scene>
</body>
</html>
The function to acheive instancing:
AFRAME.registerComponent('instancing', {
schema: {
count: {type: 'int', default: 10000}
},
var geometry = new THREE.InstancedBufferGeometry();
geometry.copy(new THREE.SphereBufferGeometry(5.0));
var translateArray = new Float32Array(count*3);
var vectorArray = new Float32Array(count*3);
var colorArray = new Float32Array(count*3);
geometry.addAttribute('translate', new THREE.InstancedBufferAttribute(translateArray, 3, 1));
geometry.addAttribute('vector', new THREE.InstancedBufferAttribute(vectorArray, 3, 1));
geometry.addAttribute('color', new THREE.InstancedBufferAttribute(colorArray, 3, 1));
var material = new THREE.ShaderMaterial({
uniforms: {
time: {value: 0}
},
vertexShader: [
'attribute vec3 translate;',
'attribute vec3 vector;',
'attribute vec3 color;',
'uniform float time;',
'varying vec3 vColor;',
'const float g = 9.8 * 1.5;',
'void main() {',
' vec3 offset;',
' offset.xz = vector.xz * time;',
' offset.y = vector.y * time - 0.5 * g * time * time;',
' gl_Position = projectionMatrix * modelViewMatrix * vec4( position + translate + offset, 1.0 );',
' vColor = color;',
'}'
].join('\n'),
fragmentShader: [
'varying vec3 vColor;',
'void main() {',
' gl_FragColor = vec4( vColor, 1 );',
'}'
].join('\n')
});
var mesh = new THREE.Mesh(geometry, material);
this.model = mesh;
el.setObject3D('mesh', mesh);
el.emit('model-loaded', {format:'mesh', model: mesh});
//try to change opacity here
material.fragmentShader = "varying vec3 vColor;void main() { gl_FragColor = vec4( vColor, 0.2 );}";
material.transparent = true;
//use the new opacity
var mesh1 = new THREE.Mesh(geometry1, material);
this.mesh = mesh1;
el.setObject3D('mesh', mesh1);
el.emit('model-loaded', {format:'mesh', model: mesh1});
}
});
}
]);
Can anyone please tell me, how to change opacity of only one sphere? Thank you in advance!
Also, suppose I am trying to replicate multiple boxes. One of which is as following:
<a-box position="19.0 1.5 23.0"
width="32.0"
height="1.0"
depth="40.0"
color="#969696"
shader="flat"
flat-shading="true">
</a-box>
What would be the values I would fill in translateArray & vectorArray ? Thanks a lot in advance!
Your colors are only
RGBvalues, notRGBA. Update yourcolorattribute to support 4 values, and change associatedvec3references to usevec4. The last value in the vector will be youralpha(transparency) value.It looks like you already know how to send the value from your
vertex shaderinto yourfragment shader, so I won't go into detail there. But in yourfragment shader, you can use the color directly, becausegl_FragColorexpects to be set to avec4.Further clarification:
When you create a
InstancedBufferAttribute, you create one attribute per instance. So yourcolorattribute currently only carriesRGBvalues for each instance.Hard-coding
1or0.2in thewplace (i.e.gl_FragColor = vec4( vColor, 1 );), you apply it universally to all instances. So, you need to define thealphavalue on a per-instance basis, and the easiest way to do this is through your already-created and instancedcolorattribute.The code above makes room for the alpha values, which you should supply for each sphere. Your
colorArraywill contain data like[ R, G, B, A, R, G, B, A, ... ].Then, in your shaders...
Now, the alpha value that you supplied for each sphere instance will be used only for that instance. For example, if you want the sphere at index 1 to be the only transparent sphere, your
colorArraybuffer should look like this:Important caveat
This implementation does not depth-sort the instances, and so blending will be dependent on render order. You can read more about this in the following question:
Transparency within instanced shapes