大世界最重要的毫无疑问是地形了,地形也是一项比拟古老,且始终在迭代更新的图形学技术。地形零碎主体技术要点,个别围绕着 LOD 来开展。最近一些年,随着 DrawInstance 和 GPU Pipeline 的风行,地形零碎又在这两个方向做了进一步倒退,这俩技术十分符合地形零碎,几乎就是为地形而生。
Unity 的整套地形零碎(包含植被),在有 DrawInstance 性能前,简直不能在挪动上应用,大家个别采纳转成 Mesh 的形式在游戏中应用。转成 Mesh 始终不是短暂之策,施展不了地形极致 LOD 的劣势。要想做大世界,本人写一套高效的地形零碎是必不可少的。
大略思路仍然是正当的材质和模型 LOD,联合 DrawInstance,在性能和成果之间进行均衡。我这里提供一个在挪动平台验证过的可行解决方案,能够参考,也可齐全依照这个计划来,至多在几万到几十万的量级不会出问题。
通过模型,能够分两局部,GPU 地形和近景四叉树,以下图每个格子是 512 的大地图为例,灰色局部为近景四叉树,蓝色局部为 GPU 地形。
GPU 地形
这里先次要讲一下配置和注意事项。
GPU 地形整体大小是 2048×2048,更新粒度是 512,也能够是 256,但若是 256,资源文件可能就太多,Unity 在资源文件爆炸后,Import 的速度会变得很慢。理论测下来,512 的粒度没有显著问题。LOD 分 5 级就足够了,因为咱们整体大小才 2048,LOD0 的 Mesh 对应密度比例是 1:1,LOD4 曾经是 16×16 了。
跟模型无关的数据倡议用保留成二进制文件,因为如果保留成 Unity 文件,如 Asset 或者贴图,须要把这些资源放到 Asset 目录下,而二进制的数据文件是能够放到目录外的,打包的时候进入 Bundle 就 ok,在 Editor 模式下,能够间接通过文件拜访,所有为了节俭 Import 耗时。二进制还有益处是,能够整合各种数据,如每个块各个 LOD 等级的一些配置信息(坐标,高度差等)、挖洞信息等。一个 2Wx2W 的大世界数据能管制在 500MB 左右。
在理论应用过程中,如果用 Hiz 来剔除会有一帧提早,不太适宜做地形的剔除,所以最好还是采纳 PVS,并且地形的排布比拟参差,比拟好做 PVS,人造省去了 PVS 里字典映射的局部。
这里说一句,DXR 用来烘焙 PVS 是个不错的框架,很适宜做一些离线自动化工具。
接着来着重说一下,PVS 的数据组织和加载应用。
家喻户晓 GPU 地形只显示 2048×2048 的地形,那么全量的 PVS 数据是多少呢?LOD0 是一个 4 ×4 的 Mesh,显示的理论空间是 4 ×4,那么 LOD0 的内存数据就是 (2048/4)x(2048/4)/1024 = 256KB,所有 LOD 加起来不超过 512KB。数据结构能够间接用一维数组来示意,因为是个全量平均排布,那么 Offset = (Pow(4,level) – 1)/3,这里 level= 0 示意最低的即 2048×2048,这里须要有个换算,还有就是可能低等级没有到 0,只有再减去无用 Index 就能够失去正确后果。其实从一个地块的数据量,就能大略推算出整个大世界地形的 PVS 数据有多少,理论还能进行压缩,这些对于包体的大小是齐全能承受的。
为了省去索引懊恼须要将硬盘数据全量寄存,这是一棵残缺的树,然而这里的内存还能压缩,因为 LOD0 是最多的,而离中心点较远的地位其实是不须要的,能够在 LOD0 只加载 64 米以内的数据,LOD1 则是 124 米,以此类推。这样能把数据压缩到十分小。就索引来说,须要做进一步换算,拿个纸笔应该能很快推算出来。
GPU 地形能够再拆分两种材质:
绿色为 RVT 材质,黄色为中景离线烘焙贴图材质
远景 RVT 材质
在这里因为应用 GPU 地形的原由,粒度会降落到 4 ×4,这样咱们能够把尺寸缩到 512,RVT 在把尺寸缩到 512 后,即便 VT 的 size 管制到 2048,也能达到一个比拟清晰的画面,不仅能节约很大的内存,而且地表的精密度也能失去很大的晋升。
中景离线烘焙贴图材质
咱们为每一块 512 地形,生成混合实现的 Albedo 和 Normal,这里有三点须要留神。
1. 烘焙的时候留神把投射 RVT 的模型也渲染到下面,比方路面、贴花这些,间接烘焙到中景贴图上。
2.Normal 间接取地形的顶点法线即可,无需把 Layer 上 Tangent 转到世界空间。相隔太远、太碎的 Normal 反而会造成噪点,起副作用,咱们只保留顶点法线的后果就 ok。
3. 烘焙时,地形的 Layer 能够适当放大,远处的纹理能够通过放大 Tile,让纹理的细节显现出来,具体放多大,取决于 RVT 远处纹理的缩放比。
在材质筹备好后,因为是 512 位单位的,咱们须要显示 2048×2048(两头扣掉一块 512 为 RVT 材质),咱们在运行时,能够采纳 TextureArray 或者动静合并 VT 的形式。以合并 VT 的形式为例,咱们能够疏忽两头 RVT 那个块,间接生成 2048×2048 的残缺贴图,间接平铺下来就 OK,要解决的是,咱们该当尽可能复用之前的数据。如:
咱们只需把右下一圈,填到左上即可。如下图,将蓝色区域填在灰色区域,而后存一个偏移即可。
这种解决办法在整个大世界的资源加载十分常见,肯定要有这个意识。
因为整个 GPU 地形应用两种材质,那么在 GPU 地形生成 Mesh 的 IndirectDraw 就要离开生成,分两个 DrawCall 提交。
近景四叉树模型
2048 以外的中央,其实也能够用 GPU 地形,只是资源的模式要分两级,2048 以外的数据粒度更大一点,保障 IO 敌对,甚至能够做四叉树的 IO,不把各个等级的数据放在一起,从技术上讲是能齐全可行的,关键问题在于,进去的模型品质很差,因为须要把 LOD 等级提到很高,根本至多要 128×128 来示意一个数据,疏密调节的灵便度在这里远远不如模型。
在确定生成近景模型后,最现实的状况是,全地图生成一个动态模型间接渲染,一个 2Wx2W 的大地图,做完极致优化,1~2W 面就能达到预期成果。这里倡议用 Houdini 主动生成,能够把山脊那局部的面加多,海拔低的盆地和平原面数能够砍得狠一点。然而咱们面临了几个问题。第一,咱们要能灵便抠掉两头 2048×2048 的空隙,粒度是 512。第二,近景模型跟 GPU 地形之间存在接缝问题。
第一个问题,难的不是抠出两头局部(VS 输入 SV_Position 为无穷大或者堕落顶点到边上),难的是 2048 的矩形边,要求抠出来是一个四四方方的,那么就须要在每个 512 上都要卡一条直线,卡线会使模型顶点数间接翻倍。
第二个问题,能够把模型往降落降,依据视角个性,能解决大部分问题,然而恰好地形接缝问题太过突兀(漏出天空盒,有人思考把天空盒的底面用地形色彩图,仍然没法解决所有问题),必须得保障 100% 没问题。那么经典的解决接缝问题就是堕落边和加裙子,因为 GPU 地形和近景地形不在一个零碎,不好堕落边,只剩加裙子了,加裙子又须要很多顶点。
为了解决这两个问题,最简略的一个方法是,切成每个 512×512 的地块,且生成裙边,而后管制地块的显隐,再通过 Static Batch 渲染。这里引申出了两个新问题,一个是面数过多,切块加裙边,面数根本翻 3 倍以上,另一个是尽管是 Static Batch,然而理论 DrawCall 数偏高。理论裙边仅仅在 2048 的那个矩形须要,其余中央造成很大的节约,能缓解第一个问题,然而还是因为切边导致面数翻番。
咱们引出一种新的解决办法,四叉树生成各个等级 LOD 的模型,而后显示矩形,如图所示:
这样的益处在于:极大缩小了因为切割增加的面,DrawCall 相比全量 LOD0 少得多。
一些想法
还是想通过堕落横跨两头矩形三角形,把矩形内的点推到矩形上,而后对于矩形内剩下的三角形,在 VS 的输入,设置 SV_Position 的 xy 为无穷大来挖空两头的局部,仿佛是一种比拟完满的解决方案。因为四叉树的近景显示,性能没太大问题,临时没尝试这种方法,前面如果有机会,能够试试这种方法。
近景的材质,间接应用一张 Diffuse 贴图就能够,然而这张贴图,不要应用工具主动生成,因为就这一张贴图,值得花一些工夫在 PS 里精雕,特地是能够在游戏场景中对着来画,一边画,一边对照游戏中的成果,在山脊的中央,勾画出山体的线条,把法线、AO 这些都间接画到 Diffuse 上,会起到意想不到的成果。
封面图来源于网络
这是侑虎科技第 1259 篇文章,感激作者狗哥老司机供稿。欢送转发分享,未经作者受权请勿转载。如果您有任何独到的见解或者发现也欢送分割咱们,一起探讨。(QQ 群:793972859)
作者主页:https://www.zhihu.com/people/…
再次感激狗哥老司机的分享,如果您有任何独到的见解或者发现也欢送分割咱们,一起探讨。(QQ 群:793972859)