📄 chapter06.html
字号:
<P>Remember that the colors across the face of a smooth-shaded polygon
are determined by the colors calculated for the vertices. Because of this,
you probably want to avoid using large polygons with local lights - if
you locate the light near the middle of the polygon, the vertices might
be too far away to receive much light, so the whole polygon will look darker
than you intended. To avoid this problem, break up the large polygon into
smaller ones.
<P>For real-world lights, the intensity of light decreases as distance
from the light increases. Since a directional light is infinitely far away,
it doesn't make sense to attenuate its intensity over distance, so attenuation
is disabled for a directional light. However, you might want to attenuate
the light from a positional light. OpenGL attenuates a light source by
multiplying the contribution of that source by an attenuation factor:
<P><IMG SRC="figures/eq601.gif" ALT="[IMAGE]" >
<P>where
<P><I>d</I> = distance between the light's position and the vertex
<P><I>k</I>c = GL_CONSTANT_ATTENUATION
<P><I>k</I>l = GL_LINEAR_ATTENUATION
<P><I>k</I>q = GL_QUADRATIC_ATTENUATION
<P>By default,<I> k</I>c is 1.0 and both <I>k</I>l and <I>k</I>q are zero,
but you can give these parameters different values:
<PRE>glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 2.0);
glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 1.0);
glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.5);</PRE>
Note that the ambient, diffuse, and specular contributions are all attenuated.
Only the emission and global ambient values aren't attenuated.
<H3>
Spotlights</H3>
As previously mentioned, you can have a positional light source act as
a spotlight - that is, by restricting the shape of the light it emits to
a cone. To create a spotlight, you need to determine the spread of the
cone of light you desire. (Remember that since spotlights are positional
lights, you also have to locate them where you want them. Again, note that
nothing prevents you from creating a directional spotlight, but it probably
won't give you the result you want.) To specify the angle between the axis
of the cone and a ray along the edge of the cone, use the GL_SPOT_CUTOFF
parameter. The angle of the cone at the apex is then twice this value,
as shown in Figure 6-2 .
<P><IMG SRC="figures/spot.gif" ALT="[IMAGE]" >
<P><B>Figure 6-2 : </B>The GL_SPOT_CUTOFF Parameter
<BR>
<BR>
<P>Note that no light is emitted beyond the edges of the cone. By default,
the spotlight feature is disabled because the GL_SPOT_CUTOFF parameter
is 180.0. This value means that light is emitted in all directions (the
angle at the cone's apex is 360 degrees, so it isn't a cone at all). The
value for GL_SPOT_CUTOFF is restricted to being within the range [0.0,90.0]
(unless it has the special value 180.0). The following line sets the cutoff
parameter to 45 degrees:
<PRE>glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 45.0);</PRE>
You also need to specify a spotlight's direction, which determines the
axis of the cone of light:
<PRE>GLfloat spot_direction[] = { -1.0, -1.0, 0.0 };
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spot_direction);</PRE>
The direction is specified in homogeneous object coordinates. By default,
the direction is (0.0, 0.0, -1.0), so if you don't explicitly set the value
of GL_SPOT_DIRECTION, the light points down the negative <I>z</I>-axis.
Also, keep in mind that a spotlight's direction is transformed by the modelview
matrix just as though it were a normal vector, and the result is stored
in eye coordinates. (See "Controlling a Light's Position and Direction"
for more information about such transformations.)
<P>In addition to the spotlight's cutoff angle and direction, you can control
the intensity distribution of the light within the cone, in two ways. First,
you can set the attenuation factor described earlier, which is multiplied
by the light's intensity. You can also set the GL_SPOT_EXPONENT parameter,
which is by default zero, to control how concentrated the light is. The
light's intensity is highest in the center of the cone. It's attenuated
toward the edges of the cone by the cosine of the angle between the direction
of the light and the direction from the light to the vertex being lighted,
raised to the power of the spot exponent. Thus, higher spot exponents result
in a more focused light source. See "The Mathematics of Lighting" for more
details on the equations used to calculate light intensity.
<H3>
Multiple Lights</H3>
As mentioned, you can have at least eight lights in your scene (possibly
more, depending on your OpenGL implementation). Since OpenGL needs to perform
calculations to determine how much light each vertex receives from each
light source, increasing the number of lights adversely affects performance.
The constants used to refer to the eight lights are GL_LIGHT0, GL_LIGHT1,
GL_LIGHT2, GL_LIGHT3, and so on. In the preceding discussions, parameters
related to GL_LIGHT0 were set. If you want an additional light, you need
to specify its parameters; also, remember that the default values are different
for these other lights than they are for GL_LIGHT0, as explained in Table
6-1 . The following lines of code define a white attenuated spotlight:
<PRE>GLfloat light1_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
GLfloat light1_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat light1_specular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat light1_position[] = { -2.0, 2.0, 1.0, 1.0 };
GLfloat spot_direction[] = { -1.0, -1.0, 0.0 };
glLightfv(GL_LIGHT1, GL_AMBIENT, light1_ambient);
glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse);
glLightfv(GL_LIGHT1, GL_SPECULAR, light1_specular);
glLightfv(GL_LIGHT1, GL_POSITION, light1_position);
glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.5);
glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.5);
glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.2);
glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, 45.0);
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spot_direction);
glLightf(GL_LIGHT1, GL_SPOT_EXPONENT, 2.0);
glEnable(GL_LIGHT1);</PRE>
If these lines were added to Example 6-1 , the sphere would be lit with
two lights, one directional and one spotlight.
<H4>
Try This</H4>
Try This
<P>Modify Example 6-1 :
<UL>Change the light to be a positional colored light rather than a directional
white one.
<BR>
<P>Add an additional colored spotlight. Hint: Use some of the code shown
in the preceding section.
<BR>
<P>Measure how these two changes affect performance.</UL>
<H3>
Controlling a Light's Position and Direction</H3>
OpenGL treats the position and direction of a light source just as it treats
the position of a geometric primitive. In other words, a light source is
subject to the same matrix transformations as a primitive. More specifically,
when <B>glLight*()</B> is called to specify the position or the direction
of a light source, the position or direction is transformed by the current
modelview matrix and stored in eye coordinates. This means you can manipulate
a light source's position or direction by changing the contents of the
modelview matrix stack. (The projection matrix has no effect on a light's
position or direction.) This section explains how to achieve three different
effects by changing the point in the program at which the light position
is set, relative to modeling or viewing transformations:
<UL>A light position that remains fixed
<BR>
<P>A light that moves around a stationary object
<BR>
<P>A light that moves along with the viewpoint</UL>
In the simplest example, as in Example 6-1 , the light position remains
fixed. To achieve this effect, you need to set the light position after
whatever viewing and/or modeling transformation you use. Here's what the
relevant code from the <B>myinit()</B> and <B>myReshape()</B> routines
might look like:
<PRE>glViewport(0, 0, w, h);
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
if (w <= h)
glOrtho (-1.5, 1.5, -1.5*h/w, 1.5*h/w, -10.0, 10.0);
else
glOrtho (-1.5*w/h, 1.5*w/h, -1.5, 1.5, -10.0, 10.0);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity();
/* later in myInit() */
GLfloat light_position[] = { 1.0, 1.0, 1.0, 1.0 };
glLightfv(GL_LIGHT0, GL_POSITION, position);</PRE>
As you can see, the viewport and projection matrices are established first.
Then, the identity matrix is loaded as the modelview matrix, after which
the light position is set. Since the identity matrix is used, the originally
specified light position (1.0, 1.0, 1.0) isn't changed by being multiplied
by the modelview matrix. Then, since neither the light position nor the
modelview matrix is modified after this point, the light remains pointing
at (1.0, 1.0, 1.0).
<P>Now suppose you want to rotate or translate the light position so that
the light moves relative to a stationary object. One way to do this is
to set the light position after the modeling transformation, which is itself
changed specifically to modify the light position. You can begin with the
same series of calls in an <B>init()</B> routine early in the program.
Then, probably within an event loop, you need to perform the desired modeling
transformation (on the modelview stack) and reset the light position. Here's
what such code might look like:
<PRE>void display(GLint spin)
{
GLfloat light_position[] = { 0.0, 0.0, 1.5, 1.0 };
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glTranslatef(0.0, 0.0, -5.0);
glPushMatrix();
glRotated((GLdouble) spin, 1.0, 0.0, 0.0);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glPopMatrix();
auxSolidTorus();
glPopMatrix();
glFlush();
}</PRE>
This <B>display()</B> command causes the scene to be redrawn with the light
rotated <I>spin</I> degrees around a stationary torus. Note the two pairs
of <B>glPushMatrix()</B> and <B>glPopMatrix()</B> calls, which are used
to isolate the viewing and modeling transformations, all of which occur
on the modelview stack. Since in this example the viewpoint remains constant,
the current matrix is pushed down the stack and then the desired viewing
transformation is loaded with <B>glTranslatef()</B>. The matrix stack is
pushed again before the modeling transformation <B>glRotated()</B> is specified.
Then the light position is set in the new, rotated coordinate system so
that the light itself appears to be rotated from its previous position.
(Remember that the light position is stored in eye coordinates, which are
obtained after transformation by the modelview matrix.) After the rotated
matrix is popped off the stack, the torus is drawn.
<P>To create a light that moves along with the viewpoint, you need to set
the light position before the viewing transformation. Then, the viewing
transformation affects both the light and the viewpoint in the same way.
For this example, let's use a slightly different set of calls in the <B>myinit()</B>
routine:
<PRE>GLfloat light_position() = { 0.0, 0.0, 1.0, 1.0 };
glViewport(0, 0, w-1, h-1);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(40.0, (GLfloat) w/(GLfloat) h, 1.0, 100.0);
glMatrixMode(GL_MODELVIEW);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);</PRE>
Then, the <B>display()</B> routine that's called from the event loop to
redraw the scene might look like this:
<PRE>void display(GLint spin)
{
glClear(GL_COLOR_BUFFER_MASK | GL_DEPTH_BUFFER_MASK);
glPushMatrix();
glTranslatef (0.0, 0.0, -5.0);
glRotatef ((GLfloat) spin, 1.0, 0.0, 0.0);
auxSolidTorus();
glPopMatrix();
glFlush();
}</PRE>
When the lighted torus is redrawn, both the light position and the viewpoint
are moved <I>spin</I> degrees. Even though you haven't respecified the
light position, the light moves because the eye coordinate system has changed.
<H4>
Try This</H4>
Try This
<P>Modify Example 6-2 :
<UL>Make the light translate past the object instead of rotating around
it. Hint: Use <B>glTranslated()</B> rather than the first <B>glRotated()</B>
in <B>display()</B>, and choose an appropriate value to use instead of
<I>spin</I>.
<BR>
<P>Change the attenuation so that the light decreases in intensity as it's
moved away from the object. Hint: Add calls to <B>glLight*()</B> to set
the desired attenuation parameters.
<P><B>Example 6-2 : </B>Moving a Light with Modeling Transformations: movelight.c
<PRE>#include <GL/gl.h>
#include <GL/glu.h>
#include "aux.h"
static int spin = 0;
void movelight (AUX_EVENTREC *event)
{
spin = (spin + 30) % 360;
}
void myinit (void)
{
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glDepthFunc(GL_LEQUAL);
glEnable(GL_DEPTH_TEST);
}
void display(void)
{
GLfloat position[] = { 0.0, 0.0, 1.5, 1.0 };
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix ();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -