要模拟真实世界,仅有环境光是不够的,需要指定更多的光源来提升真实感。OpenGL至少提供8种光源,可以在场景中的任意位置甚至是可视区域之外。你可以指定光源位于无限远处以获得平行光束,或者光源位于近处向外发射光束。还可以制造出聚光灯的效果。
你可以指定一个光源位于哪里朝哪个方向发射光线。通常光源朝所有方向发射光线,但也可以为它指定方向。在指定的方向的光源环境下,并非每个多边形都需要被照射到。我们可以制造物体的阴影效果。OpenGL可以计算光线和物体的角度。
如下图:光源发射出的光线照射到四边形上,形成一个入射角,再反射到观察者眼中形成一个反射角。根据这个入射角和反射角结合光源和材料的属性我们可以计算出该点应呈现出什么样的颜色。

在编程的角度上看,每一个多边形由一系列的顶点创建的。多边形就是由点组成的,那么如何计算光线与点之间的角度。我们无法在3D空间中找到一个切确的线与点之间的角度,因此我们为每一个点设置一个法线。三维平面的法线是垂直于该平面的三维向量。曲面在某点P处的法线为垂直于该点切平面的向量。(wiki)
法线向量是一条垂直于真实平面或虚拟平面的线。下图是2D和3D中的法线向量。

为什么需要为每个顶点设置法线向量,而不是为一个多边形指定一个法线向量。其中的原因是,并不是所有的物体的表面都是平的,有时法线向量并不需要精确地垂直于物体的表面。通过扭曲平面的法线向量可以制造出光滑的曲面的视觉效果。

如上图:为多边形的顶点(1,1,0)处指定一个法线向量,我们可以选定一另个点(1,10,0),并以(1,1,0)为起点连接第二个点(1,10,0),所形成的线就是法线向量。第二个点说明了法线向量的方向是y轴向上。法线向量的方向还可以用于表明多边形的正面和反面。法线是由正面指向外面的(或者说从法线的箭头方向往下看到的就是多边形的正面)。
用法线的第二个点减去第一个顶点,所得的结果就是相对于x、y和z的单位距离。
(1,10,0) - (1,1,0) = (0,9,0)
如果把这个顶点移动到原点,上面两点相减得出的点指定了和表面呈90度角的方向。如下图:
![Image[1] Image[1]](../../upload/2015_02/150225145693424.png)
向量的方向告诉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个表示坐标的值,指定一条垂直于三角形表面的法线向量。上面的例子的三个顶点的法线具有相同的方向。
单位法线即长度为1的法线向量。已知法线向量(x,y,z)求单位法线,先求出法线向量的长度即
,再用(x,y,z)除以这个长度,就得到单位法线。这个过程叫归一化(normalization)。在光照计算的中所有的法线向量都会归一化。
可以让OpenGL自动把法线向量转换为单位法线,通过调用glEnable接受一个参数GL_NORMALIZE;
glEnable(GL_NORMALIZE);
这种做法在某些实现上,会带来性能的开销。最好的方式在指定法线向量的时候,就直接指定为单位法线,而不是依靠OpenGL来帮你做转换。
PS:glScale缩放函数也会缩放法线的长度。如果一起使用glScale和光照,有可能会得到不是你想要的光照效果。
如果每个顶点指定的法线都是单位法线,并且glScale使用相同的比例进行缩放的情况下,有一个替代方案是使用GL_RESCALE_NORMALS替代GL_NORMALIZE.
glEnable(GL_RESCALE_NORMALS);
这样就告诉了OpenGL,你的法线向量不是单位长度的,但是可以通过相同比例因子的缩放来把它转化为单位长度(单位法线)。这样OpenGL会检查你的模型视图变换矩阵来逆转换。这样在每个顶点所做的数学操作要少一些。
个人理解:
一个单位法线向量(1.0,1.0,1.0),在进行过glScalef(2.0, 2.0, 2.0)之后,就变为了(2.0,2.0,2.0),那么在启用了GL_RESCALE_NORMALS之后,OpenGL会检查模型视图矩阵得知,可以通过反向的缩放把法线向量转换会单位法线即等比例的缩小2倍乘以0.5.又得到了(1.0, 1.0, 1.0)。
PS:最佳实践是在一开始为顶点指定法线时,就指定为单位法线。
当一个多边形不平行于任何一个轴面时,通过简单的观察来指定一个法线会比较困难。所以需要一个简单的方法来计算3D空间中任意多边形的法线。

可以通过多边形上的任意三个点来计算法线。

如上图,知道了P1,P2,P3点之后。由P1和P2可以求出向量V1,P1和P3可以求出向量V2,再用V1 X V2(叉乘)计算得出正交于V1和V2的法线向量。

math3d代码示例:
创建一个位于左上角的温和的白光光源。
?
?
?
?
?
?
效果
