0
votes

I'm using Java OpenGL (JOGL 2.x, built from Git source). I'm rendering my scene to a framebuffer object with a color and a depth attachment. I'd like to convert the [0,1] depth buffer values into world-space distances. My depth attachment is defined as follows:

    private void setupDepthFBOs(GL2 gl,
                            int width,
                            int height, 
                            int[] frameBufferIds,
                            int[] colorBufferIds,
                            int[] depthBufferIds) {        
    // based on
    // http://www.java2s.com/Code/Java/Advanced-Graphics/BloomOpenGL.htm
    //  generate a framebuffer object
    gl.glGenFramebuffers(1, frameBufferIds, 0);
    // bind the framebuffer
    gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, frameBufferIds[0]);

    // generate a texture in memory
    gl.glGenTextures(1, colorBufferIds,0);
    gl.glBindTexture(GL2.GL_TEXTURE_2D, colorBufferIds[0]);
    // this will be an RGBA texture (4 bpp) with width, height..
    gl.glTexImage2D(GL2.GL_TEXTURE_2D,      // target texture type
            0,          // mipmap LOD level
            GL2.GL_RGBA8,           // internal pixel format
            width,          // width of generated image
            height,         // height of generated image
            0,          // border of image
            GL2.GL_RGBA,        // external pixel format 
            GL2.GL_UNSIGNED_BYTE,   // datatype for each value
            null);  // buffer to store the texture in memory

    // set some texture parameters?
    gl.glTexParameteri(GL.GL_TEXTURE_2D, 
                       GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);

    // use the texture we just created in the framebuffer we just created
    gl.glFramebufferTexture2D(
            GL2.GL_FRAMEBUFFER,         // target texture type
            GL.GL_COLOR_ATTACHMENT0,    // attachment point
            GL.GL_TEXTURE_2D,           // texture target type
            colorBufferIds[0],          // on-gpu id for texture 
            0);                         // mipmap lod level

    gl.glGenTextures(1,depthBufferIds,0);
    gl.glBindTexture(GL.GL_TEXTURE_2D,depthBufferIds[0]);
    gl.glTexImage2D(GL2.GL_TEXTURE_2D,          // target texture type
            0,                          // mipmap LOD level
            GL2.GL_DEPTH_COMPONENT24,   // internal pixel format
                    //GL2.GL_DEPTH_COMPONENT
            width,                      // width of generated image
            height,                     // height of generated image
            0,                          // border of image
            GL2.GL_DEPTH_COMPONENT,     // external pixel format 
            GL2.GL_UNSIGNED_INT,        // datatype for each value
            null);  // buffer to store the texture in memory


    gl.glTexParameteri(GL.GL_TEXTURE_2D,
                       GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
    gl.glTexParameteri(GL.GL_TEXTURE_2D,
                       GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
    gl.glTexParameteri(GL.GL_TEXTURE_2D, 
                       GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
    gl.glTexParameteri(GL.GL_TEXTURE_2D, 
                       GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);

    gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER,
                              GL.GL_DEPTH_ATTACHMENT,
                              GL.GL_TEXTURE_2D,
                              depthBufferIds[0],0);


    gl.glBindTexture(GL.GL_TEXTURE_2D, 0);

    int status = gl.glCheckFramebufferStatus(GL2.GL_FRAMEBUFFER);


    if (status == GL2.GL_FRAMEBUFFER_COMPLETE) {
        gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0);
    } else {
        throw new IllegalStateException("Frame Buffer Object not created. Status was: " + status);
    }

}

This successfully creates a depth buffer, which I can read as a texture and render to the screen, or use as input to a shader (my intended eventual use case).

After some discussions on the IRC a few nights ago, I came up with the following formula relating the Projection Matrix (denoted here as p) and depth-buffer value to the distance to each point on screen in world-space:

z = (p_33)/(p_34 + depth)

(note: my projection matrix/eye is set up looking in the Z+ direction)

This produces almost-sane z-values, but there's a significant margin of error between the distance to known points in the scene, and the value returned by this equation.

Any ideas what I might be doing wrong here?

1
Please show how you setup the projection matrix with actual values. Also, are you sure you want "World space" z distance and not "Eye space" z distance? The latter usually makes more sense for what you are doing.bernie

1 Answers

3
votes

Here are my calculations, my result is different to yours:

Defined:

depth = out_z / out_w
out_z = in_z * p_33 + in_w * p_43
out_w = in_z * p_34 + in_w * p_44

Known:

in_w = 1, p_43 = -1, p_44 = 0

Working:

depth = (in_z * p_33 - 1) / (in_z * p_34)
depth = p_33 / p_34 - 1 / (in_z * p_34)
p_33 / p_34 - depth = 1 / (in_z * p_34)
1 / (p_33 / p_34 - depth) = in_z * p_34
1 / (p_33 / p_34 - depth) = in_z * p_34
1 / (p_33 - depth * p_34) = in_z