I have a glass with a transparency set. When an object loads, it doesn’t appear behind it. I only see the color set in GL.clearColor (0,0,200,255);
However, objects that were loaded before loading the glass are already visible behind it.
I suspect it’s a matter of the depth buffer. I experimented with GL.depthFunc (GL.LEQUAL);
But without results.
Will someone help with an idea how to solve it?
Depth testing and translucency don’t mix. A fragment either modifies the depth buffer or it doesn’t. A fragment either fails the depth test (in which case, it’s discarded) or it passes (in which case, it replaces the pixel in that position in the colour buffer).
If you want translucent primitives, they need to be rendered in a consistent order; either back to front or front to back (the former is simpler). You can either sort the primitives or sort the individual fragments making up those primitives. If translucent geometry is constrained in a way which makes it straightforward to sort, it’s simpler (and more efficient) to sort the primitives using e.g. a BSP tree or a topological sort. If it isn’t constrained, sorting the primitives is often infeasible and you have to sort the individual fragments. In the latter case, the main options are “depth peeling” (multiple passes, each pass renders either the nearest or farthest layer of translucent geometry which hasn’t already been rendered) and linked lists of fragments (i.e. you store a linked list of fragments for each pixel; the fragment shader adds each fragment to the linked list, and once everything has been rendered you perform a resolution pass which sorts the list and renders the fragments in order).
I looked through the code of several solutions, read a few books I have and it did not work. I stare at the code and don’t understand the idea. I don’t know how this depth peeling works. I have a moderate level of writing shaders (I’ve been doing well so far), but it’s new to me.
Can someone like a cow in a ditch explain to me how it works? I just want to surprise you, then I can handle it.
I will be grateful for any help.
Depth peeling uses multiple passes with two depth buffers. One of them is bound as the actual depth buffer and functions as normal. The other is the depth buffer from the previous pass and is bound as a texture. Each pass renders all translucent geometry as opaque (storing its alpha value in the framebuffer rather than using it for blending), and (this is the key part)
discards any fragment whose depth is less than or equal to the value from the previous frame’s depth buffer.
So at the end of the first pass, the closest fragment for each pixel ends up in the framebuffer. At the end of the second pass, the next-closest fragment ends up in the framebuffer. And so on.
At the end of each pass, the colour buffer is blended with the existing result.
That’s the “front to back” approach. The advantage is that you can terminate rendering after a fixed number of passes. Anything which is left to render will be behind multiple translucent surfaces and (hopefully) wouldn’t have much effect upon the final result. The disadvantage is that if the surfaces have specular reflection, you need two colour buffers to accumulate the result: one to store the transmissive colour (by which anything behind the surface is multiplied) and one to store the reflected colour.
The alternative is “back to front”. In that case, the depth test is
GL_GEQUAL so that the farthest surface ends up in the framebuffer, and the previous frame’s depth buffer is used to
discard any fragment whose depth is greater than or equal to the value from the previous frame’s depth buffer. So each pass renders the next farthest fragment rather than the next closest. This has the advantage of only needing one colour buffer to accumulate the result. The disadvantage is that you have to render as many passes as are needed until a pass results in no fragments begin rendered (e.g. via a
GL_ANY_SAMPLES_PASSED query), because otherwise you’ll be discarding the closest, most visible surfaces.