[UFSH2025]《三角洲行动》中的全局光照方案
[UFSH2025]《三角洲行动》中的全局光照方案 | 蔚东辰 腾讯天美J3工作室 引擎开发工程师
一、 项目背景与核心挑战
讲座首先明确了《三角洲行动》(以下简称"三角洲")在GI方案选型时面临的两个核心制约:
-
核心平台: PC 与手机端同发。
-
核心要求: 所有玩家可见区域均需实现 GI 覆盖。
-
核心挑战: PC与手游之间存在巨大的 性能鸿沟,这导致在技术选型上必须做出权衡和 "拉扯"。
二、 常见GI方案光谱 (The Spectrum of GI Solutions)
为了做出选择,讲者首先回顾了业界常见的几种GI方案,并分析了它们在“运行时性能”和“项目制作成本”两个维度上的优劣。
1. 四种主流方案
-
Light Map (光照贴图):
-
特点: 最传统、最节省 运行时性能 的方案。
-
优势: GPU效率极高(通过2U采样),美术可灵活控制局部纹素密度以提升精度。
-
-
Volume GI (PC端):
-
特点: 基于 稀疏化存储 (Sparse Storage,如稀疏体素八叉树SVO)。
-
优势: 精度高,显存占用可控。
-
代表作: 《刺客信条》、《孤岛惊魂》。
-
-
Volume GI (手游端):
-
特点: 基于 规整3D纹理 (Regular 3D Texture)。
-
优势: 结构规整,寻址采样对GPU友好,性能消耗低。
-
-
全动态GI (Full Dynamic GI):
-
特点: 以虚幻引擎的 Lumen 为代表。
-
优势: 视觉效果好,美术迭代效率 极高(制作成本接近零)。
-
2. 性能 vs 成本 对比
讲者将这几种方案放在一个“光谱”上进行对比:
| 维度 | Light Map (光谱左端) | Volume GI (光谱中间) | Lumen (光谱右端) |
|---|---|---|---|
| 运行时性能 | 最佳(消耗最低) | 居中(660显卡1080P下 <3ms) | 最差(Pass数量高一个数量级) |
| 项目制作成本 | 极高(迭代最慢) | 较低(可自动化) | 接近为零(迭代最快) |
制作成本痛点分析:
-
Light Map 的痛点:
-
需要制作和管理 2U。
-
Mesh(模型)与光照 不解耦,修改模型即需重新烘焙,"搞人心态"。
-
调整纹素密度是一个 "时间黑洞"。
-
对于大地图(如 "长空")和 TOD (Time of Day,动态时辰) 系统,全图烘焙 "根本就是一个不可能的事情"。
-
-
Volume GI 的优势:
-
无需 2U,Mesh与光照 解耦。
-
运行时根据位置查询即可,非常灵活。
-
可实现 自动化烘焙流水线,美术只需拉取数据,极大释放生产力。
-
光谱总结: 越靠近左边,运行时效率越高,但制作成本越高;越靠近右边,开发者迭代越爽,但性能消耗越大。
三、 《三角洲行动》的技术选型:“趋同进化”
基于上述分析和项目挑战,《三角洲》团队定下了选型原则:
-
第一优先: 保证所有平台的 运行效率(作为多人竞技游戏,目标 >120 FPS)。
-
第二优先: 在此基础上,尽可能提高 制作速度。
1. 详细的跨平台需求
-
PC端:
-
目标: 拔高视觉效果。
-
首要考量: 控制显存。高精度的GI(无论是Light Map还是Volume GI)都会导致显存 "几何级别的增长"。
-
倾向: 依赖 Volume GI 制作成本低 的优势(讲者给了4星期待)。
-
-
手游端:
-
首要考量 (No.1): 控制包体大小。如果全图铺 Light Map,在有 TOD 的情况下,包体将 "过于庞大"。
-
第二考量: 控制GPU消耗。手游普遍瓶颈在GPU,Volume GI 的消耗不能比 Light Map 增长太多。
-
现实: 受限于性能,手游的 Light Map 使用范围仍然更广。
-
2. 最终方案:混合使用
《三角洲》的最终选择是 "全都用了"。讲者称这是一种 "趋同进化" (Convergent Evolution),因为《远光84》、COD等项目也采用了类似的混合方案。
-
多人地图 (Multiplayer):
-
采用 Light Map + Volume GI 的组合方案。
-
其中 Volume GI 使用得更多。
-
-
单人战役 (Single Player):
-
采用 Lumen。
-
原因:没有PVP竞技的性能压力,可以最大化美术品质和迭代效率。
-
四、 方案详解:PC端的 Light Map 策略
讲座接着深入介绍了PC端 Light Map 的具体使用策略。
-
烘焙工具:
- 使用天美 自研 的 JJS烘焙器,这是一个基于 GPU 的烘焙器(从CF手游迭代至今)。
-
使用评价:
- "又爱又恨"。品质和效率好,但离线制作 "吞噬时间"。
-
应用策略 (以 "长空" 地图为例):
-
克制使用: 仅用于特定区域(地图上标记为 "浅绿色" 的地方)。
-
主要目的: 负责 室内光照。
-
烘焙内容: Light Map 上 只烘焙人工光 和 天光可见度 (Skylight Visibility)。
-
-
该策略的优势:
-
解耦TOD: 由于不烘焙太阳光,美术可以按 "切块" 独立烘焙,不受整体太阳光照方向(TOD)调整的影响。
-
效率保证: 避免了全图烘焙导致地图 "永远就发布不了" 的问题。
-
2U 的痛点解析
“2U” 是指 “第二套UV” (Second UV set),也就是 UV1。
在3D模型中,"UV" 指的是一套2D坐标,它告诉引擎该如何把一张2D贴图(比如颜色、法线)“贴”到3D模型的表面上。
-
“1U” (第一套UV,即
UV0):通常用于材质贴图(Albedo, Normal, Roughness等)。为了节省贴图空间,这套UV的某些部分可以重叠(比如一个对称模型的左右两边可以共用一套UV)。 -
“2U” (第二套UV,即
UV1):专门用于烘焙光照贴图 (Light Map)。
为什么 Light Map 需要单独一套 "2U"?
讲座里提到这是个 "烦恼",关键原因在于 Light Map 有一个绝对要求:它的UV必须是唯一且不能有任何重叠的 (Unique & Non-Overlapping)。
你想想:
-
光照是唯一的: 模型上每一个点的光照和阴影都是不一样的。比如,一个桌子的左上角和右上角接受到的间接光照是不同的。
-
烘焙就是存数据: Light Map 这张贴图,存的就是模型表面每一个点的光照信息。
-
如果UV重叠: 假设你把桌子左右两边的UV叠在一起(像“1U”那样为了省材质贴图),那么烘焙时,引擎就懵了:这个UV点到底该存左边的光照,还是右边的光照?最后的结果就是光照和阴影全错乱了。
因此,所有需要烘焙 Light Map 的静态模型,都必须由美术师(或算法)额外创建一套完全展开、互不重叠的 "2U"。
为什么 "2U" 是个大痛点?
这就回到了讲座里提到的制作成本问题:
-
制作成本高 (时间黑洞):
-
"制作这一堆2U": 为一个大场景里成千上万个模型,手动去创建和优化这第二套UV,是一个极其繁琐、耗时的工作。你不仅要展开,还得排布得紧凑(节省贴图空间),同时保证UV块之间有足够的间距(防止光照"漏"过去)。
-
"Mesh和光照不解耦": 只要你稍微改动一下模型(比如移动个顶点),这个 "2U" 就可能失效了,Light Map 就得全部重新烘焙。这就是 "搞人心态" 的地方。
-
-
包体和显存问题:
- 每个模型的 "2U" 都要挤进一张或几张巨大的 Light Map 贴图里。场景一大,这些贴图的体积和显存占用会非常恐怖。
总结
-
Light Map 的优势:运行时效率极高。GPU只需要拿到模型(专用于光照的)第二套UV坐标,去 Light Map 贴图上采个色,就拿到了光照结果。
-
Volume GI 的优势: Volume GI 是把光照信息存在三维空间体素(Voxel)里。模型在运行时是动态查询自己所在位置的空间光照,它根本不需要 Light Map 贴图,自然也就不需要那套折磨人的 "2U",实现了 "Mesh和光照解耦"。
五、 方案详解:PC端的 Volume GI (重点)
这是《三角洲》PC端覆盖范围最广的GI方案。其最大的好处是大幅提升制作效率,同时品质能接近 Light Map。
1. 警惕:"Volume GI 陷阱"
讲者首先提出了一个项目管理上的“隐秘陷阱”,这是方案选型成败的关键。
-
理想中的Volume GI: 制作代价(成本)大幅下降,而综合效果(画面+性能)轻微下降。
-
“陷阱”中的Volume GI: 团队(程序、美术)都享受到了制作解耦的“爽点”(成本下降),但忽视了Volume GI的固有问题,尤其是 漏光 (Light Leaks)。
这会导致两种灾难性后果:
| 陷阱类型 | 现象 | 最终结果 |
|---|---|---|
| 画面崩坏 | 制作成本下降,但漏光等瑕疵导致画面品质也大幅下降。 | 性价比未提升。 |
| 性能崩坏 | 画面品质尚可,但是依赖昂贵的运行时算法去修复漏光,导致性能大幅下降。 | 性价比未提升。 |
陷阱的最终结局: 测试团队提交大量Bug单(画面瑕疵或性能不佳),团队被迫 "反复折腾" 去写各种Hack代码修复,最终彻底丧失了Volume GI 低制作成本的优势。
2. 成功的关键:防漏光 (Light Leak Prevention)
基于上述陷阱,讲者得出结论:
防漏光,是Volume GI方案从项目角度讲能否成功的一个关键因素。
-
核心痛点: 如果漏光问题无法预测,就会产生海量的Bug单,这种拉扯会完全抵消掉制作效率上的优势。
-
解决方案: 必须追求 "高性价比",即方案本身必须能 "斩钉截铁地告诉美术" 如何从制作层面完全避免漏光。
3. 《三角洲》的"高性价比"防漏光策略
《三角洲》的策略不是依赖复杂的运行时算法,而是通过制定一个清晰的制作规范来从根源上解决问题。
-
核心原则: 提高体素精度,直到它成为一个可以依赖的“标准”。
-
《三角洲》的制作标准:
-
体素精度 (Voxel Precision): 0.25米 (25cm)。
-
美术规范 (Artist Guideline): 所有墙体厚度必须 > 0.25米。
-
-
为什么这个标准有效?
-
技术原理: 当墙体厚度大于一个体素的宽度时,在运行时进行 三线性插值 (Trilinear Interpolation) 时,墙体一侧的采样点“无论如何都不可能采到墙后面去”。
-
效果: 只要美术遵守这个规范,程序就能保证(Guarantee)它 一定不漏光。
-
-
如何处理例外?
-
对于必须很薄的物体(如 "铁皮房"),再单独使用 Hack 方案 处理。
-
但这个清晰的标准能从制作层面规避掉绝大多数的漏光问题。
-
-
战略重要性:
-
这个规范必须从 “项目第一天” 就开始执行。
-
否则,后期只能依赖昂贵的运行时算法(如 "world-space bent normals")来补救,这就又掉回了 "Volume GI 陷阱"。
-
4. 基础数据元素 (The Voxel Payload)
-
存储内容: 方案的基础数据单元(Payload)是经典的 "6个方向的辐照度" (6-directional Irradiance)。
-
讲者提到这在《半条命2》中就很经典,并且《使命召唤:黑色行动》系列也在使用。
-
这本质上是一个存储了6个面(+X, -X, +Y, -Y, +Z, -Z)光照信息的"Cube"。
-
-
运行时采样: 根据物体的 表面法线 (Normal),对这6个方向的辐照度进行插值,得到最终的GI结果。
5. 存储方案:稀疏树形结构 (Sparse Tree Structure)
-
核心问题: 0.25米是一个非常高的精度。如果使用规整的3D纹理(Dense Grid)来存储,显存会 "很快就炸了"(轻松上 8G / 16G)。
-
解决方案: 必须使用 稀疏化存储 (Sparse Storage)。
-
核心原则: "越靠近Mesh,才存储高精度的体素"。高精度(0.25m)的数据只应 "贴着" 静态模型表面。
-
技术参考: 团队参考了UE中集成的 OpenVDB(也是Houdini的核心组件)的存储代码,实现了一套高效方案。
6. 树的层级与构建 (Tree Hierarchy & Construction)
该方案实现了一个逻辑上的 64叉树 (64-ary Tree),具体层级如下:
-
Level 1 (基础层): 4米精度体素
- 这是一个规整的 (Regular)、粗糙的体素网格(图中的黄色体素)。
-
Level 2 (中间层): 1米精度体素
-
由4米体素 分裂产生( 个子节点)。
-
剪枝 (Pruning): 只保留那些靠近Mesh足够近(例如 < 3米)的1米体素(图中的蓝色体素),远处的直接舍弃。
-
-
Level 3 (精细层): 0.25米精度体素
-
由1米体素再次分裂( 个子节点)。
-
剪枝 (Pruning): 阈值被进一步压缩,只保留距离Mesh表面非常近(例如 < 0.3米)的体素。
-
结果: 最终的高精度体素 "基本就贴着Mesh摆了一层"。
-
实现备注: 讲者提到他们的实现中没有一个全局的根节点 (Root Node),而是直接从4米这一层开始。
7. 运行时查询算法 (Runtime Lookup Algorithm)
这是该方案的核心:如何根据一个世界坐标 (World Position),快速在Shader中查到对应的高精度体素数据。
-
数据布局 (Data Layout):
-
首先,将整个树形结构进行 广度优先遍历 (BFS)。
-
将遍历结果(所有存在的节点)存储在一个线性的序列 (Array) 中。
-
利用BFS的性质:一个父节点的所有子节点,在这个序列中必定是连续存储的。
-
-
节点中存储的关键信息:
-
exist mask(64-bit): 一个64位的位掩码。它的每一位 (bit) 对应一个 局部栅格中的子节点,1代表该子节点被保留,0代表被剪枝。 -
first_child_index(int): 一个整数M,表示该节点的第一个子节点在上述 "线性序列" 中的起始下标 (Index)。
-
-
查询步骤 (Shader Traversal):
-
给定
WorldPosition,首先定位到它所属的父节点(比如一个1米体素)。 -
计算出
Position在该父节点内的局部坐标(一个 的位置)。 -
将这个局部坐标映射到
exist mask上的某一位 (bit)。 -
检查该位:
-
如果为
0:说明该位置的子体素被剪枝了 (Miss),查询结束。 -
如果为
1:说明子体素存在 (Hit),继续下一步。
-
-
计算这个
1在它之前有多少个1。这个数量就是该子节点在 "子节点连续块" 中的局部偏移量C。关键优化: 这一步在Shader中可以 "一条指令" 完成,即
countbits(Population Count / 统计比特位为1的个数)。 -
计算子节点的绝对下标:
-
迭代: 重复这个过程(最多3次),直到找到0.25米精度的叶子节点,或者中途Miss。
-
8. 最终数据获取 (Final Data Fetch)
-
光照数据存储: 实际的 GI数据(6向辐照度)存储在另一个并行的线性序列 (Array) 中。
-
获取数据: 上一步查询算法得到的最终
ChildIndex,同时也是 GI数据数组的下标。 -
插值: 运行时需要进行 三线性插值 (Trilinear Interpolation),因此上述的整个查询过程需要执行 8次(获取周围8个邻近的体素)。
-
性能: 尽管要查8次,但由于
countbits等硬件指令的支持,这个访问速度 "其实它是很快的"。
9. 数据拟合与压缩 (Data Fitting & Compression)
在实现了0.25米的稀疏体素存储后,团队面临两个新问题:
-
数据量依然大: 每个体素存储6向辐照度(Ambient Cube)需要 48 B。
-
TOD (动态时辰): 如果有8个时段,每个体素的数据将膨胀到 ,这是不可接受的。
核心目标: 每个体素存储的数据量必须是一个很小的常量,并且不随TOD时段的增长而增长。
解决方案:基于"支撑点"的线性拟合
这个方案非常精妙,它将“重数据”和“轻数据”分离开:
-
生成支撑点 (Probes):
-
使用 OpenVDB 沿Mesh表面生成一层 稀疏的 Probes (密度远低于0.25m的体素)。
-
这些 Probes 负责存储“重数据”:即 48 B 的6向辐照度。
-
Probes 存储所有TOD时段的数据:比如8个时段,Probe就存储 的数据。因为Probe本身很稀疏,所以总包体可控。
-
-
体素存储"轻数据":
-
对于每一个 密集的体素 (Voxel),不再存储48 B 的GI数据。
-
而是找到离它 最近的4个Probes,用这4个Probe的数据进行 线性组合(拟合),来重建出当前体素的GI值。
-
因此,每个体素 只需要存储:
-
4个 Index (用于索引是哪4个Probe)
-
4个 float (用于这4个Probe的混合权重)
-
-
最终体素负载: 恒定的 16 B。
-
这个方案完美解决了两个问题:
-
空间压缩: 从 48 B 降到了 16 B。
-
TOD解耦: 无论有多少个TOD时段,体素都只存16 B 的索引和权重。TOD的变化体现在Probes的数据上,而体素的重建逻辑不变。
如何求解:全局最优化
现在的问题是:如何确定 Probes 上(所有TOD时段)的最佳GI数值,以及每个 Voxel 上的最佳4个权重?
-
目标函数: 这是一个全局优化问题。目标是让所有体素的 "拟合重建值" 与 "原始烘焙值" 之间的差的平方和 (Sum of Squared Differences) 最小。
其中
-
求解算法:
-
这是一个可解的问题,因为该函数对于"权重"和"Probe数据"的偏导数 (Partial Derivatives) 都很容易写出,且没有"病态结构"。
-
团队使用了标准的数值优化算法:共轭梯度法 (Conjugate Gradient Method)。
-
-
实现与性能:
-
团队实现了一个基于 SIMD 和 面向数据 (Data-Oriented) 的优化器。
-
性能极高: 优化一个 64x64m 的数据块,单核仅需 9ms,多核并行后平均 < 0.5秒,远快于烘焙本身的时间。
-
10. 最终成果与开发哲学
显存成果
- 通过 稀疏存储 + 拟合压缩 这一整套方案,"刀锋" 这张地图的 Volume GI 总显存 被控制在 200MB 左右,这是一个完全可以接受的量。
核心开发思路 (重要)
讲者最后强调了他们的思考顺序,这是避免"陷阱"的关键:
-
目标驱动: 一切从 "高性价比" 出发,这是项目的最高优先级。
-
推导1: 为了"性价比",必须避免昂贵的运行时防漏光Hack。
-
推导2: 这就 "推导出" 必须使用 0.25米 这种高精度体素,并制定美术规范(墙厚 > 0.25m)来从根源杜绝漏光。
-
推导3: 0.25米的高精度 "推导出" 必须使用 稀疏存储(如OpenVDB)来解决显存爆炸。
-
推导4: 稀疏存储+TOD需求 "推导出" 必须使用 拟合压缩 方案(Probes + Voxels)来进一步控制数据量。
结论: 方案的每一步都不是为了 "炫技" 或 "高大上",而是被上一步的约束和项目的最终目标(性价比)“逼” 出来的。
六、 方案详解:全地图远景GI (World-Scale GI)
在解决了近景的高精度GI(0.25m)后,团队面临一个新问题:如何用极低的成本实现全地图的GI覆盖。
1. 核心问题:视距与覆盖
-
问题: 之前的高精度 Volume GI(0.25m)是流式加载 (Streaming) 的,覆盖范围有限(只在近景)。
-
目标: 需要一个“常驻内存”、“完整的覆盖地图”的GI方案,尤其是为了太阳的间接光照,且其总数据量要极小(讲者希望能做到"近景一个tile的GI那么大的大小")。
-
挑战: 像“海岛”这样的地图视距极远,传统方案无法胜任。
2. 方案探索:从“巨型体素”到“函数拟合”
-
初步方案: 沿用之前的稀疏树形结构,但使用“巨大无比”的体素,例如 8米。
-
初步方案的缺陷: 如果一个8米的巨型体素只存一个GI值(如一个Ambient Cube),那么体素内部所有采样点得到的值都一样,远景看过去会是“糊的一团”,精度完全不够。
-
进阶目标: 需要在8米体素内部定义一个紧凑的函数
F。-
输入:体素内的局部坐标
XYZ(Local Position)。 -
输出:该局部坐标点
XYZ对应的精确光照值。 -
效果: 如讲者演示,这个
F(XYZ)应该能精确模拟出建筑屋檐下的遮蔽关系(里面是黑的)。
-
3. 最终方案:小型神经网络 (DNN)
这个紧凑的函数 F,就是用一个“小型的深度神经网络”来实现的。
-
训练 (Offline):
-
烘焙器在8米体素内部密集烘焙光照数值,提供大量训练数据(即
(XYZ, GI_Value)数据对)。 -
用这些数据去训练这个小型神经网络,使其能够“拟合”出体素内部的复杂遮挡结构。
-
-
网络架构 (Architecture):
-
结构: 2个隐藏层,宽度为 4。
-
激活函数: Leaky ReLU。
-
输入: 局部坐标
XYZ。 -
输出: 单一的Luminance(亮度)值,而不是RGB。
- 原因: 为了增大训练的精度。
-
-
推理 (Runtime):
-
在Shader中运行这个小型网络,本质上只需要 两次矩阵乘法。
-
性能: 因为网络极小,"并不会造成很大这个运行时的负担",但却能在8米范围内提供足够高的局部精度。
-
4. 方案特点与取舍
-
优化器 (Offline):
-
由于网络函数很小,团队 “手动的把里面这个各个参数这个导数写出来”,无需依赖PyTorch等重型框架。
-
继续使用之前提到的 “共轭梯度优化器” (Conjugate Gradient Optimizer) 来完成训练。
-
-
“画素描”类比 (Leaky ReLU 的本质):
-
由 Leaky ReLU 构成的神经网络,其输出本质上是一个“分段线性函数” (Piecewise Linear Function)。
-
讲者将其比喻为“画素描”:用有限数量的“折线” (Straight Lines) 去尝试拟合一个复杂的形状(如建筑的遮挡)。折线的数量取决于 Leaky ReLU 单元的数量。
-
-
可接受的漏光 (Acceptable Leaks):
-
这个方案在室内看是会漏光的(如图所示)。
-
但这是可以接受的,因为这是“远景GI”,玩家始终是从室外往室内看。
-
漏光是可预测的:优化器会优先拟合"更亮的地方",因此光 "一定是从更亮的地方往更暗的地方漏",这在远景视觉上是合理的。
-
5. 最终成果:极低的显存占用
-
视觉效果: 对比开关(仅有Streaming GI vs 全图GI),效果"非常非常明显"。
-
显存占用 (核心指标):
-
这些全图GI数据是非压缩的,直接存入显存。
-
风暴眼 (Storm Eye): 22 MB
-
刀锋 (Blade): 11 MB
-
监狱 (Prison): 8 MB
-
-
结论: "这个数据量是没有问题的",对于一个全图GI方案来说是"很合理的"。
七、 PC端方案总结:实现 "高性价比"
在详细介绍了近景和远景GI方案后,讲者对PC端的成果进行了总结,核心是"性价比"。
-
性能消耗: 以 "攀升" 地图为例(3D80显卡),整套GI方案(远景GI + 近景Streaming GI)的GPU总消耗为 0.35ms。
- 这是一个 "可以接受的代价",其消耗量级与 深度预通道 (Depth Prepass) (0.32ms) 相近。
-
显存占用: 总体控制在 200MB+。
-
核心理念 (高性价比): 讲者强调,Volume GI 的 "大幅下降的制作成本" 能够真正"有意义",其前提是必须首先解决所有的 "后顾之忧":
-
根治漏光(通过0.25m精度 + 制作规范)。
-
没有昂贵的运行时Pass(如特殊的防漏光处理)。
-
显存可控(通过稀疏存储 + 压缩)。
-
性能达标(GPU消耗接近Prepass)。
-
八、 方案详解:手游端 (Mobile) GI
手游端的挑战与PC完全不同,因此采用了截然不同的技术栈。
1. 总体方案:Light Map 为主,Volume GI 为辅
-
混合方案: 同样采用 Light Map + Volume GI 的组合。
-
重大差异: 手游端 Light Map 的使用范围 "确实是更大的"。
-
分工:
-
Light Map: 负责烘焙 人工光 (Artificial Light) 和 天光可见度 (Skylight Visibility)。
-
Volume GI: 负责 太阳光的间接光 (Solar Indirect)。
-
-
核心妥协: 手游的 Volume GI 覆盖范围很小,仅限 "眼前这么 64米"。
- 效果: 64米外的GI会回退到远景平均值,但由于有 Light Map 提供的天光可见度,房屋的明暗关系 "看起来还是OK的"。
2. 核心架构:CPU 组装 vs GPU 采样
这是手游端方案与PC端最根本的区别,是基于平台瓶颈的深度定制。
-
PC vs Mobile 瓶颈差异:
-
PC端: GPU能力强,可以在Shader中实时 "访问稀疏树 (Sparse Tree)"。
-
手游端: 普遍 GPU瓶颈,无法承受在GPU上访问树结构的 "太耗费" 的操作。
-
-
手游端方案: 充分利用手机 "4+4" 多核CPU(尤其是节能小核)的特性。
-
CPU (异步): 在小核心上 "异步组装" 一个包围相机的 规整3D纹理 (Regular 3D Texture)。
-
GPU (运行时): 性能消耗极低,只需要采样一次这个3D纹理。
-
3. 数据存储与组装 (折中方案)
为了平衡包体大小和CPU组装效率,离线数据存储也做了特殊设计。
-
核心矛盾:
-
PC式稀疏存储: 节省包体,但组装时会导致大量 CPU Cache Miss,"CPU不友好"。
-
暴力规整存储: CPU访问快,但 "非常耗费包量"(手游第一红线)。
-
-
折中方案 (分段连续存储):
-
离线 (包内): 数据被分块,且每个数据块内部被存储为 "分段连续存储"。
-
通俗理解: 每一 "段" 是一个 "规整的长方体网格"(一个局部的小型3D纹理),只包围其局部空间。
-
运行时 (CPU组装): CPU找到当前相机周围对应的 "数据块" 和 "分段",将这些小型的规整数据 "填充" (copy) 到那个包围相机的 "绿色的大框" (主3D纹理) 中。
-
-
优化:
-
主3D纹理需要做 Scrolling (滚动更新) 来保持稳定。
-
数据是 分层分帧上传 到GPU的。
-
大量使用手机特有的 半精度浮点数 (FP16) 的 SIMD 指令 来加速CPU组装过程。
-
4. 防漏光:Interior Volume (IV) 方案 (代码量最大)
这是整个GI方案中 "代码量最大的地方"。
-
根本问题: 手游的体素精度为 1米,而墙 "无论如何都不可能做到一米厚"。PC端的“制作规范”方案在此失效。
-
解决方案 (IV - Interior Volume):
-
制作: 美术随建筑额外制作一种 "简化的Mesh",称为 IV。
-
规则: 一个建筑可由多个IV拼成,但每个IV必须是凸多面体 (Convex Polyhedra)。
-
对齐: IV的"面" (Face) 被精确地 "卡在墙里面"。
-
-
工作流程:
-
离线: 识别出"墙外"那些可能导致漏光的体素,并将它们与"墙内"的IV面片进行 "关联"。
-
运行时 (CPU):
-
根据 "相机在IV面片的前与后" 的关系,判断相机是在墙内还是墙外。
-
如果发现相机在墙的"这一边",而去采样了被"关联"到墙"另一边"的体素,就判定为漏光。
-
处理: 将这个"漏光体素"的数值 Fade Out 掉。
-
-
-
妥协:
-
由于数据是分帧上传的,这个Fade Out效果会有 "滞后更新"。
-
讲者表示,这在快节奏PVP中 "没有人在意",是一个可以接受的妥协。
-
5. 手游端压缩与成果总结
-
数据压缩:
-
方案: 与PC端类似,采用 Probe 拟合 的方案,但 Probe "稀疏很多"。
-
远场: 在远离Mesh的区域,不存储体素块,而是直接采样 "距离它最近的那个Probe" 上的光照。
-
辅助: 运行时会动态生成一个 Distance Vector Field(距离矢量场),用于快速找到最近的Probe。
-
-
包体成果:
-
以"风暴眼"地图为例,Volume GI的包体为 334 MB。
-
巨大优势: 讲者估算,如果全用 Light Map 方案,包体 "会翻倍五倍不止",且工期 "不太可能"。
-
-
手游端结论:
-
制作代价: 大幅降低。
-
包体: 大幅下降。
-
防漏光: IV方案解决了拉扯问题。
-
妥协: 太阳光的间接光范围很小 (64米),但 "手游上也是确实足够了"。
-
总评: "总体性价比还是OK的"。
-
九、 其他技术点
-
手游远景GI:
-
低配: 使用一个 "平均值" 替代。
-
高配: 使用一个 "纵向俯拍的2D Texture"。
-
-
SIMD的广泛使用:
-
讲者强调,方案中 大量使用 SIMD(单指令多数据流)。
-
并称赞 UE (Unreal Engine) 对 SIMD 的封装非常完善,使用直观,无需手写 "鬼画符" 般的汇编。
-
十、 方案详解:单人战役 (黑鹰坠落)
对于单人战役,团队采用了与多人模式完全不同的GI方案。
-
核心方案: Lumen (UE5 原生方案)。
-
采用原因:
-
开发时间极短。
-
Lumen 无需烘焙等待,美术可以实时调节并立刻看到光照效果。
-
这种 "反复调整的(高)效率",使其最终的 "光照品质是更高的"。
-
-
技术实现:
-
未做二次开发: 直接使用UE5的原生Lumen。
-
软/硬件光追选择: 使用 软件光线追踪 (Software Ray Tracing),没有开启硬件光追。
-
性能考量: 根据团队试验,只有在 3080级别及以上 的显卡,Lumen的硬件光追性能才会超过软件光追。因此,为了 "稍微照顾一下" 玩家的显卡性能,统一选择了软光追。
-
十一、 讲座核心总结 (Key Takeaways)
最后,讲者对《三角洲行动》的GI方案选型哲学进行了全面总结:
-
Light Map 必须做取舍:
-
在性能和效果达标的前提下,应 "更多的使用 Volume GI 去提升制作效率"。
-
追求极致精度(如从0.25m提升到0.1m)的 "边际效应是递减的",用 Volume GI 换效率是更明智的选择。
-
-
防漏光的黄金法则 (Golden Rule):
-
"做好防漏光,是 Volume GI 跳出性价比陷阱的关键。"
-
在开发 Volume GI 方案时,"应该优先考虑防漏光",然后再去实现所有其他算法。
-
-
不同平台的首要关注点 (Platform Focus):
-
PC 端: 永远要多关注 "显存的控制"。
-
Mobile 端: 永远要多关注 "包量"(包体大小)。
-
-
对未来的展望 (Lumen):
-
Lumen 带来了 "巨大的制作效率的提升"。
-
讲者相信 "再过几年",当玩家的最低配置都达到 3080 级别时,Lumen "一定就是市面上非常最普遍的一个方案"。
-