Reputation: 8896
I'm using a custom shader material in my application. It's an extended (and renamed) copy of THREE.ShaderLib['phong']
, and it works great, except for transparency, which is hit-or-miss. Sometimes objects behind a transparent object are visible, but often get culled. To ensure I didn't break something, I created the fiddle below.
When the scene initializes, everything looks as it should--three transparent planes, all transparency working correctly. Even moving the scene around appears to work correctly at first glance. But suddenly you might notice some popping. Slowing down the rotation shows that at certain angles the planes simply cease being transparent with respect to each other. (I've added a convenient "BREAK IT" button to move the camera into one such position.)
So what's going on? Am I still/actually breaking something? I've tried initializing the materials individually, but the clone seems to do the job of cloning the materials as well. Or is this just a gotcha in how this material works, and I have to find another way to approach it?
Fiddle: http://jsfiddle.net/TheJim01/rwt64fgd/
JS:
// BufferGeometry Tester
var hostDiv, scene, renderer, camera, root, controls, light;
var WIDTH = 500;//window.innerWidth,
HEIGHT = 500;//window.innerHeight,
FOV = 35,
NEAR = 1,
FAR = 1000;
function breakIt(){
camera.position.set(-25.759449580212017, -17.239852126859287, 39.2331270225625);
camera.lookAt(scene.position);
camera.up.set(0.07701484672816412, 0.8931831414880415, 0.44304919494903006);
}
function createBufferGeometryMesh(){
var geo = new THREE.BufferGeometry();
var tl = new THREE.Vector3(-10., 10., 0.),
tr = new THREE.Vector3(10., 10., 0.),
bl = new THREE.Vector3(-10., -10., 0.),
br = new THREE.Vector3(10., -10., 0.);
var n0 = new THREE.Vector3(0., 0., 1.);
var vertices =
[
tl.x, tl.y, tl.z,
tr.x, tr.y, tr.z,
br.x, br.y, br.z,
bl.x, bl.y, bl.z
],
normals =
[
n0.x, n0.y, n0.z,
n0.x, n0.y, n0.z,
n0.x, n0.y, n0.z,
n0.x, n0.y, n0.z
],
indices = [ 0, 2, 1, 0, 3, 2 ];
geo.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( vertices ), 3 ) );
geo.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( normals ), 3 ) );
geo.addAttribute( 'index', new THREE.BufferAttribute( new Uint32Array( indices ), 1 ) );
/*/
var mat = new THREE.MeshPhongMaterial( {
color: 0xffffff,
ambient: 0xffffff,
specular: 0xffffff,
shininess: 50,
side: THREE.DoubleSide
} );
/*/
var shader = THREE.ShaderLib['phong'];
var uniforms = null,
parameters = null;
uniforms = THREE.UniformsUtils.clone(shader.uniforms);
uniforms['diffuse'].value.setHex(0xcccccc);
uniforms['ambient'].value.setHex(0x0);
uniforms['emissive'].value.setHex(0x0);
uniforms['specular'].value.setHex(0xffffff);
uniforms['shininess'].value = 1;
uniforms['opacity'].value = 0.5;
uniforms['ambient'].value.convertGammaToLinear();
parameters = {
fragmentShader: shader.fragmentShader,
vertexShader: shader.vertexShader,
uniforms: uniforms,
lights: true,
fog: false,
side: THREE.DoubleSide,
blending: THREE.NormalBlending,
transparent: (uniforms['opacity'].value < 1.0)
};
var mat = new THREE.ShaderMaterial(parameters);
var msh = new THREE.Mesh(geo, mat);
return msh;
}
function init() {
hostDiv = document.createElement('div');
document.body.appendChild(hostDiv);
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(WIDTH, HEIGHT);
renderer.setClearColor( 0x888888, 1 );
hostDiv.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 50;
camera.lookAt(scene.position);
controls = new THREE.TrackballControls(camera, renderer.domElement);
light = new THREE.PointLight(0xffffff, 1, 1000);
light.position.copy(camera.position);
scene.add(camera);
scene.add(light);
var square = createBufferGeometryMesh();
square.material.uniforms['diffuse'].value.setHex(0xff0000);
square.material.needsUpdate = true;
square.translateY(5);
square.translateX(5);
square.translateZ(5);
scene.add(square);
square = createBufferGeometryMesh();
square.material.uniforms['diffuse'].value.setHex(0x00ff00);
square.material.needsUpdate = true;
square.translateY(-5);
square.translateX(-5);
square.translateZ(-5);
scene.add(square);
square = createBufferGeometryMesh();
square.material.uniforms['diffuse'].value.setHex(0x0000ff);
square.material.needsUpdate = true;
square.scale.set(0.5, 0.5, 0.5);
scene.add(square);
animate();
var button = document.createElement('input');
button.setAttribute('type', 'button');
button.setAttribute('value', 'BREAK IT');
button.addEventListener('click', breakIt);
document.body.appendChild(button);
}
function render() {
renderer.render(scene, camera);
}
function animate() {
light.position.copy(camera.position);
requestAnimationFrame(animate);
render();
controls.update();
}
init();
Upvotes: 0
Views: 3698
Reputation: 104843
The transparency artifacts that you are seeing have nothing to do with your particular shader. The same problem will occur with MeshBasicMaterial
.
Alpha blending of transparent objects in three.js is order-dependent.
three.js sorts objects based on their distance from the camera, and renders transparent objects in order from farthest to closest. See this related answer.
Consequently, you may see artifacts when the camera is moved, and the sort order changes.
There are some use-case-specific work-arounds that may be helpful, such as controlling the render order by setting renderer.sortObjects = false
, or by changing material properties. You will have to experiment to see what works for you.
three.js r.69
Upvotes: 2