持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第31天,点击查看活动详情
前言
之前一直听说Unity的地形渲染很低效,在手机端没法用云云。正好最近要调研一个开放世界项目,目标是要兼容手机平台的,所以先研究一下当前版本的Unity(2021LTS)的地形渲染到底是怎么做的。至于手机上的地形,我了解到几乎都是转成模型,使用LOD渲染,然后会使用特定的材质来优化纹理采样和混合,但是这个技术首先是已经很古老了,效率必然是不错,但是用LOD来做模型必然连续性不够,变化太明显,这个技术我觉得只能作为一条退路。而部分游戏(公开资料不多)可能已经用上了GPU优化的地形渲染,比如基于Compute Shader的Culling,再比如Virtual Texture。而回到起点,我先弄清楚Unity内置地形渲染究竟是什么情况吧,再决定技术方向。
测试工程
- 测试工程是基于Unity2021.3LTS版本
- 基于Unity的第三人称新手包,在Unity编辑器中使用内置Terrain工具绘制了9块相连接的100x100的地形,其实是个很小的地图。做这么小其实是为了后面测试动态加载地块方便,否则跑半天才换一个块,然后这么小也可以看渲染性能了,其实主要是地面和植被渲染,而地形本身的三角形管理应该是基于四叉树的动态LOD,据我观察应该是CPU主线程上计算的,因为没看到相应的Job和Compute Shader,虽然效率一般但也还不至于是瓶颈(目测),暂时不研究这个了。
随手涂抹的地形
随便种了点草,远看还好吧
Terrain Layers和Splat Alpha
地形的混合纹理笔刷使用了Unity TerrainSampleAssets中附带的layer资源,我在一个地形块上使用了4个layer:
各个地形块使用的Layer有重复的,但是顺序不一定一样。
由于只使用4个Layer,因此一张Splat Alpha图就够了,其RGBA四个通道分别对应这4个Layer。下图是使用上面这4个Layer的地形块使用的Splat Alpha图:
而这个地形块是这样的:
可以看到,蓝色通道表示的是第3个Layer雪地那个层。
所以,地形纹理绘制的基本原理,就是用Splat Alpha图去混合不同的Layer。而每个Layer可以有Diffuse, Normal Map, Mask Map三张图组成。比如这个雪地的Layer:
这样这个地形块绘制的时候就需要至少 4X3+1=13张贴图,再加上可能有Lightmap,ShadowMap等,shader中贴图采样的次数绝对是爆炸,所以移动端不能用看上去毫不夸张了。当然这个场景我在小米10上测试了,这个测试场景自然是没有什么压力的,但如果地形更复杂,地图更大,再加上海量的植被,建筑,粒子,以及游戏中的各种物体,那压力可想而知。另外,只使用4个Layer其实是不够的,4个Layer就代表地面上的基础纹理和另外3个细节纹理层,丰富度并不够,但是由于一张Splat Alpha贴图只有4个通道,只能控制4个Layer,所以更多的Layer就需要额外的Splat Alpha贴图了。Unity默认的这个做法虽然很灵活,理论上支持非常多的Layer,但实际可使用的Layer数要受到性能的限制,因此也许真的只能做demo了。我知道的一个移动端的优化是将多个layer图合并到一张贴图上,形成一个atlas图,这样可以减少采样次数,当然这样也有其他问题,比如相邻图层交界处bilinear插值产生的接缝。
小结
本篇从资源的角度研究了Unity内置地形渲染使用的纹理混合方案,从资源中就已经可以看出Unity的做法了。而下篇中将从FrameDebugger中,以及从Shader代码中验证我们看到的情况。