1.模拟灯光方法:
(1)首先GPU为每个三角形的顶点执行关心计算,然后把计算的结果插补在顶点之间来修改每个渲染的片元的最终颜色。因此模拟灯光的质量和光滑度要取决于组成3D物体的顶点数量。
(2)灯光效果可以被预计算并被“烘焙”进纹理中,以便在完全不需要GPU光线计算的情况下产生逼真的场景。
2.环境光、漫反射光、镜面反射光
环境光来自各个方向,因此会同等地增强所有几何图形的亮度。
程序通过设置模拟环境光的颜色和亮度来设置场景中的背景灯光的基础水平。
每个光源的漫反射部分是定向的,会基于三角形相对于光线的方向来照亮场景中的每个三角形。
漫反射的光的颜色只会着色被定向的光线照射到的三角形。
从几何图形对象反射出来的光线叫做镜面反射光。
一个渲染的三角形中的每个光线组成部分的效果取决于三个相互关联的因素:光线的设置、三角形相对于光线的方向,以及三角形的材质属性。
3.光线与几何图形相互作用的关键是要计算出每个几何物体照射和散发出来多少光线。要做到这点,就需要计算出每个三角形有多么接近于与光的方向垂直的方向。
三角形可以用三个顶点表示,也可以用一个顶点和两个矢量表示。
方向与相乘的两个矢量构成的平面垂直,遵循右手法则。
GLKVector3 GLKVector3CrossProduct(GLKVector3 vectorLeft, GLKVector3 vectorRight)为GLKit计算矢量积的函数。
光线计算依赖于表面法向量,可以使用定义三角形的任意两个矢量的矢量积计算出来。
法向量也是单位向量,长度总是1.0.
计算法向量:先计算矢量积向量,然后用这个矢量积向量的长度除以矢量积的每个分量。
GLKVector3 GLKVector3Normalize(GLKVector3 vector)函数可以求单位向量。
GLKVector3Normalize(GLKVector3CrossProduct(vectorA, vectorB));可用来求向量A和向量B的法向量。
投射到三角形上的光线的数量可以通过确定光线的方向与法向量的方向之间的角度轻松计算出来。
介于一个光线方向上的单位向量和一个三角形的表面法向量之间的余弦决定了照射到三角形上的光线的数量。
如果三角形垂直于光线,那么表面法向量会平行于光线的方向,角度为0度,余弦值为1.0,这意味着最大强度的光会照射到三角形上。如果这个三角形平行于光线的方向,那么介于光线和表面法向量之间的角度就是90度,余弦值为-1,因此没有光线会照射到三角形上。
OpenGLES程序为每个顶点指定了单独的法向量。法向量通常计算一次,然后与顶点的位置和纹理的坐标一起被保存起来。
如果一个三角形的三个顶点被赋予相同的法向量,这叫做平面法线,灯光模拟会让三角形变得平坦。
如果每个顶点的法向量是包含顶点的所有三角形的法向量的一个平均值,灯光模拟会创建三角形被轻微弯曲的感觉。
4.使用GLKit灯光
GLKit使用与下面摘录的用于漫反射光的代码相似的Shading Language代码为每个光线实现了标准的灯光模拟方程式。
GLKit会自动地生成与上摘录相类似的Shading Language代码,以便同时混入每个光线的镜面反射光和环境光颜色。
这个代码会改变每个顶点的法向量来匹配正在被渲染的场景的方向,计算光线的方向与新的法向量之间的标量积,然后使用这个标量积来按比例决定光的漫反射颜色的影响力。
GLKit的GLKBaseEffect类最多可以支持三个命名为light0、light1、light2的模拟灯光。每个灯光都是GLKBaseEffect的一个同名属性。这些属性是GLKEffectPropertyLight类的实例,这些属性又可以设置每个灯光的属性,每个灯光至少有一个位置、一个环境颜色、一个漫反射色和一个镜面反射颜色。每个灯光都可以被单独的开启和关闭。
灯光的漫反射颜色被设置为不透明中等灰色。灯光的镜面反射和环境颜色保持为GLKit的默认值,分别为不透明白色和不透明黑色。这意味着灯光的漫反射部分不会影响场景并且高反光的物体会显得非常有光泽。
上面代码position用一个GLKVector4来设置光源的位置。前三个元素要么是光源的X、Y、Z位置,要么是指向一个无限远的光源的方向。第四个元素指定了前三个元素是一个位置还是一个方向。如果第四个元素是零,前三个元素就是一个方向;如果第四个元素非零,光源就从它的位置向各个方向投射光线。因此每个顶点的光线方向是多种多样的,并且必须要由GPU使用从每个顶点到光源的方向而计算出来。位置光源可以被设置为一个锥形而不是向各个方向发散光线的聚光灯。
仅仅开启一个灯光是不够的,必须为被一个灯光照射到的每一个三角形的每一个顶点提供法向量。
在一个或多个GLKBaseEffect的灯光被开启后,灯光决定了渲染的物体的颜色;GLKBaseEffect的常量颜色和所有的顶点颜色被忽略了。当GLKBaseEffect的灯光被创建之后,constantColor属性就被忽略了,及时灯光被关闭了,例如:baseEffect.light0.enable = GL_FALSE;
作为最后的手段,GLKBaseEffect的constantColor属性会告诉GLKBaseEffect为生成的片元使用什么颜色。大部分OpenGLES应使用某种灯光和纹理的结合来决定片元的颜色。constantColor属性仅适用于渲染单调不发光的物体。
作为最后手段,GLKBaseEffect的constanColor属性会告诉GLKBaseEffect为生成的片元使用什么颜色。大部分的OpenGLES应用使用某种灯光和纹理的结合来决定片元颜色。constantColor仅适用于渲染单调不发光的物体。
5.把灯光烘焙进纹理中
用灯光模拟的方法,每个被照射到的顶点,除了位置坐标,法向量的使用还需要三个浮点值的存储空间。
如果纹理可以以任何形式来使用,把灯光烘焙进一个现存的纹理中可以替代灯光模拟的方法。换句话说就是使用已经包含了本来是由灯光模拟生成的明亮和黑暗区域的纹理。
把灯光烘焙进纹理仅仅适用于几何图形和灯光都不是动态的情况下。
6.片元计算
GPU可以为每个单独的片元而不仅仅是为顶点重新计算灯光效果。可以使用为顶点计算灯光效果相同的方程式。光线方程式需要每个片元的法向量。解决方法是编码一个纹理的每个RGB纹素内的法向量X、Y、Z分量。这样的纹理成为法线贴图。
一个纹理单元决定了哪一个法线贴图纹素会影响片元。一个Shading Language程序会计算由选中的纹素和光线的方向所代表的矢量的标量积。然后用这个标量积来按比例确定最终的片元的颜色效果。
每片元光线计算即使是在动态的灯光和几何图形的情况下,仍然可以工作良好,但很耗资源。
7.小结
真实世界的灯光效果是我们感知周围环境的关键,模拟灯光效果可以为3D渲染增加真实感。结合了材质的传统OpenGLES顶点灯光模拟提供了广泛的配置选项,但还是要忍受各种限制以及嵌入式GPU相对低性能。比起OpenGLES材质,纹理总是会产生更高质量的渲染效果。在使用了纹理之后,可以在纹理中包含一些灯光效果,同时完全避免OpenGLES灯光模拟。所有运行iOS的硬件都支持每片元灯光计算技术,并且随着内存、处理器和工具支持的改进这项技术会变得更加普遍。
网友评论