This chapter explains using color blending, accumulation buffers and multisampling. Blending is useful for a variety of effects, including reflections and fog. Multisampling can reduce aliasing, by sampling each primitive several times per pixel, a sort of averaging.
Multisampling is particularly easy to do: simply select the NSOpenGLView in Interface Builder, open the inspector, and change the sampling dropdown from the default of none to 2, 4, 6, 8, 9 or 16. OpenGL takes care of the rest behind the scenes, so no developer effort is required. The view does get drawn again for each sample, so there is a performance penalty.
Reflection, the first app I worked on, is Sphere World with a checkerboard ground plane which reflects the objects above the plane. The technique is to draw reflected versions of the objects, then draw the ground using blending, and finally draw the objects a second time right side up. The reflections look great.
I kept the shadow effect from the last version of Sphere World, and it didn’t quite work. The original shadow code simply drew solid black shadows. That totally wiped out any reflection at that spot, which is unrealistic because the reflected object does illuminate that spot, so there should be something there.
Blending the black shadow helped, but there was one bit of flakiness: if two parts of the torus blocked the same spot, the shadow was extra-dark. Turning depth testing back on eliminated the shadow entirely, instead of only casting the shadow once. I discovered that I could fix that by using the stencil buffer. When drawing the shadows, increment the stencil buffer by one every time you draw a fragment, and then only draw if the stencil buffer for that spot is zero. The second time the algorithm reaches the same spot, it skips drawing the shadow.
Doing this with the stencil buffer required…
- Adding a stencil buffer to the OpenGL view in Interface Builder
- Adding GL_STENCIL_BUFFER_BIT in the glClear() call
- Adding these lines to the OpenGL initialization:
glStencilOp(GL_INCR, GL_INCR, GL_INCR); glClearStencil(0); glStencilFunc(GL_EQUAL, 0x0, 0x01);
- Enabling GL_STENCIL_TEST just before drawing the shadows, and
- Disabling GL_STENCIL_TEST just after drawing the shadows
This took perhaps 15 minutes to accomplish.