Skip to content

StochasticTerrain

1390字约5分钟

2023-02-15

StochasticTerrain

一种基于 Texture2DArray 和 Stochastic 算法的Unity地表方案


随机地表混合

基础地表混合的原理

项目中最初使用的地表方案是MTE,出于可控性能和可控加载的考虑,没有使用Unity的Terrain。

但是无论 MTE 还是 Unity Terrain,所采用的混合方式都是类似的。每个像素都会采样一张混合贴图的RGBA四个通道,归一化之后的的值作为4层地表的混合权重,然后对4层地表的RGB值做加权平均得到最终的RGB输出:

float4 weight = tex2d(splatTex, uv);
float sum = dot(float(1, 1, 1, 1), weight.xyzw);
weight = weight / (sum + 1e-5);

float3 color1 = tex2d(terrainTex1, uv);
float3 color2 = tex2d(terrainTex2, uv);
float3 color3 = tex2d(terrainTex3, uv);
float3 color4 = tex2d(terrainTex4, uv);

float3 albode = weight.x * color1 + 
                weight.y * color2 + 
                weight.z * color3 + 
                weight.w * color4;

基础地表混合的缺陷

对于一个很大地面网格(比如1000m x 1000m),1024x1024分辨率的纹理是远远不能满足清晰度需求的,大部分方案采用的是简单粗暴的重复平铺。如果地表有大量的植被覆盖而只有少量地表暴露,或许可以掩盖平铺带来的重复感。然而,一旦游戏中植被较少,或者像大部分SLG一样主要依赖场景美术的绘制地表时,重复感就相当致命了。

减少重复感的一些方案

这里不详细描述所有算法的原理和实现,仅列举部分参考:

实际使用中的优化

SLG项目使用的是Unity方案的优化版本,原有方案会对输入的UV做一些Hash运算,lerp之后得到一个用于最终采样纹理的StocUV。如果对每一层都这么来一次,单个像素的计算量直接上天。何况对于SLG游戏,地表是主要组成部分,大多数情况下会占用相当高的像素占比,很高的计算复杂的对手游性能影响巨大。

因此,在实际使用中,有一些优化:

  • 只对世界空间坐标计算一次得到StocUV,之后每层地表都使用相同的StocUV。这样相对减少了Hash次数。

  • 预计算Hash存储到一张LUT中,之后只需要采样即可。但是由于精度问题,这样做效果欠佳。于是作为较低Lod层级的计算方案。

其他优势

由于可以减少重复感,用于平铺的地表纹理不再需要通过提高分辨率来解决近距离下的重复感。于是单张纹理的精度从204822048^2降低到了102421024^2,在张数很多的情况下,这极大减少了地表纹理占用的内存。

Texture2DArray

传统方案的缺陷

在传统的地表混合方案中,如果采用PBR光照模型,每一层地表需要至少三张贴图,即:

  • 颜色贴图(Albedo)

  • 法线贴图(Normal)

  • 混合贴图(Metallic + Smoothness + AO)

在渲染管线中,每一张贴图需要绑定一个采样器。但是在大部分硬件平台(尤其是移动平台)上,采样器数量是有限制的。SM5.0标准以下一般最大允许拥有16个采样器.

FIX

这里应该是 SM5.0 对 ps_5_0 输入寄存器的数量限制,可以参考微软官方文档: (Microsoft)。 其中元素数量限制为128,采样器数量限制为16。大多数渲染API中的Sample和Texture其实是两个东西。 这么看来最大贴图数量应该是128张才对。

不过并不是所有平台都支持采样器和纹理分离,所以考虑兼容性的话还是默认最大纹理数量为16.

众所周知,一般情况下纹理贴图只有RGBA四个通道,每4层需要预留一张贴图作为混合贴图使用,因此单个地形的材质最大只能拥有四层地表:

4×3+1=13<16<5×3+2=17 4 \times 3 + 1 = 13 < 16 < 5 \times 3 + 2 = 17

纹理数组

万幸的是,哪怕移动端硬件也支持一个特性————Texture2DArray。可以参考Unity的说明: Unity--Texture2DArray

总之,Texture2DArray允许将一堆相同参数的纹理打包到一个纹理数组中,作为一个纹理资产丢给GPU使用。比如,在SLG项目中,所有区域的地表纹理加起来也不过20个,可以愉悦地打包送到Shader里,然后根据给定的地表Index值采样对应的纹理贴图即可。

这样做的另一个好处是,对于使用同一个混合纹理的数个不同地块,可以指定不同的Index数组,以达到更加细粒度的纹理控制。但是由于Mesh和纹理资源没有改变,基于Unity的GPU Instance,这几个地块使用同一个材质,可以在一次DC中绘制。

附属工具

为了使地编们更好地受益于纹理数组和随机纹理,编写了一套工具,包括:

  • 地表绘制工具

    • 使用Computer Shader快速绘制地表。
    • 支持笔刷旋转、缩放、流量等特性。
    • 支持退出时保存绘制状态。
  • Texture2DArray资产编辑工具

    • 支持纹理数组的创建保存和编辑。
    • 基于项目配置存储位置。
贡献者: Astroite