做地形材质的时候为了更多的细节往往不会直接在外部 DCC 里面去 Bake 一个 Terrain 大图,而是直接在引擎里面用各种检测算法比如 Slope 去智能的根据地形外貌进行上色。如图。
而这样做虽然效果好迭代也快但是对于资源的消耗也是很高的,毕竟每帧都要读 N 张贴图去进行各种复杂的叠加混合。于是前人便想了一种降低此过程开销的东西,那便是 VirtualTexture(简称 VT)。对于 VT 最早是由 Doom 的 John Carmack 主导的。这个版本的 VT 做法大概如下。
第一步就是把导入的贴图(JPEG, TGA, PNG)进行切割得到 PageTexture(即全部 PageTexture 合在一起叫 VirtualTexture),比如 1024×1024 的贴图切四份然后存在磁盘里(Mip 也随着一起切)。
第二步用一个 FeedbackPass 计算当前表面可见的 PageID 和 MipLevel 并将结果写入 FeedbackRT,然后用这个信息去 Load 对应的 PageTexture 并加载到 PhysicalTexture,并将 PageTexture 在 VirtualTexture 的位置和其写入到 PhysicalTexture 位置的对应关系写入到一个 IndirectTexture。
第三步材质里面把挂在网格的 UV 转到 VT 的 UV 下并通过 IndirectTexture 找到它在 PhysicalTexture 上的位置并采样输出。
一、Static Virtual Texture
这种方法在 UE 里面貌似叫 Static Virtual Texture。使用时只需把贴图的属性里面勾上 VirtualTextureStreaming 然后和以往的贴图一样用就行,剩下的细节由 UE 自己处理。
开启加载 VT
和普通贴图一样用法这里就简单混合一个 ZMask
在控制台输入 r.VT.Borders 1 和 r.VT.Flush 1 可开启 StaticVT 的 Debug 可视化。这种方法感觉比较适合 StaticMesh 这种 Primitive,在地形上估计没有啥质的提升。
二、Runtime Virtual Texture
所以地形上后来又出现了 Procedural Virtual Texture。这个和 StaticVT 主要区别是 Dynamic 和 Procedural。对于 Dynamic 来说加载的贴图并不会事先对它切割成 Page 而是直接根据情况加载整张贴图到 PhysicalTexture。对于 Procedural 因为能做到实时加载,实时卸载,实时合并那么自然而然的就能够完美支持各种 ProceduralTexture 的绘制。可以参考麦老师的 MVT。
这个东西在 UE 里面对应的是 RuntimeVT。用法首先得新建一个 VT 资源,这里面可以选择它的一些属性比如 Size 和 TileSize,BorderSize 之类的东西以及这个 VT 是只存成 Color 还是 Color 和 Normal 都存之类的。
然后材质里面和往常做智能材质一样拿贴图进来用(这部分就代表着 Procedural),然后结果不直接输出在材质的 Pin 上而是输出在 VTOutput,再在输出的 Pin 上接入 VTSampler 节点里放好创建的 VTAsset 以及对应的属性。
接入的数据和往常做地形材质一样,用程序化的模式去构建地形外貌。
然后地形上加好这个材质并在其 VT 属性栏里放入和材质里面一样的 VTAsset,并拖拽一个 VTBox 罩住某个区域其属性的 VTAsset 也要一一对应。
随后就完成了 RuntimeVT 的应用。因为 Procedural 的内容只是操作了一次并把结果写入 VT 然后地形采样 VT 来得到颜色等数据所以对比以前的 ProceduralMaterial 倒是加强了不少运行时的运算性能。像这儿实验用的材质在不用 VT 之前都是红的。
三、VT Decal
而 RuntimeVT 的另一个 Procedural 功能便是 VTDecal,像幽灵行动就是满地的 Decal 来做细节效果。在 UE 里面并不直接支持 Decal 去操作这种内容而是用了一种类似 MeshDecal 的操作。首先材质里面需要将 MaterialDomain 切换到 VirtualTexture 然后和以往做模型材质一样即可。随后场景地形上放一个 BoxMesh 并赋予 VT 材质,设置好 VT 参数便能得到下图效果。扩展一下可以直接把 SplineRoad 的材质换成 VT 贴上去。
四、Terrain Blend
有了 VT 自然而然的做 TerrainBlend 就会很轻松直接物体采样 VT 然后算个 Mask 混合一下即可,比如 SDFMask 或者自己捕捉一个 TerrainPos 去采样用模型自身 Pos 比较得到 Mask,我这儿就不整那么复杂了。
总体试了一下倒是还蛮不错的特别是 RuntimeVT。不过也有一些不足,比如一个地形材质只能放一个 VT 支持如果大地形不想 VT 精度很低,得严格的做 TileLandscape 并每个 Tile 指定一个材质和 VTAsset 和 VTVolume,这对流程会更具有规范性,倒也是好事至少能挡一些奇怪的美术操作。ennm,interesting。
















