5

I want to draw edges of an object with hidden edges removed. The idea I want to apply is to render the object's faces first to the depth buffer, then in a second pass drawing the edges with depth testing enabled.

Since not all triangle edges should be visible, the edges are stored separately (simple example: in a cube, the diagonal edges should not be visible, although they are there since a quad is rendered as two triangles). The faces are therefore drawn using GL_TRIANGLES, the edges are drawn uing GL_LINES with a separate vertex buffer.

The problem is that hidden edges are partly shown with this setup, and that visible edges are partly hidden. How can I achieve a proper result?

  • without depth testing:

    Without depth testing

  • with depth testing:

    With depth testing

  • faces which are rendered to depth buffer:

    Faces only

I use a framebuffer with an attached color and depth buffer to draw my object.

// Color buffer setup.
glBindTexture(GL_TEXTURE_2D, objectEdges);
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);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 640, 360, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil);

// Depth buffer setup.
glBindRenderbuffer(GL_RENDERBUFFER, objectFaces);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 640, 360);

// Framebuffer setup.
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 
                       GL_TEXTURE_2D, objectEdges, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, 
                          GL_RENDERBUFFER, objectFaces);
assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
assert(glGetError() == GL_NO_ERROR);

This setup works without any problems. I draw my object as following:

glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_TRUE);
glBindVertexArrayOES(vertexArray_faces);
glDrawArrays(GL_TRIANGLES, 0, vertexCount_faces);
glBindVertexArrayOES(0);

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_FALSE);
glBindVertexArrayOES(vertexArray_edges);
glDrawArrays(GL_LINES, 0, vertexCount_edges);
glBindVertexArrayOES(0);

glDisable(GL_DEPTH_TEST);

The used shader is just a standard model-view-projection vertex shader, and a fragment shader which outputs white for all fragments.

GLKMatrix4 projectionMatrix = 
    GLKMatrix4MakePerspective(
        GLKMathDegreesToRadians(65.0f), 640.0 / 360.0f, 0.01f, 10.0f);
GLKMatrix4 modelViewMatrix = 
    GLKMatrix4MakeLookAt(0.2f, 0.4f, 0.2f, 0.2f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
Etan
  • 17,014
  • 17
  • 89
  • 148

1 Answers1

7

You're running into the issue that depth values are calculated for lines slightly differently than for filled primitives. The very thing you try to do is one of the reasons for the existance of the so called "polygon offest". The whole thing is described in the official programming guide in the appendix: "Hidden-Line Removal"

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • An additional problem was that `glClear(GL_DEPTH_BUFFER_BIT)` only works when `glDepthMask(GL_TRUE)` was called before. Otherwise, the depth buffer is not writeable. – Etan Nov 24 '11 at 14:38