本文是对https://www.gdcvault.com/play/1024029/-Ghost-Recon-Wildlands-Terrain 进行的中文翻译

image-20240510220044156

大纲

《幽灵行动:荒野》是育碧迄今为止制作的最大动作冒险开放世界游戏,32km x 32km

游戏设定在玻利维亚,我们在游戏中有很多多样性,11 个完全不同的生物群落

湖泊、河流和溪流覆盖了 16 平方公里的表面

道路:超过 600 公里的道路,甚至更多的小径

一条贯穿整个游戏世界的完整铁路网络

游戏中有 200 多个特定位置:营地、地标、前哨站和 58 个完全程序生成的村庄

  • 第一个原型
  • 地形工具
  • 地形渲染的所有细节
  • 程序化工具

第一个原型

https://vimeo.com/207479227

《幽灵行动:未来战士》之后,我们开始尝试更大的地形。

使用真实世界数据,我们使用世界机器优化了 DEM 文件。然后使用 Houdini 创建了一些工具来:

  • 自动创建具有 LOD 的tile网格。

  • 定义一种splat mask,根据坡度、海拔、粗糙度和来自 worldMachine 的其他mask(如flowmap)的一些规则分配材质。

  • 根据一定的规则(坡度、海拔、材料、密度、树木间距等)撒点来创建植被。

原型帮助我们意识到两件事情:

  • 我们必须改进我们的地形工作流程。以处理网格来说,从编辑器到 DCC 应用程序来回反复是不切实际的。我们需要一种地形技术,可以让艺术家轻松编辑,并在运行时快速执行。

  • Houdini 被证明是灵活和超级高效的,TA可以在没有专门工程师的情况下创建工具。

地形工具

基于GPU的雕刻系统

image-20240510221223636

在那几个月里,艺术团队基于 WorldMachine 开发了一个高程图生成管线。地形编辑器的主要目标是获取这个高分辨率的高度图而不包含任何材质的信息作为输入并将其转化为…

image-20240510221334031

一个外观逼真且种类繁多的世界。这个截图是在提交前几周拍摄的,基本展示了我们想要实现的效果。我们希望在近距离观看时能够看到极其丰富的细节,背景中有令人印象深刻的景色,并且之间有无缝的过渡。当调整图形选项时,像素比可以达到每厘米 10 个像素,每 2 厘米一个三角形。玩家可以在游戏中随处走动,这意味着这种质量水平必须在整个世界中保持一致。

image-20240510221416993
我们在编辑器中开发的第一个功能是高度图雕刻工具。

我们出于几个原因希望在 GPU 上完成这项工作。

首先是因为它很快。

我们需要能够编辑地图的大片区域,并能够一键刷出整座山。而且这也是在编辑时获得准确的视觉反馈的最佳方式。高度图和画笔混合在一起,存储在一个float16的RT中,可以直接被游戏着色器使用。

image-20240510221849435

制作游戏是一个迭代的过程。我们需要一种方法来尝试游戏中的新想法,并有可能在不浪费太多时间的情况下恢复它们。因此,我们提出了图层的概念。世界机器的输出被保留在我们称之为“基础”图层中,编辑器中所做的每一项更改都会进入“宏”图层。当编辑的结果不令人满意时,“宏”图层的内容可以轻松擦除,以回到高度图的原始状态。

我们还为 Houdini 生成的修改创建了一个专用层。这是一个非常重要的功能,将由 Benoit 在谈话的第 2 部分中描述。简而言之,使用 Houdini,技术艺术家对地形拥有完全的读/写控制。

最后一层是包含所有与关卡设计相关的更改的层,这些更改无法使用 Houdini 完成。

材质布局

正如我之前所说,我们希望创造一个外观逼真且质量一致的世界。通过观察地图的大小,我们很快得出结论,手工绘制所有内容是一个糟糕的主意。我们需要一个半自动化的流程。因此,我们决定按程序生成材料分配。

为了更好地感知地形的变化,地形编辑工具已经根据法线方向对高程图进行了着色。当从远处观看时,这个简单的规则几乎足以产生一个令人信服的山顶。我们决定延伸这个概念。

image-20240510222414295

当规则的所有条件都满足时,我们将显示一个完整的材质,而不是显示单一的颜色。我们设计了几个基于坡度、海拔,曲率,这些条件可以在像素着色器中实时评估。(其实就是对于每个纹素,计算出其适当的材质,Houdini相关的技术已经非常成熟了,例如可以直接设定一个坡度范围,会自动生成一张Mask)

image-20240510222704878

材质布局的结果随后保存在 2 个纹理中。我们称第一个为“splatting”。它包含高度图每个纹素的材质索引。

image-20240510223659854

第二个被称为“Vista”纹理,看起来像是谷歌地图的截图。在渲染远处的地块时,它将被用作Albedo纹理。

image-20240510223751026

一切都被切割成tile,然后压缩成四叉树。这种简单的结构非常适合 LOD 生成和culling补丁。四叉树的每个节点都有一个小的有效负载,可以根据裁剪结果和到相机的距离由加载线程流式加载或卸载。

image-20240510223929400

材质

在进行渲染之前,我现在会快速描述一下我们的材质。

GR 中的每个材质都符合 PBR 标准,由使用 ZBrush 和 Substance 创建的多个高分辨率纹理制成。

image-20240510224015279

每种材质还包含了置换贴图。基于曲面细分的置换帮助我们获得了我们在开发原型时所期望的“次时代”外观。

image-20240511204025729

我见过的大多数地形渲染器同时使用 4 种材质(并基于顶点色或者权重纹理进行着色),如果你的世界只有一种环境特性,通常就足够了。由于我们世界的多样性,在制作结束时,我们在地形上总共使用了 143 种独特的材质。在开始时加载所有这些材质不是一个选择。

我们在纹理数组中缓存了玩家周围的 32 个最近材质。然后使用间接表将全局扩散索引转换为本地缓存索引。为了渲染前景(玩家附近的地貌),我们从这个缓存直接采样纹理。

image-20240511204242688

地形渲染

前景

这是前景着色器的最终结果。为了给你一个对里面发生的事情的高层次概述,我将逐步分解

我们首先获取 splatting 索引,将其转换为本地缓存索引(Texture2DArray or RVT),然后从缓存中采样所有的 pbr 纹理

image-20240511204526356

我们再次使用双线性插值方法进行 3 次操作,以实现所有材质之间的平滑过渡。

为了避免拉伸,我们在坡度上使用了双平面投影(biplanar projection)。这意味着我们必须为每个轴(x,y)都进行 2 次这样的操作。

第一个视觉上令人满意的着色器版本在性能方面也非常昂贵。像素着色器和域着色器中获取和混合了太多纹理。由于着色器复杂性导致的高寄存器压力,波前占用率(wavefront occupancy)非常低(意味着没法利用GPU的SIMD隐藏指令延迟)。

image-20240511205127527

由于着色器复杂性导致的高寄存器压力,波前占用率非常低。在寻找优化机会时,我们注意到完全平坦或完全倾斜的部分不需要与那么多不同的材料混合。我们的想法是为以下情况制作特定的着色器。

首先到达战场的是最常见的双线性插值

image-20240511211117047

然后是双平面投影的斜坡

image-20240511211248251

最后是过渡区域

image-20240511211343926

将地形切割成小独特的tile以隔离这样的区域在磁盘空间方面成本太高。对我们来说,解决方案是在它们进入视野时使用计算着色器在运行时生成它们。这绝对有助于性能方面,现在大约 80%的地形获取 4 种材质而不是 12 种。

image-20240511211451624

这时我们非常担心另一个挑战,即道路网络!在运行时生成它似乎是一个技术噩梦,我们担心如果不得不将其存储在蓝光光盘上,会耗尽磁盘空间。我们决定直接将道路雕刻到地形中,而不是用传统的几何形式来表示。但这种技术会引发一些问题,比如劣质的材料过渡和明显缺乏细节。

image-20240511211640271

屏幕空间贴花看起来是一个非常有前途的解决方案。

image-20240511211649623

但是这会导致性能成本迅速变得高昂,而且它们并不影响置换拓扑结构

image-20240511211738531

Virtual Textue

这将通过在图集中预先计算材质混合和所有贴花投影,大大减少所需的纹理获取量。虚拟纹理已经成功地应用在其他游戏中,我想在 GDC 的大多数人对这个概念已经很熟悉,所以我不会详细介绍。

image-20240511212223576

随着越来越多渲染技术和虚拟纹理耦合,虚拟纹理也带来了一些缺点。我们设法在以下几个方面改进了传统技术。

确定虚拟纹理所需的部分的最简单方法是在单独光栅化场景并在 CPU 上读取内容。这是一个昂贵的操作,尤其是当您的游戏在 4k 显示器上运行时。由于我们的地形渲染器使用曲面细分,并显示许多微小三角形,使用较低分辨率的RT会错过太多细节。

image-20240511212504175

在 gbuffer pass中,我们不再使用专用pass,而是将额外的 3D 纹理绑定为渲染目标。在渲染地形时,我们使用 UV 和 miplevel 作为输出坐标,直接标记虚拟纹理的显示部分。

image-20240511212551796

之后,计算着色器解析纹理内容并记录缺失的部分。结果(仅几个字节)稍后在 CPU 上读取。

image-20240511213123506

在以最高图形质量选项运行时,虚拟纹理显示每厘米 10 个纹素的像素比率。如果预先计算并压缩,这将需要大约 2 petabits(200万GB)的存储空间。

因此,我们决定在运行时生成内容,材质和贴花在屏幕外表面混合在一起,并使用异步计算管线实时压缩。低帧间连贯性的情况很棘手,因为在驾驶快速车辆时需要更新的瓦片数量可能会迅速降低帧率。虚拟纹理更新必须在几帧之间进行时间切片,并且必须进行大量调整以找到保持良好帧率和低延迟之间的平衡。

image-20240511213511739

我们成功地用虚拟纹理替换了昂贵的前景着色器。幸运的是,我们在前期制作阶段早早做出了这个决定,并相应调整了内存预算。当您在 1080p 分辨率下运行游戏时,需要大约 200MB 的内存空间来存储地图。当在原生 4k 下运行时,这个预算增加到 1GB。

我想通过提供一些 Xbox One 的性能统计数据来结束这部分谈话。这些是平均值,且在很大程度上取决于视图,但我们可以说大部分时间内所有内容都在 GPU 端在 8 毫秒以内完成。这 8 毫秒不包括 BC 压缩,因为它在后台免费运行。

image-20240511213649351

程序化工具

只截取部分有意思的内容,对于一些制作规范和思想进行了略过,感兴趣的同学可以去看原视频

武器的选择

规模

  • 工具必须在大规模和本地都能运行。
  • 当涉及工具时,将控制权从最小的岩石保持到最大的山脉是生产的口头禅。

迭代的

  • 我们需要能够改变主意
  • 尝试新的想法
  • 工具是一种数据:工具与世界中的任何其他实体一样被创建和存储。工具是一种元实体,它生成自己的实体。

最重要的是:我们必须每天保持一个始终可玩的世界。

  • 基于规则的相互依赖工具:用户编辑参数并调整规则以生成内容。
  • 确定性: 相同的输入产生相同的结果
  • 离线: 在运行时不生成任何内容。所有内容都在生产过程中生成。
  • Houdini: 使用 Houdini 创建了世界创建流水线和所有工具。
  • CPU:由于我们大多数工具都是基于 CPU 的(Houdini 的最新版本可以更好地处理 GPU,但在制作过程中并非如此)。依赖 CPU 不是问题:CPU 便宜且我们可以使用更多的 RAM,而且我们的一些工具需要大量的 RAM!

我们有许多工具可以填充世界中许多不同的资产。这些工具并不直接创建几何图形,而是帮助艺术家定义现有资产的放置规则。我们大部分的 Houdini 工具处理点,然后返回:

  • 矩阵
  • 对象id

image-20240511214717059

地形之上

地形不仅仅是海拔和贴图。

我们可以推断出非常有用的信息,我们将能够在许多其他工具中重复使用。

  • 从海拔中我们可以提取粗糙度,检测山脊,这将有助于放置岩石或植被。
  • 从河流中,我们可以定义湿度并将其用于植被规则或影响地形高光的材料和一些特定对象。
  • 对太阳进行平均处理,我们可以获得一个有用的蒙版,用于驱动植被和环境声音。
  • 最初的水流蒙版来自世界机器,我们将其用于影响地形的斑块。

image-20240511215043087

但由于地形被雕刻和修改,我们希望保持水流掩模最新。我们在农场上自动化处理这些地形图层的更新过程:每当用户提交地形拓扑变化时,它会触发 Houdini 农场上的重新计算。

存储在服务器上的更新文件可以立即被 Houdini 工具访问。

由于这是中间数据,随时可以更新,我们没有为此进行任何版本控制。

没有版本控制和没有备份可能听起来很可怕,但在生产过程中我们从未遇到任何问题。

我将使用道路作为一个例子,展示我们如何在程序上修改地形

我们的战略是什么

  • 我们拥有这个巨大的开放世界
  • 可能有数千公里的道路。
  • 使用传统方法,保持一致性和灵活性几乎是不可能的。而且昂贵。

在一些最初和有希望的测试之后,我们决定依靠寻路来建立道路网络
它是如何工作的:

  • 全球(世界)级别:创建元网络,主要跨世界道路
  • 瓦片级别:可以编辑 4平方公里的瓦片以创建本地网络
  • 合并所有瓦片的结果
  • 创建高度图。

无论谁使用工具创建道路部分,根据设计,都能保证一致的结果。比如最大坡度为 20 度或者如何处理十字路口。

根据这篇论文:http://arches.liris.cnrs.fr/publications/articles/EG2010_ProceduralGenerationOfRoads.pdf,我们开始尝试使用加权各向异性最短路径算法。与只考虑 4 个方向不同,关键是要在多个方向和每个方向的多个距离上进行搜索。

image-20240511215128025

参考

https://www.gdcvault.com/play/1024029/-Ghost-Recon-Wildlands-Terrain

biplanar mapping - 2020

波前占用率(wavefront occupancy)

http://arches.liris.cnrs.fr/publications/articles/EG2010_ProceduralGenerationOfRoads.pdf