19 12
发新话题
打印

DX9压榨专家 半条命2神奇渲染引擎揭密

DX9压榨专家 半条命2神奇渲染引擎揭密

第1页:革命性的技术 光能传递凹凸贴图

    编者按:本篇文章系PCPOP特约作者刘宏春供稿。刘宏春现正攻读软件工程硕士学位,他从大学本科期间开始研究计算机图形学以及DirectX编程,2004年他作为队长和3D渲染引擎主程序员,率领【北京工业大学放飞技术网】共4名队员参加微软“创新杯”大赛DirectX 9渲染项目,从全世界几十个参赛队中脱颖而出参加在巴西进行的决赛,并且在决赛中以富有民族气息的作品《龙之传说》一举夺得全球总冠军。与很多玩家一样,刘宏春平时酷爱FPS类游戏,这次PCPOP在FPS超级大作《半条命2》发布之际所做的【相关专题】大受读者欢迎,因此特约他写出一篇专业性、权威性很强的《半条命2》核心渲染技术分析以回报广大读者们的支持,同时为我们的专题划上一个圆满的句号。

    本篇文章力图深入浅出的分析讲解《半条命2》使用的核心渲染技术,以及革命性的Source引擎是如何创造性的将DirectX 9的优势发挥到极至。我们的目标绝对不是让所有人都看不懂而大喊深奥,而是让游戏玩家、DirectX程序员以及3D建模人员等不同层次和领域的读者都能够有所收获。因此我建议大家慢慢看完本篇文章,你们一定会有所收获!



    今年冬天,Valve给我们带来了2004年最令人期待的游戏大作之一——《半条命2》。相信玩过该游戏的玩家都对其中自然的光影效果、丰富的细节有着很深的印象,下面就让我们来看一看《半条命2》 的世界是如何呈现在大家的屏幕上的吧。

1.  《半条命2》中的创新理念-光能传递凹凸贴图 (Radiosity Normal Mapping)

    使光影效果更加真实自然——“光能传递(Radiosity)”

    为了使场景中的光照效果真实自然,《半条命2》 中对光影的处理使用了一种叫“光能传递”的技术。光能传递简单来说就是光的能量通过不断反射、折射,而在各个物体的表面间互相传递的过程。



    各位读者都知道,我们所在的真实世界中,不仅仅是像镜子这样表面光滑的物体能够反射光线,而是基本上所有的物体的表面都会或多或少的把照射到上面的光线反射出去,这些光线就可以再照亮附近的其他物体。



    所以一个物体表面被照亮的时候,它接受到的光不仅来自于各种光源,而且还来自于由其他物体表面反射过来的光线;这样我们就可以把照射到物体表面的光线分为两类:直接光照和间接光照。直接光照是从光源直接来的光照,而间接光照就是指从其他物体的表面反射过来的光照。

    由于物体表面会被附近其他的物体遮挡住,根据遮挡程度不同,所受到的间接光照的光能也不均匀,造成角落里比较暗,平面的地方比较亮的效果。这就是光能传递最明显的特点。

    不要小看这个光能传递过程,它是在真实的漫反射光照中最为复杂的一个过程,也是在渲染计算机动画时提升画面真实度的极为重要的一个因素。

TOP

我们可以通过下面简单的两幅图来比较一下在三维动画软件中使用光能传递与否会给画面带来什么样的区别。

    这是一个十分简单的房间场景,房间的天花板上只有一个简单的聚光灯。两幅画面都是通过3DS MAX的默认渲染器渲染的:



    没有使用光能传递,场景只受到直接光照;没被聚光灯照到的地方就是一片漆黑,光照效果十分简单、生硬。



    使用光能传递算法渲染的效果,场景既受到聚光灯直接光照,也受到间接光照;即使没有受到灯光直射的天花板和墙壁上半部分也被从地面和墙壁下部反射过来的光线照亮。而且这些受间接光照的部分也是明暗不一的,比如墙角、柱子的顶部与天花板相接的部分就比墙面暗,因为这些部位接受到的间接光照能量比其他部位少。和图1.a相比,场景明暗层次丰富,光照效果柔和自然,真实感大大提高,但是实际上场景数据没做任何改动。

    在《半条命2》的渲染引擎中,就使用了这种光能传递技术。我们可以通过下面两张截图来对比一下,打开和关闭间接光照时游戏中的效果。

    只有直接光照,不使用光能传递。场景比较阴暗,明暗分界比较强烈和生硬,不自然。



    加入了间接光照。画面中即使没被灯光照亮的部分也比较明亮,且其明暗层次更加丰富一些,更接近于人的真实感受。

    目前计算光能传递的算法有很多种,其本质是计算出每个物体表面上每一点面积所接受到的全部的光能。接受到的光能越强,该点面积就越亮。而计算光能的关键步骤,实际上就是计算前面提到的间接光照效果的过程。

    尽管算法很多,可是无论哪一种都是十分复杂的。虽然现在的显示硬件性能已经大幅提高,但是像光能传递这种极为复杂的算法还是很难完全在游戏进行时用实时的方法来实现。

    所以,目前在游戏中一般是通过三维动画软件或者游戏开发者自己编写的光能传递计算程序,将场景中的静态光影效果事先计算好,并保存在关卡场景模型的光照贴图上或顶点颜色信息里,在游戏运行时直接使用这些贴图或顶点颜色就行了。

TOP

比较典型的例子是《波斯王子-时之沙》,该游戏的做法就是在3DS MAX中使用光能传递算法和美工手动调节相结合,把光照效果预先保存在关卡场景网格模型的每个顶点颜色中,从而在游戏中达到一种奇幻、柔和、自然的光影效果。









《波斯王子-时之沙》中基于顶点的静态光效

TOP

除了保存在顶点的颜色信息里,保存在光照图中也是一个好办法。现在的主流三维动画软件如3DS MAX、Maya、SoftImage等等基本上都支持将光照效果直接渲染到场景物体的贴图上。下面就是一个使用光照贴图的简单例子。


e 物体本身材质贴图


f光照贴图


g使用该光照贴图后的效果

使场景细节更加丰富-“凹凸贴图”

    相信大家都已经对“凹凸贴图”的概念不陌生了,尤其是在ID的大作《DOOM III》推出之后。可以说该游戏对凹凸贴图的使用已经到了登峰造极的地步。我们可以比较一下在DOOM III中打开和关闭凹凸贴图的效果对比:


a 打开凹凸贴图


b 关闭凹凸贴图

    在众多种类的凹凸贴图中,最主要的一种就是“法线贴图”了。《半条命2》、DOOM III和虚幻引擎3中使用的都是这种贴图。

TOP

“法线”的概念大家一定都很熟悉,一个平面的法线就是垂直于这个平面的向量。在计算机渲染图像时,法线的作用就在于计算平面的亮度。我们都知道,在一定光线照射下,一个平面的亮度是和光线入射角度有关的,光线越接近垂直入射,平面就越亮。



    由于平面的法线可以代表这个平面的方向,所以计算机就是通过比较平面法线向量和入射光线向量的方向来计算平面亮度的。

    我们可以想象一个平面上有着密密麻麻的法线;如果所有这些法线都朝向同一个方向,那么计算得到的这个平面的亮度就是一致的;但是如果人为的给这些法线加一些扰动,使他们朝向不同的方向,那么计算得到这个平面的亮度就是明暗不一的了,给人一种凹凸不平的感觉。

    但是,“扰动”并不是随便乱加的。为了控制对法线扰动的布局和程度,可以使用“法线贴图”。所谓的“法线贴图”,就是用来记录下物体表面各点的法线的贴图。

      
DOOM III 中的法线贴图

    在新一代的游戏引擎中,为了使物体凹凸效果逼真,大部分物体(尤其是角色模型)的法线贴图都是通过比较高、低多边形数量的两个版本的模型来生成的。拿虚幻引擎3的角色模型来说,高多边形版本的模型(简称高多边形模型)一般都会有几百万个面,拥有丰富的细节;而低多边形模型只有几千个面。真正在游戏中使用的是低多边形的模型。


a 虚幻引擎3中的高多边形模型


b 虚幻引擎3中的低多边形模型

   在制作法线贴图时,将高多边形模型上每个多边形的法线和低多边形模型对应部位法线相比较,得出的差值再置入法线贴图中。通过把这张法线贴图赋予低多边形模型,并实时计算光照,就可以使得渲染出的低多边形模型的凹凸感与高多边形模型十分接近。


c 虚幻引擎3中低多边形角色实时光影效果

TOP

新的理念:“光能传递”和“凹凸贴图”的有机结合,《半条命2》渲染的核心技术

    正如前面所述,实际上在《半条命2》出现以前,就有一些游戏中已经使用到了光能传递技术和凹凸贴图技术了。但是,还没有一款游戏能把这两种技术很好的结合起来。因为光能传递技术是比较侧重对场景中的静态光照进行预先处理和场景的整体光照效果;而凹凸贴图技术则相反,只有在动态光照的情况下,它的效果才会比较明显,而且比较侧重物体表面细节;确实是很难将这两种技术很有效的用在一起既表现出可信的光影效果,同时又照顾到物体表面细节的。



    所以我们不得不说Valve在其Source引擎中使用的这种光能传递凹凸贴图技术是一项创举。它实现了两者的有机结合,同时发挥了光能传递技术和凹凸贴图技术的优点,而且具有很高的执行效率。所以这项技术成为了Half-Life2/Valve Source引擎的关键核心技术。

    那么,在下面的部分中我们就来看一看这种技术的具体实现吧。

TOP

2.照亮《半条命2》的世界-区分对待静态场景和动态模型

    在《半条命2》/Valve Source引擎中,静态的世界场景(地形,建筑……)和动态的模型(角色,箱子,车辆……)是被分开对待的,因为他们有着不同的特性。静态世界场景是那些不变的、规模比较巨大的三维网格模型;而动态模型是那些较小的、无论是在场景中的位置还是自身形状会变化的三维网格模型。对于前者的绘制,Source引擎使用一种称为“光能传递光照贴图(Radiosity light maps)”的技术;对于后者,使用一种称为“环境光立方体(Ambient cube)”的技术。

    在分别介绍这两种技术之前,先对“光能传递法线贴图(Radiosity Normal Mapping)”作一个整体的介绍。在传统的法线贴图技术中,一般每渲染一次物体的时候只能使用一盏灯光,这是由于受到光照算法的约束;当场景中有多盏灯光时,就只能把每次计算得到的光照效果叠加起来。下面的示意图说明了这种情况:


a 先画出一盏灯光的效果


b 另一盏灯光,需要再画一遍物体


c 两图叠加

    这种做法最典型的例子就是DOOM III了。在DOOM III中由于针对每盏灯光,都要重新绘制一遍物体,所以在同一帧里每个物体同时受到的光源个数是有限制的。这也是为什么DOOM III 的场景都做得比较昏暗的原因。

TOP

而使用“光能传递法线贴图”技术的《半条命2》,在每一帧绘制物体的时,都允许它受到任意多盏灯光的照射。这就是它比较先进的地方。但是有一个前提就是这些灯光必须都是静态的,也就是说,无论位置、颜色和亮度都不能变。



    前面已经提到过“光照贴图”这种技术了。在以前通过传统的光能传递算法来计算光照贴图时,都是仅仅根据所接受到的光线的颜色和亮度计算出物体表面上每一点对应到光照图中一个象素的一份颜色值,也就是说一个面上同时只有一张光照图。而在“光能传递法线贴图”技术中,每一个面同时都对应着三张光照图。那么为什么会是三张光照图呢?

    原来为了在用光照图的同时使用凹凸贴图,Valve的程序员们为场景的每个多边形本身加上了一个小的局部三维坐标系。我们都知道一个三维坐标系(或坐标空间)是由三条互相垂直的轴来确定的,而与这三条轴方向相同并且长度是1的三根向量,就称为这个坐标空间的三个基向量(Basis)。

    在《半条命2》中,这个局部三维坐标系是这样的:



b 《半条命2》 每个多边形上的局部坐标系和基向量。

    使用这三个基向量所代表的三个方向,Valve的程序员们在每个方向上计算一次光能传递的效果,就形成了三张不同的光照贴图。具体的光能传递算法,Valve并没有透露。

    总之就是根据这三个方向,将原来的一张光照图分为对应的三张光照图,而且保证当物体表面的某个法线越偏向于其中一个方向时,它所应该接受到的光照颜色就越接近于对应的光照图上的颜色。
  
    下面我们就来看一下针对静态场景和动态模型,Source引擎是如何分别使用光能传递法线贴图技术的。

TOP

《半条命2》静态场景的光影效果

    这是一张使用《半条命2》渲染技术的典型的静态场景截图。除了火焰之外,场景中其他部分全是静态的。前面说过,对于这种大型静态场景,Source引擎使用基于光照贴图(Radiosity light maps)的光能传递凹凸贴图技术。


所要达到的最终渲染效果

    从这张图中可以看出,场景里既有比较真实的光能传递的整体光照效果,石壁表面又有十分细致的凹凸贴图细节。

    核心技术-基于光照贴图(Radiosity light maps)的光能传递凹凸贴图技术

    我们先来看一下使用以前传统的光能传递算法计算出的单一光照贴图贴到场景中物体表面的效果(为了清楚的表现出光照,去掉了表面本身的贴图):


a 单一光照贴图的效果

    前面说道,为了加入凹凸贴图效果,在《半条命2》 Source引擎中,对应场景里多边形本身局部坐标系的三个基向量,会预先计算出三张光照贴图来。当分别把它们贴到场景中时看起来是这样的:


b 针对第一个基向量计算的光照贴图的效果


c 针对第二个基向量计算的光照贴图的效果


d 针对第三个基向量计算的光照贴图的效果

     我们可以看出,在这三张光照图中,同一部位上的光强和颜色都是不一样的。那么,这三张光照贴图又是如何结合起来的呢?这就需要通过物体表面法线贴图中的法线来从它们之间取值了。

TOP

我们先在场景里每个多边形上贴上法线贴图看看,结果是这个样子的:



图11 物体表面的法线贴图

    看上去很诡异……实际上,法线贴图不是这样简单的贴到物体表面上去的,因为它保存的不是颜色颜色信息,而是物体表面上很多的三维法线向量。

    在绘制一帧图像中的每个象素时,Source引擎都将从法线贴图里取出这个象素对应到物体多边形表面上那个点的法线向量,与多边形的局部空间的每个基向量做点乘(点积)操作,得到一个0到1之间的浮点数。由于法线向量和基向量都是单位向量(长度为1的向量),而两个单位向量方向越接近,点乘的结果就越大(最大到1),所以就可以通过这个浮点数来“过滤”从该光照贴图里读出的颜色。我们用下面的公式来表示一下可能会更清楚:

象素的最终的颜色 =
第一张光照贴图中取出的颜色 × 法线与第一个基向量点积的结果
   + 第二张光照贴图中取出的颜色 × 法线与第二个基向量点积的结果
          + 第三张光照贴图中取出的颜色 × 法线与第三个基向量点积的结果

    这样就保证了象素上的法线越接近于哪个基向量,对应的从哪张光照图取出这个象素的颜色占的比重就越大。

    通过这步关键的计算,就可以把凹凸贴图和光照贴图结合起来了。这时的场景看起来像下面的样子:


通过法线贴图过滤组合后的光照效果,表面凹凸细节的效果已很明显

    下面的一张是最开始的单一光照贴图效果,拿过来比较一下:



    而物体表面本身也是有材质贴图的:



物体本身的材质贴图,没有加入光照

    然后,再用有凹凸细节的光照效果和物体表面本身的材质贴图相混合,就得到了漫反射部分的完整图像:



材质贴图混合了光照后的结果。

    实际上,在混合时只要把算出的光照颜色和材质本身颜色的红绿蓝三个通道分别相乘就行了。

    我们可以再将上面这张图和最开始只使用传统的单一光能传递光照图获得的效果作一个比较:



只使用单一光照图,很多沟壑的凹凸细节都没有

    OK,漫反射部分的实现原理基本就是这样了。下面我们来总结一下这个流程。


a 截取一部分图像来说明这个流程


b

    从左到右:首先使用法线贴图在三张光照贴图中取颜色值,混合成带有凹凸细节的光照贴图,然后乘以物体本身的材质颜色贴图,得到右边最终的效果。

TOP

 19 12
发新话题