CS 381 Fall 2013 > Lecture Notes for Friday, October 25, 2013 |
CS 381 Fall 2013
Lecture Notes for Friday, October 25, 2013
Splines (cont’d)
See the Wednesday, October 23, 2013 lecture notes.
Front & Back of Polygons
Front- & Back-Facing
When a polygon is rendered, the user only sees one side of it, in any single animation frame. Thus each rendered polygon is either front-facing or back-facing. Which of these two categories a polygon lies in is determined by OpenGL at the end of Vertex Processing, after clipping.
For a primitive containing separate polygons
(GL_TRIANGLES
,
GL_QUADS
,
GL_POLYGON
),
a front-facing polygon is one in which the vertices appear
in the viewport in counterclockwise order.
The vertices of a back-facing polygon appear in clockwise order.
For a primitive describing a ribbon of triangles
(GL_TRIANGLE_STRIP
,
GL_TRIANGLE_FAN
),
the above technique is used to determine
whether the first polygon in the ribbon is front-facing.
Then the rest are handled in a consistent manner,
so that one side of the ribbon is the front,
and the other is the back.
Lastly, GL_QUAD_STRIP
is handled just as if it were
GL_TRIANGLE_STRIP
.
An important point:
- The normal vector is not used in front-facing/back-facing determination.
You can reverse the vertex order used for front-facing determination.
Call glFrontFace
with parameter GL_CW
to make clockwise vertex order indicate a front-facing polygon,
and counter-clockwise order a back-facing polygon.
[C++]
glFrontFace(GL_CW);
Pass GL_CCW
to restore the default behavior.
A GLSL fragment shader can determine whether the current fragment
comes from a front- or back-facing polygon
by reading gl_FrontFacing
,
a predefined bool
.
Culling
Many objects are drawn so that the user never sees the back side of any polygons. Thus, if we avoid drawing them, we may save time. Throwing out polygons is called culling; when we throw out back-facing polygons, it is back-face culling. In the pipeline, culling is done at the end of Vertex Processing, after clipping.
In OpenGL, turn on culling with
[C++]
glEnable(GL_CULL_FACE);
and turn it off with the corresponding glDisable
call.
OpenGL can cull back-facing polygons, front-facing polygons,
or (oddly) both.
Determine which is done using glCullFace
,
passing
GL_FRONT
,
GL_BACK
,
or GL_FRONT_AND_BACK
.
For example, to do back-face culling:
[C++]
glCullFace(GL_BACK); // This is the default setting
We can produce the same effect in a fragment shader—although it is less efficient, since it requires the polygon to be rasterized:
[GLSL fragment shader]
if (!gl_FrontFacing) discard;
Note that back-face culling can be used as a simple HSR method. It works when the only thing drawn is a convex object (sphere, cube, etc.), with all polygons facing outward. More generally, it also works when many such objects are drawn, in back-to-front order.
Another application of culling is to render the two sides of a surface using different shaders. Render using one shader with back-face culling enabled, and render using another shader with front-face culling enabled.
Two-Sided Coloring
We may wish a shader to color the two sides of a polygon differently.
GLSL supports automatic choosing of different colors for front-facing and back-facing polygons. I am not terribly fond of this automatic method, but here it is.
To enable distinct front & back colors, do (in your application):
[C++]
glEnable(GL_VERTEX_PROGRAM_TWO_SIDE);
Apparently some implementations also require the following in the vertex shader (and it does not seem to hurt):
[GLSL vertex shader]
#extension GL_VERTEX_PROGRAM_TWO_SIDE : enable
If you do the above, then you need to set both
gl_FrontColor
and gl_BackColor
in the vertex shader.
In the fragment shader,
gl_Color
is set to the interpolated color value, as usual.
However, for back-facing polygons,
the value of gl_BackColor
is used.
For front-facing polygons,
the value of gl_FrontColor
is used, as before.
Again, I am not fond of the above method, since it requires shader
functionality to be enabled in the application;
it seems to me that this is putting information in the wrong place.
I prefer to send the color for back-facing polygons in my own
varying
variable.
[GLSL vertex shader]
varying vec4 mybackcolor; ... gl_FrontColor = ...; // Color for front-facing polygon mybackcolor = ...; // Color for back-facing polygon[GLSL fragment shader]
varying vec4 mybackcolor; ... vec4 mycolor = gl_FrontFacing ? gl_Color : mybackcolor; // Select color based on which side the user sees
Alternatively, just hard-code the back-facing color into the fragment shader.
[GLSL fragment shader]
vec4 mycolor = gl_FrontFacing ? gl_Color : vec4(0.2, 0.7, 0.2, 1.0); // Select color based on which side the user sees
Two-Sided Lighting
In order to light both sides of a polygon correctly, reverse the normal when the polygon is back-facing.
[GLSL fragment shader]
if (!gl_FrontFacing) mynorm = -mynorm;
For this to work properly, the normals specified for the surface should point toward the front side of the polygons.
See
twoside_shaders.zip
for examples of shaders that do two-sided coloring/lighting.
These shaders are intended for use with
useshaders.cpp
.
Note: The teapot drawn
by glutSolidTeapot
has its normals facing outward,
as we would expect.
However, when we try two-sided lighting with the teapot,
we find that, tragically,
it has the back side of its polygons facing outward.
Thus, the normals point toward the back side of the polygons.
This can be fixed using glFrontFace
as described above.
I suggest doing this fix just before calling glutSolidTeapot
and then restoring the default behavior just afterward.
[C++]
glFrontFace(GL_CW); glutSolidTeapot(1.); glFrontFace(GL_CCW);
Application
useshaders.cpp
has been modified so that the teapot is handled as above.
The cylinder has also been fixed so that its polygons
face outward.
Application
bezierpatch.cpp
has been similarly modified.
(Apparently, the normals generated via enabling
GL_AUTO_NORMAL
are backwards—or
else I am doing something wrong).