Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

three.js - Transparent objects in Threejs

I am trying to write a small program in Three.js that displays two spheres, one inside the other. The radius of sphere2 is supposed to oscillate between 0.5 and 1.5 while the radius of sphere1 is always 1.0. Each sphere is transparent (opacity: 0.5) so that it would be possible to see the smaller sphere contained in the larger one. Of course the roles of "smaller" and "larger" change as the radius of sphere2 varies.

The problem now is that Three.js makes transparent the first sphere I define in my program but not the second one. If I define first sphere1 then it becomes transparent but then sphere2 is completely opaque. If I define first sphere2 then this is the transparent one. The order of adding them to the scene plays no role.

I include below a minimal program that shows what is going on (without the animation). In its current state only sphere1 is visible and it is not transparent. If I define sphere1 before sphere2 then sphere1 becomes transparent but sphere2 is no longer transparent. Changing sphere2's radius to 1.2 will then hide sphere1.

Is there any way to make both spheres transparent?

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);

camera.position.set(0, 0, 3);
camera.lookAt(new THREE.Vector3(0, 0, 0));
scene.add(camera);

var ambient = new THREE.AmbientLight( 0x555555 );
scene.add(ambient);

var light = new THREE.DirectionalLight( 0xffffff );
light.position = camera.position;
scene.add(light);

var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// Definition 2
var geometry2 = new THREE.SphereGeometry(0.8,32,24);
var material2 = new THREE.MeshLambertMaterial({color: 0x0000ff, transparent: true, opacity: 0.5});
var sphere2 = new THREE.Mesh(geometry2, material2);

// Definition 1
var geometry1 = new THREE.SphereGeometry(1.0,32,24);
var material1 = new THREE.MeshLambertMaterial({color: 0x00ff00, transparent: true, opacity: 0.5});
var sphere1 = new THREE.Mesh(geometry1, material1);

scene.add(sphere1);
scene.add(sphere2);

renderer.render(scene, camera);
Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Both your spheres are transparent, and are remaining so. What is happening is that the smaller sphere is not being rendered at all.

Transparency in WebGL is tricky. You can google the issue to find out more about it.

But you have stumbled upon an issue related to how three.js in particular handles transparency.

The WebGLRenderer in three.js sorts objects based upon their distance from the camera, and renders transparent objects in order from farthest to closest. (This is an important point: It sorts objects based on their position, and renders objects in the sorted order.)

So for two transparent objects to render correctly, the object that is in back -- the smaller sphere in your case -- must be rendered first. Otherwise, it will not be rendered at all, due to the depth buffer.

But in your case, you have two spheres that are in the same location, and hence are equidistant from the camera. That is the problem -- which one to render first; it is a toss-up.

So you need to place the smaller sphere further away from the camera than the larger sphere in order for the scene to render correctly.

One solution is to move the smaller sphere back a little.

Another solution is to set renderer.sortObjects = false. Then the objects will render in the order they are added to the scene. In that case, be sure to add the smaller sphere to the scene first.

A third solution is to set material1.depthWrite = false and material2.depthWrite = false.

EDIT:

Renderable objects having material.transparent = false (opaque objects) are rendered before objects having material.transparent = true (transparent objects).

So a fourth solution is to make the smaller sphere opaque so it is rendered first.

New feature for r.71:

There is now an Object3D.renderOrder property. Within each class of object (opaque or transparent), objects are rendered in the order specified by object.renderOrder. The default value of renderOrder is 0. Note that renderOrder is not inherited by child objects; you must set it for each renderable object.

Objects with the same renderOrder (ties), are sorted by depth, as described above.

So a fifth solution is to set renderOrder = 1 for the larger sphere. This is likely the best solution in your case.

three.js r.71


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...