13 Drawing Lines over Polygons and Using Polygon Offset
13.010 What are the basics for using polygon
offset?
It's difficult to render coplanar
primitives in OpenGL for two reasons:
- Given two overlapping coplanar
primitives with different vertices, floating point
round-off errors from the two polygons can generate
different depth values for overlapping pixels. With
depth test enabled, some of the second polygon's
pixels will pass the depth test, while some will fail.
- For coplanar lines and polygons,
vastly different depth values for common pixels can
result. This is because depth values from polygon
rasterization derive from the polygon's plane
equation, while depth values from line rasterization
derive from linear interpolation.
Setting the depth function to GL_LEQUAL or
GL_EQUAL won't resolve the problem. The visual result is
referred to as stitching, bleeding, or Z
fighting.
Polygon offset was an extension to OpenGL 1.0,
and is now incorporated into OpenGL 1.1. It allows an
application to define a depth offset, which can apply to
filled primitives, and under OpenGL 1.1, it can be separately
enabled or disabled depending on whether the primitives are
rendered in fill, line, or point mode. Thus, an application
can render coplanar primitives by first rendering one
primitive, then by applying an offset and rendering the
second primitive.
While polygon offset can alter the depth
value of filled primitives in point and line mode, under no
circumstances will polygon offset affect the depth values of
GL_POINTS, GL_LINES, GL_LINE_STRIP, or GL_LINE_LOOP
primitives. If you are trying to render point or line
primitives over filled primitives, use polygon offset to push
the filled primitives back. (It can't be used to pull the
point and line primitives forward.)
Because polygon offset alters the correct Z
value calculated during rasterization, the resulting Z value,
which is stored in the depth buffer will contain this offset
and can adversely affect the resulting image. In many
circumstances, undesirable "bleed-through" effects
can result. Indeed, polygon offset may cause some primitives
to pass the depth test entirely when they normally would not,
or vice versa. When models intersect, polygon offset can
cause an inaccurate rendering of the intersection point.
13.020 What are the two parameters in a
glPolygonOffset() call and what do they mean?
Polygon offset allows the application to
specify a depth offset with two parameters, factor and
units. factor scales the maximum Z slope,
with respect to X or Y of the polygon, and units scales
the minimum resolvable depth buffer value. The results are
summed to produce the depth offset. This offset is applied in
screen space, typically with positive Z pointing into the
screen.
The factor parameter is required
to ensure correct results for filled primitives that are
nearly edge-on to the viewer. In this case, the difference
between Z values for the same pixel generated by two coplanar
primitives can be as great as the maximum Z slope in X or Y.
This Z slope will be large for nearly edge-on primitives, and
almost non-existent for face-on primitives. The factor parameter
lets you add this type of variable difference into the
resulting depth offset.
A typical use might be to set factor and
units to 1.0 to offset primitives into positive Z (into
the screen) and enable polygon offset for fill mode. Two
passes are then made, once with the model's solid geometry
and once again with the line geometry. Nearly edge-on filled
polygons are pushed substantially away from the eyepoint, to
minimize interference with the line geometry, while nearly
planar polygons are drawn at least one depth buffer unit
behind the line geometry.
13.030 What's the difference between the OpenGL
1.0 polygon offset extension and OpenGL 1.1 (and later) polygon
offset interfaces?
The 1.0 polygon offset extension didn't let
you apply the offset to filled primitives in line or point
mode. Only filled primitives in fill mode could be offset.
In the 1.0 extension, a bias
parameter was added to the normalized (0.0 - 1.0) depth value,
in place of the 1.1 units parameter. Typical
applications might obtain a good offset by specifying a bias
of 0.001.
See the GLUT example, which
renders two cylinders, one using the 1.0 polygon offset
extension and the other using the 1.1 polygon offset
interface.
13.040 Why doesn't polygon offset work when I
draw line primitives over filled primitives?
Polygon offset, as its name implies, only
works with polygonal primitives. It affects only the filled
primitives: GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN,
GL_QUADS, GL_QUAD_STRIP, and GL_POLYGON. Polygon offset will
work when you render them with glPolygonMode set to GL_FILL,
GL_LINE, or GL_POINT.
Polygon offset doesn't affect non-polygonal
primitives. The GL_POINTS, GL_LINES, GL_LINE_STRIP, and GL_LINE_LOOP
primitives can't be offset with glPolygonOffset().
13.050 What other options do I have for drawing
coplanar primitives when I don't want to use polygon offset?
You can simulate the effects of polygon
offset by tinkering with glDepthRange(). For example, you
might code the following:
glDepthRange (0.1, 1.0);
/* Draw underlying geometry */
glDepthRange (0.0, 0.9);
/* Draw overlying geometry */
This code provides a fixed offset in Z, but
doesn't account for the polygon slope. It's roughly
equivalent to using glPolygonOffset with a factor parameter
of 0.0.
You can render coplanar primitives with the
Stencil buffer in many creative ways. The OpenGL
Programming Guide outlines one
well-know method. The algorithm for drawing a polygon and its
outline is as follows:
- Draw the outline into the color, depth,
and stencil buffers.
- Draw the filled primitive into the
color buffer and depth buffer, but only where the
stencil buffer is clear.
- Mask off the color and depth buffers,
and render the outline to clear the stencil buffer.
On some SGI OpenGL platforms, an
application can use the SGIX_reference_plane extension. With
this extension, the user specifies a plane equation in object
coordinates corresponding to a set of coplanar primitives.
You can enable or disable the plane. When the plane is
enabled, all fragment Z values will derive from the specified
plane equation. Thus, for any given fragment XY location, the
depth value is guaranteed to be identical regardless of which
primitive rendered it.
|