Clayman's Graphics Corner

DirectX,Shader & Game Engine Programming

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

 Terrain Reandering, Again !

作者:clayman
仅供个人学习使用,转载请保留作者以及原文链接,勿用于任何商业用途。

       前段时间修改了一个基于roam的古老地形系统,于是有了此文。
       首先,关于roam,虽然早已经忘了具体算法,也从来没有自己实现过基于roam的地形,但和以前blog里说的一样,roam已经完完全全过时了,原因有以下几点:
1.roam需要在CPU端实现,需要使用dynamic buffer,bad to gpu :(
2.
roam对视点位置太敏感,微小的移动都会导致重新镶嵌,而重新镶嵌出的模型常常只在局部有微小变化,不但对性能提升无益,还导致视点连续改变时,popping现象严重:(
3.
roam计算量太大。本人没有做具体性能分析,但同事告知,如果镶嵌密度提高一倍,即使i5的cpu算起来也很吃力,况且我们游戏的视野并不算远:(

     由于以上几点原因,我希望新的地形1,提高性能,对CPU和GPU都相对友好;2,算法简单;3,减少popping现象。最终,我们有了一个chunk lod + interlocking tile的方案。有趣的是完成新系统前,我都没有看过<<Simplified Terrain Using Interlocking Tiles>>,而是从wow的实现得到了很多灵感。有没有搞错Chunk lod + interlocking tile?? 高端前沿开发者在鄙视我了,确实,这并不是什么新技术。但下面我就告诉你为什么它已经足够好了!!

       首先,geo clipmap,dynamic tessellation之类的方案一开始就被否决了,原因很简单,对GPU不友好。我希望新系统在dx9 sm2.0级别的硬件上也能跑。此外,一直以来我都认为通过动态计算来实现地形这类相对“静态”的物体太不值得。即使目标平台是DX11,也不值得通过tessellator计算地形,这两种技术虽然只需要非常少的vertex buffer,但计算过程所消耗的带宽和计算量远远超过小buffer带来的好处。

     Chunk lod对地形来说是非常理想的算法,对整个地形块进行lod改变,避免了roam过度敏感的问题,此外,算法非常简单,甚至可以说不需要算法:每个chunk使用固定的vertex buffer,预先计算好几个不同LOD下的索引数据,选择不同的index就完成了lod变换,static vertex buffer,static index buffer,没有运行时计算,并且index可以被所有相同lod的chunk共享。最经典的例子莫过于wow的地形(不熟悉的赶紧google)。Chunk lod的问题在于如何与相邻不同lod的chunk相连接,避免出现T-junctions。这个问题初看起来很复杂,但如果我们限制第n层lod的chunk只能与n+1层的chunk相连接,那问题就变得非常简单,我们只需要为每层LOD创建9种不同的索引,保证边缘的顶点一致就可以,如下图所示:

       对于有n层lod的地形,需要n*10种不同索引,听起来似乎有些多,但考虑到同层的所有chunk都能够共享这10块buffer,其实数据量并没有多少。有什么没考虑到的吗?

 要对地形打洞怎么办?
很常见的需求。如何从顶点级的层面来考虑,似乎完全无法和目前的方法兼容;但如果从像素级的层面考虑,地形上的洞其实就是透明像素而已,完全可以使用贴图或者添加顶点数据实现特定部分的透明:)

精度最高的层是每个顶点之间2个单位的距离,大部分情况下很好,但在某些山尖的地方稍微有些粗糙,不把最高lod增加到1个单位间距的情况下,有什么方法可以改进?
注意观察上图,interlocking tile的精髓就在于只要边缘保持和邻接层一致,内部可以任意镶嵌。如何你觉得0级的lod在某些情况下精度还不够,可以再创建0+级,边缘和0级的保持一致,内部精度可以再提高n倍,对于有需要的特定chunk使用0+级索引,其余大部分chunk仍使用0级:)

Chunk lod改变时还是会出现popping现象,有什么解决方案吗?
方案1,让你的第0级chunk范围足够大,当远处的chunk lod改变时,不太容易发现popping :)
方案2,所有地面都覆盖上草地,就算有popping你也看不到了:)
方案3,使用Geo-Morphing,我们可以得到完美的lod转变。比如每个顶点都保存了在不同LOD层下的高端,当LOD变化时,插值变换到正确的高度。
方案4,把以上3种方案结合起来,让0级范围足够大,对0到1级之间的转变使用morphing,植树种草,你基本看不到popping了:)

那我的顶点数据不是要多很多?
压缩,压缩,压缩。地形数据并不需要32位float精度。通常16位就足够了,尝试使用short,ubyte,udec3之类的格式保存位置,法线和纹理坐标。

如何管理组织地形?
每个chunk不管处于任何lod状态,大小都是固定的,基本上二位数组就够了,对,完全没有必要使用四叉树!

如何实现无限大地形?
你大概需要把刚才的二位数组改为环形二维数组,当视线移动时,删除看不到的chunk,并且把新chunk加载到之前删除的空间,类似这样

对于wow类型的游戏,多少级lod才够,每个chunk多大,多少三角形合适?
如果你看过介绍wow地形的文章,大都会告诉你wow使用的了2级lod。下图的场景中,大部分地形都处于0级,从有雾的区域开始,非常远的地形才会使用1级。0级Lod每个chunk512个三角形,1级256个。下图的场景大概有250~300个chunk,每个chunk一次DP!三个,不要超过三个LOD,不能再多了:)

地形渲染的瓶颈在什么地方?multipass纹理性能如何。
根据上一个问题,chunk/DP数量其实都不是问题,顶点/三角形数量对性能的影响非常小。曾经在perfhud的测试中发现不同lod但覆盖近似大小屏幕区域的chunk渲染费时几乎一样,主要计算都花费在pixel shader,因此,shader lod也很重要。比如对只对最近的n个tile使用normal map,对远端的chunk直接用一张预计算的纹理或者雾的颜色,尽量避免multipass。

关于texture blend有什么要说的吗?
1, 最大四层纹理对于大多数游戏都足够了。我们的古老地形系统使用multipass理论上可以支持无限层纹理,但最终很少有一个chunk会使用4层以上,并且multipass最终成了最大瓶颈。2,如果你觉得普通线性混合出的纹理效果不好,那么可以参考泰坦之旅的方法。3. 通常把4层纹理的权重保存在一个ubyte4中,如果保证权重总和为一,那么可以通过x-rgb计算第四层纹理的权重,在r通道中保存一些其他东西:)

好吧,还有什么更高级的技术吗?
类wow游戏中,基于height map的技术已经非常成熟,由于硬件的发展,lod已经变的越来越不太重要。现有技术就可以完全满足需要。如果想有突破,那么09年GDC《HALO WARS: The Terrain of Next-Gen》中基于vector field的地形解决了height map不能出现高度重叠的问题,非常值得尝试(一直没明白他们如何解决碰撞检测,求高人指点)

 

 

posted on 2012-12-10 02:00  clayman  阅读(2826)  评论(2编辑  收藏  举报