我们在使用光源时,除了强度和颜色之外,还需要指定光源的位置和方向,并且这些光源的位置和方向将会极大地影响场景的外观。
OpenGL至少支持8种独立的光源。当我们指定一个光源时,便要告诉OpenGL这个光源的位置以及它的照射方向。光源经常向四周照射,但也可以向一个方向照射。无论在哪种情况下,对于我们所绘制的任何物体,来自任何光源的光线(除了纯粹的环境光源之外)都将根据一个角度撞击组成这个物体的多边形的表面。为了计算围绕多边形表面的着色效果,OpenGL必须能够计算光线与多边形表面之间的角度。
设想一下,一个多边形被一束来自某个光源的光线所照射,当这束光线照射到多边形的表面时,它与平面会形成一个角度(A);然后,光线按照一个角度(B)向观察者反射(观察者不一定能够看到它)。通过这些角度,再加上我们之前所讨论的光照参数和材料属性,就可以计算这个位置的外观颜色了,如图所示
由于OpenGL根据每个顶点计算外观颜色,那么当这每个顶点从某个角度被一束光线所照射时,我们该如何计算顶点和光线之间的角度呢?当然,我们无法用几何的方法找到3D空间中一个点和一条直线的角度,因为它存在无穷的可能性。为了解决这个问题,我们必须将每个顶点与一些信息相关联,这就是我们接下来要说的,表示每个顶点垂直向上的向量——法线。
表面法线
在一个假想的平面(或多边形)上,一条垂直向上的向量称为法线向量。实际上,它就是一条指向某个方向的直线(向量),它与多边形的表面呈90度角。
指定法线
在OpenGL中,下面这段代码指定了一条法线向量:
glBegin(GL_TRIANGLES);
glNormal3f(0.0f, -1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 60.0f);
glVertex3f(-15.0f, 0.0f, 30.0f);
glVertex3f(15.0f, 0.0f, 30.0f);
glEnd();
glNormal3f函数接受3个表示坐标的值(X,Y,Z),它指定了一条垂直于这个三角形表面的法线向量。在这里,所有三个顶点的法线具有相同的方向,都是沿Y轴的负方向向下。这是一个简单的示例,因为这个三角形平平地躺在X-Z平面上。
当我们开始绘图时,为每个顶点或多边形指定一条法线的任务几乎是不可能的,尤其是在只有很少一部分表面是平行于三个主平面的情况下。当然,我们会有解决的办法,可以通过调用一个glTools函数库中的方法,来生成我们所需要的法线。
生成法线
我们可以通过取多边形平面上的3个点来计算它的法线向量。如图所示,平面上的3个点P1、P2、P3,我们可以定义两个向量,从P1至p2的向量V1以及从P1至P3的向量V2。从数学的角度而言,三维空间中的两个向量定义了一个平面。我们对这两个向量求叉积(V1×V2),其结果所产生的向量与这个平面垂直,也就是我们所需的法线向量,如图中的向量V3所示:
glTool函数库包含了一个函数,专门用于根据一个多边形上的3个点计算一条法线向量:
void m3dFindNormal(M3DVector3f vNormal,
const M3DVector3f vP1,
const M3DVector3f vP2,
const M3DVector3f vP3);
该方法的第一个参数用于存储求得的法线向量,还要另外向它传递3个向量,表示取自多边形或三角形上的点(以逆时针环绕方向指定)。注意,该方法返回的法线向量并不一定是单位长度的。
单位法线
所谓单位法线,就是长度为1的法线。将法线转换为单位法线的过程称为法线的规范化。在OpenGL的实现中,对于光照计算,所有的法线向量都必须先进行规范化,然后再参与计算。