Search code examples
copenglglutdepth-bufferculling

What could cause polygons in OpenGL to be rendered out of order?


I'm trying to get some hands-on experience with OpenGL so I've been writing a few basic programs. The short program below is my first attempt at rendering a solid object --a rotating cube-- but for some reason some back polygons seem to be getting drawn over front polygons. My question is what could cause this? Does it have something to do with the depth buffer? I've found that enabling face culling will hide the effect in this case, but why should that be necessary? Shouldn't a face which is occluded by a nearer face be hidden regardless?

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

typedef struct{
    int width;
    int height;
    char * title;
} window;
window win;

float theta = 0;
const float rotRate = 0.05;//complete rotations per second
int lastTime;

const float verts[][3] = {
    {0.0,0.0,0.0},
    {1.0,0.0,0.0},
    {0.0,1.0,0.0},
    {0.0,0.0,1.0},
    {0.0,1.0,1.0},
    {1.0,0.0,1.0},
    {1.0,1.0,0.0},
    {1.0,1.0,1.0}};

const int faceIndices[][4] = {
    {3,5,7,4},//front
    {1,0,2,6},//back
    {4,7,6,2},//top
    {0,1,5,3},//bottom
    {5,1,6,7},//right
    {0,3,4,2}};//left

void display(){
    //timing and rotation 
    int currentTime = glutGet(GLUT_ELAPSED_TIME);
    int dt = lastTime - currentTime;
    lastTime = currentTime;
    theta += (float)dt/1000.0*rotRate*360.0;
    if (theta > 360.0) theta += -360.0;

    //draw
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0, 0.0, -5.0);
    glRotatef(theta, 0.0, 1.0, 0.0);
    glTranslatef(-1.0,-1.0,-1.0);
    glScalef(2.0, 2.0, 2.0);
    int f;
    for(f=0; f<6;f++){
        glBegin(GL_POLYGON);
        int v;
        for(v=0; v<4; v++){
            glColor3fv(verts[faceIndices[f][v]]);
            glVertex3fv(verts[faceIndices[f][v]]);
        }
        glEnd();
    }

    glutSwapBuffers();
}

void initializeGLUT(int * argc, char ** argv){
    glutInit(argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
    glutInitWindowSize(win.width, win.height);
    glutCreateWindow("OpenGL Cube");
    glutDisplayFunc(display);
    glutIdleFunc(display);
}

void initializeGL(){
    //Setup Viewport matrix
    glViewport(0,0,win.width, win.height);

    //Setup Projection matrix
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45,(float) win.width/win.height, 0.1, 100.0);

    //Initialize Modelview matrix
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    //Other
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClearDepth(1.0);
}

int main(int argc, char** argv){
    win.width = 640;
    win.height = 480;

    initializeGLUT(&argc, argv);
    initializeGL();

    glutMainLoop();

    return 0;
}

Solution

  • Does it have something to do with the depth buffer?

    Yes, this is a depth buffer issue, you enabled depth buffer in your code, but obviously you lost some steps, to use depth buffer

    1. Enable depth test by calling glEnable(GL_DEPTH_TEST)
    2. Set the depth test function by glDepthFunc, GL_LEQUAL is the recommended choice for most case, glDepthFunc(GL_LEQUAL); the default value is GL_LESS.
    3. Call glClearDepth to set the cleared value, the initial value is 1.0, this step is not mandatory if you want the default value.
    4. Don't forgot to clear the depth bit before drawing, glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    I've found that enabling face culling will hide the effect in this case, but why should that be necessary?

    By default, OpenGL does't cull any face, the recommend option is

    1. Define vertices in Clock Wise order as back face(this is also OpenGL's choice)
    2. Cull back faces when necessary.

    In your case, you defined the polygon vertices all in CCW order, so they are all front faces by default, you just need to cull the back-faces to prevent them from drawing, so the following code also solves your problem.

    glEnable(GL_CULL_FACE);
    glFrontFace(GL_CCW);
    glCullFace(GL_BACK);
    

    Shouldn't a face which is occluded by a nearer face be hidden regardless?

    Yes, that make sense as we are human being, but for the computer, it's your responsibility to tell it how to do that.

    References:

    Depth buffer

    Face culling