1

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

Mitchell van Zuylen
  • 3,905
  • 4
  • 27
  • 64
  • My answer has been edited. – Halo Mar 01 '22 at 21:51
  • You have changed your implementation from creating a transparent cube to creating three intersecting planes. Which is the end result you are seeking with this question? – TheJim01 Mar 03 '22 at 17:15
  • I'm seeking a method of creating transparency that results in correct variations in color. I change my implementation to highlight problems with the current best solution – Mitchell van Zuylen Mar 04 '22 at 00:18

2 Answers2

1

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

Halo
  • 1,730
  • 1
  • 8
  • 31
  • I created individual panes as suggested but the behavior is a little bit odd. I have updated my original question. – Mitchell van Zuylen Mar 01 '22 at 09:08
  • @MitchellvanZuylen, I have updated my answer for some more detail. If you run the snippet, you can see the shading is working on overlapped areas. I would recommend using the `Pane()` function I created to create each pane. If you wanted to do this multiple times, then you could expand the function to `Pane(color, opacity, size)` so it can adapt to any sized cube that you would want. – Halo Mar 01 '22 at 13:57
  • Thank you for the edits. This works, but only if the transparent panes overlap in all dimensions. If they only overlap from the viewing direction (e.g., adding `one.position.z = 0.2`) the transparency no longer stacks and both panes have the exact same color. – Mitchell van Zuylen Mar 02 '22 at 01:23
  • I updated my answer again. Now, the panes aren't touching/overlapping in ANY dimensions, yet I am still able to see the shading in action. Maybe if you were to reproduce my example it would work for you? – Halo Mar 02 '22 at 13:23
  • Hmm, that's odd. The current snippet appears to be the desired result. I had tried to reproduce your example but adding `one.position.z = 0.2` did not show the desired result. I don't see why the current should work while `one.position.z = 0.2` does not. I assume I made a mistake somewhere. I shall try this first thing in the morning tomorrow. Thank you for your continued assistance, by the way. – Mitchell van Zuylen Mar 02 '22 at 13:37
  • Ofcourse! But really, that is strange. I just added `one.position.z = 0.2` to the snippet and it works fine for me! Try the snippet now – Halo Mar 02 '22 at 14:59
  • 1
    Adding `one.position.z = 0.2` now does work. I will chalk it up to some trivial error on my part. However, nevertheless, when the panes intersect the transparency is still not successful. I shall an add a snipped to my question showing the problem. – Mitchell van Zuylen Mar 03 '22 at 06:20
  • Wow. This is really strange indeed. I updated my answer to show a screenshot of what I see in your snippet, and it seems to be working fine for me! – Halo Mar 03 '22 at 13:13
  • 1
    It's looking better, indeed, but it's still not correct. If you uncomment `two.position.set(0.5, 0.5, 0.5);` it does look correct, but with that line the transparency is still wrong. I'll add an image to illustrate. – Mitchell van Zuylen Mar 04 '22 at 00:41
  • 1
    Ah, I see what's wrong. Add `material.depthWrite = false` to the `Pane()` function, like I did to the snippet. As you can see, perfection! – Halo Mar 04 '22 at 13:15
  • 1
    @MitchellvanZuylen, That one line is everything. I think that should do it! – Halo Mar 05 '22 at 00:57
  • 1
    That did it! Thank you! You've gone out of your way to help. Very much appreciated! – Mitchell van Zuylen Mar 05 '22 at 01:31
  • Of course! Happy to help! – Halo Mar 05 '22 at 16:18
0

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.

TheJim01
  • 8,411
  • 1
  • 30
  • 54
  • Did you see my answer? The shading seems to work fine, and I'm using `MeshBasicMaterial`... – Halo Mar 01 '22 at 21:50
  • @Anye I did, but I interpreted the question differently. From my reading, OP is simply trying to understand why transparency blending isn't working as expected. – TheJim01 Mar 01 '22 at 22:12
  • I see. That makes more sense – Halo Mar 01 '22 at 22:15
  • 1
    Depth peeling is a (fairly complicated) technique that can be used to create better transparency rendering. See e.g. https://discourse.threejs.org/t/depth-peel-and-transparency/5365 – Diarmid Mackenzie Mar 04 '22 at 22:09