天美大牛GDC“硬核演讲”:《三角洲行动》跨平台技术解密!

【GameLook专稿,未经授权不得转载!】

GameLook报道/近几年,跨平台研发就成为了大多数游戏工作室的选择,尤其是跨手游和PC平台,成为越来越多跨端产品的选择。

只不过,跨平台研发从来都不是容易的事情,想要同时平衡PC玩家对游戏品质的高要求和手游设备的性能限制,不仅需要强大的技术和团队支持,还需要长时间的试错、迭代。

在近几年的跨平台游戏中,腾讯游戏天美工作室群斥资20亿元打造的《三角洲行动》可以说是国产游戏的代表作之一。根据腾讯2025Q1财报数字,这款游戏的双端DAU已经突破了1200万,并且屡次成为腾讯高管点名夸奖的产品。

GDC 2025大会上,腾讯游戏天美工作室群TA团队主管Lichuan Wang和腾讯游戏天美工作室群引擎团队主管Hang Jiao,做了题为“三角洲行动:适用于PC和移动设备的高性能优质地形与生物群落技术”的演讲,谈到了《三角洲行动》跨端生物群落着色、程序化内容生成,以及跨端地形渲染、性能优化遇到的挑战与解决方法。

以下是Gamelook编译的完整演讲内容:

Lichuan Wang:

大家好,我们很荣幸站在这里代表腾讯游戏参加GDC 2025大会,欢迎来到“三角洲行动:适用于PC和移动设备的高性能优质地形与生物群落技术”主题分享。

首先,请允许我做一个简单的做我介绍并给出一些简要的背景信息。我的名字叫Lichuan Wang,从2005年开始就进入了游戏行业,到现在快20年了,这对我来说有点难以相信。

我过去曾在育碧上海和蒙特利尔工作室就职16年,大多数是从事3A射击游戏,主要在《汤姆克兰西》系列,包括《幽灵行动》和《分裂细胞》,我相信你们可能知道这些IP,还有《孤岛惊魂》系列,从《孤岛惊魂4》一直到《孤岛惊魂6》。

随后在2020年,我加入了腾讯游戏,具体说是Team JADE,作为一个工作室首席TA支持很多款游戏,比如《穿越火线手游》和《使命召唤手游》,它们都很成功。

最近,我在《三角洲行动》项目投入了好几年,我主要聚焦于渲染、程序化、管线、工具等。

首先,我想为大家说明一下今天这场分享的预期。这并不是一场单纯的编程演讲,它更像是编程类与视觉艺术/技术美术流程工具相关内容结合的演讲。

那么,来看看我们对《三角洲行动》的目标与宗旨。

《三角洲行动》是一款超写实FPS游戏,整体渲染解决方案和艺术呈现是基于PBR,所以你可以将其想象为一个纯物理渲染项目。这是创造一个自然开放世界的关键。尽管时间和人力有限,我们的技术框架也应该有效地想两个平台交付游戏。最后,游戏需要是高性能的,这就意味着它需要在PC和大量手游设备上流畅运行。

接着说说我们今天的安排:首先,我们会简短介绍《三角洲行动》以免有人对该游戏不熟悉。我会讲述一些有关地形和生物群落的核心渲染技术,这些技术带来了你们今天在两个平台看到的内容。

然后,我会讲到我们是如何使用程序化工具帮助生物群落的生成与管理。从技术角度而言,我们将介绍地形纹理和地形几何的解决方案。这些方案具有极低的资源消耗和极少的人工,适用于PC端和移动端。

接下来,我们将深入探讨多种性能优化技术。最后,我们将总结一些最后的想法以及我们已经在开展的相关后续工作。

《三角洲行动》的技术限制与游戏模式

下面我们来看看我们面临的技术限制。

《三角洲行动》将在多个平台上发行,其中包括PC端,还覆盖从低端到高端的各类移动设备。我们的目标是在PC端实现最高4K 144帧的运行效果,同时在旗舰级移动设备上也能达到最高120帧的运行帧率。

游戏拥有一个完整的昼夜循环系统,其开放世界包含室内和室外环境。因此,包括生物群落、地形等在内的整个世界构建都需要适配一天中的各个时段。

我们有一个100平方公里的主世界和一些相关的小地图。

说到地图尺寸,我简单介绍一下游戏的范围。

我们有三种游戏模式。撤离模式,就像我之前说的,它是主世界,是一个10公里×10公里的开放世界。这片广袤地图上随机发生的事件将持续吸引玩家的注意力。

大规模PvP模式包含多张4×4和2×2地图,每张地图的具体尺寸由关卡设计师根据游戏玩法目的进行定制。

该模式允许最多64名玩家同时在战场上对战,同时还能保持极高的帧率。

此外,此模式拥有逼真的地形,宏大的战争场景中包含军用车辆、坦克和直升机。我们还为该游戏模式实现了广泛的战争损伤效果和大规模破坏效果。

最后要介绍的是战役模式,这是一种非常典型的线性任务游戏模式,它重启了经典游戏《黑鹰坠落》(Black Hawk Down)。该模式同样设定在一幅巨大的地图上。这一模式的地形、环境以及游戏体验将带你重温25年前经典的《三角洲部队》IP 。

跨平台生物群落渲染

在开始讲东西之前,我们应该谈谈生物群落,就这个方面,我会强调跨平台框架。

这款游戏设置了多种生物群落,让整个《三角洲行动》的世界更加真实可信。主世界设定在摩洛哥,而其他游戏模式将发生在世界各地不同气候、环境以及受损程度的地点。

如何在这两个差异巨大的平台上实现这些特性,对其进行系统化管理,甚至尽可能多地复用资产呢?这实际上就是我们今天要讨论的阴影技术的主要话题。

考虑到植被种类的多样性,我们不想依赖随机方式来实现,因为这种方式缺乏可信度。而且,通过材质派生来实现这一效果会大幅增加绘制调用次数,同时让资源管理和迭代变得更加困难。

因此,起初我们尝试从一张大纹理中进行采样,以提供可控的颜色变化。然而,对于一个10×10的开放世界而言,一张1万的纹理大约会占用100Mb的内存,这对于移动设备来说是不可行的。

所以,我们决定采用Clipmap来实现。

简而言之,Clipmap 是一种动态纹理表示方法,它能够高效地将任意大尺寸的纹理缓存到有限的实际内存中,从而实现实时渲染速率。

如你所见,纹理更新会随相机移动而变化,在相机范围内使用 mip0 级纹理,并随着距离增加逐渐降低 mip 级别。这显著降低了内存占用,同时保证了近距离细节的精度。最终,内存占用从最初的 100MB 左右降至约 2.5MB,使其适用于移动设备。

与常规纹理采样相比,clip map的更新需要消耗更多的指令。因此,我们决定不仅仅局限于表现植被的色调或颜色,而是尽可能多地在这个单一clip map中打包更多信息。这包括植被的季节状态、水文数据、地面的湿度/干湿状况等信息,都会被整合到这张纹理之中。

它本质上包含了我们游戏的所有生物群落信息。各个生物群落模块的着色器都会对这个纹理图进行采样。该框架在PC端和移动端是统一的,我接下来会详细说明。

首先,我们来谈谈植被相关的情况。在一个10×10的开放世界中,由于周边环境的差异,不同区域的植被健康状态各不相同。此外,在其他游戏模式里,地图还设有不同的季节设定。

我们希望单一的植被模型能够呈现出多种不同的形态,以此实现资产的最大化复用。因此,我们引入了健康等级这一概念。我们将树木和草丛的健康信息分别打包存储在clipmap中。

此外,我们将战斗损伤也视为健康状况的一部分,所以同样将损伤信息编码到这两个通道中。数值范围在0.1 – 0.85之间用于存储季节信息,而战斗损伤信息则被限制在0.9 – 1这个区间内。

除了将clipmap作为全局输入来表示健康状态等级外,各个资产还准备了相应的健康状态和战斗损伤外观表现。因此,我们为每个植被模型创建了颜色变化效果,并将这些颜色变化存储在独立的查找表(LUTs)中。通过使用clipmap中的数值对查找表进行采样,我们最终得到了看似随机却可控的效果。

此外,这种方法在两个平台上都能保持一致性。

资产方面,在PC端,我们使用打包到法线贴图中的遮罩作为着色遮罩,并将其与基础颜色进行线性插值处理。

在移动端,为了进一步优化性能,我们摒弃了基础颜色,而是完全依赖一种我们称之为“strands mask”的灰度图,该图被打包进了法线贴图中。

这样一来,输入数据就减少了一个纹理,这极大地降低了纹理采样次数。这种方法既实用又成本低廉,而且在视觉效果方面,我们对此十分满意。

当植物的健康程度达到极端状态时,植物就会进入燃烧状态,其数值范围在0.9至1之间。这种设定使得状态转换更加平滑,同时无需额外进行材质衍生处理。

当然,受性能限制,在移动设备上,植物燃烧状态仅以深色显示。而在PC端,我们会添加更多阴影细节和复杂效果。

为了营造出更出色的氛围并增强真实感,我们按照一定规则融入了弹坑和燃烧效果。此外,我们还增添了更多材质细节,比如烧焦的树叶和燃烧的火星。所有这些元素都可以通过材质质量等级轻松进行降级处理,以确保在各个平台上风格和效果的一致性。

我们还将与水文相关的信息打包进了这张clipmap中。

从空间角度来看,河流和陆地生物群落是相互排斥的。这意味着大多数植物不会同时在水下和陆地上生长,所以我们可以将它们合并为一类,二者不会产生冲突。

关于全球河流数据,我们存储了以下信息:红色(R)通道和绿色(G)通道存储了两个表示河流流速的向量(通常称为流向图);蓝色(B)通道存储了河流吸收率数据,这基本上用于控制水的颜色;而河流浮沫信息则存放在Alpha通道中。

对流向图进行多次采样可以模拟水波运动。由于波浪在其周期内会重复出现,因此采样周期越多,呈现出的效果就越自然。对于PC端,我们对波浪法线进行三次采样,利用余弦函数作为权重在各个采样层之间进行混合处理,同时还能模拟出更清晰的小波浪。

对于高端移动设备,我们将采样次数减少至两次,并每隔1/2个周期进行一次混合;对于配置较低的移动设备,我们直接采用简单的线性周期混合方式,以减少余弦指令带来的开销。

在颜色处理方面,我们使用从clip map中获取的吸收遮罩来定义基础的水体浑浊度水平,同时将吸收LUT和散射LUT进行映射,以此共同描述水色和散射颜色。这使得我们能够在无需额外绘制操作的情况下处理自然水体效果,例如区分河流、湖泊以及泥沙被冲刷走的效果。

移动端水体采用半透明渲染方式,在数据上与PC端有所不同,其关闭了散射效果。因此,它仅对吸收LUT进行采样。此外,它还将LUT的Alpha通道进行映射,以此模拟水的浑浊度。

这很容易理解:水越浑浊、深度越大,光线穿透能力就越弱,水体也就显得越不透明。在半透明效果方面,我们实际上进行了更广泛的优化,以适配低配置手机。

正如前文所述,大部分较深水域往往是不透明的,而且截至目前我们还没有任何水下游戏玩法。

因此,我们对标准地形渲染、半透明连接部分以及几乎不透明的深水区域进行了不同程度的性能优化,这大大降低了图形处理器(GPU)的时钟频率(以减少能耗和发热等,优化性能)。

在水面反射效果方面,在PC端,我们采用了屏幕空间反射和多重图像光照(IBLs)相结合的方式。

在移动端,出于性能方面的考虑,目前仅使用了IBL。不过,为了使整体视觉效果的相对准确性更佳,我们会基于采集到的数据手动调整IBL,以获得更好的匹配效果。

最后,我们将湿度信息打包到clipmap中,作为潮湿地面、潮湿岩石、水坑等的输入信息。

Clipmap的精度仅为每米一个像素,但在地形上,我们可以采样高度层并将其与wet mask阈值进行混合。然后对暗度蒙版进行限制(截断处理),从而得到水坑区域。与使用贴花相比,这种实现湿润效果和水坑效果的方法可减少额外的绘制调用次数。

一旦这些潮湿区域被嵌入到clip map中,其他内容就能加以利用,比如我们的岩石,从而营造出与潮湿地面和积水的真实交互效果。

对于植物而言,全局光照会显著影响其外观表现,其中环境光遮蔽(AO)是最为重要的一个方面。

在PC端,我们采用屏幕空间光线追踪环境光遮蔽(SSRTAO)来实现全局光照的遮挡效果,同时通过纹理补充精细的结构化环境光遮蔽。

针对植物的预烘焙顶点环境光遮蔽仅对次表面散射起作用,用于模拟多次散射遮挡效果。

移动端的植物仅使用顶点环境光遮蔽。对于草类植物,可以通过额外的UV坐标来获取环境光遮蔽。

此外,在移动端,环境光遮蔽还参与虚假阴影的计算,这对于在移动设备上的表现十分重要。

在场景中处理大量植物时,顶点着色和像素着色都极为耗费资源,而阴影计算尤其昂贵。对于树木而言,我们不仅需要减少顶点数量,还需使用低细节层次(LOD)模型来投射阴影。

但这会导致阴影质量低到令人无法接受,你可以看到树冠上到处都是精度低、边缘生硬的情况,这真的很糟糕。

因此,我们将顶点环境光遮蔽(AO)应用于直接光照阴影处理,以此近似模拟阴影效果,在获得更平滑过渡效果的同时降低阴影采样成本。

通过调整虚拟阴影,我们还能够近似模拟多种透光效果,避免场景中的阴影和像素颜色过暗。

最终,你能看到一个更稳定、更流畅且更统一的结果。最重要的是,它在性能方面的开销很小。

草皮的渲染开销远高于树木,因此它既不投射阴影也不接收阴影。

我们采用逐实例随机值与环境光遮蔽(AO)混合的方式模拟阴影,这与处理树木阴影的方法类似。 稍复杂的部分在于,这种模拟阴影会根据观察距离和角度产生衰减效果,从而避免在远距离观察或俯视时出现生硬的黑色过渡。

此外,由于草的数量众多,它不会投射阴影。我们将草的位置信息烘焙到地形的clipmap中以用于地形采样,并为地形额外添加直接光照和镜面遮挡效果。这在地形和草实际上并未被阴影覆盖时,能有效改善二者之间的融合效果。

我们来聊聊远处的植被。为了在所有平台上都能实现良好的性能和视觉效果,PC端和移动端在植被远景渲染方面存在显著差异,因此我们准备了三种不同的处理方法。

首先,是Impostor,它仅在PC端使用,作为植被网格的最终细节层次(LOD)。其次,是广告牌(Billboard),它专门用于移动端远处的模型。

最后,我们称之为组合卡片(Assembly Card),它是一种真正的背景元素,用于地图边界以及游戏玩法完全无法到达的区域。这种方法在两个平台上都会用到。

首先,让我们来看看impostor。我们正在使用一种相当常见的技术,这种技术在许多之前的演讲和论文中都有提及,实际上,这是一种基于网格卡片(mesh cards)的方法。它的概念相当简单:从不同角度捕捉卡片,挑选出效果最佳的那一面,然后对模型的剩余部分重复这一过程,直至没有剩余部分为止。

我们分别对树冠和树干进行处理,然后尽可能少地使用纹理卡片来捕捉纹理,同时使纹理更好地贴合树模型。

例如,通常只需3张卡片就能让树干呈现出非常令人满意的效果。

将这两部分分开生成的另一个优势在于,我们可以对它们采取不同的处理方式。比如,树冠需要更注重聚类和体积感的呈现,而树干和树枝则无需如此。

最后,我们将这两部分合并为一个整体。

我们在整棵树上实现了抖动时间抗锯齿(Dither Temporal AA),以实现平滑过渡。我们利用顶点颜色来区分不同部分,从而能够分别为每个部分设置不同的参数。这种方法减轻了树干类似卡片的视觉效果,同时保持了树冠的较高密度。

与PC端的impostors相比,billboards在性能较低的平台上的使用更为广泛。

在移动端,我们采用这种方式来处理远处的植被。根据树木种类的不同,我们处理billboards的方式也有所差异。

通常来说,我们有三种处理方式:第一种是单平面billboard,这是最为常见的billboard类型。我们在游戏里大多数对称植被的处理上都使用这种billboard。

它的原理很简单,我们选取一个合适的视角进行捕捉,无论玩家在世界中的位置如何,billboard始终面向玩家。

然后,我们运用抖动技术,在观察角度发生变化时,让两个平行平面之间实现平滑过渡。

最后,我们会根据相机距离动态调整两个平面之间的间距,以防止在远距离观察时出现 Z 轴闪烁(深度冲突)现象。

我们把另一种类型的广告牌称作“倾斜平面billboard”。这种billboard通常用于树干倾斜度极大、外形高度不对称的树木,比如棕榈树。我们会从树木外形最对称的角度采集纹理作为原始纹理,然后在着色器中对纹理进行倾斜处理。

在着色器中,我们将树干和树冠部分分开处理,对树冠顶部应用偏移、旋转甚至扭曲操作。

对于树干,我们调整弯曲曲率和枢轴偏移量。这实际上是一种主观性很强的操作,力求让形状与实际模型相匹配。通过线性插值(Lerp)函数,我们最终将倾斜树木的轮廓恢复到一个平面上。

我想指出的另一个技巧是,无论billboard的类型如何,我们最初都会对画面的尺寸进行标准化处理,而不会考虑树木的实际大小。

然后,我们会在阴影处理环节调整尺寸以使其与实际情况相符。这种方法在移动设备上便于批量处理,并且能显著降低渲染成本。

对于那些难以到达的森林区域,我们采取了更进一步的做法。

这实际上是billboard技术的一种延伸。美术师们手动放置各种不同尺寸的billboard,将它们组合在一起形成集合体。然后将其捕捉作为原始纹理,并应用到条带平面上,由此创建出我们所称之为的“assembly cards”。

通过组合不同类型的assembly cards,进而构建出《三角洲行动》世界中那些遥远且难以到达区域的森林。

程序化内容生成

我们继续探讨程序化生成(proceduralism)相关的实现内容。在程序化世界生成方面,行业内已经有许多成熟的解决方案。

是的,我们采用基于Houdini的工作流程,生成过程是离线进行的。

我们会获取地形信息,根据特定规则生成点云数据,然后将这些点云数据返回到引擎中,并实例化生物群落对象。由于这种方法相当常见,我觉得大多数人都了解,所以今天我就不展开详细讲了。

相反,我会更多地聚焦于讨论我们如何进行跨平台实现。

当然,首先要提到的是我们刚刚谈到的Clipmap,它存储的大部分数据都是由程序化内容生成(PCG)系统生成和修改的。

在我们的游戏里,几乎所有的地形和生物群落都是相互关联且基于特定规则构建而成的,同时我们会为美术师和内容创作者提供预设的笔刷工具。这些预设包含地形、植被资源、贴花以及特效(如飘落的树叶、悬浮的灰尘、烟雾、雾气等)。

美术师只需简单地选择相应的预设,就能在世界中他们想要的位置进行绘制,无需进行任何额外的操作或设置。

这让我们可以直接在编辑器中使用一套预设工具,但我们的配方是完全分离的,这意味着同一个配方针对每个平台都有单独的HDA文件。

最后,在PC和移动设备上我们会得到不同的结果,主要是为了应对平台限制和性能问题。

虽然PC端和移动端是两条独立的项目管线,但由于它们共用相同的生物群落类型,我们仍希望二者在外观上保持相似。

当然,在创建和迭代过程中,我们不想重复劳动。因此,我们在两个版本之间寻找继承关系,并在Houdini软件中进行降级操作。我们的方法是在配方中锁定高优先级元素,并将其继承到移动端版本,同时去除一些不太重要的元素,比如矮树、小灌木和装饰性物件。

最终得到的是一个简化版的移动端版本,整体上仍能保持与PC端相似的外观。

如果仍然存在一些性能热点问题,仅靠降低配方复杂度无法满足要求,我们还会设计并创建专门的配方,在某些特定方面更激进地降低密度和类别。你可以将其视为一种仅适用于移动端或专门针对移动端的工具,它仅对移动版本产生影响。

我们尽量让生物群落以预设为基础进行构建,但为了满足非常特定的游戏玩法需求,我们也提供了手动放置树木和大灌木的灵活性。不过,周边的装饰布置会采用程序化处理方式。

最终,细节会在周边生成,并且融合得非常好。

我们部署了一种自动化修正方案,用于处理大片植被的重叠与交叉问题。棕榈树就是最典型的例子。

首先,我们需要找到一种方法来验证它们是否相交。为此,我们对几何体进行了体素化处理。三维体素相当于值为 1 的三维数组。如果多个三维数组求和后的最大值等于 2 甚至更大,就意味着模型发生了相交。

接下来,我们会自动改变其中一个模型的方向。

然后,我们再次进行验证和修正。并且不断重复这一过程,直至不存在任何相交情况。我们同时在 PC 端和移动端开展这项工作,以确保所有树木都能摆放得整齐美观。

草皮悬浮是一个普遍存在的问题,尤其是在移动端。由于预算和性能限制,移动端草丛模型簇的bounding box通常非常大。

我们的解决方法如下:

首先,向地形投射射线以形成投影,测量草体模型枢轴点与地形之间的距离。如果测量结果超过阈值,就将悬浮的草稍微往下拉一点。再进行一次验证,移除悬浮的草;如果性能允许,也可以用更小的草替换它们。

由于一切皆由PCG控制,因此我们可以灵活调整密度以减少三角形数量,或者替换某些类型的模型来减少绘制调用次数,从而在每个画质等级下实现视觉效果与性能之间的最佳平衡。

最终,我们得到了在感知上相似的PC版和移动版,同时从根本上满足了这两个平台在质量和性能方面的需求。

最后,道路也是地形的一部分,下面我来详细说明我们进行的一些优化。

在一些低配置的移动设备上,由于性能限制,我们无法支持虚拟纹理(VT)。此外,地形精度已从每格1米降低到了每格2米。

如你所见,道路和地形几乎在所有地方都有重叠,而且Z轴深度冲突(z – fighting)现象极为严重。所以,即便输入的数据相同,针对道路和地形各自的处理方法也需要有所不同。

在程序化处理过程中,我们专门为低配置设备生成了额外的道路模型。在这些模型中,需要将拓扑结构(topology)调整为 2 米的精度,以便与地形网格精确匹配。

从视觉效果上看,这样处理后看起来是正确的。然而,这又引发了另一个问题,即三角形数量急剧增加,这对于低配置设备来说同样难以承受。因此,我们对远处道路进行了多边形简化,将其处理为LOD模型。

为了避免重叠问题再次出现,在顶点着色器中,我们逐渐将远处的顶点向上拉升。

最终,一切看起来都很完美。

好了,现在我要把舞台交给我们的引擎主程 HangJiao,他将为大家介绍核心技术功能。

Hang Jiao:

大家好,我是Jade工作室引擎团队负责人Hang Jiao。你可以叫我Jesse。

我从事手游开发工作已有10年,参与过几款成功游戏的制作,其中包括使用虚幻引擎开发的《穿越火线》和《使命召唤手游》。

今天我将重点介绍在运用虚幻引擎4.24版本研发的《三角洲行动》这款游戏中,地形与生物群落渲染的技术方面相关内容。

地形纹理(Terrain Texturing)

首先,我们来说地形纹理。

正如Lichuan之前提到的,我们使用虚拟纹理(Virtual Texture,简称VT)来渲染地形纹理。虚拟纹理本质上是一种纹理缓存算法,能够节省带宽。

此外,它还允许在地形上添加大量贴花(Decal)。这使得我们能够在PC端和移动设备上渲染出高质量的地形。我们在虚幻引擎中使用了自适应虚拟纹理技术。

在移动设备上使用虚拟纹理(VT)时,我们需要在写入虚拟纹理之前对纹理进行运行时压缩。如果不进行压缩,那就意味着每帧会产生大量的带宽消耗,还会导致设备发热。因此,我们将其压缩成ASTC 4×4 或 6×6 格式。

我们采用了一种非常简单的算法,该算法仅支持两种端点模式,并且在ASTC压缩中不进行分区操作。出于兼容性方面的考虑,我们在所有移动设备上都使用像素着色器来进行压缩。

首先,通过像素着色器将数据输出到一个无符号整数4通道纹理中,其中每个像素为128位,适合表示ASTC中的一个块。然后将此纹理复制到一个缓冲区,之后再将其转换为压缩纹理。

结果如上,在带宽方面,每帧仅占用四分之一或九分之一的带宽。峰值信噪比(PSNR)达到40到50,我们的美术师对最终呈现的效果很满意。而且我们压缩一个VT页面仅需约0.2毫秒。

接下来,我们来谈谈地形纹理的基础,混合贴图(Splat Map)。在两个平台上,我们对混合贴图基本采用了相同的方法。这使得移动端游戏拥有非常高质量的地形。

我们在移动端将基于物理的渲染(PBR)纹理的通道打包到更少的纹理中。这里有两组纹理数组,一组用于反照率(Albedo)和高度图(Heightmap),另一组用于法线、粗糙度和环境光遮蔽。

让我们来看看需求。我们的美术师希望有更多图层,至少32个图层,以让游戏世界更具多样性,我们得想办法使用一张ID贴图。

我们需要基于高度的混合功能,并且混合权重要可控。这样就能在较大区域内对两个图层进行混合,使过渡效果更加自然。

我们需要为地形上的每个点都设置每个图层的权重,而且我们需要低带宽消耗,这意味着我们不能使用过多的纹理采样。

但是,像虚幻引擎默认采用的那种每图层单独设置权重的方法,无法支持如此多的图层。因为要得到最终结果,你需要对所有可能的图层进行采样。

此外,像《孤岛惊魂》和《幽灵行动:荒野》采用的ID贴图方法,无法支持权重混合。它们在一个点上只能有一个ID。

我们该怎么办呢?

那么,让我们从一种简单的方法入手来简化这个问题。

首先,我们可以为这些图层赋予一个明确的顺序,让它们从下到上相互覆盖。对于地形上的每个点,我们存储两个图层 ID,即底层和顶层。我们不允许同一个点上有三个图层。因此,我们可以假定底层始终具有 1 的权重值,所以我们只需要存储顶层的权重。

我们使用一个 ID 映射表来存储这些信息,其分辨率为每米一个点。

为了获取ID映射图,我们的美术师通常会在编辑器中为每一层绘制权重。在地形的每个点上,我们仅保留权重最大的两层。

因此,对于地形上的每个像素点,我们需要获取其周围的4个点,并在这4个点之间进行插值。

我们需要对4个ID贴图点进行采样,接着对8层进行采样,这意味着在移动设备上总共需要进行20次采样。

这样的采样次数太多了,需要减少。

首先,我们来介绍一个小技巧。我们可以在三角形内部进行插值,而不是在其周围的四边形内插值。这样能将层数从8层减少到6层。这可能会让地面呈现出一些三角形的视觉效果,但通过适当的图层混合处理,这种效果几乎难以察觉。

不过,即便如此,仍然存在6层图层,而大多数情况下,美术师们不会在同一位置绘制这么多层。倘若我们利用这一信息,将不同ID的数量从6个限制为3个会怎样呢?

采用这种方式,我们虽然有6个ID编号,但只需采样3层图层。我们已经对这种方法进行了测试,发现3层是维持功能过渡的最小数量,并且足以表现复杂的地形。

那么,我们如何从三角形内的6个ID中得到3个不同的层级呢?我们会在离线状态下进行一次ID固定处理。当美术师在编辑器中绘制权重时,对于每个三角形,我们会得到6个层级编号,即3个底层ID和3个顶层ID。

在开始之前,创建一个大小为3的空集合。首先,将所有3个底层ID添加到该集合中。如果集合未满,就继续逐个添加顶层ID。注意,要优先添加权重最大的顶层ID。如果集合已满,就通过将顶层ID设置为与底层相同的ID,来移除多余的顶层ID。

由于我们仅移除 ID,处理相邻三角形不会破坏当前三角形。现在,在这 6 个数字中仅有 3 个不同的 ID。并且请记住,我们为这些层定义了明确的顺序,所以顶部的 ID 始终大于底部的 ID。

接下来,我们需要在着色器中从这 6 个数字里获取 3 个 ID,即最小 ID、中间 ID 和最大 ID。

在着色器中,对于每个需要着色的像素,首先我们需要解码 3 个 ID 贴图采样值,以获取 6 个 ID 编号和 3 个权重。

然后,我们可以使用以下代码从这 6 个编号中获取最小、中间和最大的 ID。

最后,我们可以得到每个三角形顶点上各层的权重,并对这些权重进行混合处理。

以下是总结:

与传统的逐层量化(Weight – per – layer)方法相比,我们的方法允许更多的分层。与《孤岛惊魂》或者《幽灵行动:荒野》所采用的 ID 贴图(ID – map)方法相比,我们的方法允许权重混合。

如果在 ID贴图方法中想要实现过渡效果,就必须额外创建一个混合两层的图层,这会消耗更多的内存。而在我们的方法中,可以任意混合两层,并且不会产生额外的内存消耗。

此外,这两层的混合还能产生类似《巫师 3:狂猎》中的法线叠加和衰减等效果。

针对悬崖渲染:在虚拟纹理(VT)中,通常是从 XY 世界坐标映射UV,这会导致悬崖呈现出拉伸的效果。

因此,有一种方法是使用三平面映射(tri – planar)。这种方法需要额外对纹理进行 2 次采样,分别从 XZ 和 YZ 映射 UV,并在这两者之间进行混合,这意味着每帧都会产生额外的开销。

还有其他优化三平面映射的方法,不过这些方法通常仍需要在基础通道(base pass)中对额外的纹理进行采样。

那么为什么不直接将三平面映射渲染到VT中呢?让我来解释一下。

我们发现,采用XY映射(即从上方俯视地形的映射方式),只有在平坦地形上看起来效果才好。在悬崖这类地形上,如果仍然使用XY映射,其UV会显得非常拉伸。

如果我们把 XZ 或 YZ 映射到悬崖上,UV看起来就不会那么拉伸了。需要注意的是,在 VT 页面上它可能看起来是拉伸的,但在网格本身上并非如此。

因此,获取正确 UV 的一个简单方法是:对于 VT 通道中的每个像素,我们检查其法线方向。

基于此,我们决定使用 XY、XZ 还是 YZ 作为最佳映射平面,并使用该映射平面对应的 UV 来绘制这个像素。

为实现这一目标,我们需要获取Z轴位置。

我们需要将实际的地形网格(terrain mesh)输入到VT管线中,而不是原始的简单四边形(quad)。另外,请记住使用SampleGrad函数。由于我们每像素都在改变UV坐标,所以需要手动指定正确的mipmap。

以下是结果。可以看到,UV不再被拉伸了,但存在明显的接缝。那这个接缝该怎么处理呢?

为修复接缝问题,我们采用了一种受《孤岛惊魂》启发的随机方法。令我们惊讶的是,这种方法在VT中看起来美观且自然,它在帧与帧之间保持一致,因为我们不会频繁刷新虚拟纹理缓冲区。而且由于它具备mipmapping功能,所以不会出现锯齿问题。

因此,我们以最低的成本实现了与三平面(Tri – planar)类似的效果。我们还尝试了双平面(bi – planar)方法,它的混合效果更好,但成本有所增加。随机方法(stochastic method)看起来足够好,这就是我们在游戏中采用的方案。

不过,直接将悬崖绘制到虚拟纹理中存在一个限制,即近距离观察时,最大分辨率会降低,我们认为这是一个可以接受的结果。

还有其他一些技巧可以提升视觉质量。

在使用高度混合(Height Blending)时,在相机附近看起来效果不错,但在远处会呈现出块状效果。

这是因为在远处,混合操作使用的是高LOD的mips,而这些纹理的精度不足,从而导致出现块状外观。

除了使用远距离平铺因子(far tiling factor)之外,我们还引入了另一种技巧。我们在远处逐渐切换到线性混合(linear blending)。

这样看起来更加柔和,并且解决了块状问题。

另一个问题在运行时出现了。

我们的美术人员在编辑器中绘制地形,整体看起来细节丰富。 但在运行时,尤其是在移动设备上,远处的地形看起来很平坦,沟壑和侵蚀效果被平滑掉了。

原因在于,在编辑器中,地形的面数为38万个,而运行时版本的面数仅为8万个。为了提升性能,我们大幅降低了远处网格的密度。因此,映射在顶点法线上的几何细节也相应减少了。

为了还原细节,我们引入了流式虚拟纹理(Streaming Virtual Textures)来解决这一问题。

由于在网格中可用的空间不足以存储远距离处的顶点法线信息,我们可以将其烘焙到纹理中。整个地形的较低级细节层次(mipmaps)被烘焙成流式虚拟纹理(SVT)格式。

在虚拟纹理处理过程中,当我们接收到一个页面请求时,会检查是否存在流式虚拟纹理数据。

如果存在,我们会直接将其加载到物理纹理中,而不是通过实时虚拟纹理(RVT)方法进行渲染。流式虚拟纹理和实时虚拟纹理共用同一个物理纹理,因此不会产生额外的内存开销。

所以我们把顶点法线烘焙到缩放虚拟纹理(SVT)中,并且在渲染到屏幕时忽略地形网格上的顶点法线。现在,法线信息完全存储在缩放虚拟纹理里了。

但是,当缩放虚拟纹理在靠近相机的区域与真实虚拟纹理(RVT)混合时,我们必须采用相同的方法来避免接缝问题。也就是说,我们也要将顶点法线渲染到真实虚拟纹理中。这就要求我们像处理悬崖那样,使用实际的地形网格来渲染虚拟纹理(VT)。

最终效果很不错,在远距离下也没有丢失任何细节。而且我们的美术师甚至可以通过修改离线状态下的缩放虚拟纹理,手动定制远景外观。

此外,我们的游戏中有许多出色的微调。

例如,由于我们的游戏是一款快节奏的FPS 游戏,当我们使用枪械的瞄准镜时,相机视野(FOV)变化非常快,这会导致虚拟纹理重新绘制为更高的精度。这一现象相当明显,因为我们可以看到地形四处闪烁,干扰了我们寻找敌人。

因此,我们在打开瞄准镜时引入了一个额外的偏移量,以在视野(FOV)变化时保持所需的mip level。这使得游戏操作更加流畅,不过代价是在打开瞄准镜时地形看起来会有点模糊。

地形几何体

接下来我们说说地形几何体。

在虚幻引擎的默认解决方案中,地形的每个瓷片(tile)都需要单独的绘制调用,因此渲染整个地形需要大量的绘制调用。这在移动设备上会带来巨大的性能开销。

为此,我们引入了CDLOD(Continuous Distance – Dependent Level of Detail,连续距离依赖细节层次技术)来渲染地形网格。它利用绘制实例来渲染瓦片的不同细节层次。一个实例是一个网格网格,靠近相机的部分较小,在远处的部分较大。

所有实例的顶点数量相同,所以靠近我们的地方网格更密集,而远处则更稀疏。通过这种方式,我们只需1到2个绘制调用就能绘制整个地形。

此外,我们运用了一个小技巧来减少实例数量。

当我们请求提高地形瓷片的LOD时,通常的做法是将其细分为四个更高LOD的瓷片,这样就需要渲染 4 个实例。但我们也可以裁剪出低细节层次网格的相应部分,仅对所需部分进行细分。

这种裁剪操作在顶点着色器中完成,并统一设置顶点位置。通过这种方式,我们只需绘制 2 个实例。每个实例的顶点数量相同,因此我们绘制的顶点总数减少了。

关于曲面细分技术,硬件曲面细分存在诸多技术瓶颈:它的性能表现欠佳;三角化模式不理想,易引发几何误差累积;移动端适配性差。

因此,我们研发了软件曲面细分方案:该方案作为CDLOD(连续细节层次优化)的自然延伸,在地形区块中引入LOD -1、-2等更低层级,通过顶点着色器(VT)阶段采样高度图动态调整几何形态。

采用柔和细分(soft tessellation)时,我们希望在近处实现非常高的几何密度,并且希望密度能随着距离的增加迅速降低,但连续细节层次(CDLOD)要求相邻补丁之间的LOD差值必须为1。

这一限制导致了许多多余的补丁。如你所见,我们裁剪了额外的补丁以满足这一限制条件。我们放宽了这一限制,允许对细节层次进行多次划分,同时保留裁剪剔除方法。传统方式是进行 2×2 的细分,而我们尝试了 4×4 和 8×8 的细分方式。

正如之前介绍的,借助裁剪技巧,实例数量减少了,并且可以更快地降低网格密度。我们选择了 4×4 的细分方式,因为它在误差率和实例数量之间实现了良好的平衡。

与硬件曲面细分相比,这种方法性能更佳,且三角图案布局更优,由此产生的误差更小,我们已在PC端应用了该方法。

此方法理论上也适用于移动设备,但由于时间有限,我们未能将其集成到最终版本的游戏当中。在中高端设备上尝试使用该方法值得一试。

性能优化

我们有很多的性能优化,主要是在移动设备优化时间与内存,更重要的是发热问题。

如Lichuan此前所说,我们在很多地方使用了clipmap,这是一种实用的技术,可根据需要保留细节的距离远近,在摄像机周围加载高精度纹理,而在远处加载低精度纹理。这样做可以节省大量内存。

我们还把splat ID map转换成了clipmap,因为我们只需要附近的高精度数据。我们没有为每个tile使用一个虚拟纹理渲染器,而是采用了整个场景渲染器来管理剪辑图流送,actor数量减少了,从而减少了卡顿现象。

如果将全部32层的纹理都加载到纹理数组中,将会消耗过多的内存。在渲染我们附近的地面时,我们只会用到这32层中的部分纹理。因此,我们实现了一种动态纹理数组,在需要时才加载纹理,并填充到纹理数组的空缺位置。

在渲染中距离地面时,我们使用完整的32层纹理数组,但采用较小的mipmaps。这种方式控制了需要加载的全分辨率纹理层的数量。

我们自动监测所使用的层数,并进行可视化的图层检查。在兴趣点之间的过渡区域,内存压力较低,因此我们可以放宽限制以实现更优的质量。

还有另一个优化机会。

我们允许对每个像素进行 3 个不同图层的混合,但许多图块并不需要使用那么多图层。有很大一片区域仅使用单图层,比如草地和泥土。而且大部分区域混合的图层数不超过 2 层。

需要注意的是,我们会频繁绘制高精度LOD虚拟纹理页面,这些页面会不断地在物理纹理中切换进出。

因此,我们为着色器制作了 3 种不同的变体,并根据即将渲染的页面所使用的图层数来选择相应的变体。这些数据是在离线状态下收集的,且内存占用极少。

针对移动端,我们可以对虚拟纹理通道中使用的渲染目标进行优化。我们希望在虚拟纹理中使用 2 张纹理,并将通道进行合并,以此减少基础通道中的采样次数。

但由于我们对贴花使用了阿尔法混合(alpha blend),所以在渲染虚拟纹理页面时,仍然需要将不透明度作为阿尔法通道。

在此之后,我们使用一个子通道(sub pass)或者帧缓冲区提取(frame buffer fetch)操作,将通道重新排列成理想的形式。由于这种方式利用了移动端芯片上的内存,因此数据不会回传到主内存。

然后,我们可以将纹理压缩为 ASTC 格式。为了进一步优化,如果我们发现某个特定的图块不包含阿尔法混合,那么我们直接将其绘制到具有理想通道排列的渲染目标(RT)上。这些信息也会在离线状态下进行预处理。

在跨设备适配方面,我们有很多参数可供调整。

不同设备可以设置不同的最大分辨率。降低分辨率意味着VT页面 dirty的情况会减少。

此外,我们可以设置VT mip bias以及纹理数组多级渐远纹理偏移量(texture array mip bias)。这样做能在低端设备上节省一些内存。

在高端设备上,地形网格的高度图精度为 1 米;而在低端设备上,则会回退到 2 米精度。这意味着需要渲染的光栅化顶点数量会减少。

地形网格细节层次(LOD)距离比例系数为我们提供了另一种减少顶点数量的方法,它能让远处网格的密度下降得更快。我们可以限制每一帧填充的页面数量,以降低卡顿出现的可能性。

另外,各向异性纹理采样(aniso – texture sampling)的值可以根据画质等级进行不同的设置。

在配置极低的移动设备上,我们采用了一种特殊的地形渲染方式。这主要是出于兼容性方面的考虑。部分低配设备在使用虚拟纹理时会出现问题。因此,我们必须找到一条不使用虚拟纹理的渲染途径。

我们在基础渲染通道中直接渲染纹理混合图(splat map)。由于该设备没有良好的动态纹理数组支持,我们使用了分辨率为较低的全 32 层纹理数组。我们将纹理混合图简化为每个顶点对应单层纹理,实际上这就回退到了 ID 图方法,我们移除了法线贴图。

而且由于设备不支持贴花效果,道路需要作为单独的网格进行渲染。

我们在众多不同的设备上对所有参数进行了微调,确保每台设备都能流畅运行,并且外观达到最佳状态。

以下是不同配置设备上的最终内存占用情况。

性能优化结果

这里是一些性能结果数据。

我们测试了地形渲染性能,并将我们的方法与移动端广泛使用的逐层权重法(4 层)进行了对比。所有数据均来自骁龙 855 处理器,且测试时仅渲染地形。

我们可以看到,无论是在高性能配置还是低性能配置下,我们的方法在帧率、功耗、带宽和图形处理器(GPU)处理时间方面都更胜一筹。

更值得一提的是,我们的方法能够支持多达 32 层的地形渲染,同时还能添加大量贴花。

以下是今天的关键要点总结:

在生物群落和地形渲染方面,我们限制了复杂度,并使其适配PC端和移动端。

我们整合了程序化方法,以加快迭代速度并提升迭代效果。

我们引入了独特的地形纹理处理和几何处理方法,以实现性能与可扩展性方面的优化。

对于后续工作,我们可以更好地实现天气系统,并探索水下特性。

此外,我们可以针对地形法线阻尼进行更多测试,以提升视觉效果。

非常感谢我们了不起的同事们,他们才是真正的英雄。

此外,我们要感谢整个《三角洲行动》团队,还要感谢游戏开发者大会(GDC)委员会,感谢大家前来聆听我们的演讲。

如若转载,请注明出处:http://www.gamelook.com.cn/2025/05/570573/

关注微信