我正在尝试在Three.js中编写一个显示两个球体的小程序,一个在另一个内部。球2的半径应该在0.5和1.5之间振荡,而球1的半径总是1.0。每个球体都是透明的(不透明度:0.5),因此可以看到较大球体中包含的较小球体。当然,随着球体2的半径变化,“较小”和“较大”的作用会发生变化。
现在的问题是Three.js使我在程序中定义的第一个球体透明,而不是第二个球体。如果我定义第一个sphere1然后它变得透明但是sphere2完全不透明。如果我定义第一个sphere2,那么这是透明的。将它们添加到场景的顺序不起作用。
我在下面包含一个显示正在发生的事情的最小程序(没有动画)。在当前状态下,只有sphere1可见且不透明。如果我在sphere2之前定义sphere1,那么sphere1变为透明但sphere2不再透明。将sphere2的半径更改为1.2将隐藏sphere1。
有没有办法让两个球体透明?
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);
答案 0 :(得分:138)
你的球体都是透明的,并且仍然如此。发生的事情是较小的球体根本没有被渲染。
WebGL的透明度很棘手。您可以谷歌搜索该问题以了解更多相关信息。
但是你偶然发现了一个与 three.js特别是如何处理透明度有关的问题。
three.js中的WebGLRenderer
根据与相机的距离对对象进行排序,并按照从最远到最近的顺序呈现透明对象。 (这一点很重要:它根据位置对对象进行排序,并按排序顺序呈现对象。)
因此,要正确渲染两个透明对象,必须先渲染后面的对象(在您的情况下为较小的球体)。否则,由于深度缓冲区,它根本不会被渲染。
但在您的情况下,您有两个位于同一位置的球体,因此与相机等距。这就是问题 - 首先要渲染的问题;这是一个折腾。
因此,您需要将较小的球体放置在远离相机的位置,而不是较大的球体,以便场景正确渲染。
一种解决方案是将较小的球体向后移动一点。
另一种解决方案是设置renderer.sortObjects = false
。然后,对象将按照添加到场景的顺序进行渲染。在这种情况下,请务必先将较小的球体添加到场景中。
第三种解决方案是设置material1.depthWrite = false
和material2.depthWrite = false
。
编辑:
具有material.transparent = false
(不透明对象)的可渲染对象在具有material.transparent = true
(透明对象)的对象之前呈现。
因此,第四个解决方案是使较小的球体不透明,以便首先渲染。
r.71的新功能:
现在有一个Object3D.renderOrder
属性。在每个对象类(不透明或透明)中,对象按object.renderOrder
指定的顺序呈现。 renderOrder
的默认值为0
。请注意,renderOrder
不会被子对象继承;您必须为每个可渲染对象设置它。
具有相同renderOrder
(领带)的对象按深度排序,如上所述。
所以第五个解决方案是为更大的球体设置renderOrder = 1
。这可能是您案例中的最佳解决方案。
three.js r.71
答案 1 :(得分:7)
一对评论。
首先。如果您要问一个希望人们审核代码的问题,请将其放在jsfiddle中。如果你这样做,你会得到更多的人偷看。虽然如此,这里是jsfiddle中代码的略微修改版本,请将其用作未来问题的指南。 jsfiddle example
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);
renderer.sortObjects = false;
// 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: 0xff0000, transparent: true, opacity: 0.5});
var sphere1 = new THREE.Mesh(geometry1, material1);
scene.add(sphere2);
scene.add(sphere1);
renderer.render(scene, camera);
我在代码中更改的是将sortObjects设置为false,然后更改了球体添加到场景中的顺序。这是因为下两个链接中的信息
答案 2 :(得分:0)
对于什么值,我无法使用上述方法解决相同的问题,但发现具有:
scene = new THREE.Scene();
group = new THREE.Group();
scene.add( group );
在我的init()中,然后将前侧网格物体添加到场景中,但是对后侧网格物体进行分组解决了该问题。即:
var materialfront = new THREE.MeshPhongMaterial({
opacity:1, map:texture });
materialfront.transparent = true ;
materialfront.side = THREE.FrontSide ;
frontthing = new THREE.Mesh( geometry, materialfront );
frontthing.renderOrder = 2;
scene.add(frontthing);
然后
var texture2 = texture.clone();
texture2.needsUpdate = true;
var materialBack = new THREE.MeshPhongMaterial({
opacity:0.1, map: texture2})
materialBack.transparent = true ;
materialBack.side = THREE.BackSide;
backthing = new THREE.Mesh( geometryback, materialBack );
backthing.renderOrder = 1;
group.add(backthing);
我的材质贴图是透明的.png纹理。
我无法解释为什么其他建议的方法对我不起作用,但是我希望上面的方法可以对处于相似位置的某些人有所帮助。
答案 3 :(得分:0)
你所要做的就是:
mesh.transparent = true; //This must be set to true
mesh.opacity = 1; //Value from 0-1
然后你可以用 dat.GUI 和其他什么来控制这些属性
-阿奈塔尔