ThreeJS - 创建表面透明的立方体而不是立方体体积

ThreeJS - Create cube where the surfaces are transparent instead of the cube volume

我正在使用以下代码创建这个 3D 透明立方体。

// Create the cube itself
const cubeGeom = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( {color: 0x00ff00,  opacity:0.4, transparent:true});
const cube = new THREE.Mesh( cubeGeom, material );


// Also add a wireframe to the cube to better see the depth
const _wireframe = new THREE.EdgesGeometry( cubeGeom ); // or WireframeGeometry( geometry )
const wireframe = new THREE.LineSegments( _wireframe);

// Rotate it a little for a better vantage point
cube.rotation.set(0.2, -0.2, -0.1)
wireframe.rotation.set(0.2, -0.2, -0.1)

// add to scene
scene.add( cube ) 
scene.add( wireframe );

可以看出,立方体显示为透明的单个体积。相反,我想创建一个具有 6 个透明面的空心立方体。想象一个由 6 个透明和彩色 window 窗格组成的立方体。看这个例子:我想要的结果是 6 个面孔中的每一个的示例 1,但现在它就像示例 2。

更新 我试图创建个人 'window panes'。然而,行为并不像我预期的那样。

我像这样创建单独的窗格:

geometry = new THREE.PlaneGeometry( 1, 1 );
material = new THREE.MeshBasicMaterial( {color: 0x00ff00,  side: THREE.DoubleSide, transparent:true, opacity:0.2});

planeX = new THREE.Mesh( geometry, material);
planeY = new THREE.Mesh( geometry, material);
planeZ = new THREE.Mesh( geometry, material);

然后我将所有三个平面添加到 wireframe

然后我稍微旋转它们,使它们以不同的方向相交。

const RAD_TO_DEG = Math.PI * 2 / 360;
planeX.rotation.y = RAD_TO_DEG * 90
planeY.rotation.x = RAD_TO_DEG * 90

现在我可以看到 'stacking' 窗格相互重叠的效果,但它不是应该的。

相反,我会期待这样的基于真实物理学的东西(用糟糕的绘画技巧制作)。也就是说,颜色取决于重叠窗格的数量。

编辑

当透明窗格从观看方向重叠时,透明度似乎完美无缺。但是,当窗格相交时它会破裂。

这里我复制了@Anye 提供的片段并添加了 one.rotation.y = Math.PI * 0.5 并注释掉 two.position.set(0.5, 0.5, 0.5); 以便窗格相交。

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

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

var cube = new THREE.Group();
one = new Pane();
two = new Pane();

one.rotation.y = Math.PI * 0.5

one.position.z = 0.2;
// two.position.set(0.5, 0.5, 0.5);

cube.add(one);
cube.add(two);

cube.rotation.set(Math.PI / 4, Math.PI / 4, Math.PI / 4);
scene.add(cube);

function Pane() {
  let geometry = new THREE.PlaneGeometry(1, 1);
  let material = new THREE.MeshBasicMaterial({color:0x00ff00, transparent: true, opacity: 0.4});
  let mesh = new THREE.Mesh(geometry, material);
  
  return mesh;
}

camera.position.z = 2;

var animate = function () {
    requestAnimationFrame( animate );
    renderer.render(scene, camera);
};

animate();
body {
  margin: 0;
  overflow: hidden;
}

canvas {
  width: 640px;
  height: 360px;
}
<html>
<head>
    <title>Demo</title>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script>
</body>
</html>

编辑

剪下来的看起来还不错;它清楚地显示了窗格重叠的不同颜色。但是,它并没有在所有地方显示这一点。看到这张图片。左边是代码片段生成的内容,右边是它应该看起来的样子。只有交叉点前面的重叠部分显示变色,而交叉点后面的部分应该,但没有显示变色。

您可能想看看 CSG, Constructive Solid Geometry. With CSG, you can create a hole in your original cube using a boolean. To start, you could take a look at this 快速教程。以下是您可以使用 CSG 执行的一些示例。

var cube = new CSG.cube();
var sphere = CSG.sphere({radius: 1.3, stacks: 16});
var geometry = cube.subtract(sphere);

=>

但是,

CSG 有一些限制,因为它不是专门为 three.js 设计的。一种廉价的替代方法是创建六个单独的半透明窗格,并将它们格式化以创建一个立方体。然后你可以将它们分组:

var group = new THREE.Group();
group.add(pane1);
group.add(pane2);
group.add(pane3);
group.add(pane4);
group.add(pane5);
group.add(pane6);

更新

您的代码可能有问题,这就是为什么它没有为您相应地着色。请参阅这个最小的示例,它显示了窗格如何根据重叠适当地着色。

更新 2

我更新了代码片段,因此 2 个窗格根本没有接触...我仍然能够看到阴影。也许您要尝试重现此示例?

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

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

var cube = new THREE.Group();
one = new Pane();
two = new Pane();

one.rotation.y = Math.PI * 0.5;
one.position.z = 0.2;

cube.add(one);
cube.add(two);

cube.rotation.set(Math.PI / 4, Math.PI / 4, Math.PI / 4);
scene.add(cube);

function Pane() {
  let geometry = new THREE.PlaneGeometry(1, 1);
  let material = new THREE.MeshBasicMaterial({color:0x00ff00, transparent: true, opacity: 0.4});
  material.depthWrite = false
  let mesh = new THREE.Mesh(geometry, material);
  
  return mesh;
}

camera.position.z = 2;

var animate = function () {
    requestAnimationFrame( animate );
    renderer.render(scene, camera);
};

animate();
body {
  margin: 0;
  overflow: hidden;
}

canvas {
  width: 640px;
  height: 360px;
}
<html>
<head>
    <title>Demo</title>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script>
</body>
</html>

更新 3

下面是我在您的代码段中看到的屏幕截图...似乎工作正常...

您正在体验我的第一个 head-scratchers: ShaderMaterial transparency

正如该问题的答案所述,three.js 透明度系统执行 order-dependent 透明度。通常,它会选择距离相机最近的物体(通过网格位置),但是因为所有平面都以同一点为中心,所以没有赢家,所以你会得到一些奇怪的透明效果。

如果您将平面网格移出以形成盒子的实际侧面,那么您应该会看到您正在寻找的效果。但这不会是奇怪的透明效果的终结,您需要实现自己的 Order-Independent Transparency(或找到一个为您做的扩展库)以实现更多 physically-accurate 透明效果。