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

发布于 2025-01-10 11:49:15 字数 4030 浏览 0 评论 0原文

我使用以下代码来创建这个 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 个透明且彩色的窗玻璃制成的立方体。请参阅此示例:对于 6 个面中的每一个,我想要的结果都是示例 1,但现在它就像示例 2。

在此处输入图像描述

更新 我尝试创建单独的“窗格”。然而,这种行为并不像我预期的那样。

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

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);

然后将所有三个平面添加到线框。

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

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

现在我可以看到将窗格“堆叠”在彼此之上的效果,但它并不是应有的样子。

输入图片这里的描述

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

输入图片此处描述

编辑

当透明窗格从观看方向重叠时,透明度似乎可以完美发挥作用。然而,当玻璃相交时,它就会破裂。

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

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>

编辑

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

输入图片此处描述

I am using the following code to create this 3D transparent cube.

// 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 );

enter image description here

As can been seen, the cube appears as a single volume that is transparent. Instead, I would want to create a hollow cube with 6 transparent faces. Think of a cube made out of 6 transparent and colored window-panes. See this example: my desired result would be example 1 for each of the 6 faces, but now it is like example 2.

enter image description here

Update
I tried to create individual 'window panes'. However the behavior is not as I would expect.

I create individual panes like so:

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);

And then I add all three planes to wireframe.

Then I rotate them a little, so they intersect at different orientations.

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

Now I can see the effect of 'stacking' the panes on top of each other, however it is not as it should be.

enter image description here

I would instead expect something like this based on real physics (made with terrible paint-skills). That is, the color depends on the number of overlapping panes.

enter image description here

EDIT

When transparent panes overlap from the viewing direciton, transparancy appears to work perfectly. However, when the panes intersect it breaks.

Here I have copied the snipped provided by @Anye and added one.rotation.y = Math.PI * 0.5 and commented out two.position.set(0.5, 0.5, 0.5); so that the panes intersect.

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>

EDIT

The snipped looks pretty good; it clearly shows a different color where the panes overlap. However, it does not show this everywhere. See this image. The left is what the snippet generates, the right is what it should look like. Only the portion of overlap that is in front of the intersection shows the discoloration, while the section behind the intersection should, but does not show discoloration.

enter image description here

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

放低过去 2025-01-17 11:49:15

您可能想看看CSG,构造实体几何。使用 CSG,您可以使用布尔值在原始立方体中创建一个洞。首先,您可以查看 这个快速教程。以下是您可以使用 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

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

工作正常

You might want to take a look at 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 quick tutorial. Below are some examples of what you can do with CSG.

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

=>

PICTURE

CSG, though, has some limitations, since it isn't made specifically for three.js. A cheap alternative would be to create six individual translucent panes, and format them to create a cube. Then you could group them:

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

Update

Something may be wrong with your code, which is why it isn't shading accordingly for you. See this minimal example, which shows how the panes shade appropriately based on overlaps.

Update 2

I updated the snippet so the 2 panes aren't touching at all... I am still able to see the shading. Maybe if you were to try to reproduce this example?

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>

Update 3

Below is a screenshot of what I see in your snippet... Seems to be working fine...

Working fine

洋洋洒洒 2025-01-17 11:49:15

你正在经历我第一个令人头疼的事情:
ShaderMaterial 透明度

正如该问题的答案所示, Three.js 透明度系统执行依赖于顺序的透明度。通常,它将采用最接近相机的物体(通过网格位置),但由于所有平面都以同一点为中心,因此没有赢家,因此您会得到一些奇怪的透明效果。

如果您将平面网格移出以形成盒子的实际侧面,那么您应该会看到您正在寻找的效果。但这不会是奇怪的透明效果的结束,您需要实现自己的 Order - 独立透明度(或找到一个可以为您完成此操作的扩展库)以实现更物理精确的透明度效果。

You're experiencing one of my first head-scratchers:
ShaderMaterial transparency

As the answer to that question states, the three.js transparency system performs order-dependent transparency. Normally, it will take whichever object is closest to the camera (by mesh position), but because all of your planes are centered at the same point, there is no winner, so you get some strange transparency effects.

If you move the plane meshes out to form the actual sides of the box, then you should see the effect you're looking for. But that won't be the end of strange transparency effects, And you would need to implement your own Order-Independent Transparency (or find an extension library that does it for you) to achieve more physically-accurate transparency effects.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文