游戏开发论坛

 找回密码
 立即注册
搜索
查看: 4146|回复: 0

基于Unity3D引擎的大地形生成研究

[复制链接]

1万

主题

1万

帖子

3万

积分

论坛元老

Rank: 8Rank: 8

积分
36572
发表于 2019-11-28 10:54:29 | 显示全部楼层 |阅读模式
v2-84278829c43705d07f51fb8b4e226625_1200x500.jpg

之前文章中我们已经完成一套基于PC主机等高端平台的地形系统基本的渲染,加载和资源结构。但是很显然光有这些是远远不够的,因为即使基本功能齐全,这套系统终究是要给人用的,而我们作为技术美术,除了实现所有的引擎底层必需的功能以外,也要把生产流程全部打通,先挂一下上一篇的地址,防止有新来的看官接不上:

MaxwellGeng:基于Unity3D引擎的大地形加载研究

题图来自Lute Li使用World Creator制作的测试场景,感觉很有美感就放上了,实际上我们本篇将用很大篇幅介绍使用World Creator这款DCC的经历,这是一款精致快捷的地形开发软件,效率优秀,效果可靠(起码能达到AA级以上的游戏要求),完全可以成为生产流程研发的测试工具。

我们在Unity引擎中开发生产工具链时,核心思想有以下三点,这三点一直贯穿本人的开发始末:

1.        尽可能使用自定义的加载和序列化,避免使用Unity内置的资源管理。

2.        尽可能开发运行时编辑工具,避免使用Unity Editor完成复杂功能。

3.        大道至简,引擎内功能能少就少,凡是能在DCC中完成的工作不要试图搬运进引擎。

第一条比较容易理解,比如上一篇文章中我们就曾讲过,为了保证运行性能和生产效率,除了必须经过SRP渲染的贴花GameObject以外,完全放弃了Unity内置的资源管理,包括Resources,AssetBundle等,直接使用二进制文件存储,而.Net提供的FileStream可以非常直白的计算字节偏移和读写缓冲区大小,并且可以既读又写,因此资源导入导出部分的代码我们可以直接复用,API也可以变成傻瓜式的“传入一张RenderTexture,传出存储到硬盘的数据”,消灭掉额外的内存碎片垃圾,实例化等消耗,因为自定义的资源管理系统在自由度上有绝对优势,因此常常能在性能和生产效率双方面碾压内置资源管理方法。

尽可能使用运行时则是纯粹的经验之谈,在Scene窗口中使用Unity Editor不仅提供的API不够,比如一些复杂的如物理模拟等效果没法实现,而且其生命周期具有不确定性,很容易出现奇妙的问题,相反,在运行时让美术制作人员像玩游戏一样操作控制台,优越性自然而然就可以体现出来。

引擎功能能少就少是我们最重要的一点,不像自研引擎,MPipeline是一款开源的渲染模块,在通用性上有很高的要求,因此对于一些细碎庞杂的功能我们在开发时要保证能少就少。

下面正式进入正题,由于我们设定将一张接近于无穷大的地图,物理分割为多张固定大小的图,如将16384m * 16384m的地图分割成256张1024m * 1024m的大块,每块地形数据结构都是独立存储的,它们之间唯一关系就是在执行逻辑时经过统一的二叉树。因此我们读写的时候可以随意指定每次需要导入的块,而且可以一次性导入多个相邻在一起的块。

按照Ghost Recon提供的规格,使用2 texel / m也就是半米一格的高度图,表达材质分布的Material Index Map则使用和高度图同样的大小,所以每1024m一格的地形将会使用2048 * 2048分辨率的高度图,因此如果需要制作一张大小为2048m * 2048m的地形,我们需要导出一张4096分辨率的高度图,由于高度图对精度要求要保证无损,因此无论储存还是导出时都不应该有任何压缩,这里在World Creator使用TIFF格式输出,保证输出的结果是完全无压缩的R16_UNorm格式。

在World Creator中制作一个简单的Voronoid Erosion Terrain:

image003.jpg

导出时使用完全没有压缩的TIFF格式,这个格式在Unity中可以被正常读为R16_UNorm:

image004.jpg
World Creator截图

导出到MPipeline中的渲染效果:

image006.jpg

可以看到效果非常接近,这说明这套高度图导入完全没有问题,这个步骤也非常简单,我们只需要注意让DCC导入规格与引擎内渲染规格统一即可。

接下来是材质,相比于高度图的导入,材质系统的复杂程度就要高的多了,这套使用Index Map作为材质媒介的工作流程,在大幅度简化渲染的情况下,也严重增加了工具的复杂程度,因为这并不是一个已经存在很久的通用的工作流程,所以我们必须定制一套工具。

Indexmap的原理十分简单,用R8_UNorm格式的贴图存储使用的材质索引,其中每一张材质都由两套贴图按比例混合而成,之所以要这样做,是因为这种两两按比例混合的方法是比较适合对自然景观进行还原的,同时这样也非常适合按层来进行叠加的方法,可以随时修改,比如这里我们拿出两张参考图,分别来自黄土高原和张掖的丹霞地貌:

image008.jpg

image010.jpg

之所以要用这两张参考图,是因为这两张图都非常明显的表现出了一个地貌的一个重要特点,那就是地貌材质“大聚居,小杂居”,从宏观上看,整个地貌棱角分明,该是黄土的就不会长草,该是草皮的就不会大片出现黄土,丹霞地貌也同样,可以看得出同一种颜色基本“抱团”在一起,但是在两种材质交融的部分则表现出非常丰富的混合效果。当然,这样地貌的形成需要经过种种侵蚀和风化以及植被的生长,这些属于地质学内容,不在我们讨论范围内,而需要我们着重讨论的则是如何将这样的过程表现出来。

在World Creator中,材质的分布就正是遵循了这样的自然规律,同一种材质分布聚集在其中的一块,每一种材质都以层的形式进行叠加分布。而每一个像素,也就是一个0.5m * 0.5m的地块内有且只有一种材质。每两种材质的边缘是“硬切”的,也就是只有会用上一篇文章中讲过Bilinear Interpolation的方法实现过渡。

为契合这样的流程,我们首先要在Unity中完成材质编辑,毕竟材质是引擎的第一手资源,直接决定了渲染效果,这是有必要在引擎内完成编辑和导出的:

image012.jpg

工具布置基本比较简单,和普通材质并没有什么两样,无非就是Albedo Normal Smoothness Metallic Occlusion这些PBR选项,然后将材质绘制到Render Texture上实现预览。

在完成材质编辑和预览后,我们需要导出材质,World Creator使用了XML文件作为贴图的索引,所以对于我们而言只需要用脚本解析XML并把新贴图名字填写进去即可:

image014.jpg

因为World Creator的贴图追求精简,所以并没有使用完整PBR流程,这里只需要填入Albedo Normal即可,导出后启动软件可以看到结果令人满意:

image015.jpg

接下来我们在World Creator内随便摆布一下材质分布,当然,本人不是专业的地编,不好看在所难免:

image017.jpg

这幅截图表现出了两个问题,那就是边缘接缝锯齿感严重和贴图重复严重。要解决这两个问题实际并不是难事。先来说一下重复感的问题,World Creator中已经提供了Eliminate Tilling,效果不错:

image019.jpg

在引擎中消除重复感的方法我们则是使用了一张随机的UV Map的方法让贴图进行随机采样。随机生成的原理是这样的,把一张贴图的采样纵横各切3刀,划分为4x4的块,然后对这16个块进行随机的排列组合,之后再对每个随机排列以后的块进行随机翻转和旋转,这套运算可以在Compute Shader内完成,因此我们在游戏开始时启动一个Dispatch Pass将随机结果存储到一张128 x 128的贴图内,因为我们是8米一张贴图的分布,这意味着在256米以内很难出现重复的分布,极大的增加了贴图分布的随机性,随机贴图的生成代码如下:

image021.jpg

在投影VT的Shader中读取这张贴图,并对比随机前后效果:

image023.jpg
原始效果

image025.jpg
随机排列后

可以看到效果有了明显的提升,但依然有一些小瑕疵,那就是在两块随机UV接缝处有很明显的裂隙,这让贴图有了一种砖块感,然而我们并不想要这种感觉,所以只需要在对Tile Map采样的时候加一个随机的UV偏移,就能消除掉规整的裂隙了:

image027.jpg


采样代码如下:

image029.jpg


这里的Noise Texture就是外部传入的一个固定的噪波图,而NoiseTillingTexture则是前边实时生成的UV Map,这里个人建议Noise Texture使用Substance Designer中Cloud节点生成的RG通道的随机图,因为Cloud节点生成的随机较为圆润且分布不规律,比较容易表现出边缘错落的感觉。

完成之后在引擎里测试随机采样的效果:

image031.jpg


可以看到,几乎完全没有人类肉眼可见的重复感,这还是整个地形只用了一张贴图的情况,效果令人满意。这种廉价的Trick方法,比起ShaderToy上IQ大神的Hash算法来说,随机程度肯定远远不如的,但是IQ大神的方法每次采样起码要采3张贴图,而我们这种方法则却不增加采样个数,唯一增加的一张Noise Texture采样也会马上得到复用。

解决了重复的问题以后就需要解决不同材质接缝处的问题了。之前提到我们使用了2 texels / m的规格存储高度图和材质图,这意味着材质之间的切割感是肉眼可见的,那就避免不了锯齿感的诞生:

image033.jpg


使用Bilinear进行插值采样后虽然效果好很多,但是依然有比较明显的锯齿感:

image035.jpg

Billinear Interpolation


为了消灭锯齿感,Ghost Recon PPT中提到了在Bilinear Interpolation之前对UV进行随机的扰动,我们也同样使用了这样的方法解决问题,同时因为采样都在同一个Dispatch Kernel内,所以完全可以复用刚才使用的噪波图的采样:

image037.jpg


采样效果:

image039.jpg


走到这里时,整个流程就只剩下最后一步了,那就是如何让引擎接受World Creator导出的结果,也就是让MPipeline中的工具完成从Splat Map到Material Index Map的转变,这里我们首先需要从World Creator中正确导出一张Splat Map,注意,由于图片格式限制,一张Splat Map只能容纳4套材质的分布表现,所以我们一次只能输出4套材质的分布,如果选择超过4套材质,World Creator会自动导出多张Splat Map:

image041.jpg


在引擎内导入Splat Map,注意,格式必须是非压缩的线性格式,防止出现意外:

image042.jpg


因为每一张Splat Map的每一个通道与每一种材质都是一对一固定死的,在完成材质编辑以后改动幅度比较小,所以我们可以选择在每一种材质下指定对应的通道和贴图,这样每次美术人员在导出贴图后直接把贴图暴力复制到文件夹里就可以进引擎一键刷新,非常方便:

image043.jpg


最终导入效果:

image045.jpg


image047.jpg


可以看到宏观效果还是近处效果都基本达到了我们的需求,实现了与DCC的数据同步。

最后总结一下,我们制定的这套工作流程有很多优秀的特点,如通用性,鲁棒性强,导入导出的高度图,材质分布图格式与各种DCC都非常容易契合;如解耦程度高,在制定流程时并没有像某些工具一样竭尽所能的把功能在引擎内集成,避免使游戏引擎臃肿庞大,保留了修改和兼容其他流程的可行性。但同时这套流程目前完成度还很低,比如Virtual Texture最主要的无限贴花功能,则还依然停留在渲染Demo阶段,暂未成功打通Houdini Engine;Time of Day, Realtime GI等等高级效果表现也还在TODO List上,我们将会在接下来的文章中一步一步把这些难题克服。

相关阅读:基于Unity3D引擎的大地形加载研究

作者:MaxwellGeng
专栏地址:https://zhuanlan.zhihu.com/p/89396980


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2024-4-19 22:16

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表