ThreeJS and the transparent problem

I know there are many issues about the transparent problem in the internet, but I need a solution.

The Problem:
I create some mesh objects with a simple transparent material. And when the objects are overlaps, sometimes (ok always) when I rotate the camera the objects not displaying correct.

The mesh objects has 2-4 Units space between. When I increase the space it works, but I must rotate the objects. Means i cannot increase the space between the objects.

Here some informations I test it:

  • depthWrite: false
  • depthTest: false
  • transparent: true
  • alphaTest: 0.5
  • nearPlane : 100
  • renderOrder (It works, but is has many other problems. Not the correct solution)
  • many more…
    (From many issues I found in the internet)

I use threejs v101 (Same Problems with the newest v111).

See attachment (The close button, the texte are and the big button has all transparent (alpha) values. And when i rotate the camera, sometime it looks correct and often damaged.

Thanks for helping :slight_smile:

One way to solve such depth sorting issues it to explicitly define the rendering order via Object3D.renderOrder. It seems from your listing you have not tested this approach so far. It would be interesting to know if using Object3D.renderOrder helps in this use case.

Yes, i test it with renderOrder too. And it works (for only this case)!
But the problem is then is the z-index ignored. Means when I have e.g. two complex objects one above the other, the view is incorrect. Because not the render order is relevant for 3D, but the z-index.
That’s not what I can use meaningfully.

I’am not sure is it ThreeJS or the webGL render that makes the problem. I read the WebGL Rednerer renders first objects who has no transparent, and then all with transparent (very stupid :dizzy_face:)

I’m afraid this is the correct approach. You want to render opaque objects first from front to back in order to avoid overdraw as much as possible. Then you render transparent objects from back to front in order to do the blending right.

BTW: Instead of sharing animated gifs or videos, it’s in general easier to help users when live examples are shared.

/cc

You generally should not write depth for transparent objects. depthWrite = true is why you get some blue (sky) pixels sometimes. It happens when those objects are rendered before the gray plane - which makes me wonder if those objects are really transparent, because then they should be rendered after the plane and it should work… Or did you also set the plane as transparent? A live example would really help.

Try:

  • depthWrite: false
  • depthTest: true

Here is my use case on codepen: When you rotate to left then the transparent background is the background from behind the second mesh. If i use depthTest/depthWrite the image is hidden.
What can I do?

Note: I cannot use renderOrder! It ignores the z-axes from the other objects in my project.

^For that particular example, and cases where the texture has no semitransparency (everything is either 0% opaque or 100% opaque) you will want to use alphaTest without setting transparent=true. Then you shouldn’t have to worry about depth settings or sorting issues.

It will also work if you remove transparent=true on the crate (which does not have any transparent part, as I can see it).

An option you can try is polygonOffset on the front object:

material.polygonOffset = true;
material.polygonOffsetFactor = -4;

That should respect z-order. I believe the offset factor is how much closer to the camera to move those fragments in the depth buffer.

The offset factor can be set as high as you want. I’m not sure what the units are but normally a small amount should be enough.

The polygonOffset we tried too, but on iOS it isn’t shown correct. It’s gray over gray.

When two transparent planes are close and one is both in front and behind the other, it’s really hard to get them to behave as intended. It seems as though the fragments on a given primitive are being assigned the same depth value (e.g the value of the fragment closest to the camera). The plane in front disappears instantly, it doesn’t gradually get clipped behind the other when rotating the planes.

If it was possible to implement, what would be useful is a more granular form of renderOrder like render order groups to be able to say which objects should definitely be in front of others but otherwise use the depth buffer.

A material could perhaps have a renderOrderGroups property:

decalsLayer1.material.renderOrderGroups = {decalsGroup:1}
decalsLayer2.material.renderOrderGroups = {decalsGroup:2}
object.material.renderOrderGroups = {decalsGroup:0}

If a material being rendered had a renderOrderGroup and a fragment of a material that had the same group had a renderOrder above it, it would put that one in front. Then render according to the depth buffer.

It seems like it should be possible to fix this problem automatically though. Shaders can calculate a floating point camera-space z-value of a fragment and use that to determine render order. Even if it was too slow to do on a whole scene, it should be fast enough for selected objects.

1 Like

thanks, @ [donmccurdy], but i have semitransparency. The png I used in the demo on codepen only has no semitransparency. But in my reale case i use it.

I updated the codepen demo with an png with alpha.

polygonOffset not running. Can you modify my codepen, that it works?

I couldn’t get polygonOffset to work on that example. Only renderOrder and setting the background object to non-transparent worked but neither of those are ideal. polygonOffset should work with a large enough value.

Looking at the sorting functions in ThreeJS, it could be to do with the z value here swapping when rotating the camera. The relevant functions are WebGLRenderList.sort() and WebGLPrograms.painterSortStable and reversePainterSortStable.

It looks like those tests override some of the depth buffer. It checks renderlist.groupOrder, then renderOrder, then z value (I assume camera space z-value of the object), then lastly the id, which would be the order the objects are added to the scene.

If you are ok with modifying the ThreeJS library, you could add a custom condition in that sort function before the others that checks a custom value in the render objects and you’d set these values in the game like the renderOrderGroups I mentioned above.