Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Page 1 of 3 123 LastLast
Results 1 to 10 of 29

Thread: Reading the depth buffer into texture memory

  1. #1
    Junior Member Newbie
    Join Date
    Jul 2013
    Posts
    18

    Reading the depth buffer into texture memory

    Hello,

    I'm trying to get a simple shadowmapping demo up and running but I've run into a bit of a problem. I need to translate to the light's position, save the depth values into texture memory and finally generate texture coordinates based on the depth values. Now, my current code isn't working so I've been debugging it the whole day and I suspect the problem is with the transfer of depth buffer info into a texture.

    Here's my code:

    init:
    Code :
    glGenTextures(1, &shadowmap_);
    glBindTexture(GL_TEXTURE_2D, shadowmap_);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 512, 512, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);

    render:

    Code :
    // position the light
    glLightfv(GL_LIGHT0, GL_POSITION, lightPos_);
     
    // set up the projection parameters from the light's POV
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    	glLoadIdentity();
    	gluPerspective(lightFOV_, lightAspect_, lightNear_, lightFar_);
     
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    	glLoadIdentity();
    	// translate to the light's position
    	gluLookAt(lightPos_[0], lightPos_[1], lightPos_[2], -1.0f, 0.0f, 5.0f, 0.0f, 1.0f, 0.0f);
     
    	// render the scene to get the depth information
    	renderSceneElements();
    glPopMatrix();
     
    // end the projection modification
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
     
    // copy over the depth information
    glBindTexture(GL_TEXTURE_2D, shadowmap_);
    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 512, 512);
     
    // render a simple quad with the shadowmap for debugging
    glPushMatrix();
    	glEnable(GL_TEXTURE_2D);
    	glBindTexture(GL_TEXTURE_2D, shadowmap_);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     
    	glTranslatef(3.0f, 2.0f, 5.0f);
    	glBegin(GL_QUADS);
    		glTexCoord2f(0.0f, 0.0f);
    		glVertex3f(0.0f, 0.0f, 0.0f);
     
    		glTexCoord2f(1.0f, 0.0f);
    		glVertex3f(3.0f, 0.0f, 0.0f);
     
    		glTexCoord2f(1.0f, 1.0f);
    		glVertex3f(3.0f, 3.0f, 0.0f);
     
    		glTexCoord2f(0.0f, 1.0f);
    		glVertex3f(0.0f, 3.0f, 0.0f);
    	glEnd();
    	glDisable(GL_TEXTURE_2D);
    glPopMatrix();

    The result is a white quad :/

    renderSceneElements() contains a bunch of VAOs.

    Also, I know there's a way to copy over the depth buffer using FBOs. I want to implement that afterwards but first I'm curious as to what on Earth I'm doing wrong here.

    Thanks in advance!
    Last edited by dr4cula; 07-24-2013 at 10:55 AM.

  2. #2
    Member Regular Contributor
    Join Date
    Jun 2013
    Posts
    490
    Quote Originally Posted by dr4cula View Post
    I'm trying to get a simple shadowmapping demo up and running but I've run into a bit of a problem. I need to translate to the light's position, save the depth values into texture memory and finally generate texture coordinates based on the depth values. Now, my current code isn't working so I've been debugging it the whole day and I suspect the problem is with the transfer of depth buffer info into a texture.
    Why do you think that the problem is with the transfer? If it's because it's a white quad, have you analysed what the expected range of values should be? The mapping between Z and depth is highly non-linear, particularly if the near plane is too close.

    Also: try storing your own data in the depth texture, to make sure that your debug code is working.
    Last edited by GClements; 07-24-2013 at 03:57 PM.

  3. #3
    Junior Member Newbie
    Join Date
    Jul 2013
    Posts
    18
    Thanks for your reply!

    I decided to use FBOs: I can actually see the shadowmap when I map it onto a quad (it's faint but it's visible at least). However, my problems don't end there: now the entire scene is black (except for the textured quad and brownish glClearColor() defined background). I'm guessing my texture coordinate generation is wrong but not sure. Any help would be greatly appreciated!

    new init:
    Code :
    glGenTextures(1, &shadowmap_);
    glBindTexture(GL_TEXTURE_2D, shadowmap_);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 512, 512, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
     
    //glGenRenderbuffers(1, &renderbuffer_);
    //glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer_);
    //glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, 512, 512);
     
    glGenFramebuffers(1, &framebuffer_);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
    //glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer_);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadowmap_, 0);

    new render:
    Code :
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
    //glDrawBuffer(GL_NONE);
    //glReadBuffer(GL_NONE);
     
    glClearColor(0.5, 0.2, 0.1, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // position the light
    glLightfv(GL_LIGHT0, GL_POSITION, lightPos_);
     
    // set up the projection parameters from the light's POV
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluPerspective(lightFOV_, lightAspect_, lightNear_, lightFar_);
     
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();
    // translate to the light's position
    gluLookAt(lightPos_[0], lightPos_[1], lightPos_[2], -1.0f, 0.0f, 5.0f, 0.0f, 1.0f, 0.0f);
     
    // render the scene to get the depth information
    renderSceneElements();
    glPopMatrix();
     
    // end the projection modification
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
     
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
     
    // copy over the depth information
    //glBindTexture(GL_TEXTURE_2D, shadowmap_);
    //glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 512, 512);
     
    // matrix defining the planes for S, Q, R, T components for texture generation
    float planeMatrix[16];
    glPushMatrix();
    glLoadIdentity();
    // compensate for the eye-coordinate to texture coordinate conversion: [-1,1] to [0,1]
    glTranslatef(0.5f, 0.5f, 0.0f);
    glScalef(0.5f, 0.5f, 1.0f);
     
    // do the perspective projection and translate to the light's position
    gluPerspective(lightFOV_, lightAspect_, lightNear_, lightFar_);
    gluLookAt(lightPos_[0], lightPos_[1], lightPos_[2], -1.0f, 0.0f, 5.0f, 0.0f, 1.0f, 0.0f);
     
    glGetFloatv(GL_MODELVIEW_MATRIX, planeMatrix);
    glPopMatrix();
     
    // go from OpenGL's column-major to row-major matrix form
    transposeMatrix16(planeMatrix); 
     
    // set up the type for texture generation
    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
    glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
    glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
     
    // data for texture generation
    glTexGenfv(GL_S, GL_OBJECT_PLANE, &planeMatrix[0]);
    glTexGenfv(GL_T, GL_OBJECT_PLANE, &planeMatrix[4]);
    glTexGenfv(GL_R, GL_OBJECT_PLANE, &planeMatrix[8]);
    glTexGenfv(GL_Q, GL_OBJECT_PLANE, &planeMatrix[12]);
     
    glEnable(GL_TEXTURE_GEN_S);
    glEnable(GL_TEXTURE_GEN_T);
    glEnable(GL_TEXTURE_GEN_R);
    glEnable(GL_TEXTURE_GEN_Q);
     
     
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, shadowmap_);
     
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
    glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
     
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
     
    renderSceneElements();
     
    glDisable(GL_LIGHTING);
    glDisable(GL_LIGHT0);
     
    glDisable(GL_TEXTURE_2D);
     
    glDisable(GL_TEXTURE_GEN_Q);
    glDisable(GL_TEXTURE_GEN_R);
    glDisable(GL_TEXTURE_GEN_T);
    glDisable(GL_TEXTURE_GEN_S);
     
    glPushMatrix();
    glEnable(GL_TEXTURE_2D);
    //glBindTexture(GL_TEXTURE_2D, shadowmap_);
    glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_NONE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
    glTranslatef(3.0f, 2.0f, 5.0f);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(0.0f, 0.0f, 0.0f);
     
    glTexCoord2f(1.0f, 0.0f);
    glVertex3f(3.0f, 0.0f, 0.0f);
     
    glTexCoord2f(1.0f, 1.0f);
    glVertex3f(3.0f, 3.0f, 0.0f);
     
    glTexCoord2f(0.0f, 1.0f);
    glVertex3f(0.0f, 3.0f, 0.0f);
    glEnd();
    glDisable(GL_TEXTURE_2D);
    glPopMatrix();

    Note that I had to explicitly bind the texture - I thought it would be automatically related to the framebuffer and hence enabling texturing would have caused that texture to be used? If I don't have that binding there, OpenGL selects my previously used texture.

    Thanks in advance!

  4. #4
    Member Regular Contributor
    Join Date
    Jun 2013
    Posts
    490
    Quote Originally Posted by dr4cula View Post
    Code :
    // compensate for the eye-coordinate to texture coordinate conversion: [-1,1] to [0,1]
    glTranslatef(0.5f, 0.5f, 0.0f);
    glScalef(0.5f, 0.5f, 1.0f);
    You need to perform the same signed-to-unsigned conversion for the Z coordinate, as the depth values in the texture are returned as 0..1.

    Also, you should bind a renderbuffer (or texture) to GL_COLOR_ATTACHMENT0 even if you're not using it.

    Quote Originally Posted by dr4cula View Post
    Note that I had to explicitly bind the texture - I thought it would be automatically related to the framebuffer and hence enabling texturing would have caused that texture to be used?
    No. glFramebufferTexture2D() causes rendered output to be directed to the texture (or rather, a specific mipmap level of it). It doesn't associate the texture with a texture unit. In fact, having a texture used as both a source and destination simultaneously is undefined.

  5. #5
    Senior Member OpenGL Guru
    Join Date
    May 2009
    Posts
    4,948
    In fact, having a texture used as both a source and destination simultaneously is undefined.
    Only if you sample from the same image as you're writing to. Sampling from one mipmap level and writing to another is fine.

  6. #6
    Member Regular Contributor
    Join Date
    Jun 2013
    Posts
    490
    Quote Originally Posted by Alfonse Reinheart View Post
    Only if you sample from the same image as you're writing to. Sampling from one mipmap level and writing to another is fine.
    That depends upon how you define "sampling". My understanding of the 4.3 specification (8.14.2.1) is that the behaviour is undefined if the attached level is within the range of levels available for reading, regardless of which levels are actually read. So if mipmapping is disabled and the attached level is GL_TEXTURE_BASE_LEVEL, or mipmapping is enabled and the attached level is within the range GL_TEXTURE_BASE_LEVEL to GL_TEXTURE_MAX_LEVEL, the behaviour is undefined.

  7. #7
    Senior Member OpenGL Guru
    Join Date
    May 2009
    Posts
    4,948
    Quote Originally Posted by GClements View Post
    That depends upon how you define "sampling". My understanding of the 4.3 specification (8.14.2.1) is that the behaviour is undefined if the attached level is within the range of levels available for reading, regardless of which levels are actually read. So if mipmapping is disabled and the attached level is GL_TEXTURE_BASE_LEVEL, or mipmapping is enabled and the attached level is within the range GL_TEXTURE_BASE_LEVEL to GL_TEXTURE_MAX_LEVEL, the behaviour is undefined.
    You know, while I was looking at this part of the spec, something occurred to me. They never updated the feedback language to handle view textures. Just look at the way it keeps talking about "texture object T"; it never takes into account the possibility of "texture object T" having an image attached and reading from "texture object VT", which is a view of T.

    I was behind on my bug quota, not having submitted one since well, yesterday, so I fired that one off.

    But in any cause, yes, you must actively prevent sampling being at all possible from any image attached to the framebuffer in order to not hit undefined behavior. That doesn't mean you can't sample from the same texture you're rendering to. You just need to know how to do it correctly.

    Though to be honest, it'd be great if the rules were a bit more reasonable. The way it's specified now, you can't even access a different array layer in the same mipmap. In fact, it's undefined behavior even if you can't render to the attached image (because it's not in the glDrawBuffers list).

    Though I'll grant that the last may be a performance optimization. To allow that to work, changing the glDrawBuffers set would have to clear the framebuffer cache. And that would kill lots of optimization possibilities.

  8. #8
    Junior Member Newbie
    Join Date
    Jul 2013
    Posts
    18
    Quote Originally Posted by GClements View Post
    You need to perform the same signed-to-unsigned conversion for the Z coordinate, as the depth values in the texture are returned as 0..1.

    Also, you should bind a renderbuffer (or texture) to GL_COLOR_ATTACHMENT0 even if you're not using it.
    The Red Book suggested translation only in x- and y- directions which I found a bit odd. Changed it to translate in the z-direction as well and added the recommended renderbuffer. However, everything is still black in the scene :/

    Quote Originally Posted by GClements View Post
    No. glFramebufferTexture2D() causes rendered output to be directed to the texture (or rather, a specific mipmap level of it). It doesn't associate the texture with a texture unit. In fact, having a texture used as both a source and destination simultaneously is undefined.
    Ah, thanks for clarifying!

    I've uploaded the code to pastebin since the forum editing kinda sucks: http://pastebin.com/G1jT0FfR

    To be honest, I'm really confused as to how OpenGL will know how to map the shadowmap to the scene if, for example, I can't use it for texture mapping a quad. I suppose it's got something to do with the GL_COMPARE_R_TO_TEXTURE but I'm a bit confused Thoughts anyone?

    Thanks in advance!

  9. #9
    Member Regular Contributor
    Join Date
    Jun 2013
    Posts
    490
    Quote Originally Posted by dr4cula View Post
    However, everything is still black in the scene :/
    Are you performing a glClear() for the physical framebuffer? I don't see it in the code, but that may just be because it's not part of the render function.

    You need to call glViewport(0, 0, 512, 512) for the FBO (then set it back to cover the window for the second pass).

    The depth is being offset by 0.5 but still scaled by 1.0. All 6 values should be 0.5.

    Quote Originally Posted by dr4cula View Post
    To be honest, I'm really confused as to how OpenGL will know how to map the shadowmap to the scene if, for example, I can't use it for texture mapping a quad. I suppose it's got something to do with the GL_COMPARE_R_TO_TEXTURE but I'm a bit confused
    When GL_TEXTURE_COMPARE_MODE is GL_COMPARE_R_TO_TEXTURE, the first two texture coordinates are used to sample the texture, and the third texture coordinate is compared to the sampled value using GL_TEXTURE_COMPARE_FUNC. If the test passes, the luminance (R,G,B), intensity (R,G,B,A) or alpha are one, otherwise they're zero.

  10. #10
    Junior Member Newbie
    Join Date
    Jul 2013
    Posts
    18
    Quote Originally Posted by GClements View Post
    Are you performing a glClear() for the physical framebuffer? I don't see it in the code, but that may just be because it's not part of the render function.

    You need to call glViewport(0, 0, 512, 512) for the FBO (then set it back to cover the window for the second pass).

    The depth is being offset by 0.5 but still scaled by 1.0. All 6 values should be 0.5.
    Thanks for your reply once again! My viewport and window size are both 512x512 so the calls to glViewport() should be redundant. I added them in (just in case) and nothing changed (as expected). Also changed the scale but nothing. glClear() is called on the physical buffer before entering the rendering state of this particular scene but just in case, I added another clear after switching to it for the second pass.

    New render: http://pastebin.com/MHGDxsSf

    Any other ideas?

    Thanks in advance!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •