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 sett
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
After discussion with WestLangley in the comments, I added a sorter for my instances. It sorts the instances positions based on their distance from the camera.
(Side-note: If I had any other THREE.InstancedBufferAttributes
I would need to re-order them at the same time.)
The biggest downside to this is that it becomes more and more expensive as the scene gets bigger, both in instanced and non-instanced shapes.
// Instances Sorter, called each frame
function sortObjectInstances(obj) {
if (obj.geometry) {
if (obj.geometry instanceof THREE.InstancedBufferGeometry) {
var array = obj.geometry.attributes.cubePos.array,
vecArray = [];
for (var i = 0, l = array.length / 3; i < l; ++i) {
vecArray.push(new THREE.Vector3(array[(i * 3)], array[(i * 3) + 1], array[(i * 3) + 2]));
}
vecArray.sort(function(a, b) {
if (a.distanceTo(camera.position) > b.distanceTo(camera.position)) {
return -1;
}
if (a.distanceTo(camera.position) < b.distanceTo(camera.position)) {
return 1;
}
return 0;
});
for (var i = 0, l = vecArray.length; i < l; ++i) {
array[(i * 3)] = vecArray[i].x;
array[(i * 3) + 1] = vecArray[i].y;
array[(i * 3) + 2] = vecArray[i].z;
}
obj.geometry.attributes.cubePos.needsUpdate = true;
}
} else {
for (var i = 0, l = obj.children.length; i < l; ++i) {
sortObjectInstances(obj.children[i]);
}
}
}
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, 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="http://threejs.org/build/three.js"></script>
<script src="http://threejs.org/examples/js/controls/TrackballControls.js"></script>
<script src="http://threejs.org/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 sortObjectInstances !== "undefined") {
sortObjectInstances(scene); // Sort the instances
}
renderer.render(scene, camera);
stats.update();
}
function animate() {
requestAnimationFrame(animate);
trackballControl.update();
render();
}
animate();
</script>