OpenGL法线

法线

前面的教程中我们一直在处理法线,但是并不知道法线到底是什么。

##三角形法线

一个平面的法线是一个长度为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 ); 

*************************************************************************

我们在使用光源时,除了强度和颜色之外,还需要指定光源的位置和方向,并且这些光源的位置和方向将会极大地影响场景的外观。


  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的实现中,对于光照计算,所有的法线向量都必须先进行规范化,然后再参与计算。

**********************************************

GL光照——法线向量

 

OpenGL中,既可以为每个多边形只指定一条法线(多边形各顶点的法线方向相同),也可以为每个多边形指定多条法线(多边形顶点的法线方向有不同)。

在OpenGL中,除了顶点之外,不能为多边形其他地方分配法线。

 

法线向量:

物体的法线向量定义了它的表面在空间中的朝向,即,定义了表面相对于光源的方向。因为OpenGL是使用法线向量来确定一个物体表面的某个顶点所接受的光照的。

如果不想使用OpenGL的光照功能,就可以免去为顶点指定法线向量这个步骤。GL工具库中某些模型的法线向量已经内部给定好啦,如glutWireCube()等,所绘制的模型各个顶点的法线向量已经系统给出了,又如绘制一个球Sphere等。

 

指定法线向量:

glNormal*()函数是把当前法线向量这个状态量设置为这个函数的参数所指示的向量值。后续顶点glVertex*()的法线向量将默认为此时法线向量状态量所保存的向量值。通常,顶点常具有不同的法线向量,因此常需要交替调用这两个函数。

 

在一个表面的顶点上,将会有两条向量垂直于这个表面,这两条向量指向相反的方向,而指向外侧的向量才为当前表面的法线向量。

 

法线向量只表示方向,不表示大小(即与长度无关)。理论上可以为顶点指定任意大小的法线向量,但在OpenGL执行光照操作时,会将顶点的法线向量规范化(单位化),而这样必然会降低程序的性能,所以一般应由我们自己提供各个顶点的规范化法线向量。

如果只是对模型进行移动、旋转操作,法线向量的长度将不会发生变化,而如果对模型进行缩放操作,长度则将变化。如果需要OpenGL对法线向量进行规范化,就需要启用这个功能,glEnable(GL_NORMALIZE),默认这个功能是关闭的。

如果所进行的缩放是均匀缩放,则可以使用glEnable(GL_RESCALE_NORMAL),这样性能相比较GL_NORMALIZE更优。默认情况下,GL_RESCALE_NORMAL也是被禁用的。 


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值