I've been playing with THREE.InstancedBufferGeometry
. I finally got an example to work, and have now been playing with the shader. The first thing I tried was setting transparency. My example code is below.
The initial state, and from a host of other camera angles (e.g. drag your mouse to the left), the transparency doesn't seem to have any effect. But then at other camera angles (e.g. reload and drag your mouse to the right), the shapes clearly overlap, which is what I was expecting.
Is depth-sorting handled differently for instanced shapes, or am I doing something wrong, or missing something? Do I somehow need to update the shapes so the camera knows their proper depth in the scene?
var cubeGeo = new THREE.InstancedBufferGeometry().copy(new THREE.BoxBufferGeometry(10, 10, 10));
//cubeGeo.maxInstancedCount = 8;
cubeGeo.addAttribute("cubePos", new THREE.InstancedBufferAttribute(new Float32Array([
25, 25, 25,
25, 25, -25, -25, 25, 25, -25, 25, -25,
25, -25, 25,
25, -25, -25, -25, -25, 25, -25, -25, -25
]), 3, false, 1));
var vertexShader = [
"precision highp float;",
"",
"uniform mat4 modelViewMatrix;",
"uniform mat4 projectionMatrix;",
"",
"attribute vec3 position;",
"attribute vec3 cubePos;",
"",
"void main() {",
"",
" gl_Position = projectionMatrix * modelViewMatrix * vec4( cubePos + position, 1.0 );",
"",
"}"
].join("\n");
var fragmentShader = [
"precision highp float;",
"",
"void main() {",
"",
" gl_FragColor = vec4(1.0, 0.0, 0.0, 0.5);",
"",
"}"
].join("\n");
var mat = new THREE.RawShaderMaterial({
uniforms: {},
vertexShader: vertexShader,
fragmentShader: fragmentShader,
transparent: true
});
var mesh = new THREE.Mesh(cubeGeo, mat);
scene.add(mesh);
html * {
padding: 0;
margin: 0;
width: 100%;
overflow: hidden;
}
#host {
width: 100%;
height: 100%;
}
<script src="https://cdn.jsdelivr.net/npm/three@0.108.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.108.0/examples/js/controls/TrackballControls.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.108.0/examples/js/libs/stats.min.js"></script>
<div id="host"></div>
<script>
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight,
FOV = 35,
NEAR = 1,
FAR = 1000;
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(WIDTH, HEIGHT);
document.getElementById('host').appendChild(renderer.domElement);
var stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0';
document.body.appendChild(stats.domElement);
var camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 250;
var trackballControl = new THREE.TrackballControls(camera, renderer.domElement);
trackballControl.rotateSpeed = 2.0; // need to speed it up a little
var scene = new THREE.Scene();
var light = new THREE.PointLight(0xffffff, 1, Infinity);
camera.add(light);
scene.add(light);
function render() {
if (typeof updateVertices !== "undefined") {
updateVertices();
}
renderer.render(scene, camera);
stats.update();
}
function animate() {
requestAnimationFrame(animate);
trackballControl.update();
render();
}
animate();
</script>
You are using InstancedBufferGeometry
with meshes that are translucent.
The instances are rendered in the order they appear in the buffer. The faces of each instance are rendered in the ordered specified by the geometry.
Consequently, if you use instancing with translucency, you will likely have artifacts depending on the viewing angle.
Depending on your use case, you can try setting material.depthWrite = false
, but that can lead to other artifacts.
If your mesh textures have areas of complete transparency (rather than partial) you should be able to use material.alphaTest
to discard unwanted fragments without artifacts.
three.js r.84