Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

threejs does not handle transparency correctly #31600

Closed
sagetrac-tmonteil mannequin opened this issue Apr 3, 2021 · 25 comments
Closed

threejs does not handle transparency correctly #31600

sagetrac-tmonteil mannequin opened this issue Apr 3, 2021 · 25 comments

Comments

@sagetrac-tmonteil
Copy link
Mannequin

sagetrac-tmonteil mannequin commented Apr 3, 2021

Here is an example to reproduce it yourself:

theta,z=var('theta,z')
cb = cylindrical_plot3d(2,(theta,0,2*pi),(z,-5,5), alpha=0.3, color='blue')
cr = cylindrical_plot3d(1,(theta,0,2*pi),(z,-7,7), alpha=0.3, color='red')
cv = cylindrical_plot3d(0.5,(theta,0,2*pi),(z,-10,10), alpha=0.5, color='green')
p = cb + cr + cv

Compare

p.show(aspect_ratio=1)

with

p.show(viewer='jmol', aspect_ratio=1)

As you can see, with threejs, when you rotate the picture, on some angles, the transparency is correct, but sometimes there is no transparency at all, and sometimes there is a wrong transparency, see the attached pictures.



CC: @egourgoulhon @jcamp0x2a @paulmasson @slel @guenterrote

Component: graphics

Author: Joshua Campbell

Branch/Commit: 515bcf4

Reviewer: Matthias Koeppe

Issue created by migration from https://trac.sagemath.org/ticket/31600

@sagetrac-tmonteil sagetrac-tmonteil mannequin added this to the sage-9.4 milestone Apr 3, 2021
@sagetrac-tmonteil
Copy link
Mannequin Author

sagetrac-tmonteil mannequin commented Apr 3, 2021

Attachment: threejs.good.transparency.png

@sagetrac-tmonteil
Copy link
Mannequin Author

sagetrac-tmonteil mannequin commented Apr 3, 2021

Attachment: threejs.no.transparency.png

@sagetrac-tmonteil

This comment has been minimized.

@sagetrac-tmonteil
Copy link
Mannequin Author

sagetrac-tmonteil mannequin commented Apr 3, 2021

comment:1

Attachment: threejs.wrong.transparency.png

@jcamp0x2a
Copy link
Mannequin

jcamp0x2a mannequin commented Apr 3, 2021

Commit: f52b0ad

@jcamp0x2a
Copy link
Mannequin

jcamp0x2a mannequin commented Apr 3, 2021

Branch: u/gh-jcamp0x2a/31600-transparency

@jcamp0x2a
Copy link
Mannequin

jcamp0x2a mannequin commented Apr 3, 2021

comment:2

I've used the render_order option in the past to help with nested transparent surfaces. So you'd make sure your cylinders are rendered innermost to outermost (green then red then blue):

theta,z=var('theta,z')
cb = cylindrical_plot3d(2,(theta,0,2*pi),(z,-5,5), alpha=0.3, color='blue', render_order=-1)
cr = cylindrical_plot3d(1,(theta,0,2*pi),(z,-7,7), alpha=0.3, color='red', render_order=-2)
cv = cylindrical_plot3d(0.5,(theta,0,2*pi),(z,-10,10), alpha=0.5, color='green', render_order=-3)
p = cb + cr + cv
show(p, viewer='threejs')

You'll still see issues with self-transparency due to the order in which the faces of each cylinder are drawn. Also, adjusting the render order can conflict with the text sprites, so I try to use negative values.


I don't know of a way to automatically set the render order or that it'd even be possible for some arbitrary set of transparent surfaces.

But for the self-transparency and text sprite problems I mentioned, perhaps it would be better to just disable writing to the depth-buffer entirely for transparent surfaces and text? I'm trying to think through whether this might result in other graphical glitches itself, since it seems so straight-forward that why wouldn't Three.js do this automatically when you make a material transparent?

I've pushed a commit with this change and will attach an example with the same cylinders plus an intersecting opaque surface for comparison.


New commits:

f52b0adDisable depth-buffer write for text and transparent surfaces

@jcamp0x2a
Copy link
Mannequin

jcamp0x2a mannequin commented Apr 3, 2021

Attachment: noDepthWrite.html.gz

Example with depth write of text and transparent surfaces disabled

@guenterrote
Copy link
Mannequin

guenterrote mannequin commented Apr 4, 2021

comment:3

Replying to @jcamp0x2a:

But for the self-transparency and text sprite problems I mentioned, perhaps it would be better to just disable writing to the depth-buffer entirely for transparent surfaces and text? I'm trying to think through whether this might result in other graphical glitches itself, since it seems so straight-forward that why wouldn't Three.js do this automatically when you make a material transparent?

It might be for performance reasons. On such a small scene the difference might not be
noticeable. (By the way, I noticed that on my little desktop, the noDepthWrite.html example ignites the cooling fan one second after I start rotating the scene, when viewing with the Chromium browser. But the situation is the same with the "normal" sage graphics output for this scene).

It is known that, in principle, a depth buffer cannot handle multiple transparent surfaces correctly unless they come in a consistent order (front-to-back or back-to-front).

Is there some documentation on what threejs does when setting depthWrite: false

Maybe the behaviour should be controllable by an option by the user.

I've pushed a commit with this change and will attach an example with the same cylinders plus an intersecting opaque surface for comparison.

The example looks good!

@jcamp0x2a
Copy link
Mannequin

jcamp0x2a mannequin commented Apr 4, 2021

comment:4

Replying to @guenterrote:

Is there some documentation on what threejs does when setting depthWrite: false

The doc for it is basic, but it looks like it's only ever passed along as-is to WebGL's glDepthMask prior to rendering each object. A bit of the call stack in three.js:

Maybe the behaviour should be controllable by an option by the user.

Would want to default to disabling depth write I assume? So the user can toggle on the depth write only if they're having performance problems with lots of transparent surfaces. I'm still struggling to come up a non-performance reason for turning it on, though I have a limited imagination ;)

@paulmasson
Copy link
Mannequin

paulmasson mannequin commented Apr 4, 2021

comment:5

Replying to @jcamp0x2a:

Replying to @guenterrote:

Is there some documentation on what threejs does when setting depthWrite: false

The doc for it is basic, but it looks like it's only ever passed along as-is to WebGL's glDepthMask prior to rendering each object. A bit of the call stack in three.js:

Maybe the behaviour should be controllable by an option by the user.

Would want to default to disabling depth write I assume? So the user can toggle on the depth write only if they're having performance problems with lots of transparent surfaces. I'm still struggling to come up a non-performance reason for turning it on, though I have a limited imagination ;)

Joshua, why don’t you ask these questions in the Three.js forum: https://discourse.threejs.org/

@jcamp0x2a
Copy link
Mannequin

jcamp0x2a mannequin commented Apr 4, 2021

comment:6

Replying to @paulmasson:

Joshua, why don’t you ask these questions in the Three.js forum: https://discourse.threejs.org/

Hi Paul! Thanks for the link to the forum. I'll try to keep it in mind as an additional resource for questions in the future.

It looks like something similar to this issue been discussed there quite a bit already. I found ThreeJS and the transparent problem to be an interesting read. The rule of thumb indeed seems to be to disable depth write for transparent objects. I also found GLTFLoader: Use depthWrite=false for transparent materials which seems to support that and makes up my lack of imagination with some links to cases where you'd want the depth-write to remain enabled.

There's also a few interesting techniques mentioned in there like screendoor transparency and "Weighted Blended Order-Independent Transparency" that I didn't know about and will have to read up on now. :)

@guenterrote
Copy link
Mannequin

guenterrote mannequin commented Apr 5, 2021

comment:7

I checked those references, and I must correct myself. This has no effect on performance. (I thought the use of the framebuffer is completely abandoned when setting depthWrite:false.)

The effect of depthWrite:false is that the order of the transparent surfaces is ignored: A blue transparent surface in front of a red transparent surface will tint the objects behind them in the same way as in the opposite order. The difference would probably only be noticeable if the transparent surfaces have some shinyness of their own.

For example, the top of the cylinders gives no visual clue whether you can see "into the pipe" from above or whether you look "up" to the pipe from below. (If you view it with tachyon, it really looks much better. Look at the transition between the green and red cylinder in the upper half and in the lower half)

Another case is when you have two transparent surfaces (plane) that intersect. Then you probably would like to see the difference, which one is in front and in particular where they intersect and change the order. But depthWrite:true also does not resolve such a case correctly.

I would advocate to switch to disabling depth-write by default.

@guenterrote
Copy link
Mannequin

guenterrote mannequin commented Apr 5, 2021

comment:8

Here is an example where depthWrite:false would eliminate a visual distinction:

sage: var("u v")                                                                                                        
sage: red1 = parametric_plot3d([u,v,0.1-u-v],(u,0,2),(v,0,2),alpha=0.6, color='red',render_order=1)                     
sage: green= parametric_plot3d([u,v,0.2-u-v],(u,1,3),(v,1,3),alpha=0.6, color='green',render_order=2)                  
sage: red2 = parametric_plot3d([u,v,0.3-u-v],(u,0,2),(v,2.2,4.2),alpha=0.6, color='red',render_order=3)                 
sage: (red1+green+red2).show()                                                                                          

But surprise when looking from behind!
(Why did I find no documentation on the render_order option?)

@jcamp0x2a
Copy link
Mannequin

jcamp0x2a mannequin commented Apr 6, 2021

comment:9

Replying to @guenterrote:

(If you view it with tachyon, it really looks much better. Look at the transition between the green and red cylinder in the upper half and in the lower half)

Ray-tracing is the future :)

@jcamp0x2a
Copy link
Mannequin

jcamp0x2a mannequin commented Apr 6, 2021

comment:10

Replying to @guenterrote:

(Why did I find no documentation on the render_order option?)

It's listed in the reference manual for the Three.js viewer: https://doc.sagemath.org/html/en/reference/plot3d/threejs.html

Cool example :)

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 8, 2021

Branch pushed to git repo; I updated commit sha1. This was a forced push. New commits:

7f2eafeDisable depth-buffer write for text and transparent surfaces
ec7cc0cAdd option to disable depth-buffer write for surfaces

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 8, 2021

Changed commit from f52b0ad to ec7cc0c

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 8, 2021

Changed commit from ec7cc0c to 515bcf4

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 8, 2021

Branch pushed to git repo; I updated commit sha1. New commits:

515bcf4Update three.js viewer doc with depth_write option

@jcamp0x2a
Copy link
Mannequin

jcamp0x2a mannequin commented Apr 8, 2021

Author: Joshua Campbell

@jcamp0x2a
Copy link
Mannequin

jcamp0x2a mannequin commented Apr 8, 2021

comment:13

I've added an option and documentation for controlling whether to do the depth-write for surfaces.

@mkoeppe
Copy link
Contributor

mkoeppe commented Apr 15, 2021

comment:15

Just ran into this problem myself. This fixes it.

@mkoeppe
Copy link
Contributor

mkoeppe commented Apr 15, 2021

Reviewer: Matthias Koeppe

@vbraun
Copy link
Member

vbraun commented Apr 26, 2021

Changed branch from u/gh-jcamp0x2a/31600-transparency to 515bcf4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants