Before diving into lighting, let’s take a
closer look at depth buffers, since we’ll need to add one to wireframe
viewer.Example 1. Depth buffer setup
// Create the depth buffer. glGenRenderbuffersOES(1, &m_depthRenderbuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_depthRenderbuffer); glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, width, height); // Create the framebuffer object; attach the depth and color buffers. glGenFramebuffersOES(1, &m_framebuffer); glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_framebuffer); glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, m_colorRenderbuffer); glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, m_depthRenderbuffer); // Bind the color buffer for rendering. glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_colorRenderbuffer);
glViewport(0, 0, width, height); glEnable(GL_DEPTH_TEST);
...
|
Why does HelloCone need a depth buffer when
wireframe viewer does not? When the scene is composed of nothing but
monochrome lines, we don’t care about the visibility
problem; this means we don’t care which lines are obscured by
other lines. HelloCone uses triangles rather than lines, so the visibility
problem needs to be addressed. OpenGL uses the depth buffer to handle this
problem efficiently.
Figure 1 depicts
ModelViewer’s depth buffer in grayscale: white pixels are far away, black
pixels are nearby. Even though users can’t see the depth buffer, OpenGL
needs it for its rendering algorithm. If it didn’t have a depth buffer,
you’d be forced to carefully order your draw calls from farthest to
nearest.
OpenGL uses a technique called depth
testing to solve the visibility problem. Suppose you were to
render a red triangle directly in front of the camera and then draw a
green triangle directly behind the red triangle. Even though the green
triangle is drawn last, you’d want to the red triangle to be visible; the
green triangle is said to be occluded. Here’s how
it works: every rasterized pixel not only has its RGB values written to
the color buffer but also has its Z value written to the depth buffer.
OpenGL “rejects” occluded pixels by checking whether their Z value is
greater than the Z value that’s already in the depth buffer. In
pseudocode, the algorithm looks like this:
void WritePixel(x, y, z, color)
{
if (DepthTestDisabled || z < DepthBuffer[x, y]) {
DepthBuffer[x, y] = z;
ColorBuffer[x, y] = color;
}
}
1. Beware the Scourge of Depth Artifacts
Something to watch out for with depth buffers
is Z-fighting, which is a visual artifact that
occurs when overlapping triangles have depths that are too close to each
other (see Figure 2).
Recall that the projection matrix defines a
viewing frustum bounded by six planes . The two planes that are perpendicular
to the viewing direction are called the near plane
and far plane. In ES 1.1, these planes are
arguments to the glOrtho or
glPerspective functions; in ES 2.0, they’re passed to
a custom function like the mat4::Frustum method in
the C++ vector library from the appendix.
It turns out that if the near plane is too
close to the camera or if the far plane is too distant, this can cause
precision issues that result in Z-fighting. However this is only one
possible cause for Z-fighting; there are many more. Take a look at the
following list of suggestions if you ever see artifacts like the ones in
Figure 2.
Push out your near plane.
For perspective projections, having the
near plane close to zero can be detrimental to precision.
Pull in your far plane.
Similarly, the far plane should still
be pulled in as far as possible without clipping away portions of
your scene.
Scale your scene smaller.
Try to avoid defining an
astronomical-scale scene with huge extents.
Increase the bit width of your depth buffer.
All iPhones and iPod touches (at the
time of this writing) support 16-bit and 24-bit depth formats. The
bit width is determined according to the argument you pass to
glRenderbufferStorageOES when allocating the
depth buffer.
Are you accidentally rendering coplanar triangles?
The fault might not lie with OpenGL but
with your application code. Perhaps your generated vertices are
lying on the same Z plane because of a rounding error.
Do you really need depth testing in the first place?
In some cases you should probably
disable depth testing anyway. For example, you don’t need it if
you’re rendering a 2D heads-up display. Disabling the depth test
can also boost performance.