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