首页 > 其他分享 >Unity实现无缝大世界--地形

Unity实现无缝大世界--地形

时间:2022-12-16 14:55:45浏览次数:69  
标签:LOD -- 模型 2048 地形 Unity GPU 512 无缝

大世界最重要的毫无疑问是地形了,地形也是一项比较古老,且一直在迭代更新的图形学技术。地形系统主体技术要点,一般围绕着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/yang-yang-90-83

再次感谢狗哥老司机的分享,如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群:793972859)

标签:LOD,--,模型,2048,地形,Unity,GPU,512,无缝
From: https://www.cnblogs.com/uwatech/p/16987344.html

相关文章

  • 查看svchost的服务列表
    下载ProcessExplorer(Microsoft免费软件)和更多信息:https://technet.microsoft.com/en-gb/sysinternals/bb896653 打开ProcessExplorer并单击菜单栏上的“查看”。......
  • Linux管道符|
    demo1.py中print("HelloWorld!")print(50)demo2.py中importsyss=sys.stdin.readline()print('Thisismygotstring:%s'%s)data=sys.stdin.readline()prin......
  • MySQL数据库:SQL语句的执行过程
    目录一、客户端的MySQL驱动二、MySql架构的Server层1、连接器2、查询缓存3、分析/解析器4、优化器5、执行器三、InnoDB存储引擎1、BufferPool2、undolog日志3、redolog......
  • vue记录点
    1.vue2路由监听watch:{$route:function(to,from){//事件处理}} 2.vue3路由监听watch(()=>Router.currentRoute.value.path,()=>{......
  • difference between send() and sendTo() in C for a UDP network implementation
    Thesendtofunctionistheonethat'sgenerallyusedforUDPsockets.AsUDPisconnectionless,thisfunctionallowsyoutospecifytheIPandportthateach......
  • 初识js
      js是一个客户端脚本语言,scrip是脚本的意思,脚本语言不需要翻译,运行过程由js解释器(js引擎)来运行,现在可进行Node.js技术进行服务器端编程。js脚本语言属于编程性语言,而H......
  • vr工地安全教育培训系统助力工地安全施工
    在工地施工现场隐藏着许多安全隐患,工地地势复杂;建筑材料摆放密集且混乱;存在大量易燃、可燃物品;人员众多难以集中管理……如果缺乏安全意识就很容易发生事故,所以工地安全教......
  • CAD写块命令快捷键是什么?
    CAD写块命令快捷键是什么?你知道吗?浩辰CAD软件中CAD写块命令:WBLOCK,快捷键:W;主要用于将选定对象保存到指定的图形文件或将块转换为指定的图形文件。本文小编就以浩辰CAD软件为......
  • CAD三维图怎么画?CAD画三维长方体步骤
    CAD中怎么画长方体?CAD画三维图?可以实现吗?CAD软件虽常用于二维绘图,但也可以进行一些简单的三维设计,本文小编就以浩辰CAD软件为例来给大家分享一下CAD画三维图之长方体的具体......
  • uniapp开发的微信小程序页面在IOS上页面可被拖拽的问题
    经排查发现是设计稿宽度不是750px而IOS宽度最低都是375(750/2)这时如果你的设计稿是751px的在IOS上就可以被自由拖拽 解决方案:1让UI出750px的设计稿2设......