Until now, we have set the current color only once and drawn only a single shape. Now, with multiple polygons, things get slightly more interesting. We want to use different colors so we can see our work more easily. Colors are actually specified per vertex, not per polygon. The shading model affects whether the polygon is then solidly colored (using the current color selected when the last vertex was specified), or smoothly shaded between the colors specified for each vertex.
The line glShadeModel(GL_FLAT); tells OpenGL to fill the polygons with the solid color that was current when the polygon’s last vertex was specified. This is why we can simply change the current color to red or green before specifying the next vertex in our triangle fan. On the other hand, the line glShadeModel(GL_SMOOTH); would tell OpenGL to shade the triangles smoothly from each vertex, attempting to interpolate the colors between those specified for each vertex. You’ll be learning much more about color and shading in Chapter 8.
Hold down one of the arrow keys to spin the cone around, and don’t select anything from the Effects menu yet. You’ll notice something unsettling: The cone appears to be swinging back and forth plus and minus 180?, with the bottom of the cone always facing you, but not rotating a full 360?. Figure 6-17 shows this more clearly.
This is occurring because the bottom of the cone is being drawn after the sides of the cone are drawn. This means, no matter how the cone is oriented, the bottom is then drawn on top of it, producing the “wobbling” illusion. This effect is not limited to just the various sides and parts of an object. If more than one object is drawn and one is in front of the other (from the viewer’s perspective), the last object drawn will still appear over the previously drawn object.
You can correct this peculiarity with a simple technique called hidden surface removal, and OpenGL has functions that will do this for you behind the scenes. The concept is simple: When a pixel is drawn, it is assigned a value (called the z value) that denotes its distance from the viewer’s perspective. Later, when another pixel needs to be drawn to that screen location, the new pixel’s z value is compared to that of the pixel that is already stored there. If the new pixel’s z value is higher, then it is closer to the viewer and thus in front of the previous pixel, so the previous pixel will be obscured by the new pixel. If the new pixel’s z value is lower, then it must be behind the existing pixel and thus would not be obscured. This maneuver is accomplished internally by a depth buffer, which will be discussed in Chapter 15.
To enable depth testing, simply call
This is done in Listing 6-8 when the bDepth variable is set to True, and depth testing is disabled if bDepth is False.
// Enable depth testing if flag is set if(bDepth) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
The bDepth variable is set when Depth Test is selected from the Effects menu. In addition, the depth buffer must be cleared each time the scene is rendered. The depth buffer is analogous to the color buffer in that it contains information about the distance of the pixels from the observer. This is used to determine if any pixels are hidden by pixels closer to the observer.
// Clear the window and the depth buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Figure 6-18 shows the Effects menu with depth testing enabled. It also shows the cone with the bottom correctly hidden behind the sides. You can see that depth testing is practically a prerequisite to creation of 3D objects out of solid polygons.
You can see that there are obvious visual advantages to not drawing a surface that is obstructed by another. Even so, you pay some performance overhead because every pixel drawn must be compared with the previous pixel’s z value. Sometimes, however, you know that a surface will never be drawn anyway, so why specify it? The answer is that you may not wish to draw the back sides of the surface.
In our working example, the cone is a closed surface and we never see the inside. OpenGL is actually (internally) drawing the back sides of the far side of the cone, and then the front sides of the polygons facing us. Then, by a comparison of z buffer values, the far side of the cone is eliminated. Figures 6-19a and 6-19b show our cone at a particular orientation with depth testing turned on (a) and off (b). Notice that the green and red triangles that make up the cone sides change when depth testing is enabled. Without depth testing, the sides of the triangles at the far side of the cone show through.
Earlier in the chapter we explained how OpenGL uses winding to determine the front and back sides of polygons, and that it is important to keep the polygons that define the outside of your objects wound in a consistent direction. This consistency is what allows us to tell OpenGL to render only the front, only the back, or both sides of polygons. By eliminating the back sides of the polygons, we can drastically reduce the amount of necessary processing to render the image. Even though depth testing will eliminate the appearance of the inside of objects, internally OpenGL must take them into account unless we explicitly tell it not to.
The elimination of the front or back of polygons is called culling. Culling is enabled or disabled for our program by the following code fragment from Listing 6-8:
// Clockwise-wound polygons are front facing; this is reversed // because we are using triangle fans glFrontFace(GL_CW); … … // Turn culling on if flag is set if(bCull) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);