第四章 场景的真实感渲染技术
第三章讨论了怎样有效绘制地形的三角形面片,关于怎么在面片上表现真实的地表(草地、沙粒),以及怎么表现光照、天空、植物等,这一系列问题涉及的就是场景的真实感渲染技术。
4.1纹理映射技术
4.1.1概念
在游戏设计中,由于客观世界千变万化、错综复杂,要把真实世界的各种细微结构直接用几何模型表示出来,不仅模型难以建立,而且计算量庞大,难以满足实时显示的需要,比如一张曲面可以用许多微小的多边形(或曲面片)表示其表面细节,假定每个微小多边形具有近似相同的表面特征,要显示这样一个曲面,就必须对这些微小多边形(或曲面片)进行分别处理,这将需要大量的存储空间和处理时间,因此在实际应用中,为了获得比较高的显示速度,往往以牺牲图形的真实感为代价。尽管这样,显示一幅比较复杂的图像仍然需要很长时间。于是,人们就想象是否可以用“贴墙纸"的方法将反映物体表面的细节的图案贴到物体表面,从而开辟了一个新的研究领域一纹理映射(Texture Mappirig)。处理过程如下图:

纹理映射的方式主要分为三种:
颜色纹理映射:最简单和最基本的应用是将一幅花纹图案映射到物体表面,采用与表面上点的位置有关的值(如:参数曲面上一点的参数值)来确定纹理坐标并采样包含图案的纹理,采样值用定义该点的颜色。通过此类方法在图案和表面点之间建立的固定联系,不会因视线的改变而改变。当花纹或图案绘上之后,表面仍光滑如故,这种纹理称为颜色纹理,形如在物体表面绘制了一些花纹图案。
凸凹纹理映射:根据粗糙表面的光反射原理,通过一个扰动函数扰动物体表面法向量,使光滑表面得到调制,并在光线下呈现出凸凹不平的形状,这种纹理称为凸凹纹理(或几何纹理)。利用扰动函数可以很好的模拟皮毛、头发、衣服之类的物体。
过程纹理映射:过程纹理映射属于三维纹理,它就是将三维的纹理函数映射到三维物体上。也就是说在物体的内部也会受到纹理的影响。例如,木材和大理石的纹理,需要考虑每一个相邻面的纹理映射,通常每个面分别进行颜色纹理映射时,由于面的边界处纹理不连续,往往难以表现出自然的纹理,这种场合定义三维的纹理函数进行立体纹理映射是非常有效的。用过程纹理模拟物体表面细节,能够在非常复杂的曲面上表现连续的纹理,且纹理效果不受物体表面形状的影响,可以很大程度的解决纹理走样问题。
4.1.2 OpenGL实现纹理映射的步骤
(1)指定纹理
在最简单的情况下,纹理是单个图像。利用称为MipMap的技术,程序员可以指定同一纹理的多级分辨率图像,在进行纹理映射过程中,该技术可以按照景物表面在屏幕上所占区域分大小,自动选择合适分辨率的纹理图像对景物表面进行映射,以避免因纹理映射中的点采样方式所导致的纹理走样。另外,图像的定义可以包括边界值,以防止物体的纹理坐标超出有效区域。边界值允许程序员把多个纹理映射平滑的粘贴在一起,从而增加最大可用纹理的有效尺寸。相关函数:glTexImage2D(),glTexGen()。
(2)纹理如何作用于每个像素点
OpenGL根据片元的颜色和纹理图像数据中的颜色来计算最终的RGBA值。程序员可以选择下面三种功能中的一种:a.可以简单的使用纹理颜色作为最终的颜色,这种方法如同贴花一样,纹理被粘贴在片元的表面上;b.可以用纹理来调整片元的颜色和颜色比例,该技术对光照和纹理的综合效果非常有用;C.用纹理值将一个固定的颜色和片元的颜色混合在一起。相关函数:glTexParameter(···),glTexEnv(···)。
(3)激活纹理颜色
在绘制场景之前需激活纹理映射。相关函数:glEnable(),glDisable()。
(4)利用纹理坐标和几何坐标绘制几何场景
在粘贴纹理之前,必须说明纹理相当于片元是如何排列的。也就是说,必须指定场景中物体的纹理坐标和几何坐标。对于二维纹理图可指定其两个方向上的纹理坐标均为[O.0,1.0],而要粘贴纹理的物体坐标可以是任意的。相关函数:glTexCoord*()。
4.2 用多层纹理混合贴图模拟融合性地表
4.2.1纹理混合贴图
对于小的地形,我们可以考虑在整个地形上贴一张纹理。比如65×65的地形贴上一张128×128的图片。但是当地形很大时,我们的贴图不可能很小。当贴图很小时,地形会被拉伸产生失真;当把贴图选得太大,显卡会承受不了。于是有人想到了,利用纹理贴图坐标(texture coordinate)的设定,我们可以将一张图片一次次的重复贴至地形场景上,也就是所谓图素纹理(tile texture)的方法;不把128x1.28的图片直接映射(mapping)至整个地形范围,而是以每个128x128大小为一个单位,将图文件完整贴上,如此反复进行贴图,就像是铺地板砖一样。这样的方法,的确可以大幅减低贴图失真的情形,可是如果还想进一步实现更真实的地形场景贴图,就会发现这个作法还是有不足之处。一个地形场景通常不会只拥有单独一种样式的地貌。举例来说,可能依照地形高度的不同,在平地上会有草地,高度往上的山坡会有沙地,在山顶或陡峭之处可能会有岩地或雪地等等多种可能的组合。如果想达成这种形式的地形纹理贴图,就不是简单的使用图素纹理(tile texture)可以实现的了。这就需要纹理混合贴图(Texture Blending)技术。
两层纹理混合原理公式:C=a*alpha+b*(1-alpha)。
4.2.2基于索引图的纹理混合贴图
在很多游戏中,地形纹理又分为粗纹理和细节纹理。粗纹理是指表现地表大致图像的纹理,比如一片草地,它的粗纹理就可以是一张绿色的图片。而细节纹理顾名思义是指表现细节的纹理,在例子中,它就可以是一张表现草的细节、沙石细节的图片。一张粗纹理一般映射得很广,甚至映射到整个地形;而细节纹理一般是反复贴图(铺砖)。室外场景一般2到3张细节纹理足以表现出漂亮的结果。当多张细节纹理混合时,怎么体现不同纹理间的平滑过渡呢·可以应用纹理索引图技术。如图4.2

在灰度图表示的纹理混合索引图中每个像素对应地形中的一块区域(不能太大),白色像素表示那块区域贴岩石纹理,黑色像素表示贴草地纹理,灰色像素即是两种细节纹理的混合。比如灰度值是78,则在混合过程中岩石纹理的alpha=78/256×100%。
同理,我们可以用彩色图来表示更多细节纹理的混合索引。令索引贴图的R分量代表沙滩的纹理,G分量代表草地,B分量代表岩石。如果索引贴图上一个像素的值是(0,255,0),即绿色,则这个像素所对应的地形区域的具体纹理就为草地。如果该像素颜色值是(127;127,0),即黄色,则该像素所对应的地形区域的纹理为草地和沙滩的混合,看起来既有草,又有沙。这种方法的优点是简单、灵活,方便美工人员设计地表的纹理。
4.2.3 纹理混合的OpenGL实现
在OpenGL中,纹理混合有三种方式来实现:多通道多遍渲染,单通道多重渲染,用GLSL(GL Shading Language)实现多纹理混合。
·多通道多遍渲染
我们把地形用第一种纹理画出一次,然后再开启阿尔法混合(alphablending)的功能,用第二种纹理再画出地形一次,这样用不同纹理重复多次画出地形。这种做法适应配置低端的显卡,但渲染速度会受很大影响。
·单通道多重渲染
这种方法要用到两个OpenGL扩展指令(OpenGL Extensions),分别是GL_ARB_multitexture与GL_EXT_texture_env_combine。GL_ARB_multitexture可以让我们一次控制二到多个纹理单元(texture unit)的操作。这种技术适合于配置中端的显卡,速度也很快,但是支持的纹理个数有限。其步骤:
(1)地形初始化时,由纹理混合索引图确定每个地形顶点的所有纹理的映射坐标,纹理间的混合alpha值。
(2)初始化多重纹理,载入纹理贴图,设置相关参数。
(3)渲染时,用glMultiTexCoord2fARB设置顶点的纹理映射坐标,关键代码如下:


·用shader实现多纹理混合
所谓shader是基于显卡的着色器。OpenGL和DirectX都先后发布了自己的着色器语言,如OpenGL的GLSL(GL Shader Language)。这是目前比较新的渲染技术,这种方式编写程序简单,速度很快,支持的纹理也很多,但显卡必须是比较高阶的支持着色器语言的。这种方法在下面章节光照贴图中会有相关代码。
4.3 室外场景的光影处理
要绘制逼真的场景必须做光照处理。没有光照的三维物体模型与二维物体没有任何差别,没有一点立体感。只有具有光照的物体才是真正的三维物体。光照射到物体表面时,可能被物体吸收、反射或透射。光的反射和透视部分进入视觉系统使我们能看见物体。光的颜色是由其波长决定,一束白光含有所有可见波长的光。白光照射物体时,只有所有可见光被等量吸收物体才会呈现灰色;如果被不等量吸收,物体会呈现其它的颜色。光的亮度由光强决定。从物体表面反射出来的光的强度取决于光源的位置、光强、物体材质、物体表面位置、物体表面法线和视点的位置。
4.3.1光照模型介绍
光照射到物体表面时,光线可能被吸收、反射和透射。被物体吸收的部分转化为热,反射、透射的光进入人的视觉系统,使我们能看见物体。为模拟这一现象,我们建立一些数学模型来替代复杂的物理模型,这些模型就称为明暗效应模型或者光照模型。
在真实感图形学中,我们把仅处理光源直接照射物体表面的光照模型称为局部光照模型,而与此相对应的,可以处理在物体之间光照的相互作用的模型称为整体光照模型。
●Phong光照模型
Phong光照模型综合反映了漫反射、镜面反射和环境光对表面的作用1。他属于局部光照模型,在游戏场景的实时渲染中经常用到。其光照强度方程式如下:

式中,M表示对场景有贡献的点光源的总个数;I是光照表面点(x,y)处的光强;Ia是入射环境光的光强;Ini、fi(d)是第i个点光源发出的入射光光强和光源强度衰减因子;ka为景物表面对环境光的漫反射系数;kd为景物表面的漫反射系数;ks为景物表面的镜面反射系数;n称为镜面高光指数,被用来模拟镜面反射光在空间的会聚程度;Li为第i个点光源发射方向单位向量;N是点(x,y)处的表面单位法向量;Hi为将入射光反射到观察者方向的理想镜面的单位法向量Hi=(Li+V)/2,V为观察者视线单位向量。
·光线跟踪算法
光线跟踪或称光迹追踪是计算机图形学的核心算法之一n¨。在算法中,光线从光源被抛射出来,当他们经过物体表面的时候,对他们应用种种符合物理光学定律的变换。最终,光线进入虚拟的摄像机底片中,图片被生成出来。由于该算法是全局光照模型,所以可以模拟生成十分复杂的图片。
光线跟踪算法伪代码:


·辐射度算法
辐射度算法本质是将光看作一种物理辐射,然后计算辐射的传导就能获得加之于每个对象物体上的光照强度,从而获得正确的渲染结果。和其他渲染方法相比,辐射度算法更接近于光的自然传播原理。辐射度算法解决了光在不同面之间反射的计算技术,但不分布镜面光,也不产生聚光(Caustic)。辐射度算法也是一种全局光照模型。
全局光照虽然渲染效果很好,但速度很慢,一般还不能应用在实时渲染中,它一般应用在非实时渲染中,比如光照贴图就可以用全局光照算法来生成。
4.3.2室外场景中地形的光照处理
室外场景由于涉及多边形面片太多,如果实时计算光照,那速度是很慢的。并且,用局部光照模型来计算出的效果也不太理想。由于地形的多边形网格是实时产生的,它会随着视点的移动而变化,因此如果直接使用OpenGL内置的顶点光照,就会得到极度不稳定的光照效果,会看到地形表面因为你的移动而不断跳动。所以目前在游戏引擎中,关于地形的光照大多通过光照贴图来实现。
这样的实现方法是一种静态光照,不是实时的。现在国外最顶级的游戏引擎开始把全局光照或接近全局光照的模型引入场景的实时渲染中,这是今后的一个发展方向。在本文中,地形的光照处理通过光照贴图来实现。
●光照贴图的概念
光照贴图顾名思义它是一个覆盖了场景中所有多边形的贴图n钔。通过给贴图赋值,我们可以得到多边形表面复杂的光照效果。使用好的算法计算出来的光照贴图可以模拟极度逼真的光影效果。它给我们带来的视觉享受远远地超过了OpenGL的局部光照。

●光照贴图和多细节纹理混合模拟地表
首先,我们准备3张地表细节贴图,1张光照贴图,1个纹理索引贴图。把3张细节贴图和光照贴图绑定到相应的纹理通道上。然后使用Vertex Shader为每个顶点计算拉伸后的索引贴图的纹理坐标。下面是用GLSL写的相关Vertex Shader代码:


在Fragment Shader里,对索引贴图进行纹理查找,使用查找得到的颜色值的RGB颜色信息按比例混合3张细节贴图,得到当前像素的颜色。最后还应该把这个颜色和光照贴图中的值相乘,得到最终的结果。相关的Fragment Shader代码如下:

●光照贴图的生成
光照贴图可以用全局光照模型,比如光线跟踪、辐射度算法等来预处理得到。由三个数据来生成:a.地形高度图;b.光源位置;C.光照强度。
4.3.3场景中非地形的阴影算法简介
地形的光照阴影用上面介绍的光照贴图解决了,针对树木、人物的阴影我们必须另外实现。人物的阴影是动态的,有两种常用的实时阴影生成技术。一种是ShadowVolume,还有一种是ShadowMap。
·ShadowVolume算法
ShadowVolume基于的是几何体算法,通过延伸光照轮廓区域进行正反面两次渲染,在屏幕的模板缓冲区内分离出阴影区域,能够得到十分精确的阴影区域,画质较好,而且渲染流程十分简单,缺点是建立光照轮廓延伸体十分费时费资源,另外阴影边缘视觉上比较生硬。
●ShadowMap算法
ShadowMap基于的是Shader技术,通过建立Z深度图、逐像素比较深度得到阴影区域。该算法针对一些固定的建筑物之类的图素形成阴影尤其出色,对于一些运动物体的阴影最多只需渲染两次物体即可形成阴影。缺点是需要显卡对Vertex Shader、Fragment shader的支持,而且内存消耗较大,同时只能生成平行光下的阴影,而且无法做到将阴影投影到所有的物体。
· 用阴影贴图创建简单阴影
可以把物体的阴影通过纹理贴图技术贴在平面上简单模拟阴影。下面描述实现步骤:
(1)渲染地形。
(2)根据光源方向和物体的位置计算物体的阴影在地形上方的位置,并构成阴影平面面片。
(3)对于要创建阴影的每个物体,使用支持alpha混合和透明的纹理映射函数在第(2)步计算的阴影平面上贴上物体的阴影。 |