p5.js
p5.js copied to clipboard
Fix transparency ordering in WebGL
Nature of issue?
- [X] Found a bug
- [ ] Existing feature enhancement
- [ ] New feature request
Most appropriate sub-area of p5.js?
- [ ] Color
- [ ] Core/Environment/Rendering
- [ ] Data
- [ ] Events
- [ ] Image
- [ ] IO
- [ ] Math
- [ ] Typography
- [ ] Utilities
- [X] WebGL
- [ ] Other (specify if possible)
Which platform were you using when you encountered this?
- [ ] Mobile/Tablet (touch devices)
- [X] Desktop/Laptop
- [ ] Others (specify if possible)
Details about the bug:
- p5.js version: 0.8.0
- Web browser and version: Chrome 74.0.3729.131
- Operating System: Windows 10
- Steps to reproduce this:
setAttributes('depth', false);
doesnt seen to be working:
setAttributes('depth', false);
createCanvas(windowWidth, windowHeight, WEBGL);
Work around:
let renderer = createCanvas(windowWidth, windowHeight, WEBGL);
renderer.drawingContext.disable(renderer.drawingContext.DEPTH_TEST);
Welcome! 👋 Thanks for opening your first issue here! And to ensure the community is able to respond to your issue, be sure to follow the issue template if you haven't already.
Actually, by disabling the Depth, it makes the transparency from images work(the example above).
But when I draw some semi-transparent shapes in between the images, the depth is not right:
I am trying to accomplish a fog effect...
@Vamoss Can you provide a code which reproduces this bug?
Hi @vedhant, sorry for the late answer, I was not notified of your answer by mail... Here is the working code: https://www.openprocessing.org/sketch/709566
Line 7 did the trick:
renderer.drawingContext.disable(renderer.drawingContext.DEPTH_TEST);
Is there ever a case when a user would want there to be no depth buffer and still enable depth testing? If not, we should only enable DEPTH_TEST when the 'depth' attribute is true.
Hi @stalgiag. Again I was not notified about an answer in this topic, sorry for the delay.
Once in a while I got this issue again.
Here I am using 1.2 version:

renderer.drawingContext.disable(renderer.drawingContext.DEPTH_TEST);
result:

I don't think it is a DEPTH_TEST issue, seems like a transparency related issue.
Link: https://www.openprocessing.org/sketch/913474
I just discovered there is a name to solve it Order independent transparency.
@tsherif implemented it here https://tsherif.github.io/webgl2examples/oit-dual-depth-peeling.html
Unfortunately, it goes beyond my knowledge.
Hi!
I don't now anything about p5, but I can provide the maintainers with some info that might be useful: The problem with alpha masked objects (i.e. when you're hiding pixels by setting alpha to 0, but other pixels on the same object are opaque) is that the masked pixels still write to the depth buffer. However, if you turn off depth testing, the opaque parts won't occlude each other.
There are a couple of solutions I can imagine for this. Depth peeling, mentioned by @Vamoss, might help but is probably overkill. One option would be to explicitly have an alpha cutout option that instead of rendering fragments as transparent, discards them in your shader, e.g.
if (color.a < 0.5) {
discard;
}
Caveat is that this might make the edges look rough.
For the cylinder scene by @Vamoss where it's front-facing surfaces occluding back-facing surfaces, you could draw the surfaces in two passes using face culling to turn off one side or the other. In pure WebGL, this would look like:
gl.enable(gl.CULL_FACE);
gl.cullFace(gl.FRONT);
// Draw
gl.cullFace(gl.BACK);
// Draw
Hope that helps!
I've been going through consolidating a number of related issues into this one. Some suggestions from the various issues to help address this issue:
- Create documentation around 3D transparent texture techniques
- Use
discard
in shaders to prevent occlusion from fully transparent pixels (partial transparency will still be an issue) - Implement an order-independent transparency algorithm such as depth peeling (a bigger system-level change. may also have performance implications, and may need a WebGL version upgrade to support writing out multiple buffers at once)
- Use CULL_FACE to draw back faces first (transparency between multiple objects will still be an issue)
Let me know if there are more suggestions on how to help mitigate this issue!
@davepagurek Any updates on this one?
@rdsilver The biggest update is that p5 1.7.0 now supports WebGL 2, so it could potentially now be feasible to use an order-independent transparency algorithm. To decide whether or not to do so, we probably need to test the performance tradeoff. If it looks like it'll take a big performance hit, then we'd need to evaluate whether or not it would be feasible to have it as a togglable feature without making the code too complicated or unmaintainable.
As a stopgap, we can also document the issue maybe with a Learn tutorial on the p5 reference explaining what's going on and manual workarounds one can use.
@davepagurek I'm not sure if its related, but I isolated a transparency / webgl rendering issue in these 2 sketches:
https://editor.p5js.org/kevcx2/sketches/6QfXUHSa5 https://editor.p5js.org/kevcx2/sketches/E7oGGeEp8
In this case, the ball in front of the ring has an alpha value of 0, but it still renders over the ring:
Edit: I did some testing, and the issue is introduced v1.0.0. The sketches above work when using v0.10.2
I think this is the same issue as above: to do proper transparency blending, you'll have to draw transparent objects last. In your case, I think drawing the ellipse after the orbiter will do the trick:
addObject(bg)
addObject(o1) // Previously this was the last entry
addObject(e1)
Otherwise, the transparent object is drawn first, but WebGL depth testing just records the depth that geometry was drawn at, not whether or not it's transparent, so subsequent objects will see that something else is in front of it, and not draw.