Search code examples
c++openglopengl-compatdepth-testing

How do I bring a polygon to the foreground in OpenGL?


The code below creates 2 square polygons, red and green.
I'm trying to place a red square on top of the green, but I can't.
The depth buffer is declared, cleaned when necessary, an orthogonal system is configured correctly.
If I specify a value outside the range (2;-2), the polygon disappears as it should.

#include <...>

constexpr auto FPS_RATE = 120;
int windowHeight = 600, windowWidth = 600, windowDepth = 600;

void init();
void idleFunction();
void displayFunction();
double getTime();

double getTime()
{
  using Duration = std::chrono::duration<double>;
  return std::chrono::duration_cast<Duration>(
    std::chrono::high_resolution_clock::now().time_since_epoch()
    ).count();
}

const double frame_delay = 1.0 / FPS_RATE;
double last_render = 0;

void init()
{
  glutDisplayFunc(displayFunction);
  glutIdleFunc(idleFunction);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-windowWidth / 2, windowWidth / 2, -windowHeight / 2, windowHeight / 2, 2, -2);
  glClearColor(0.0, 0.0, 0.0, 0.0);
}

void idleFunction()
{
  const double current_time = getTime();
  if ((current_time - last_render) > frame_delay)
  {
    last_render = current_time;
    glutPostRedisplay();
  }
}

void displayFunction()
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glPushMatrix();
  //move the red square to the foreground
  glTranslatef(-32.5, -32.5, 2);
  glColor3f(1, 0, 0);
  glBegin(GL_POLYGON);
  glVertex3i(-150, 150, 0);
  glVertex3i(150, 150, 0);
  glVertex3i(150, -150, 0);
  glVertex3i(-150, -150, 0);
  glEnd();
  glPopMatrix();
  glPushMatrix();
  //move the green square to the background
  glTranslatef(32.5, 32.5, -2);
  glColor3f(0, 1, 0);
  glBegin(GL_POLYGON);
  glVertex3i(-150, 150, 0);
  glVertex3i(150, 150, 0);
  glVertex3i(150, -150, 0);
  glVertex3i(-150, -150, 0);
  glEnd();
  glPopMatrix();
  glutSwapBuffers();
}

int main(int argc, char* argv[])
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
  glutInitWindowSize(windowWidth, windowHeight);
  glutInitWindowPosition((GetSystemMetrics(SM_CXSCREEN) - windowWidth) / 2, (GetSystemMetrics(SM_CYSCREEN) - windowHeight) / 2);
  glutCreateWindow("Window");
  init();
  glutMainLoop();
  return 0;
}

Solution

  • You've to enable the Depth Test:

    glEnable( GL_DEPTH_TEST );
    

    The default depth test function (glDepthFunc) is < (GL_LESS).
    If the distance to the far plane is 2.0 and the geometry is drawn with z coordinate of 2.0, then the geometry is clipped by the far plane, because the depth of the geometry is not less than the initialization depth of the depth buffer.

    Change the depth function to <= (GL_LEQUAL):

    glDepthFunc( GL_LEQUAL );
    

    In a Right-handed system the viewspace z-axis points out of the viewport. So if the z coordinate is "less than", then the object is "behind" an other object.

    The projection matrix transforms from view space to normalized device space. In compare to the view space, the normalized device space is a left handed system, where the z-axis points in the viewport. The normalized device z-coordinate in range [-1, 1] (from the front to the back), is mapped to the depth value (in general in range [0, 1]), which is used for the depth test.
    To deal with that glOrtho inverts the z-axis, if the near parameter is set less then the far parameter (this is how the function is suggested to be used).
    This cause that the depth (z) order doesn't change, when the geometry is transformed form view space to normalized device space.

    Note, glOrtho(-w, w, -h, h, -z, z) is the same as glScaled(1.0/w, 1.0/h, -1.0/z)

    Since the z-axis is not inverted by the orthographic projection in your example, because near > far,

    glOrtho(-windowWidth / 2, windowWidth / 2, -windowHeight / 2, windowHeight / 2, 2, -2);
    

    the z coordinate has to be greater, to be "behind".

    If the green rectangle should be behind the red one, then you've to change the orthographic projection (near < far). e.g.:

    glOrtho(-windowWidth / 2, windowWidth / 2, -windowHeight / 2, windowHeight / 2, -2, 2);
    

    If you don't want to change the projection, then you've to swap the z-coordinates of the geometry:

    glPushMatrix();
    //move the red square to the foreground
    glTranslatef(-32.5, -32.5, -2.0); // foreground because near > far
    // ...
    glPopMatrix();
    
    glPushMatrix();
    //move the green square to the background
    glTranslatef(32.5, 32.5, 2.0);   // background because near > far
    // ...
    glPopMatrix();