24

When you have two planes in Three.js / WebGL and one or both of them are transparent, sometimes the plane behind will be hidden by the transparent plane above. Why is this?

Wilt
  • 41,477
  • 12
  • 152
  • 203
MaiaVictor
  • 51,090
  • 44
  • 144
  • 286
  • 1
    [related](https://stackoverflow.com/questions/4127242/opengl-rendering-two-transparent-planes-intersecting-each-other-impossible-or) – gman Oct 07 '20 at 06:35

7 Answers7

25

Setting the depthWrite property to false solved my issue.

new THREE.MeshBasicMaterial({ 
    opacity: 0.25, 
    transparent: true, 
    side: THREE.DoubleSide, 
    depthWrite: false
});
Wilt
  • 41,477
  • 12
  • 152
  • 203
24

Let's say that you are using some transparent *.png image. Then this would help:

new THREE.MeshBasicMaterial( { side:THREE.BackSide,map:texture, depthWrite: false, depthTest: false });
Alex Under
  • 1,406
  • 1
  • 12
  • 25
21

Try adding alphaTest: 0.5 to the material.

mrdoob
  • 19,334
  • 4
  • 63
  • 62
  • Have tried that, didn't work :/ I guess it is an webgl problem so I have to redesign how my game is displaying planes. It'll look a bit hacked though. – MaiaVictor Jul 04 '12 at 01:45
  • 1
    I have a plane with a canvas-based texture mapped to it displaying text. At certain angles/positions I was having issues with the transparency of the texture material and this fixed it, so thanks! – plyawn May 28 '13 at 21:04
  • 3
    Note: `alphaTest` is a treshold that will make semi-transparent areas in textures either fully opaque (when pixel opacity > treshold) or fully transparent (when pixel opacity < treshold). This may result in ugly edges. – Blaise Oct 16 '14 at 12:12
15

This is not a bug, it's just how OpenGL (and, hence, WebGL) works. Transparent surfaces don't play well with the z-buffer, and as such must be manually sorted and rendered back-to-front. Three JS is attempting to do this for you (which is why the problem goes away when you set the X value > 0) but cannot robustly handle the case of intersecting geometry like you're showing.

I've explained the issue more in-depth in a different SO question, so you may want to reference that.

Community
  • 1
  • 1
Toji
  • 33,927
  • 22
  • 105
  • 115
  • 1
    Thanks for the answer but so you are telling me it's better just to redesign my game or is a solution pursuitable hacking Three.JS? – MaiaVictor Jun 22 '12 at 23:40
  • 1
    @Toji the question you refer to is not dealing with exactly the same issue. In the other question it is only part of the object/texture that is transparent where in this question the whole plane is transparent and this is easily solvable by simply deactivating the `depthWrite` totally for the transparent plane. – Wilt Jan 14 '16 at 12:58
6

fwiw, if you have lots of parallel planes (can't see your sample, google can't resolve your domain), it's easy to keep them sorted along the perpendicular axis. For a list of planes [A B C D] the order-to-draw will be either [A B C D] or [D C B A] and nothing else! So there need not be a performance hit from sorting. Just keep them in order as you go.

bjorke
  • 3,295
  • 1
  • 16
  • 20
  • the problem is that I have some planes that should be rendered crossing the others. Like if they were 3d flat swords. Might I ask, how did you find that question so long after it was asked and how did 4 upvoters come here? – MaiaVictor Oct 25 '12 at 16:47
  • Your questions abut people's behavior I cannot answer :) However, if you have polygons that cross one another, there is no easy correct solution other than per-fragment ones like K-Buffers or the original A-Buffer algorithm, unless you specifically police the triangle intersections and tesselate them on the fly. – bjorke Jul 16 '15 at 02:04
1

Setting Mesh renderOrder to solve my problem, below is my code, you can modify node.renderOrder value:

loader.load(model_url, (gltf)=>{
            let scene = gltf.scene

            scene.traverse((node)=>{

                if(node.isMesh){
                    node.material.transparent = true

                    if(node.name === 'car_windows'){
                        node.material.opacity = 0.4
                        node.material.side = 0
                        node.renderOrder = 110
                    }

                    if(node.name === 'car_body'){
                        node.material.opacity = 0.4
                        node.renderOrder = 100
                    }

                    if(node.name === 'car_seats'){
                        node.material.opacity = .5
                        node.renderOrder = 90
                    }

                    mesh_arr.push(node)
                    mesh_objs[node.name] = node
                }
            })
})
JK Bi
  • 407
  • 4
  • 9
  • 1
    Although, the answer doesn't include any examples and not really descriptive, this is the only option worked for me, thanks! – Footniko Jul 14 '20 at 14:27
  • @Footniko Sorry for no example code, I've added it, hope it help. :) – JK Bi Jul 16 '20 at 10:54
1

This worked for me, don't use the literal boolean value true try using 1 instead.

object3d.material.transparent = 1; 
David Clews
  • 795
  • 6
  • 14