next up previous contents
Next: 7.5 Preventing Smooth Wide Up: 7 Line Rendering Techniques Previous: 7.3 Haloed Lines   Contents


7.4 Silhouette Edges

Sometimes it can be useful for highlighting purposes to draw a silhouette edge around a complex object. A silhouette edge defines the outer boundaries of the object with respect to the viewer as shown in Figure 46.

The stencil buffer can be used to render a silhouette edge around an object. With this technique, you can render the object, then draw a silhouette around it, or just draw the silhouette itself [87].

The object is drawn 4 times; each time displaced by one pixel in the $x$ or $y$ direction. This offset must be done in window coordinates. An easy way to do this is to change the viewport coordinates each time, changing the viewport transform. The color and depth values are turned off, so only the stencil buffer is affected.

Every time the object covers a pixel, it increments the pixel's stencil value. When the four passes have been completed, the perimeter pixels of the object will have stencil values of 2 or 3. The interior will have values of 4, and all pixels surrounding the object exterior will have values of 0 or 1.

Here is the algorithm in detail:

  1. If you want to see the object itself, render it in the usual way.
  2. Clear the stencil buffer to zero.
  3. Disable writing to the color and depth buffers.
  4. Set the stencil function to always pass, set the stencil operation to increment.
  5. Translate the object by +1 pixel in $y$, using glViewport().
  6. Render the object.
  7. Translate the object by -2 pixels in $y$, using glViewport().
  8. Render the object.
  9. Translate by +1 pixel $x$ and +1 pixel in $y$.
  10. Render.
  11. Translate by -2 pixel in $x$.
  12. Render.
  13. Translate by +1 pixel in $x$. You should be back to the original position.
  14. Turn on the color and depth buffer.
  15. Set the stencil function to pass if the stencil value is 2 or 3. Since the possible values range from 0 to 4, the stencil function can pass if stencil bit 1 is set (counting from 0).
  16. Rendering any primitive that covers the object will draw only the pixels of the silhouette. For a solid color silhouette, render a polygon of the color desired over the object.

One of the bigger drawbacks of this algorithm is that it takes a large number of drawing passes to generate the edges. A somewhat more efficient algorithm suggested by Akeley[4] is to use glPolygonOffset() to draw an offset depth image and then draw the polygons using GL_ LINE polygon mode. The stencil buffer is again used to count the number of times each pixel is written. However, instead of counting the absolute number of writes to a pixel, the stencil value is inverted on each write. The resulting stencil buffer will contain a one wherever a pixel has been drawn an odd number of times. This ensures that lines drawn at the shared edges of polygon faces have stencil values of zero since the lines will be drawn twice. While this algorithm is a little more approximate then the previous algorithm it only requires two passes through the geometry.


% latex2html id marker 7306
\fbox{\begin{tabular}{c}
\vrule width 0pt height 0.1...
... Solid Image, Silhouette Edges,
Silhouette and Boundary Edges}\\
\end{tabular}}

The faster algorithm does not generate quite the same result as the first algorithm since it counts even and odd transitions and relies on the depth image to ensure that other non-visible surfaces do not interfere with the stencil count. The differences arise in that boundary edges within one object that are in front of another object will be rendered as part of the silhouette image. By boundary edges we mean the true edges edges of the modeled geometry and do not include the interior shared-face edges. In many cases this artifact is useful as silhouette edges by themselves often do not provide sufficient information about the shape of objects. It is possible to combine the algorithm for drawing silhouettes with an additional step in which all of the boundary edges of the geometry are drawn as lines to produce a hidden line drawing displaying boundary edges plus silhouette edges.

The steps of the combined algorithm are:

  1. Clear the depth and color buffers and clear the stencil buffer to zero.
  2. Disable color buffer writes.
  3. Draw the depth buffered geometry using glPolygonOffset() to offset the image towards the far clipping plane.
  4. Disable writing to the depth buffer and glPolygonOffset().
  5. Set the stencil function to always pass and set the stencil operation to invert.
  6. Enable face culling.
  7. Draw the geometry as lines using glPolygonMode().
  8. Enable writes to the color buffer, disable face culling.
  9. Set the stencil function to pass if the stencil value is 1.
  10. Rendering a rectangle that fills the entire window (this will produce the silhouette image).
  11. Draw the true edges of the geometry.
  12. Enable writes to the depth buffer.

Since the algorithm uses an offset depth image it is susceptible to minor artifacts from the interaction of the lines and the depth image similar to those present when using glPolygonOffset() for hidden line drawings.


next up previous contents
Next: 7.5 Preventing Smooth Wide Up: 7 Line Rendering Techniques Previous: 7.3 Haloed Lines   Contents
2001-01-10