转自http://hi.baidu.com/vrman/blog/item/faba6881f6501cd8bc3e1e3b.html
Source引擎 光能传递和凹凸贴图的革命性结合
新的理念:“光能传递”和“凹凸贴图”的有机结合,《半条命2》渲染的核心技术
正如前面所述,实际上在《半条命2》出现以前,就有一些游戏中已经使用到了光能传递技术和凹凸贴图技术了。但是,还没有一款游戏能把这两种技术很好的结合起来。因为光能传递技术是比较侧重对场景中的静态光照进行预先处理和场景的整体光照效果;而凹凸贴图技术则相反,只有在动态光照的情况下,它的效果才会比较明显,而且比较侧重物体表面细节;确实是很难将这两种技术很有效的用在一起既表现出可信的光影效果,同时又照顾到物体表面细节的。 所以我们不得不说Valve在其Source引擎中使用的这种光能传递凹凸贴图技术是一项创举。它实现了两者的有机结合,同时发挥了光能传递技术和凹凸贴图技术的优点,而且具有很高的执行效率。所以这项技术成为了Half-Life2/Valve Source引擎的关键核心技术。
那么,在下面的部分中我们就来看一看这种技术的具体实现吧。
照亮《半条命2》的世界 比Doom3效率高
2.照亮《半条命2》的世界-区分对待静态场景和动态模型
在《半条命2》/Valve Source引擎中,静态的世界场景(地形,建筑……)和动态的模型(角色,箱子,车辆……)是被分开对待的,因为他们有着不同的特性。静态世界场景是那些不变的、规模比较巨大的三维网格模型;而动态模型是那些较小的、无论是在场景中的位置还是自身形状会变化的三维网格模型。对于前者的绘制,Source引擎使用一种称为“光能传递光照贴图(Radiosity light maps)”的技术;对于后者,使用一种称为“环境光立方体(Ambient cube)”的技术。
在分别介绍这两种技术之前,先对“光能传递法线贴图(Radiosity Normal Mapping)”作一个整体的介绍。在传统的法线贴图技术中,一般每渲染一次物体的时候只能使用一盏灯光,这是由于受到光照算法的约束;当场景中有多盏灯光时,就只能把每次计算得到的光照效果叠加起来。下面的示意图说明了这种情况:
图7.a 先画出一盏灯光的效果
图7.b 另一盏灯光,需要再画一遍物体
图7.c 两图叠加
这种做法最典型的例子就是DOOM III了。在DOOM III中由于针对每盏灯光,都要重新绘制一遍物体,所以在同一帧里每个物体同时受到的光源个数是有限制的。这也是为什么DOOM III 的场景都做得比较昏暗的原因。
无限制的光照数量 打造逼真的视觉效果
而使用“光能传递法线贴图”技术的《半条命2》,在每一帧绘制物体的时,都允许它受到任意多盏灯光的照射。这就是它比较先进的地方。但是有一个前提就是这些灯光必须都是静态的,也就是说,无论位置、颜色和亮度都不能变。
前面已经提到过“光照贴图”这种技术了。在以前通过传统的光能传递算法来计算光照贴图时,都是仅仅根据所接受到的光线的颜色和亮度计算出物体表面上每一点对应到光照图中一个象素的一份颜色值,也就是说一个面上同时只有一张光照图。而在“光能传递法线贴图”技术中,每一个面同时都对应着三张光照图。那么为什么会是三张光照图呢?
原来为了在用光照图的同时使用凹凸贴图,Valve的程序员们为场景的每个多边形本身加上了一个小的局部三维坐标系。我们都知道一个三维坐标系(或坐标空间)是由三条互相垂直的轴来确定的,而与这三条轴方向相同并且长度是1的三根向量,就称为这个坐标空间的三个基向量(Basis)。
在《半条命2》中,这个局部三维坐标系是这样的:
图8.b 《半条命2》 每个多边形上的局部坐标系和基向量。
使用这三个基向量所代表的三个方向,Valve的程序员们在每个方向上计算一次光能传递的效果,就形成了三张不同的光照贴图。具体的光能传递算法,Valve并没有透露。
总之就是根据这三个方向,将原来的一张光照图分为对应的三张光照图,而且保证当物体表面的某个法线越偏向于其中一个方向时,它所应该接受到的光照颜色就越接近于对应的光照图上的颜色。
下面我们就来看一下针对静态场景和动态模型,Source引擎是如何分别使用光能传递法线贴图技术的。
看图说话 逐步记录逼真场景诞生过程
《半条命2》静态场景的光影效果
这是一张使用《半条命2》渲染技术的典型的静态场景截图。除了火焰之外,场景中其他部分全是静态的。前面说过,对于这种大型静态场景,Source引擎使用基于光照贴图(Radiosity light maps)的光能传递凹凸贴图技术。
从这张图中可以看出,场景里既有比较真实的光能传递的整体光照效果,石壁表面又有十分细致的凹凸贴图细节。
核心技术-基于光照贴图(Radiosity light maps)的光能传递凹凸贴图技术
我们先来看一下使用以前传统的光能传递算法计算出的单一光照贴图贴到场景中物体表面的效果(为了清楚的表现出光照,去掉了表面本身的贴图):
图10.a 单一光照贴图的效果
前面说道,为了加入凹凸贴图效果,在《半条命2》 Source引擎中,对应场景里多边形本身局部坐标系的三个基向量,会预先计算出三张光照贴图来。当分别把它们贴到场景中时看起来是这样的:
图10.b 针对第一个基向量计算的光照贴图的效果
图10.c 针对第二个基向量计算的光照贴图的效果
图 10.d 针对第三个基向量计算的光照贴图的效果
我们可以看出,在这三张光照图中,同一部位上的光强和颜色都是不一样的。那么,这三张光照贴图又是如何结合起来的呢?这就需要通过物体表面法线贴图中的法线来从它们之间取值了。
看图说话 法线贴图的神奇效果
我们先在场景里每个多边形上贴上法线贴图看看,结果是这个样子的:
图11 物体表面的法线贴图
看上去很诡异……实际上,法线贴图不是这样简单的贴到物体表面上去的,因为它保存的不是颜色颜色信息,而是物体表面上很多的三维法线向量。
在绘制一帧图像中的每个象素时,Source引擎都将从法线贴图里取出这个象素对应到物体多边形表面上那个点的法线向量,与多边形的局部空间的每个基向量做点乘(点积)操作,得到一个0到1之间的浮点数。由于法线向量和基向量都是单位向量(长度为1的向量),而两个单位向量方向越接近,点乘的结果就越大(最大到1),所以就可以通过这个浮点数来“过滤”从该光照贴图里读出的颜色。我们用下面的公式来表示一下可能会更清楚:
象素的最终的颜色 =
第一张光照贴图中取出的颜色 × 法线与第一个基向量点积的结果
+ 第二张光照贴图中取出的颜色 × 法线与第二个基向量点积的结果
+ 第三张光照贴图中取出的颜色 × 法线与第三个基向量点积的结果
这样就保证了象素上的法线越接近于哪个基向量,对应的从哪张光照图取出这个象素的颜色占的比重就越大。
通过这步关键的计算,就可以把凹凸贴图和光照贴图结合起来了。这时的场景看起来像下面的样子:
图12 通过法线贴图过滤组合后的光照效果,表面凹凸细节的效果已很明显
下面的一张是最开始的单一光照贴图效果,拿过来比较一下:
而物体表面本身也是有材质贴图的:
图13 物体本身的材质贴图,没有加入光照
然后,再用有凹凸细节的光照效果和物体表面本身的材质贴图相混合,就得到了漫反射部分的完整图像:
图14 材质贴图混合了光照后的结果。
实际上,在混合时只要把算出的光照颜色和材质本身颜色的红绿蓝三个通道分别相乘就行了。
我们可以再将上面这张图和最开始只使用传统的单一光能传递光照图获得的效果作一个比较:
图15 只使用单一光照图,很多沟壑的凹凸细节都没有
OK,漫反射部分的实现原理基本就是这样了。下面我们来总结一下这个流程。
图16.a 截取一部分图像来说明这个流程
图16.b
从左到右:首先使用法线贴图在三张光照贴图中取颜色值,混合成带有凹凸细节的光照贴图,然后乘以物体本身的材质颜色贴图,得到右边最终的效果。
高光环境反射贴图 突破传统的高光算法
高光环境反射贴图
前面说过,光照图法线贴图技术只适用于与玩家观察角度无关的漫反射光照。那么,在场景中物体表面的高光又是如何绘制的呢?
如果按照传统的算法,对物体表面高光和反射的计算是整个光照计算中比较复杂的过程。它需要较多的向量运算和浮点数的乘方运算,而且由于它与玩家的视线方向有关,所以几乎每帧都要更新。
为了提升效率,Valve的技术人员不使用传统的高光算法,而是使用性能较好的高光环境反射贴图的方法。
所谓的环境反射贴图,就是把一个物体周围的环境投影到包围在这个物体的一个立方体上。我们可以把这个过程想象成在这个物体的位置上放一个90度视野的照相机,用它向东、南、西、北、天、地六个方向各拍一张照片,糊在一个房间的六个对应内壁上,这样当我们站在房间中间看时,就好像能看见所拍摄到的整个的环境一样。
图18 环境贴图
如果我们在这个房间的中间放置一个金属物体,那么它反射墙壁上的照片中的景象,与把它放在真正的景物环境中反射的景象就十分相似了。在计算机中,墙壁上的照片就相当于是环境贴图了。用这些“照片”来代替真正的景物来计算反射效果,会使计算量大大减轻。
在《半条命2》中整个的工作流程是这样的:Valve的美工们先使用地图编辑器在关卡地图中放置一些“取样点”,实际上就是用来放置摄像机拍摄环境贴图的地方。具体放置的位置一般是在那些有高光和反射效果的物体附近。
图19 美工在《半条命2》地图编辑器中向一个场景里放置的
环境贴图“取样点”
然后,地图编辑器自动在场景的这些地方放置“摄像机”并预先“拍摄(渲染)”出环境贴图来。每一个点都有六个方向的贴图。
在场景中有高光和环境反射效果的物体会根据离自己最近的“取样点”来决定使用哪张环境贴图。如果自动获得的效果不是很理想,美工也可以手动的将某个环境贴图赋给某个物体的某个多边形上。
Source引擎在游戏进行中实战详解
在游戏进行中,Source引擎把环境贴图贴到物体上,并根据物体表面的法线以及游戏者的视线方向对贴图进行扭曲,使物体表面看起来好像在反射环境。
下面这张图是最开始那个场景仅贴上环境贴图的效果。
图20 加入高光环境贴图的效果
可能有读者会问了:怎么这么黑呀?其实这是因为这些高光是要叠加到原来的画面上的。我们实际上是用高光贴图去“加亮”原来漫反射得到的颜色,所以这里黑的地方就相当于保持原来漫反射颜色不变,而亮的地方就是使原来的部分更亮。
由于在从环境反射贴图中取哪个位置的象素的颜色是和物体表面法线有关的,所以仍然可以使用法线贴图中的法线信息,得到更细致的高光效果:
图21 应用法线贴图来“扭曲”后的环境反射效果,
可以看出凹凸不平的表面上高光的感觉
另外在材质不均匀的物体的表面上,并不是所有的地方都有高光效果的。有的地方高光就强烈一些,有的地方就弱一些。所以,Valve的技术人员会再加上一层贴图来“过滤”物体表面的高光。
图22.a 物体表面的高光强度贴图,其中越白的地方高光强度就越大
图22.b用高光强度贴图过滤高光后的效果,只有很少的高光被保留了下来
最后,把高光叠加到场景中去,得到最终的效果:
图22.c 叠加了高光的场景
OK,现在让我们来补完一下整个的渲染流程吧:
图23
从左到右:首先通过法线贴图(Normal)来寻址环境反射贴图(Cube Map)和三张光照贴图(Lightmaps),得到的结果分别与高光强度贴图(Specular Factor)和物体材质颜色贴图(Albedo)混合相乘,得到物体表面颜色的高光部分和漫反射部分,这两部分再相加得到最终的效果。
说完静物说动态 环境光立方体不难理解
《半条命2》动态物件和人物模型的光影效果
前面介绍了静态场景的光影效果,下面我们来看看在《半条命2》中的动态模型是如何绘制的。
核心技术-基于环境光立方体 (Ambient cube)的光能传递凹凸贴图技术
由于动态模型的位置和形状都有可能发生变化,所以就不能像静态场景那样处理了。动态模型所受到的光照也是由直接光照和间接光照组成的。在Source引擎中,只能从照射一个模型的所有直接光源中选出有两个最主要的来使用比较复杂的光照公式进行实时计算,而其他比较次要的直接光源以及场景中的间接光源都会被预先保存到一个称为“环境光立方体”的东西中。
图24 环境光立方体
首先Valve的技术人员在关卡场景中放置一个虚拟的立方体,然后地图编辑器就分别对立方体的每个面计算出垂直通过这个面的环境光的颜色和强度。场景里那些比较次要(远或暗)的直接光源,也被计算在内。
对于每个面都计算一个光照颜色,可以得到六个颜色,基本上代表了从四面八方射来的穿过这个体积中的所有比较次要的光线的信息。这六个颜色是可以预先计算出来保存在关卡场景数据里的。
通过模型表面的法线来决定取这六个颜色的比重并将它们混合,就可以得到一种类似于光能传递的效果。在环境光立方体的六个面中,必然有至少三个面会在法线的相反方向,这些面的颜色忽略不计。所以一般只有另外三个面的颜色会用到。法线方向越正对哪个面,这个面对应颜色所占的比重就越大。
图25
用环境光立方体技术渲染的蚁狮模型,虽然没有加入凹凸贴图,但是已经有一种类似光能传递的效果。
看图说话 一只蚁狮诞生记
在前面说道为了结合使用法线贴图,对于静态场景,一个面对应着三张光照贴图。而在这里也是类似的,一个动态模型对应着三个环境光立方体。这三个环境光立方体面上的颜色是分别根据各面局部坐标系的三个基向量来计算出来的。具体的算法由于太核心了,Valve目前也没有公布出来。
图26.a.b.c分别使用相对于三个基向量的环境光照图和环境光立方体的效果
我们把法线贴图贴到场景中看一看:
图27 法线贴图
按照和静态场景类似的方法用法线贴图来寻址环境光立方体的颜色,就可以得到下面的效果了:
和前面没有凹凸细节的图对比一下吧:
不要忘了,模型本身也是有材质贴图的。我们把它贴上去看一看:
图29 模型自身的材质贴图,没有光照
如果把光照效果和它混合,就成了这个样子:
图30.a 混合操作依然是颜色各通道相乘。角色表面具有凹凸细节。
和没有使用法线贴图的效果对比一下:
图30.b 没有凹凸贴图
漫反射的部分完成后,就该加入高光了。对于角色的高光,Source引擎仍然使用高光环境反射贴图。
图31 完全的高光环境反射,也没有加入凹凸贴图,好一只光滑的金属蚁狮
图32 加了凹凸贴图
图33 高光强度贴图,也是越白的地方高光越强,
我们可以看出蚁狮表面的高光并不是很强的
图34 用高光强度贴图去过滤高光
图35 把高光叠加到漫反射部分上去,得到最后的效果
充分利用DX9的神奇本领 制造一个透明的世界
3.制造一个透明的世界——水、玻璃窗、反射和折射特效
图37 《半条命2》中的反射、折射
充分发挥DirectX9的动力-离屏渲染、反射和折射贴图技术
在实现玻璃、水这样的反射和折射特效时,同很多DX9游戏一样,《半条命2》 Source引擎中活用了离屏渲染、反射和折射贴图技术。
我们知道一般来说游戏的每一帧画面都是为了画到屏幕上的。但是现在的显示硬件不仅能够往屏幕上画,还能往贴图上画。这些贴图就可以再次赋到场景中的物体上去——这就是所谓的“离屏渲染”。
这种技术可以实现很多有意思的效果,比如DOOM III和《半条命2》中实时的监视器屏幕、电视屏幕等等。它们就是通过先把监视器看到的场景或电视中的场景实时绘制到一张贴图上,再把它们贴到监视器或电视屏幕上去(还记得《半条命2》一开始出现在各种屏幕上的那个喋喋不休的老头吗?)
图38 离屏渲染
如果我们把场景中的一个多边形面所挡住的东西画到贴图上,再贴到这个面上去,那么这个面看起来就是透明的了;如果我们在贴这个贴图时做一个扰动,比如加一些水波或者凹凸的扰动,那么这个面看起来就像是水面或者凹凸不平的玻璃的折射的效果了。借助法线贴图和最新的DirectX 9.0 Pixel Shader技术,加入这个扰动的过程十分容易实现。
我们不妨把要贴到这个面上的贴图叫做折射贴图;在向屏幕上绘制这个面的每一个象素时,根据这个象素的位置在面的法线贴图上得到一个法线,根据这个法线算出一个偏移后的位置,再根据它去折射贴图上取一个象素的颜色画到当前的这个象素上。这样就实现了“偏移”并可以用法线贴图来控制扰动的图案了。
图39 示意:加入扰动
下面我们就具体的看看《半条命2》中的水的效果是怎么实现的。
:《半条命2》中逼真的水 说白了其实很简单
《半条命2》中的水
首先我们来看反射。Source引擎首先将水面上要反射的景物上下颠倒地画在一张贴图上。
图40.a 反射贴图
然后是折射。将水下的景物画在另一张贴图上。
图40.b 折射贴图
然后将这两张贴图贴到水面的多边形上,同时根据凹凸贴图,使用Pixel Shader分别加入扰动,得到水的最终效果!简单吧。
图40.c 最终效果
如果没有DirectX 9.0中的Pixel Shader技术,想对反射和折射贴图进行象素级的扰动来实现这样的水面效果是十分困难的。
大量运用DX9特性的典范 《半条命2》属于未来
4.小结
总之,Half Life2/Valve Source引擎对于大规模室外场景的光影表现是有着其独到之处的。尽管使用光能传递凹凸贴图技术只能达到一种“近似”真实而非“严格”真实的视觉效果(实际上现在的游戏引擎基本上不可能做到严格的真实),但是《半条命2》的图像从色彩、光影和各种特效上来说基本上算是比较“养眼”了。
另外,在Source引擎中对DirectX 9.0技术的大量使用,给我们带来了很多与以前的游戏所不一样的视觉感受,例如场景中令人难忘的水面效果。可以说,《半条命2》是目前最能体现DirectX 9.0技术的游戏之一。