当前位置: 首页 > news >正文

三角洲行动-高性能高品质的端手地形和生态技术文章解读

GDC 2025 | 《三角洲行动》:高性能高品质的端手地形和生态技术(上篇 )

GDC 2025 | 《三角洲行动》:高性能高品质的端手地形和生态技术(下篇)

三角洲行动作为多平台跨端产品,依旧达到了较高且令人信服的的画质品质,无疑是一款相对成功的国产产品,而国内在游戏多平台跨端领域也无疑处于全球第一梯队,所以三角洲在多平台跨端适配上的经验具有相当的含金量。

三角洲行动旨在PC上达到高达4K 144帧,同时在旗舰移动设备上仍能高达120帧。开放世界中有一个完整的昼夜循环,因此,世界构建中的生物群落、地形等都需要与一天中的所有时间兼容。

为了增加生态表现得丰富性,我们不希望通过完全的随机shading来实现这件事情,视觉效果丰富但可信度比较低,如果通过材质变体实现,会让DrawCall急剧增加,也不易于管理和迭代。

猜想:这里完全随机的shading即同类物体共享一个材质,通过实例参数输入不同的随机种子以实现差异化,但这会导致缺乏生态合理性。

clipmap技术:

于是三角洲在开发的早期我们尝试采样了一张大型纹理,来为生态提供可控的色彩变化。但是对于10公里×10公里的开放世界来说,10k分辨率占用内存约100Mb,这在移动端没法落地。(这里看就是用一亿像素去控制100平方公里的颜色,即1像素控制1平方米的色彩)

三角洲行动通过clipmap技术解决了纹理占用问题,将100M的占用降低到了2.5M级别,使得在手游平台的落地变为了可能。

clipmap是一种动态纹理表示,能够高效地在有限的物理内存中缓存任意大小的纹理,以实现实时渲染速率。随相机移动进行更新,相机范围内的纹理使用mip0,随着距离逐级递减,这种方式可以大幅减少内存占用,同时保证了近距离的精度。

这是clipmap的存储信息总览:

实现clipmap相关pdf:https://notkyon.moe/vt/Clipmap.pdf

植被:

而维持clipmap更新本身也需要消耗,所以为了节省占用,整合更多的信息到这唯一一张clipmap中,其中包含了植被状态,四季变化,水域数据,地形潮湿等,以供各个生态模块的shader采样。

 为了让一个植被资产能有不同的四季表现,属性健康度即诞生了。

存储方面:

R通道和B通道储存了树和草的健康度信息。之所以分开,是因为树木和草两者差异明显,不能共用信息。而不只是健康度,战损信息也存储在这里,只是和健康度划分了界限,0.1~0.85属于健康度,0.9~1属于战损信息。借此进一步节省占用。

资产方面:

对于每个资产,创建对应的映射表以根据不同的健康度和战损应用不同的配置以展示正确的外观。

由于算力限制。在手游上所呈现的烧焦仅是一个颜色变黑,在PC平台为了营造更好的氛围感和真实度,会按照一定的规则整合弹坑以及燃烧特效,这些内容都可以轻易通过材质质量分级进行性能降配。

河流:

存储方面:

三角洲在设计上河流和植被互斥,植物不会同时生长在水下和陆地上,这样做法的目的是天然使得河流和植被的生态是互斥的,那么植被所使用的通道在河流上依旧可用,因为数据无交集。

RG通道储存河流velocity的两个分向量(flowmap),B通道储存了河流的吸收率控制颜色,A通道保存了河流的泡沫区域。

PC平台对波浪法线进行3次采样,每层之间的过度通过余弦函数作为权重进行混合,手游高配,只需要减少一次采样,每1/2周期进行混合即可,性能较低的机型,直接使用简单的线性周期混合。

颜色方面:

将吸收LUT和散射LUT两张纹理做了统一索引,让相同的传入值可以同时映射到正确的吸收和散射值,所以才能实现仅占用clipmap的B通道存储。

手游的水使用半透明渲染,省去散射LUT采样同时使用吸收LUT的alpha通道表示透明程度,故大部分水比较深的地方几乎倾向于不透明状态。

在水下的衔接部分,从透明到不透明逐渐剔除各项计算以降低开销

 这样在水下的不透明区域已不存在高消耗的计算。

反射方面:

PC平台上使用屏幕空间反射和多个IBL(Image-Based Lighting)相结合的方式。在移动端只有使用IBL,为了整体视觉上的相对正确性,人为的去修改IBL。

潮湿方面:

湿润岩石、地形、水坑等湿度信息写在clipmap的alpha通道中(0.1~0.5)

在地性上采样高度图并以湿度作为阈值裁切可获得水坑区域,这种水坑做法相比使用贴花的方式能节省额外的绘制。

阴影方面:

PC平台上使用SSRTAO作为全局光照遮蔽,是SSAO技术的进化版,通过光线步进 在实时渲染中实现了接近硬件光追的遮挡精度,同时避免了RTX显卡的硬性依赖。适合需要高动态精度细腻阴影的场景。

移动端对于大量的植被如草,使用顶点的额外uv作为ao,同时参与假阴影的计算,且不投射阴影。

 通过逐instance的随机和ao混合作为假阴影,这和树是类似的,稍微复杂点的是fake shadow会根据视距和视线角度进行衰减,远处和低头查看草时不会产生生硬的黑色过渡。

 文章中还提到在clipmap中烘培了草的位置信息以供地形采样, 为地形做了额外的直接光和高光遮蔽,这很好的缓解了没有投影时地形和草的衔接问题。

这部分没有理解到如何在clipmap中记录的草的位置信息,也没有提到额外遮蔽是如何实现的,线索不足暂不深究。

远处的植被方案:

PC端远景使用impostor,手游端远景使用Billboard,地图边界均使用cluster Card。

impostor即是通过动态或预先生成的多角度2D图替代模型,应用不一定于整个模型,可以对模型部分使用以处理消耗较大的部分。

但是三角洲在处理时考虑到如果一股脑的对所有树木,或某些种类的整棵树进行card生成和纹理捕获,因为有着多个地图多个地域,他们的植被类型截然不同,会存在着card的滥用以及纹理的浪费。

三角洲是这么做的:

将树冠和树干进行了拆分生成,用尽量少并且更贴合树木模型的card对其分别进行纹理捕获和生成。通常只要3个面也就能表现出令人满意的树干,因为树干相对于团簇过于简单,不需要那么多的纹理即可表示。

分开生成的还有一个好处即可以对其进行不同处理,比如树冠需要更强调团簇感和体积感,而树干树枝则不需要,最后将二者进行合并。

对整棵树进行了dither temporal AA,通过vertex color存的信息区分,分别对树冠和树枝进行了不一样的调参设置,减轻树干插片感的同时,保证树冠有更高的密度。

Dither Temporal AA(DTAA)是时域抗锯齿(TAA)与抖动(Dithering)的结合技术

1.抖动Dither :在树干Card的添加稀疏的抖动噪声,破坏规则的几何轮廓。

2.时域混合TAA:通过多帧累积,将抖动噪声转化为柔和的过渡,模拟“伪体积感”。

三角洲处理在Billboard时也根据针对不同外貌特性选择不同的方案。

1.大多比较对称植被用采用Single-plane billboard:找一个较为合适的角度对植被进行捕捉,总是朝向玩家。为什么只对对称植被采用这种方案?非对称会导致无数远处植被视觉跳变的问题,因为采样的角度不一定是角色刚好面对的角度,所以从模型切换成面片时,非对称的植被会“露馅”。

2.非对称植被采用double planes billboard:采样两张纹理角度相差90,也是永远朝向玩家,当视角发生变化后用dither来顺滑过度2者的切换,并通过根据摄像机机位的距离来动态调整两个片面间的间距,从而防止远处观察的z-fighting问题。

 3.对于非对称倾倒的植被,如椰枣树,采用Titled Plane,抓取相对最对称的角度作为纹理原图,然后在shader中进行操作将其压倒,区分了树干和树冠部分,对树冠进行偏移,旋转,甚至扭曲。对于树干进行弯曲曲率的调整以及起始点的偏移,完全凭借主观调整还原。

无论是哪种类型的biiboard,我们在创建之初,对于面片的尺寸做了均一化处理,然后在shading中进行尺寸的调节来匹配真实性。 

 对于玩家远远无法触及的远景,使用billboard组合成群簇处理,进一步减少计算量。

 程序化世界生成方案:

 三角洲使用的是基于houdini的开发流程,每个平台上都有单独的HDA(Houdini)文件,以应对平台限制和性能问题。

PC和移动设备是两个独立的管线,为了不需要做两次工作,在创建和迭代过程中,锁定配方中的高优先级元素,并将其继承到移动设备版本,同时去掉不太重要的元素,如矮树、小灌木和装饰物,总体上保持移动设备版本相似的外观。

模型交叉处理:

实现自动修正来处理大块植被的重叠和交叉,首先需要找到一种方法来验证它们是否交叉。因此将几何体体素化,3D体素相当于具有值1的3D数组。如果多个3D数组的和的最大值等于2或更大,这意味着模型交叉了。接下来,我们自动更改其中一个模型的方向后再次进行验证和修正,直到不再交叉。

草漂浮问题处理:

草漂浮是一个普遍的问题,尤其是在移动设备上,为了节省性能模型的边界框通常很大。向地形投射射线,测量草模型的支点与地形本身的距离。如果结果超过阈值,将漂浮的草稍微向下拉。再进行一次验证,移除漂浮的草,或者如果性能允许,用较小的草替换。

道路问题处理:

由于低端移动设备性能限制,无法支持VT(虚拟纹理)且地形精度也从每格1米降低到每格2米。但由于地形精度降低,网格拓扑变得稀疏,道路模型与地形接触面的顶点对齐更困难。只能为低端设备生成额外的道路模型,拓扑结构需要更改为2米以精确匹配地形网格否则会出现z-fighting,而在远处的道路由于LOD的作用下面数减少也会导致z-fighting问题,解决方式是将远处的顶点渐进逐渐向上拉。

地形贴图技术:

虚拟纹理:

虚拟纹理以增加CPU/GPU负载为代价实现减少内存占用、支持更高分辨率纹理。

在移动端使用VT时,我们需要在写入VT前,对纹理进行运行时压缩(ASTC 4 * 4或6 * 6,仅支持ASTC的双端点模式,不含ASTC的分区模式),否则每帧都会消耗大量带宽,导致设备发热。

具体处理就是在像素着色器中实现运行时ASTC压缩,再将压缩后的数据写入VT。决定在像素着色器中处理是考虑到兼容性问题。

虚拟纹理(VT)更多相关信息:https://zhuanlan.zhihu.com/p/11611037946

虚拟纹理分RVT(运行时)和SVT(静态流式)

地形贴图处理:

采用splat map方案,存储不同纹理的权重值(0~1),通过混合这些权重,实现多张纹理的无缝过渡。

在PBR纹理的处理中,尽可能将多个通道打包到更少的贴图中。具体用了两个texture arrays:一个存储albedo和heightmap,另一个储存normal、roughness及ambient occlusion。

从需求上美术需要至少32层基于高度的权重混合且权重可控,但这需要地形上每个点需要为每层材质单独存储权重值,且因受带宽限制,纹理采样次数必须严格控制。

这样一来,常规的每层权重方案无法支持如此多层,因为每一层贴图都需要采样做混合。而ID map方案又不支持权重混合,每个点只能指定单一材质ID。

三角洲是这么做的:

为所有材质层设定明确的顺序,采用从底层到顶层的覆盖逻辑。在地形每个采样点存储两个layer ID:底层(bottom)和顶层(top)。禁止同一位置出现超过两层的叠加。这样底层权重始终为1,只需存储顶层(top)的混合权重值。

但这样混合自由度会受到较大影响,且材质层的顺序影响可混合的层。

我们采用每米一个采样点的ID map精度存储这些数据。但对于每米上的每一个像素来说,只用所在区块的混合是不行的,会导致一块一块的。这时候需要每个像素获取自身周围的四个采样点进行插值计算,进而采样8个材质层,在移动端总计产生20次采样。(每个点自己产生一次采样,随后分别采样底层和顶层的Albedo和Heightmap产生四次采样,所以一个点产生五次采样,共四个点)

一个像素采样20次的开销显然过大,三角洲使用了一个小技巧处理这个问题。

即将插值计算范围从方形区域改为在三角形区域进行插值,也就是从四个点减少到三个点。而在实际开发中美术师很少在同一区域叠加很多层,ID通常会产生重复,多数的情况下只采样三个不同的ID即可呈现复杂的地形效果。所以可强制规整为三个ID,处理方式为离线编辑时处理,当然有需求也可以运行时根据权重处理。

悬崖渲染方案:

UV通常基于世界坐标的XY轴投影,这会导致悬崖面出现纹理拉伸。常规解决方案是采用tri-planar映射——需要额外增加XZ和YZ轴的纹理投影采样,并进行混合。但这会导致每帧的额外性能开销(因为执行三次采样)。目前存在tri-planar的优化方案,不过通常仍需在base pass中增加纹理采样次数。

// 世界坐标下的三轴投影
float2 uv_xy = worldPos.xy;
float2 uv_xz = worldPos.xz;
float2 uv_yz = worldPos.yz;// 采样纹理(假设使用同一张纹理)
float3 color_xy = texture2D(_MainTex, uv_xy).rgb;
float3 color_xz = texture2D(_MainTex, uv_xz).rgb;
float3 color_yz = texture2D(_MainTex, uv_yz).rgb;

三角洲是这么做的:

绘制阶段,对每个像素:检测法线方向(normal direction),基于此自动选择最佳投影平面(XY/XZ/YZ),这样只采样一次即可,但这样缺少三面混合会导致uv不连续即表现上为出现明显接缝,如何消除接缝?

三角洲采用受《Far Cry》启发的随机分布方案,在VT中这种处理显得非常自然:由于VT缓冲区不会频繁刷新,帧间稳定性得到保证;同时得益于mipmapping机制,避免了aliasing锯齿问题。

随机分布方案通过引入随机性来打破接缝的规则

uint stochasticMaxweightId()
{//  MidId、MidId、MaxId是那三个混合层float scale = max(0.0f, Normal[MidId] - 0.56f) / (Normal[MaxId] - 0.56f);float Rand = Random(worldposition);Rand = Rand * Scale;float Hash = lerp(Nornal[MidId], Mormal[MaxId] * 2.0f - Normal[MidId], Rand);return N[MaxId] > Hash ? MaxId : MidId;
}

随机方案在效果与性能间达到平衡,最终成为游戏内的实装方案。不过直接在VT中渲染悬崖存在一个限制——当玩家近距离观察时,最大分辨率会有所下降。但认为这是一个可接受的取舍。

提升视觉质量的技巧:

远景因为远距离混合使用高LOD层级的mipmap,精度不足导致出现块状瑕疵。在远处采用线性混合方案:随着距离增加,逐步从高度混合过渡到线性混合模式。这是因为在远景中,由于mipmap层级降低(纹理精度不足),高度混合的计算会基于低分辨率的纹理数据,导致问题,故此时过渡为线性更能保证远景质量。

远景由于减模导致细节缺失(沟壑与侵蚀等),原本是由顶点法线表现的细节由于顶点减少受到了影响,为恢复这些细节,三角洲引入了Streaming Virtual Textures(流式虚拟纹理)解决方案,既然远景区域的网格无法存储足够顶点法线细节,那就将其烘焙到纹理中。将整个地形的低层级mipmap烘焙成SVT格式。在VT生成阶段,当收到页面请求时,我们会检查是否存有SVT数据。若存在,则直接将数据载入physical texture,而非通过RVT方式实时渲染。SVT与RVT共享同一块physical texture内存,因此不会产生额外内存消耗。但SVT与近景RVT混合时,必须采用相同的法线生成逻辑才能避免接缝。这意味着在生成RVT时,同样需要通过实际地形网格实时渲染顶点法线。

更多细节:当开镜时摄像机FOV会急速变化,导致VT需要立即重绘更高精度内容——这会产生明显的地形突变现象,因此三角洲在开镜时引入额外偏移值(bias),使FOV变化时维持当前mip level。显著提升了操作流畅度,代价是开镜时地形会略显模糊。

地形几何方案:

在虚幻引擎默认方案中,每块地形tile都需要单独Draw Call,导致渲染整个地形需要大量绘制调用。这在移动设备上会产生巨大开销。

三角洲是这么做的:

采用CDLOD(Continuous Distance-Dependent Level of Detail)方案渲染地形网格。该技术通过draw instance同时渲染所有LOD层级的地形tile。每个实例对应一个网格单元——离摄像机越近的单元尺寸越小,越远的单元尺寸越大。所有实例保持相同顶点数,因此近处网格更密集,远处更稀疏。通过这种方式,整个地形只需1~2个Draw Call即可完成渲染。

CDLOD是一种高效的地形渲染技术,核心思想是通过动态调整网格密度实例化渲染来实现远近距离的细节分级,同时保持极低的Draw Call开销。

所有层级的网格单元(Tile)使用相同的顶点数,但物理尺寸随距离变化,越远网格单元尺寸越大以实现透视,当发生LOD层级切换时用顶点变形平滑处理,这样需要每个实例存储位置、尺寸、LOD层级等数据,随后顶点着色器根据实例参数动态计算顶点位置。这样就实现了以极低的CPU开销,适合渲染超大规模地形。

此外三角洲还通过一个小技巧进一步降低实例数量,常规情况下,当请求某地形tile的高LOD层级时,会将其细分为四个高LOD子tile,导致需要4个实例渲染。但我们采用顶点着色器裁剪方案:将低LOD网格中需要细分的区域切割出来,仅对该区域进行细分。切割操作通过顶点着色器动态调整顶点位置实现。这使得总实例数降至2个,且每个实例保持相同顶点数,从而减少总顶点绘制量。

但CDLOD在近景的几何密度不够,而使用曲面细分在移动端不适用不仅是硬件问题也有性能问题。三角洲实现了软细分方案,这是对传统CDLOD的拓展,通过为地形块引入LOD -1、-2等级别,实现近景高密度。

在传统CDLOD中每个地形块在降低LOD时,会被拆分成2X2个地块,而三角洲通过允许一次性拆分成4X4个地块,使得LOD层级的下降可以更激进,即连跳几个层级,使得它在误差率和实例数量之间取得了最佳平衡。

http://www.xdnf.cn/news/295741.html

相关文章:

  • 2022年全国青少年信息素养大赛 Python编程挑战赛 小学/初中组 初赛真题答案详细解析
  • 为React组件库引入自动化测试:从零到完善的实践之路
  • 音视频作品:AI生成音乐、短视频的邻接权保护
  • 【day03】简写单词 | dd爱框框 | 除2!
  • AD创建元件符号
  • ERP系统源码,java版ERP管理系统源码,云端ERP
  • 【阿里云大模型高级工程师ACP习题集】2.9 大模型应用生产实践(下篇)
  • BC35 判断字母
  • 预训练到微调:深入理解AI的上下游任务
  • 网络延时 第四次CCF-CSP计算机软件能力认证
  • 41.寻找缺失的第一个正数:原地哈希算法详解
  • pyqt写一个单片机配置界面
  • DockerDesktop替换方案
  • AVL树 和 红黑树 的插入算法
  • 模拟芯片设计中数字信号处理一些常用概念(一)
  • Agent2Agent(谷歌A2A)协议原理讲解
  • Linux 文件系统深度解析
  • (二)MMA(整洁架构)
  • 中阳策略:如何从K线行为中提取交易逻辑信号?
  • spring中spring-boot-configuration-processor的使用
  • wordperss AI插件:AI图文+视频+长尾关键词自动生成,已内置deepseek、kimi全模型,支持简单一键接入更多自定义API
  • 动态规划之子序列问题1
  • n8n中Wait节点的使用详解:流程暂停与恢复的实战指南
  • CodeQL-CLI工具小白入门
  • hp主机安装ubuntu 22.04版本并换阿里源
  • 【Unity】一个AssetBundle热更新的使用小例子
  • n8n 中 Compare Datasets 节点使用详解
  • 怎么使用nacos作注册中心 + 配置中心。
  • PCA降维详解
  • 信息安全导论 第八章 入侵检测技术