Far Cry and Half Life2 Engine scene technique
当前对于流行的第一人称游戏来说,将室内场景渲染和室外场景渲染进行融合是主流的趋势,本文试图从
场景架构入手对当前流行的两个游戏引擎Far Cry和Half Life2进行一些非常浅显的介绍、分析、比较,
由于本人学识浅薄,本文中可能存在一些错误之处,请大家予以谅解,并请高手给予指正。
在过去由于硬件发展的限制因此第一人称游戏只能在室内场景进行,随着硬件技术的发展它逐渐由室内向
室外场景进行过渡,发展到现在已经有大量的游戏可以同时对室内和室外场景进行渲染,当前这类游戏的
代表就是Far Cry和Half Life2,它们完全使用了两种不同的方法来实现室内外场景的融合。
Far Cry:
Far Cry将场景分为室内和室外两部分,对于室内场景它使用了流行的BSP方法来对场景进行组织,BSP方法
在网上有大量的资源可以进行参考,这里我不在进行详细的阐述,在Half Life2部分我再进行一些简单的
介绍,这里我将重点介绍一下它室外场景部分所使用的技术。通常对于室外场景部分最重要的是地形部分
的渲染,当前对地形的渲染存在着大量的算法,其中比较主流的有ROAM、GeoMipmap、Chunk LOD等等,
Far Cry并没有使用上述的算法,为什么呢?对于ROAM来说它确实是一种非常良好的地形渲染解决方案,
但是它存在一个非常大的弊端就是它的灵敏度非常高,因为它是一种连续的基于三角形分割的LOD算法,
因此只要Camera的位置发生很小的位移,整个场景的LOD都需要发生变化,这非常不适应于当前的硬件架构
。而对于GeoMipmap这种利用当前显卡高渲染速度通过增加填充率来加速地形渲染的方法其实也不符合
当前游戏引擎的发展方向,正是由于这些方法的限制Far Cry使用了一种比较折中的地形渲染方法,这种
方法非常类似与Game Programming Gems2中Greg Snook提出的Interlocking Tiles方法,但是还存在着
一些相差别的地方,在Far Cry的关卡编辑器SandBox中我们可以看到通常它使用的是1025*1025大小的
HeightMap,每一个Tile的大小为(32+1)*(32+1),存在4级的LOD,因此一个Tile最简化时的大小为(4+1)
*(4+1),对于不同LOD级别Tile的连接使用了和Interlocking Tiles一样的方法,使用固定的连接模版进行
连接,这里两者差异最大的地方是对Tile内部三角形的生成方式,对于Interlocking Tiles方法它使用
的类似于以前CLOD的方法,分割后的形状象是一个“米”字的形状,而Far Cry的分割方式是对角分割线
的方向都是一致的,这样做的好处是非常容易生成Trigle Strip。好了现在我们看看使用这种方法有那些
优点。其一,由于每一个Tile只存在4级LOD因此避免了ROAM那种灵敏度过高的弊端。其二,由于地形是由
一个个Tile构成,因此非常适合使用Quad Tree这样的结构来进行管理。其三,由于每一级LOD的Tile顶点
顺序都是固定的,因此可以对地形顶点数据进行预先的优化,如提前就可以确定生成Trigle Strip的索引
缓冲区。其四、可以使用预先计算好的LOD查找表的方法来获得每一个Tile的LOD级别,而不需要通过实时
计算来获得。这种方法还有许多其他的优点需要实际使用时认真的体会我就不再一一列举了,Far Cry在
对地形进行渲染时还使用一种OC方法来对地形中被遮挡住的Tile进行剔除,相关的论文请参考A.James
Stewart发表于Eurographics Rendering Workshop的论文Hierarchical Visibility in Terrains,june
1997,我不再进行介绍。当Far Cry需要同时对室内外场景进行渲染时,它使用portal来对场景进行渲染,
如果从整体上来说的话,Far Cry的做法是对室内外场景的渲染分别进行处理,它们完全是两个部分,当
这两个部分需要发生联系时,通过portal来进行处理,这样Far Cry的场景架构就显得非常简单明了,至
于portal的工作过程我后面再进行介绍。下面来看看Half Life2的做法。
Half Life2:
在介绍Half Life2架构之前,我需要先解释一下surface的概念,因为Half Life2的场景都是由surface
所组成,它是场景中最基本的图元。surface是由共处于一个平面上多个顶点所组成的物体,例如一面
墙我们可以称它为surface,因为组成墙的所有顶点位于同一个平面内。通常在Half Life2中把surface
分为四类:水面、水面之上、水面之下、横跨水面。了解了surface的基本概念,下面看一下如何进行
场景处理。在Half Life2中场景被分为三部分:cluster,area,displacement。其中cluster对应于室内
场景的处理,area对应于类似于城市这样建筑物众多的室外场景,displacement对应于地形这样的室外
场景。必须注意的是在Half Life2中对所有的场景都使用BSP来进行场景管理,而不是象Far Cry那样
分别进行处理,它是如何实现的呢,下面我们根据不同的场景分别进行介绍。
对于cluster这样的室内场景,使用最基本的BSP方法来对场景进行管理,也就是基于多边形对齐的BSP
算法,什么是cluster呢?实际上一个房间就可以看作是一个cluster,一个cluster是有严格的定义的,
它必须保证组成cluster的多边形形成一个“凸”物体,也就是组成cluster的任意两个顶点之间的直线
不能位于cluster的外面,一个室内场景完全由一个一个的cluster所组成,cluster之间通过portal进行
连接,房间中的门、窗户都可以看作是一个protal,它是由处于同一个平面上的多个顶点组成的闭合的
多边形,通过使用portal可以对每一个cluster做光线跟踪处理,获得在每一个cluster上可见的cluster
列表,这就是PVS数据了,室内场景在实际运行时可以通过PVS数据快速的进行可见性检查,剔除不可见
的cluster来加速场景的渲染。室内场景中的portal通常是由程序自动生成的,相关的算法非常多,一篇
非常好的参考文章是位于GameDev.net上的由国人胡灵和李振霄所写的Automatic Portal Generation,
非常佩服,这是我在gamedev.net上看到的第一篇由中国人所写的技术文章。当获得了场景的cluster
信息后就可以将它和BSP的节点信息相连了,由于portal必然处于节点的分割面上,因此这一步非常简单。
下面看看如何处理城市这样的室外场景,这类场景由于拥有大量的建筑物并将场景分割成一块一块的区域
,因此也可以使用上面处理室内场景的方法来对它进行处理。这方面的论文也有很多,比较好的一篇是
由Alon Lerner等人写的Breaking the Walls: Scene Partitioning and Portal Creation。我这里只是
简单的介绍一下,这类场景可以看作是由一个个的area组成,area的概念非常类似cluster,一条街道,
一栋楼房都可以看作是一个area,这里必须提醒一下组成area的surface的法线必须指向其内部,cluster
也是这样,因此街道这样的area是由楼房的外表面所组成,而楼房这样的area是由其内表面所组成。每
一个area也是通过portal进行连接,对于这样的场景不能再使用预计算PVS的方法进行可见性检查,而
需要通过使用portal对场景进行实时的剪切剔除运算,必须注意这里对portal的记录方式,在室内场景中
的portal通常需要记录它所连接的两个cluster,而这里的area portal只记录一个与其相连的area,也就
是这里的portal都是单向的,这样做的原因是为了方便进行area的可见性检查,只要知道portal可见那么
和其相连的area一定可见。area和cluster存在一个非常大的差异地方是area不再必须是一个“凸”物体,
它可以是任意的由surface组成的一个空间,因此如果对这样的场景进行BSP分割后,一个area可能只包含
一个叶节点,也可能是包含多个叶节点的完整BSP子树。对于area进行处理时还有一种特殊的情况,就是area
和cluster发生连接时,通常这个时候cluster被包含在area内部,例如对于场景中的一栋建筑物可以看作是
一个area,通向外部的门、窗可以看作是此area的portal,这个建筑物的内部可以看作是一个完整的室内场
景,形成一个BSP子树,这样通过portal可以巧妙的将两种场景融合到一起。下面具体看看如何通过portal
来对area进行可见性检查,需要注意一点area没有AABB,而只有portal和叶节点才拥有AABB,方法如下:
1、由摄象机所位于的area出发递归遍历场景中所有的area portal,检查哪些portal是可见的(使用简单的
frustum culling),通过这些portal我们可以获得一个当前可见area列表。
2、对于所有可见的portal将其顶点投影到屏幕上,获得它在屏幕上的一个包围框。
3、对portal在屏幕上的包围框使用视口的包围框进行剪切,获得portal包围框在当前视口完全可见的部分。
4、通过使用这个包围框可以获得一个frustum,这样每一个可见的portal都可以对其相连的area所包含叶节
点的AABB进行frustum culling运算,从而最终获得一个当前可见的叶节点列表(当然对当前所在的area所
包含的叶节点也需要进行frustum culling)。
最后来看看HL2是如何来处理地形这样的大动态场景的。HL2处理地形使用的是一种非常特殊的displacement
系统,现在heightmap是记录地形信息最流行的做法,但是HL2并没有使用这种方法,他使用了一种称为
displacement map的物体来记录地形信息,为什么这样做呢?根据我的看法主要有以下的原因:首先使用
heightmap记录地形信息其实有一个非常大的弊端,它不能处理非常复杂的地形,用它生成的地形都是连续
的,这是因为在每一个位置它只能记录一个高度值,当同一个位置发生高度重叠时它无能为力。其次我前面
介绍过HL2场景完全是由surface所组成的,使用heightmap你无法将地形信息和surface相连,而通过使用
displacement map可以简单的将地形信息和surface连接起来。下面我们具体来看看它是如何工作的,通常
一个displacement map对应一块地形,它最大可以表示(16+1)*(16+1)这样的地形块,它非常类似heightmap,
不过heightmap只记录每一个位置上的高度值,而displacement map不仅记录高度值而且记录每一点的法线,
由于法线的存在它就可以记录非常复杂的地形,不过displacement map存在一个限制,它的四角的顶点必须
位于同一个平面上,正是这个限制的存在从而使displacement map和surface巧妙的联系在一起,因为surface
上的所有顶点必须位于同一平面上。每一个displacement map不仅记录高度信息还记录了邻边和邻角的
displacement map信息,通过这些信息可以对一片地形快速的建立一个Quad tree,通过它来对地形进行
有效的管理。每一个displacement map都拥有四级LOD,从(2+1)*(2+1)到(16+1)*(16+1)。下面来看看HL2
是如何将地形和BSP相联系的,对于一片地形我们可以把它看作是一个area,这样与其它场景可以通过portal
相连接起来,而每一个displacement map你可以把它看作一个BSP的叶节点,通过前面建立的Quad tree,
你可以快速的将它转换为BSP tree(其实这一步不是必须的,你只需要知道displacement map和叶节点的
连接关系即可),当进行地形的可见性检查时使用Quad tree进行,最终还是可以获得一个可见的叶节点
列表。
通过上面的介绍可以看出Far Cry和HL2的场景架构差异是非常大的,Far Cry的各个场景之间是完全独立的,
而HL2则巧妙的将其融合到BSP格式中,形成了架构的统一性。正是因为如此使HL2对付任意的场景都应付自如
,而Far Cry却只能限制游戏的场景形式,因此HL2的游戏架构明显优于Far Cry,成为了3D引擎发展的主流
趋势,不过Far Cry在一些场景细节上的处理确实非常优秀,如海岸边的潮起潮落,这是HL2需要明显改进的
地方,不过HL2在光源处理上确实是技术超群,全lightmap光照架构,将lightmap应用到极至的bumped lightmap
技术,只能是令人叹服,这里不再一一列举,我只能说HL2是当前商业引擎中的NO.1。
由于行文仓促,难免存在许多错误,尤其是HL2的地形部分由于我还没有完全研究透彻,因此避免不了会出现
错误,请研究HL2的高手给予指正,我这里表示感谢,如果你有什么关于HL2的技术问题希望和我进行交流,
我非常欢迎你发送邮件到我的邮箱
[email protected],也欢迎你在我的blog流言,地址是
http://blog.gameres.com/show.asp?blogID=118&column=0【转贴者按:此Blog已经无效,请访问开头给出的作者Blog】
^_^算是为我的blog做一下广告。