OpenGL - How to access depth buffer values? - Or: gl_FragCoord.z vs. Rendering depth to texture
Asked Answered
P

3

18

I want to access the depth buffer value at the currently processed pixel in a pixel shader.

How can we achieve this goal? Basically, there seems to be two options:

  1. Render depth to texture. How can we do this and what is the tradeoff?
  2. Use the value provided by gl_FragCoord.z - But: Is this the correct value?
Poff answered 29/4, 2014 at 10:29 Comment(3)
gl_FragCoord.z is the depth value of the currently processed fragment, not the current value in the depth buffer. To get a depth texture, create a texture with a depth format, render to an FBO with your depth texture attached to GL_DEPTH_ATTACHMENT, and then bind the depth texture for sampling in your fragment shader.Seldon
@RetoKoradi, that sounds like an answer, why did you make it a comment?Emmalynn
@JWWalker: Because I was in a rush, and normally try to formulate my answers a little more carefully, and with more detail. I'll turn it into an answer.Seldon
S
33

On question 1: You can't directly read from the depth buffer in the fragment shader (unless there are recent extensions I'm not familiar with). You need to render to a Frame Buffer Object (FBO). Typical steps:

  1. Create and bind an FBO. Look up calls like glGenFramebuffers and glBindFramebuffer if you have not used FBOs before.
  2. Create a texture or renderbuffer to be used as your color buffer, and attach it to the GL_COLOR_ATTACHMENT0 attachment point of your FBO with glFramebufferTexture2D or glFramebufferRenderbuffer. If you only care about the depth from this rendering pass, you can skip this and render without a color buffer.
  3. Create a depth texture, and attach it to the GL_DEPTH_ATTACHMENT attachment point of the FBO.
  4. Do your rendering that creates the depth you want to use.
  5. Use glBindFramebuffer to switch back to the default framebuffer.
  6. Bind your depth texture to a sampler used by your fragment shader.
  7. Your fragment shader can now sample from the depth texture.

On question 2: gl_FragCoord.z is the depth value of the fragment that your shader is operating on, not the current value of the depth buffer at the fragment position.

Seldon answered 29/4, 2014 at 17:55 Comment(4)
Suppose I render into a frame buffer of the same size as my second pass. Will texture(depth_texture,vec2(float(gl_FragCoord.x)/width,float(gl_FragCoord.y)/height)).r index _exactly the right texture value and retrieve the depth value of the previous pass? I'm seeing "ringing" artifacts near large discontinuities in the depth buffer.Bugger
How to convert back a depth value you get with a texture() call inside the fragment shader back to 'real' world coordinates ? If I understand correctly the depth values are clamped to [0;1] - so what it is value to multiply the clamped depth with ?Backswept
The depth is found by using the near and far values set in the projection matrix. Simply multiply by the difference between near and far and add the near value to get your original value ( ((far - near) * Depth) + near).Vivacious
Note: Renderbuffer can not be used to sample with in later shading stages.Tumular
K
9

gl_FragCoord.z is the window-space depth value of the current fragment. It has nothing to do with the value stored in the depth buffer. The value may later be written to the depth buffer, if the fragment is not discarded and it passes a stencil/depth test.

Technically there are some hardware optimizations that will write/test the depth early, but for all intents and purposes gl_FragCoord.z is not the value stored in the depth buffer.

Unless you render in multiple passes, you cannot read and write to the depth buffer in a fragment shader. That is to say, you cannot use a depth texture to read the depth and then turn around and write a new depth. This is akin to trying to implement blending in the fragment shader, unless you do something exotic with DX11 class hardware and image load/store, it just is not going to work.

If you only need the depth of the final drawn scene for something like shadow mapping, then you can do a depth-only pre-pass to fill the depth buffer. In the second pass, you would read the depth buffer but not write to it.

Karlykarlyn answered 29/4, 2014 at 18:1 Comment(0)
M
0

If anyone is reading this these days, the "exotic" load/store talked about by Andon is available using the extension:

#extension GL_ARB_shader_image_load_store: require

In particular it allows the use of a frame buffer with a single attachment, that being a depth buffer (no render buffers), where the depth test is done prior to the fragment shader, and allows writing to the same depth buffer, e.g. in my case I needed to do this:

if(!gl_FrontFacing) depth = 0.0;
    else depth = gl_FragCoord.z;
Mulholland answered 4/8 at 0:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.