持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第32天,点击查看活动详情
上篇中我们从地形使用的资源的角度进行了分析,本篇就深入内部分析具体的渲染方法了。
Draw Call分析
由于我启用了Instancing,地形块会自动合并绘制,如下:
这儿看到的draw call是在当前摄像机视角下需要绘制的地形面,Unity内部是通过四叉树管理可见的地形面并进行LOD。在Scene View下可以使用线框模式查看LOD的情况,下图中高亮部分就是上面那个地块(9个地块之一),这个地块的尺寸是100x100,可以看到Unity对于地块在实际渲染时会动态的改变LOD,在需要的地方会使用更密集的三角形。
而绘制这些不同LOD的更小的块是会使用不同的draw call,通过Instancing会把可以合并绘制的draw call进行合并。
材质分析
上图是某个draw call使用的材质,可以看到使用了很多贴图,这是因为我们使用了默认的Terrain Lit材质,这是个PBR材质,而基于上一篇的分析,对于Terrain Layer会使用很多的贴图,这儿就有12张Splat以及normal,maks图,还有一张 control图,即splat alhpa图。由于我还使用了静态GI,因此会有两张lightmap图。另外还有两张shadowmap。当然不能忘了主角地形的heightmap。另外有一个terrainNormalmapTexture法线贴图。 除贴图外,这个材质使用了大量的property,其中有很多是和PBR计算有关,和地形渲染相关的部分如下:
URP Terrain Shader
下面分析UPR的terrain shader,文件为TerrainLitPasses.hlsl
Terrain Instancing
主要是TerrainInstancing这个函数。其中有一个Instance数据_TerrainPatchInstanceData。
float2 sampleCoords = (patchVertex.xy + instanceData.xy) * instanceData.z; // (xy + float2(xBase,yBase)) * skipScale
float height = UnpackHeightmap(_TerrainHeightmapTexture.Load(int3(sampleCoords, 0)));
可以看到Unity将地形块分为了patch,而patch之间可以instanceing绘制。这儿的_TerrainPatchInstanceData对patch的坐标进行偏移,再使用偏移后的坐标去采样高度图,获取高度。需要注意的是,如果不使用Instancing,就不会有HeightMap图,顶点数据应该是在CPU端计算好了,因此不需要HeightMap。而使用Instancing就必须在GPU上读取Heightmap。
Instancing时的法线贴图
使用Instancing时,Unity会从高度图计算出逐像素的法线,并存储在一张法线贴图中,也就是terrainNormalmapTexture。这提供了更细节的法线。如果不是这样,法线就只能是逐顶点的。并且在低LOD时,由于顶点数更少法线也更粗,所以此时可以使用法线贴图将会大大提高显示效果。
该功能需要在材质上开启:
待续
接下来会详细分析terrain渲染时如何将terrain layer进行混合。