2

I have been trying for some time to find a way to read the depth value for a particular mouse coordinate (x, y). Everything works fine on win10 with opengl 4.x, but not for opengl es 3.x

My approaches:

  1. glReadPixels() does not work on openGL es for depth buffer
  2. ray cast is not suitable because I work with a large terrain model
  3. subsequent method would suffice, but unfortunately too inaccurate, also on win10 but why?

    #version 420 uniform vec2 screenXy; uniform vec2 screenSize; out vec4 fragColor; void main(void) { if((int(gl_FragCoord.x) == int(screenXy.x)) && ((int(screenSize.y) - int(gl_FragCoord.y)) == int(screenXy.y))) { fragColor.r = gl_FragCoord.z; } else { fragColor = vec4(1, 1, 1, 1.0); } } 

I submit the mouse xy coordinates to the fragementshader (screenXy). If the clicked pixel is in the row, I write the depth value in the color buffer. This works, but the value gl_FragCoord.z and the one from the depth buffer are not exactly the same (I know this one from the depth buffer is correct). Although gl_FragCoord.z and the depth buffer value is float, and so I think 32bit.

GLfloat zd; // from depth buffer GLfloat zc[4]; // from color buffer m_func->glReadPixels(xy.x(), m_pFbo->height() - xy.y(), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &zd); m_func->glReadPixels(xy.x(), m_pFbo->height() - xy.y(), 1, 1, GL_RGBA, GL_FLOAT, zc); 

Reasons:

  1. the deviation occurs through an internal type conversion, but where?
  2. because the GL_DEPTH_TEST is executed after the fragmentshader gl_FragCoord.z is not the closest one (to the camera), but which is saved in the depth buffer. So it would also make no sense to save gl_FragCoord.z in a separat Frambuffer, because its not the correct value.

Can maybe someone help me and solve the knot, because I can not find any other explanation?

Here some measured values:

zc 0.984314 zd 0.985363 zc 0.552941 zd 0.554653 zc 1 -> extremly critical zd 0.999181 
0

3 Answers 3

2

Since 0.984314 * 255.0 is exactly 251.0, I assume the internal format of the color plane is GL_RGBA8. That means there is 1 byte for each color channel and zc can only have 256 different values, from 0.0 to 1.0 in steps of 1/256.

If it is supported by the OpenGL ES version which you use, then you can change the format of the render buffer storage (e,g. GL_R32F - only red color channel, but 32 bit floating point).

Or you can encode the depth to the 4 channels of the 4 * 8 bit color plane:

vec4 PackDepth( in float depth ) { depth *= (256.0*256.0*256.0 - 1.0) / (256.0*256.0*256.0); vec4 encode = fract( depth * vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) ); return vec4( encode.xyz - encode.yzw / 256.0, encode.w ) + 1.0/512.0; } .... fragColor = PackDepth(gl_FragCoord.z); 

And you can decode it after reading the value:

GLfloat zc[4]; // from color buffer m_func->glReadPixels(xy.x(), m_pFbo->height() - xy.y(), 1, 1, GL_RGBA, GL_FLOAT, zc); float depth = zc[0] + zc[1]/256.0 + zc[2]/(256.0*256.0) + zc[3]/(256.0*256.0*256.0); depth = depth * (255.0f/256.0f) * (256.0*256.0*256.0) / (256.0*256.0*256.0 - 1.0); 
Sign up to request clarification or add additional context in comments.

3 Comments

Just to answer the "if it's supported" comment - float framebuffers are only core functionality only in OpenGL ES 3.2, but are available via an extension on some earlier OpenGL ES 3.0 GPUs.
@Rabbid76: So far, I have tried 4 different decodeFloat32ToRgba8 algorithms, unfortunately all are lossy. The only one that is exact for small values is aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final but if z is close to 1, then this too fails. And GL_R32F does not exist, only GL_RED (also opengl 4.x) and that is only 8bit.
Unfortunately not precise enough. The best one is in the link above. But the result is not really understandable. This works 80% exactly regardless of the size of the value. Then suddenly a kind of overflow. Here some results: zc 0.485301 zd 0.485301 zc 0.999121 zd 0.999121 zc 1.00394 zd 0.998262 It's always if one of the rgba values reaches 1. e.g. for the last value rgba = (1, 1, 1, 0).
0

I still do not know with certainty whether gl_FragCoord.z is the same as in the depth buffer after the depth test. With a counter you could make a query

if(gl_FragCoord.z_last > gl_FragCoord.z) fragColor.z = gl_FragCoord.z; 

so basically what the depth test does. but how does one store gl_FragCoord.z_last as static? Is there a way lossless to read the depth value in openGL 3.x? According to solidpixel there is a float framebuffer only from opengl 3.2.

Comments

0

It seems so that the value in the depth buffer is not exactly the same as gl_FragCoord.z. I think the following example demonstrates it.

#version 420 uniform vec4 color; uniform vec2 screenXy; uniform vec2 screenSize; in vec2 vBC; out vec4 fragColor; void main(void) { fragColor.r = gl_FragCoord.z; } 

C++

GLfloat zd; GLubyte zc[4]; m_func->glReadPixels(xy.x(), m_pFbo->height() - xy.y(), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &zd); m_func->glReadPixels(xy.x(), m_pFbo->height() - xy.y(), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, zc); 

Data: zc0 = 255 (from gl_FragCoord.z); zd = 0.996561 (from depth buffer)

But it should be 254, because 0.996561 * 255 = 254.123055.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.