4

I have the following C++ OpenGL code which renders the RGB values of pixels in a scene:

glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, _windowWidth, _windowHeight);

glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);

glMatrixMode( GL_PROJECTION );
glLoadIdentity();

float aspectRatio = float(_windowWidth) / float(_windowHeight);
gluPerspective(60.0f, aspectRatio, 0.1f, 1000.0f);

glMatrixMode(GL_MODELVIEW);
_camera.Update();
glLoadMatrixf(_camera.Matrix()[0]);

_scene.Render();

glutSwapBuffers();

However, I would like to instead render the depth buffer of the scene, i.e. so that each pixels value is the z-distance in front of the camera.

That is, I am currently getting the top image but I want the bottom image (image from here):

enter image description here

How should I go about this? I don't think I'm using shaders (am I?) – and the advice given here seems to suggest this might only be possible with shaders.

There is also this code, although this too looks like it is using shaders. How difficult would it be to modify my code to use shaders?

jozxyqk
  • 16,424
  • 12
  • 91
  • 180
Bill Cheatham
  • 11,396
  • 17
  • 69
  • 104
  • I'm not a graphics expert but that ball seems a phong-blinn, probably also the monkey and the cube.. I believe you're using shaders but the main point is: how can we help without code? – Marco A. Jun 17 '14 at 14:50

2 Answers2

6

You can slurp the depthbuffer to host memory via glReadPixels() and GL_DEPTH_COMPONENT and re-upload the buffer as a GL_LUMINANCE texture:

screenshot

#include <GL/glew.h>
#include <GL/glut.h>

#include <vector>
using namespace std;

void display()
{
    int w = glutGet( GLUT_WINDOW_WIDTH );
    int h = glutGet( GLUT_WINDOW_HEIGHT );

    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    double ar = w / static_cast< double >( h );
    const float zNear = 0.1;
    const float zFar = 10.0;
    gluPerspective( 60, ar, zNear, zFar );

    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
    glTranslatef( 0, 0, -4 );

    static float angle = 0;
    angle += 3;

    glPushMatrix();
    glRotatef( angle, 0.1, 0.5, 0.3 );
    glColor3ub( 255, 0, 0 );
    glutSolidTeapot( 1 );
    glPopMatrix();

    vector< GLfloat > depth( w * h, 0 );
    glReadPixels( 0, 0, w, h, GL_DEPTH_COMPONENT, GL_FLOAT, &depth[0] ); 

    // linearize depth
    // http://www.geeks3d.com/20091216/geexlab-how-to-visualize-the-depth-buffer-in-glsl/
    for( size_t i = 0; i < depth.size(); ++i )
    {
        depth[i] = ( 2.0 * zNear ) / ( zFar + zNear - depth[i] * ( zFar - zNear ) );
    }

    static GLuint tex = 0;
    if( tex > 0 )
        glDeleteTextures( 1, &tex );
    glGenTextures(1, &tex);
    glBindTexture( GL_TEXTURE_2D, tex);
    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_MAG_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexImage2D( GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_FLOAT, &depth[0] );

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    glOrtho( 0, w, 0, h, -1, 1 );

    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();

    glEnable( GL_TEXTURE_2D );
    glColor3ub( 255, 255, 255 );
    glScalef( 0.3, 0.3, 1 );
    glBegin( GL_QUADS );
    glTexCoord2i( 0, 0 );
    glVertex2i( 0, 0 );
    glTexCoord2i( 1, 0 );
    glVertex2i( w, 0 );
    glTexCoord2i( 1, 1 );
    glVertex2i( w, h);
    glTexCoord2i( 0, 1 );
    glVertex2i( 0, h );
    glEnd();
    glDisable( GL_TEXTURE_2D );

    glutSwapBuffers();
}

void timer( int value )
{
    glutPostRedisplay();
    glutTimerFunc( 16, timer, 0 );
}

int main( int argc, char **argv )
{
    glutInit( &argc, argv );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
    glutInitWindowSize( 600, 600 );
    glutCreateWindow( "GLUT" );
    glewInit();
    glutDisplayFunc( display );
    glutTimerFunc( 0, timer, 0 );
    glEnable( GL_DEPTH_TEST );
    glutMainLoop();
    return 0;
}

Round-tripping to the CPU isn't terribly fast (especially with the host-side linearization). You can use PBOs to make it a GPU-to-GPU transfer but you'll lose the linearization.

genpfault
  • 51,148
  • 11
  • 85
  • 139
6

You can use Framebuffer Object (fbo) to render the depth values into texture. Then in a second rendering pass draw a quad over the whole viewport and apply depth texture on it.

Or you can write a fragment shader that will output fragments colors according to their depth, this can render in single pass.

See here some examples. You can find more searching for 'fbo'.

Archie
  • 2,644
  • 21
  • 21