法线
前面的教程中我们一直在处理法线,但是并不知道法线到底是什么。
##三角形法线
一个平面的法线是一个长度为1并且垂直于这个平面的向量。 一个三角形的法线是一个长度为1并且垂直于这个三角形的向量。通过简单地将三角形两条边进行叉乘计算(向量a和b的叉乘结果是一个同时垂直于a和b的向量,还记得吗?),然后归一化:使长度为1。伪代码如下:
triangle ( v1, v2, v3 ) edge1 = v2-v1 edge2 = v3-v1 triangle.normal = cross(edge1, edge2).normalize() ```
不要将法线(normal)和normalize()混淆。Normalize()是让一个向量(任意向量,不一定是法线)除以其长度,从而使新长度为1。法线(normal)则是某一类向量的名字。
##顶点法线
引申开来:顶点的法线,是包含该顶点的所有三角形的法线的均值。这带来了不少便利–因为在顶点着色器中,我们处理顶点,而不是三角形;所以最好把信息放在顶点上。况且在OpenGL中,我们没有任何办法获得三角形信息。伪代码如下: ```
vertex v1, v2, v3, …. triangle tr1, tr2, tr3 // all share vertex v1
v1.normal = normalize( tr1.normal + tr2.normal + tr3.normal )
##在OpenGL中使用顶点法线
在OpenGL中使用法线很简单。法线是顶点的属性,就像位置,颜色,UV坐标等一样;按处理其他属性的方式处理即可。第七课的loadOBJ函数已经将它们从OBJ文件中读出来了。 ```
GLuint normalbuffer; glGenBuffers(1, &normalbuffer); glBindBuffer(GL_ARRAY_BUFFER, normalbuffer); glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), &normals[0], GL_STATIC_DRAW);
// 3rd attribute buffer :
normals glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);
glVertexAttribPointer( 2, // attribute 3, // size GL_FLOAT, // type GL_FALSE, // normalized? 0, // stride (void*)0 // array buffer offset );
*************************************************************************
我们在使用光源时,除了强度和颜色之外,还需要指定光源的位置和方向,并且这些光源的位置和方向将会极大地影响场景的外观。
**********************************************
GL光照——法线向量
OpenGL中,既可以为每个多边形只指定一条法线(多边形各顶点的法线方向相同),也可以为每个多边形指定多条法线(多边形顶点的法线方向有不同)。
在OpenGL中,除了顶点之外,不能为多边形其他地方分配法线。
法线向量:
物体的法线向量定义了它的表面在空间中的朝向,即,定义了表面相对于光源的方向。因为OpenGL是使用法线向量来确定一个物体表面的某个顶点所接受的光照的。
如果不想使用OpenGL的光照功能,就可以免去为顶点指定法线向量这个步骤。GL工具库中某些模型的法线向量已经内部给定好啦,如glutWireCube()等,所绘制的模型各个顶点的法线向量已经系统给出了,又如绘制一个球Sphere等。
指定法线向量:
glNormal*()函数是把当前法线向量这个状态量设置为这个函数的参数所指示的向量值。后续顶点glVertex*()的法线向量将默认为此时法线向量状态量所保存的向量值。通常,顶点常具有不同的法线向量,因此常需要交替调用这两个函数。
在一个表面的顶点上,将会有两条向量垂直于这个表面,这两条向量指向相反的方向,而指向外侧的向量才为当前表面的法线向量。
法线向量只表示方向,不表示大小(即与长度无关)。理论上可以为顶点指定任意大小的法线向量,但在OpenGL执行光照操作时,会将顶点的法线向量规范化(单位化),而这样必然会降低程序的性能,所以一般应由我们自己提供各个顶点的规范化法线向量。
如果只是对模型进行移动、旋转操作,法线向量的长度将不会发生变化,而如果对模型进行缩放操作,长度则将变化。如果需要OpenGL对法线向量进行规范化,就需要启用这个功能,glEnable(GL_NORMALIZE),默认这个功能是关闭的。
如果所进行的缩放是均匀缩放,则可以使用glEnable(GL_RESCALE_NORMAL),这样性能相比较GL_NORMALIZE更优。默认情况下,GL_RESCALE_NORMAL也是被禁用的。