基于 Surfel 的全局光照(GIBS)
Global Illumination Based on Surfels
技术概述与背景
什么是 GIBS
- GIBS(Global Illumination Based on Surfels) 是一种用于 实时计算间接漫反射光照 的方案
- 由 Electronic Arts 的 SEED R&D 组 和 Frostbite 引擎团队 联合开发
- 核心思路:将 硬件光线追踪(Hardware Ray Tracing) 与 场景几何的离散化(Discretization) 相结合,在时间和空间上 缓存并分摊光照计算开销
核心优势
- 无需任何预计算 :不需要 lightmap 烘焙、不需要特殊的 mesh、不需要特殊的 UV 集
- 支持高保真光照 :可处理任意规模的内容(从小物件到大型环境)
- 全动态 :支持蒙皮角色、动态物体、复杂动态光照交互,场景中所有物体都可以变化
- 加速制作流程 :省去了耗时的烘焙和传统光照技术的繁琐设置工作
与其他方案的对比
| 对比方案 | GIBS 的优势 |
|---|---|
| Probe Grid(探针网格)方案 | GIBS 在场景表面上精确执行光线追踪和缓存,而非在空间中均匀撒探针 |
| 屏幕空间滤波/缓存技术 | Surfel 缓存是 持久化的(Persistent) ,当表面移出视野再回来时无需重新计算 |
发展历史
- 原始 GIBS 算法于 2018 年 作为 EA SEED 的 PICA PICA 光追 Demo 的一部分实现(主要作者:Tomasz Stachowiak)
- 之后经过大幅 扩展和优化 ,能够处理任意几何体(包括蒙皮角色和大型环境),同时改善了收敛时间和质量
- 现已成为 Frostbite 引擎 工具集的一部分,供 EA 内部各开发团队使用
⚠️ 讲者强调该技术仍在持续开发中,预计还会有显著改进。
什么是 Surfel
基本概念
- 类比:就像一张图像可以被 像素(Pixel) 离散化一样,一个几何表面也可以被离散化
- Surfel 是 Surface Element(表面元素) 的简写
- 每个 Surfel 由三个属性定义:
- 位置(Position)
- 半径(Radius)
- 法线(Normal)
- 它 近似描述了给定位置附近的一小块表面邻域
为什么适合全局光照
- 分辨率无关(Resolution Independent) :性能和质量可灵活扩展
- 在表面上精确地执行和缓存光追操作 —— "在需要的地方做需要的事"
- 持久化缓存避免重复计算
场景的 Surfel 化(Surfelization)
生成流程
- 当几何体 进入视野 时,从 G-Buffer 中 生成(Spawn) Surfel
- 生成后 Surfel 是 持久化的 ,可以高效地跨帧 累积辐照度(Accumulate Radiance)
- 缓存了昂贵的计算结果,不会因物体暂时离开屏幕就丢弃
生成算法细节
- 屏幕空间 Tile 划分 :屏幕被划分为 16×16 的 Tile
- 每个 Tile 找到当前 覆盖率最低的纹素(Texel)
- 如果覆盖率低于一个 随机化阈值(Randomized Threshold) ,就使用 G-Buffer 中的几何信息生成一个新的 Surfel
- 当某个 Tile 达到一定覆盖率后,停止生成新 Surfel
- 正常情况下生成速度足够快,用户 几乎感知不到覆盖空洞
更多生成细节可参考 Tomasz 2018 年的演讲。
Transform ID 与动态跟踪
机制原理
- Surfel 虽然是持久化的,但 每帧都会更新其位置 ,以跟随它所附着的表面
- 实现方式:每个 Surfel 追踪一个 唯一的 Transform 标识符(Transform ID)
- 这个 ID 在生成时从 G-Buffer 中获取
Frostbite 中的实现
- Frostbite 维护了一个 全局 Transform 缓冲区(Global Transform Buffer) ,包含场景中所有几何体的变换信息(包括 骨骼蒙皮的骨骼变换 )
- 每帧利用 Surfel 的 相对位置(Relative Position) + Transform ID + 全局 Transform 缓冲区 → 计算出新的 世界空间位置
蒙皮网格(Skinned Meshes)支持
工作方式
- 对于蒙皮几何体,写入 G-Buffer 时使用的 Transform ID 是 权重最高的那根骨骼的 ID
- 这等效于对 Surfel 做 单骨骼蒙皮(One Bone Skinning) ,即使原始网格使用了任意数量的蒙皮权重
精度与容错
- 由于只使用一根骨骼,某些情况下 Surfel 不会完全精确地跟随表面
- 但 Surfel 的应用算法对此有较好的 容错性(Fairly Forgiving)
动态交互
- 因为 GIBS 假设一切都是动态的,所以 蒙皮和运动中的几何体 与静态几何体以完全相同的方式参与全局光照
- 蒙皮几何体同时 投射和接收全局光照
尺度自适应(Scale)
屏幕空间恒定密度
- Surfel 化在 任意距离 下都能工作
- Surfel 的大小会被缩放,使得它们在 屏幕空间中的投影大致恒定
- 这既适用于 生成时 ,也适用于 相机移动时
具体行为
| 相机操作 | Surfel 行为 |
|---|---|
| 靠近物体 | Surfel 缩小 ,同时 生成更多新 Surfel 以维持屏幕空间密度 |
| 远离物体 | Surfel 放大 ,移除多余 Surfel (覆盖率过高的地方) |
重要意义
- 质量恒定 :无论远近,光照质量保持一致
- 性能恒定 :由于屏幕空间 Surfel 密度恒定,计算量也大致恒定 —— 这是一个非常重要的性能特性
Surfel 管理与内存控制
资源预分配策略
- 由于 Surfel 是在移动过程中动态生成的,必须控制 内存和性能的一致性
- 解决方案:算法在初始化时 预先分配所有 Surfel 管理所需的资源
- 这意味着存在一个 Surfel 总数上限
Surfel 回收算法(Recycling)
为了有效利用有限资源,使用了一个基于 栈(Stack) 的回收机制:
- 初始化 :关卡加载时,栈中包含指向整个 Surfel 空间的 间接索引(Indirections)
- 生成 Surfel :通过 GPU 上的 原子操作(Atomic Operation) 递减栈计数器,从栈顶取出可用 Surfel 的 ID
- 回收 Surfel :当某个 Surfel 不再需要时(被判定为 "不再相关"),递增栈计数器,并将其 ID 写回栈缓冲区
关键实现特点
- 全部在 GPU 上完成 :生成、回收、管理操作均不涉及 CPU 回读,保证了高效的 GPU 驱动流水线
- 预分配 + 栈式管理 = 零运行时内存分配 ,内存占用完全可预测
Surfel 回收启发式算法(Recycling Heuristic)
为什么需要回收
- 系统维护一个 全局 Surfel 预算(Global Budget) ,用一个进度条显示当前存活的 Surfel 数量占总预算的比例
- 回收算法负责将存活 Surfel 数量控制在预算内
- 但 不能过度回收 ——每个 Surfel 上已经积累了大量光照计算结果,丢弃意味着浪费之前的工作
启发式因子
回收决策基于以下几个因素的综合评估:
- 当前存活 Surfel 数量 :总量越接近预算上限,回收压力越大
- 上次贡献时间 :该 Surfel 最后一次对场景光照产生贡献的时间戳(越久未贡献越容易被回收)
- 距离 :Surfel 与摄像机的距离(越远越容易被回收)
回收机制
- 将上述因子组合后与一个 均匀分布的随机数 进行比较
- 相关性越低的 Surfel 被赋予 更高的回收概率
- 这是一种 概率性淘汰 策略,避免了硬性截断带来的视觉跳变
加速结构(Acceleration Structure)
问题背景
- 算法中很多步骤需要 快速查找给定位置附近的 Surfel
- 原始 PICA PICA 版本使用了 均匀网格(Uniform Grid) 来加速空间查询
均匀网格的工作方式
- 每帧将所有 Surfel 插入网格
- 根据 Surfel 的位置计算其所在的网格单元(均匀网格下这是一个简单运算)
- 由于 Surfel 有半径,需检查是否与 相邻单元重叠 ,若重叠则也插入相邻单元
- 保证 Surfel 半径永远不超过网格单元边长 ,因此只需检查 直接相邻的单元
均匀网格的局限性
- 对 PICA PICA 的小场景效果很好,但 无法扩展到现代游戏的大规模关卡
- 回顾前面提到的:Surfel 半径在 屏幕空间大致恒定 ,这意味着 远处的 Surfel 在世界空间中半径很大
- 为了避免光照应用或查找时的不连续性,就需要使用 非常大的网格单元尺寸 ,这会 抵消均匀网格带来的加速优势
理想加速结构的需求
- 需要类似 投影变换(Projection Transform) 的特性:近处分辨率高,远处分辨率低
- 同时要 足够简单 ,确保查找速度快
最终方案:均匀网格 + 梯形网格(Uniform + Trapezoidal Grid)
- 近处 :保持一个 均匀网格 区域
- 远处 :沿每个 主轴方向(Principal Axis) 使用 梯形网格(Trapezoidal Grid)
- 梯形网格的 切片厚度随距中心距离增大而增大
关键特性
- 完美匹配 Surfel 随距离增长的方式 :远处网格单元更大,正好容纳远处更大的 Surfel
- 查找和插入依然非常快 :梯形网格本质上是均匀网格加上一个 非线性变换
- 调试可视化显示:
- 中心灰色区域 = 线性增长的均匀网格
- 周围粉色/绿色/黄色区域 = 非均匀的梯形网格
- 视觉上梯形网格在不同距离下保持 恒定的网格单元视觉大小 (类似光学错觉效果)
热力图观察
- 几何复杂度高的区域 → 更多 Surfel 生成 → 每个网格单元中 Surfel 密度更高
光照应用(Light Apply)
应用流程
- 对每个 屏幕空间像素 ,重建其 世界空间位置
- 在加速结构中找到 覆盖该位置的网格单元
- 获取该单元中 所有已插入的 Surfel
- 遍历所有 Surfel,将每个 Surfel 视为 虚拟点光源(Virtual Point Light)
- 根据以下因素 加权 计算对当前像素的辐照度贡献:
- Surfel 与像素之间的 距离
- Surfel 与像素的 朝向关系(Orientation)
- 其他几何因子
光照渗漏问题(Light Bleeding)
现象描述
- 以上述方式应用光照时,可能在墙壁和天花板上出现 斑块状伪影(Blotchy Artifacts)
- 地板靠近墙壁的区域也会出现类似问题
原因分析
- 某些 Surfel 之前生成在室外更明亮的环境中
- 这些 Surfel 在其半径范围内 对所有临近的表面都施加了光照 ,包括墙壁另一侧
- 根本原因:Surfel 本身并不知道周围的几何遮挡关系
深度函数(Depth Function)
解决思路
- 为每个 Surfel 构建一个 深度函数(Depth Function) ,让 Surfel "感知"周围的几何环境
- 初始化为 Surfel 的半径
- 在累积辐照度的光线追踪过程中,如果在半径范围内检测到几何体,就 更新深度函数 以表示该几何体的存在
径向高斯深度(Radial Gaussian Depth)
存储方式
- 不仅仅存储深度值,而是存储深度的 滑动平均(Moving Average) :
- 深度均值
- 深度平方均值
- 由此可以重建:
- 均值估计(Mean Estimate) :
- 方差估计(Variance Estimate) :
切比雪夫不等式深度测试(Chebyshev's Inequality)
- 利用均值和方差,通过 切比雪夫不等式(Chebyshev's Inequality) 进行 软深度测试
- 提供了一个 平滑的深度测试 ,在深度空间中 非常适合重建斜面/坡度(Slopes)
- 该技术灵感来源于 方差阴影贴图(Variance Shadow Maps, VSM) 和 DDGI
与 DDGI 的区别
| DDGI | GIBS | |
|---|---|---|
| 深度关注范围 | 探针的完整视角 | 仅关注 Surfel 直径范围内 的深度("有先天优势") |
| 方向关注 | 全球面 | 仅关注半球 |
深度图分辨率
- 可配置,但大多数情况下每个 Surfel 仅使用 4×4 纹素 的深度图(对应半球)
- 即便如此低的分辨率,配合切比雪夫不等式也 足以消除渗漏伪影
效果
- 应用深度函数后,墙壁和天花板上的 斑块伪影完全消失
- 室内外交界区域的光照渗漏问题得到 有效缓解
辐照度积分(Integrating Irradiance)
设计目标
- 一切都是 动态的 :支持自发光表面和材质、可破坏环境、动态物体
- 需要 高效积分辐照度 并对场景变化做出快速响应
基本光线追踪算法
以一个合成场景为例(地面上有 Surfel、蓝色天空、光源和一些几何体):
- 从 Surfel 向场景 随机发射光线
- 若未命中任何几何体(Miss) :
- 认为命中了天空
- 在该方向评估 天空光照(Sky Lighting)
- 若命中几何体(Hit) :
- 需要评估来自场景光源的 直接光照
- 向光源发射 遮挡/阴影光线(Shadow Rays)
- 若阴影光线被遮挡 → 该方向无贡献
- 若阴影光线未被遮挡 → 有光照贡献
- 在命中点评估 BRDF ,将结果沿原方向返回
多次弹射(Multi-Bounce)
- 上述过程只能得到 单次弹射(Single Bounce) 的间接光照
- 但命中点上可能 已经存在其他 Surfel ,这些 Surfel 随时间已经积累了辐照度
- 利用命中点处 Surfel 的已缓存辐照度 ,可以 随时间获得无限次弹射(Infinite Bounce) 的光照效果
时间分摊策略
- 为了支持动态环境并 降低单帧光追开销 ,不需要一次性发射所有光线
- 光线 分摊到多帧 完成累积
- 使用 修改版的滑动平均估计器(Modified Moving Average Estimator) 来融合多帧结果
这种设计使得系统既能保持实时性能,又能随时间收敛到高质量的全局光照结果。
积分器(Integrator)—— 自适应混合估计
常规移动平均的困境
- 普通的 移动平均估计器(Moving Average Estimator) 需要手动选择一个 混合因子(Blend Factor)
- 这带来了一个两难选择:
- 混合因子大 → 响应快 ,但噪声多、不收敛
- 混合因子小 → 收敛好 ,但对场景变化反应迟钝
- 理想目标:两者兼得 ,且全自动
多尺度均值估计器(Multi-Scale Mean Estimator)
- 在积累 长期移动平均(Long-Term Moving Average) 的同时,额外追踪:
- 短期均值(Short-Term Mean)
- 短期方差(Short-Term Variance)
- 短期估计器用于 动态调整长期均值的混合因子
- 效果:场景变化时快速响应,稳定后收敛到无噪声结果
📎 相关代码已开源在 GitHub 上(Multi-Scale Mean Estimator),详见 Tomasz 2018 年演讲。
基于方差的自适应光线数量
由于每个 Surfel 都在追踪方差,可以将方差信息用于 自适应调节每帧发射的光线数量 :
| 方差状态 | 光线策略 |
|---|---|
| 高方差 | 发射 更多光线 ,加速收敛 |
| 低方差 | 发射 极少光线 ,节省开销 |
| 已收敛 | 进入 近休眠状态(Almost Dormant State) ,可能每帧仅 1 条光线,甚至每隔几帧 1 条 |
可视化表现
- 蓝色 = 低方差(已收敛区域)
- 红色 = 高方差(需要更多光线的区域)
- 场景变化发生时(物体移动或灯光改变)→ Surfel 方差飙升 → 光线数量自动增加 → 快速收敛 → 回到休眠状态
- 新区域进入视野时,新生成的 Surfel 初始方差高 → 快速发射大量光线 → 收敛后自动降低
全局光线预算(Global Ray Budget)
- 每个 Surfel 独立请求自己需要的光线数量
- 系统维护一个 全局用户定义的光线预算 ,将所有 Surfel 的请求总和与该预算进行比较
- 确保 性能可预测 :无论场景复杂度如何,总光线数量始终受控
BRDF 重要性采样(Importance Sampling the BRDF)
朴素方法
- 最简单的方式:在 半球上均匀发射光线
- 效率低,浪费大量光线在贡献小的方向上
余弦采样(Cosine Lobe Sampling)
- 由于 BRDF 是 Lambertian(漫反射) ,可以对 余弦波瓣(Cosine Lobe) 进行重要性采样
- 即:在 法线方向附近(BRDF 值较大的方向) 发射更多光线
- 在大多数环境中效果良好,但在 光照方向与法线方向不一致 的场景中效果很差
光线引导(Ray Guiding)
问题场景
- 当大部分光线来自 与法线无关的特定方向 时,余弦采样效率极低
- 例:一个房间中只有右侧墙壁被阳光直射,其余区域很暗
- 两个 Surfel 发射相同数量和方向的光线
- 靠近亮墙的 Surfel 命中光源的概率是远处 Surfel 的 两倍
- 远处 Surfel 因估计不准而表现为明显 噪声
- 核心需求:精确了解光照从哪个方向来 ,在那些方向上集中发射光线
理论基础
- 灵感来自:
- Müller 等人的 Practical Path Guiding for Efficient Light Transport Simulation :在空间二叉树的每个节点上构建球面四叉树,细分直到每个叶节点具有相等辐射度,再用于重要性采样
- Mikula 等人的 Probability Trees
- McCool 等人的离散函数重要性采样技术
GIBS 的简化实现:辐照度图(Irradiance Map)
由于不可能每帧为每个 Surfel 构建四叉树,GIBS 采用了更轻量的方案:
- 将 半球映射到一个四边形(Quad) ,形成一个小的 2D 辐照度方向图
- 每个 Surfel 维护自己的辐照度图,追踪各方向上的辐照度
存储规格:
| 参数 | 配置 |
|---|---|
| 分辨率 | 6×6 纹素(可配置) |
| 每分量精度 | 8 bits |
| 缩放值 | 每个 Surfel 一个 16 bit 的全局缩放因子 |
| 归一化 | 每次迭代后对整个函数 归一化 |
离散函数的重要性采样算法
对任意一维离散函数 :
- 从均匀分布中采样一个随机变量
- 计算
- 从任意端开始遍历函数,逐步累加函数值
- 当累加值 时,当前位置即为重要性采样的结果
关键性质: 采样变量的生成概率 正比于函数值大小 —— 即光照多的方向获得更多光线
扩展到 2D
- 6×6 的 2D 辐照度图可以被 展平为 1D 函数 处理
- 也可以用 层级化方式 :先在 3×3 的高层做重要性采样,再在对应的 2×2 子区域中做第二步
- 线性滤波可用于纹素交界处的插值
- 每一步的 PDF 等于该位置的函数值 ,因此整个过程是 无偏的(Unbiased)
- 得到的 UV 坐标通过 半球到四边形变换的逆变换 转换为光线方向
实验验证
使用 HDR 环境贴图作为测试场景,对比:
- 横轴 :采样数量
- 纵轴 :相对误差
| 场景类型 | 余弦采样 | 光线引导 |
|---|---|---|
| 光照集中在非法线方向 | 收敛缓慢 | 初始几个样本后 学习到光照方向 ,快速收敛 |
| 光照均匀且匹配余弦分布 | 表现良好 | 无额外优势(但也不会更差) |
| 极小亮光源 | 表现差 | 同样表现差 ——6×6 分辨率太低无法精确定位 |
对于极小亮光源的场景,在实际游戏引擎中这些光源通常是 解析光源(Analytical Light Sources) ,会被直接采样,不依赖光线引导。
Surfel 间辐照度共享(Irradiance Sharing)
机制
- 利用 加速结构 ,每个 Surfel 天然知道自己所在的网格单元
- 可以访问 同一网格单元中的其他 Surfel
- 对相邻 Surfel 之间进行 加权辐照度共享 ,跨越表面边界传播信息
效果对比(64 个样本下)
| 模式 | 结果 |
|---|---|
| 无辐照度共享 | 结果 斑块感严重(Blotchy) ,噪声明显 |
| 有辐照度共享 | 仅 64 个样本即可获得 相当合理的结果 |
- 室内复杂光照场景:改善极为显著
- 室外场景:虽然理论上收敛更快,但无共享时 64 样本仍然斑块明显,开启共享后效果大幅改善
光线排序(Ray Sorting)
为什么需要排序
- 如果光线之间 空间相干性(Spatial Coherence) 很差,GPU 性能会严重下降
- 原因:同一个 Warp/Wave 中的线程可能遍历 加速结构的完全不同部分 ,导致 缓存利用率极差
Ray Binning 策略
- 采用与 Battlefield V 相同的 光线分桶(Ray Binning) 方案
- 每条光线根据 位置 + 方向 被分配到一个桶(Bin)中
Bin Index 的计算方式:
| 组成部分 | 来源 | 作用 |
|---|---|---|
| 空间哈希(主导值) | Surfel 所在的网格单元坐标转为 1D 索引 | 保证空间上相近的光线被分到一起 |
| 方向分量 | 从该 Surfel 发射的光线编号 | 进一步细分同一位置不同方向的光线 |
排序流程
- 第一遍 :统计每个 Bin 的光线数量和偏移量
- 第二遍 :生成重排序所需的索引信息
- 第三遍 :根据计算好的 Bin 计数和偏移量,重新排列光线
📎 详见 Battlefield V 的演讲资料。
多光源采样(Many Light Sampling)
问题背景
- 前面讨论了如何积分光照(Integrator),现在要解决:光线命中交点后如何计算该处的直接光照
- 压力测试场景(PvZ 地图):摄像机视锥内有 1400 盏灯光
朴素方案的瓶颈
| 方案 | 适用范围 | 问题 |
|---|---|---|
| 遍历所有光源 | 少量光源 | 光源数过多时完全不可行 |
| 随机采样 N 盏光源 (N ≪ 总数) | 数十盏光源 | 性能极低(便宜),但光源达到数百/数千时 收敛极慢 |
| 重要性采样 | 大规模光源 | 需要更复杂的算法来解决收敛问题 |
两条最有前景的技术路线
- 随机光切割(Stochastic Lightcuts)
- 蓄水池采样(Reservoir Sampling)
随机光切割(Stochastic Lightcuts)
理论基础
- 基于 Yuksel 等人在 HPG 2019 上发表的工作
- 在经典 Lightcuts 基础上引入 最小化偏差的光源树采样 ,提升采样效率
- 核心优势 :
- 不需要大量样本即可收敛
- 无需额外的空间或时间存储 (不像蓄水池重采样那样需要保存历史样本)
光源树构建(Building)
- 将光源位置存储在 视空间(View Space) 中(为精度考虑)
- 按位置的 莫顿编码(Morton Code) 对光源排序
- 莫顿编码天然将空间上相近的光源分组,使树具有 隐式的空间相关性
- 避免了 BVH 构建中需要的多次排序
- 自底向上(Bottom-Up) 构建树
- 每个 内部节点 存储其子节点的 合并包围盒 和 合并光照强度
光源树采样(Sampling)
- 选择切割(Cut) :从树中选择一条 切割线 ,切割线上的节点数由用户定义的 节点限制 控制
- 典型值:2~8 个节点 (取决于质量等级或平台)
- 节点数 = 最终要发射 阴影光线(Shadow Ray) 进行可见性检查的光源数量
- 最小化误差 :选择使 光照误差最小化 的节点来构成切割线(基于采样位置与节点的关系)
- 随机下行遍历 :从切割线上的每个节点出发,向下随机遍历
- 对每个内部节点,根据 重要性权重(Importance Weights) 为其左右子节点分配概率
- 随机选择左或右子节点
- 最终到达叶节点,得到具体的光源
蓄水池采样(Reservoir Sampling)
理论基础
- 基于 NVIDIA 的 ReSTIR 研究
- 核心思想:随机评估 N 盏光源,但只选出 M 个 "赢家" 来发射光线
核心优势
| 特性 | 说明 |
|---|---|
| 无需预计算数据结构 | 不像 Lightcuts 需要构建光源树 |
| 每个样本仅需 1 条光线 | 在主机平台上比 Stochastic Lightcuts 实现 更便宜 |
采样流程
- 随机采样 4~8 盏光源
- 对每盏光源计算 权重(Weight) ,基于:
- 距离(Distance)
- 光照强度(Intensity)
- 生成一个随机数,若该随机数小于 (当前光源权重 / 累计总权重),则 选择该光源
- 最终用所有采样光源的 总权重归一化结果 PDF
- 最终只选出 1 个赢家 发射光线
蓄水池重采样(Reservoir Resampling)的挑战
- 为了充分发挥蓄水池采样的潜力,理想情况下应该将当前选择的样本与 空间邻域和时间邻域的样本进行比较和重采样
- 这就是 蓄水池重采样(Reservoir Resampling / ReSTIR)
- 但在 间接光照场景 中实现较为复杂:
- 朴素方法需要 大量额外存储 来保存已选样本
- 然后再与时间和空间邻域样本进行比较
- 讲者表示这是 正在积极开发中 的功能
各方案效果对比
测试场景
- 视野内约 700 盏灯 ,视锥外另有 700 盏灯
- 以下对比均在仅求解 15 帧 后的间接漫反射结果
| 方案 | 配置 | 质量表现 |
|---|---|---|
| 暴力随机采样 | 2 条阴影光线 | 极度噪声 ,大量光照贡献缺失 |
| 蓄水池采样 | 8 个候选样本 | 接近收敛结果,远超随机采样,但仍有明显的 斑点和萤火虫噪声(Fireflies) |
| 随机光切割 | 4 个节点 | 最接近收敛结果 ,但在主机上 计算开销更高 |
关键结论
- Stochastic Lightcuts 在质量上表现最佳,但计算成本更高
- Reservoir Sampling 在性能和质量之间取得较好平衡,尤其适合主机平台
- 两者各有优劣,具体选择取决于 目标平台和性能预算
收敛速度
- 从演示中可以看到,仅经过 15 帧 就能得到非常接近完全收敛的结果
- 至此,不透明表面的方案已经完整介绍
透明物体支持(Transparency)
核心挑战
- Surfel 非常适合为 不透明表面 缓存辐照度
- 但 Surfel 依赖于从 G-Buffer 生成 ,而 透明物体不写入 G-Buffer ,因此同样的方法难以直接移植
解决方案:光线追踪探针(Ray Traced Probes)
- 不再将 Surfel 附着于透明表面,而是采用 探针体积(Probe Volume) 的方式
- 探针 持久存在 于体积中,像 Surfel 一样以光线追踪方式 收集辐射度(Gather Radiance)
- 由于收集的是 漫反射辐射度 ,可以将其 投影到球谐空间(Spherical Harmonics, SH)
- 使用与 Surfel 相同的 自适应 MSME 积分器 来跨帧累积探针样本
探针辐照度的帧摊分计算
- 单帧内 无法实时计算完整的 SH 系数积分 ,因为需要大量样本才能收敛
- 因此采用 跨帧摊分(Amortize) 策略:
- 每帧仅发射 少量光线 ,采样入射辐射度
- 将采样结果 投影到 SH 基函数
- 使用自适应 MSME 积分器 跨时间累积投影后的辐射度
- 积分器同时根据 方差 决定下一帧需要发射的光线数量
探针体积结构(RT Probes Volume Structure)
选型过程
- 探针同样面临与 Surfel 一样的 尺度问题(Scale Problem)
- 评估了多种方案后,最终选择了 体积裁剪图(Volume Clipmap)
Clipmap 的核心思想
- 一组 嵌套的体积层级(Nested Volumes)
- 每个层级覆盖 更大的区域 但 细节更少
- 效果:
- 近处 :高密度探针,细节丰富
- 远处 :低密度探针,覆盖广
- 内存开销低
- 不同层级可以 异步更新 ,低细节层级可以为高细节层级 预填充(Prime)
Clipmap 更新算法
当摄像机移动时:
- 每帧检查摄像机的世界空间位置
- 如果摄像机移出某层级的 中心网格 ,则将该层级的探针 朝摄像机方向平移 ,保持摄像机始终在中心
- 平移距离取决于摄像机偏离中心的 网格单元数
- 平移后产生的 新探针 使用 更高层级(更低细节)的探针插值初始化
关键优化
- 由于层级网格覆盖范围大,大部分探针在更新后 仍然有效
- 有效探针只需 复制到新坐标 ,而非丢弃重建
- 这是 最大限度保留已缓存辐照度、提升性能 的关键
四层级结构可视化
- Level 1 :最高细节,覆盖摄像机附近最小区域
- Level 2 :中等细节
- Level 3 :较低细节
- Level 4 :最低细节,覆盖最大区域
- 调试视图中:不同颜色标识像素从哪个层级采样,最高细节集中在 屏幕底部中心(摄像机近处) ,最低细节出现在 地平线(远处)
Clipmap 采样策略
朴素采样
- 找到 包含当前像素的最高细节层级 ,直接采样该层级的 SH 系数并重建辐射度
- 问题 :层级边界处会出现 可见的不连续跳变(Discontinuities)
- 对于动态场景尤为严重(物体可能在某帧突然跳到低细节层级)
混合采样(Blending)
- 为每个层级创建一个 过渡边界带(Transition Border)
- 位于边界带内的像素同时从 当前层级 和 相邻层级 采样
- 根据 到边界的归一化距离 计算混合权重
- 结果:平滑过渡,无跳变
- 缺点 :每个着色样本需要 采样两个体积 ,开销翻倍
蓝噪声抖动采样(Blue Noise Dithered Sampling)
- 目标:只采样一个体积 ,同时达到接近混合的质量
- 方法:
- 使用 蓝噪声(Blue Noise) 值(低差异分布)决定采样当前层级还是下一层级
- 蓝噪声 易于滤波 ,与 TAA 配合良好
- 结果:质量略低于混合方案,但 过渡平滑 ,且 性能开销仅为单次采样
动态场景表现
- 透明物体方案在 完全动态世界 中表现良好
- 无需任何预计算
- 场景几何或光照变化时探针 自动更新
- 同时能 快速收敛 到新的环境光照
帧结构与性能概览(Frame Overview)
四大阶段
| 阶段 | 主要工作 | 性能特征 |
|---|---|---|
| 1. 持久化 Surfel 工作(Persistent Surfel Work) | 光线追踪、辐照度累积 | 光线追踪 主导,是最耗时的部分 |
| 2. 新生成 Surfel 工作(Spawn Work) | 从 G-Buffer 生成新 Surfel | 取决于生成数量:光线追踪 或 几何法线重建 主导;高分辨率下 Gap Fill 可能主导 |
| 3. 滤波(Filtering) | Surfel 数据滤波 | 非常快,平均仅约 0.2 ms |
| 4. 应用到屏幕(Apply) | 将 Surfel 光照写入屏幕像素 | 光照应用 Pass(Lighting Apply) 主导 |
性能关键点
- 光线追踪始终是瓶颈 ,特别是在持久化 Surfel 的工作中
- 后续将给出 最坏情况 和 典型游戏内容 的性能数据
压力测试设置(Stress Test Settings)
测试平台与参数
| 参数 | 配置 |
|---|---|
| 平台 | PlayStation 5 |
| 输出分辨率 | 4K |
| 屏幕空间 Pass 分辨率 | 1080p(输出面积的 1/4 ) |
| Surfel 生成距离限制 | 无限制(一直生成到地平线) |
| 光线截断距离 | 无限制 |
| 光源采样方案 | 8 样本蓄水池采样(除非另有说明) |
测试策略
- 测试 最坏情况 :从 空场景 开始,观察收敛到可接受质量所需的时间和开销
- 一旦收敛完成,后续开销会 显著下降 ,因此重点关注 收敛阶段(最贵阶段) 的性能
性能测试场景
场景 1:PvZ 城镇中心(仅日光)
- 光照条件:仅太阳光
- 收敛时间:约 3.5 秒 达到可接受质量
- 平均耗时:约 7 毫秒
- 注意事项:
- 用于 GI 的 albedo 颜色与实际 albedo 纹理 不完全一致
- 由于 绘制距离很大 ,Surfel 生成和光线追踪一直延伸到地平线
场景 2a:PvZ 1400 盏灯光压力测试(默认光线预算)
- 光照条件:1400 盏局部光源 在视锥内
- 收敛时间:基本不收敛 (默认光线预算不足)
- 性能观察:
- 相比日间场景,局部光源采样有额外开销
- 虽然难以收敛,但系统仍在持续工作
场景 2b:PvZ 1400 盏灯光(100 万光线预算)
- 光线预算提升至 1,000,000 条/帧
- 收敛时间:约 10 秒 ,仍有一定噪声但在持续改善
- 性能表现:
- 初始生成阶段 峰值很高
- 快速回落,但稳定后仍在 约 11 毫秒
- 讲者承认这是需要继续优化的领域
场景 3:PvZ 1400 盏灯光(不同视角)
- 同样 1400 盏灯光,但视角更受限
- 收敛时间:约 7 秒
- 性能:显著低于场景 2 ,因为视角受限意味着需要处理的 Surfel 更少
场景 3 变体:使用 4 样本 Lightcuts
- 将采样方案从蓄水池采样切换为 4 样本 Lightcuts
- 收敛时间:约 5 秒 (比蓄水池快)
- 代价:平均耗时上升到约 11.3 毫秒
- 观察:图表右侧 持久化 Surfel 和新生成 Surfel 的开销都在下降 ,方差趋于稳定
场景 4:内部 Demo 关卡(约 140 盏灯光)
- 收敛时间:约 2 秒
- 平均耗时:约 5.5 毫秒 ,比 PvZ 街景更便宜
自由漫游测试(Free Roam Tests)
为什么更具代表性
- 压力测试从零开始是 最坏情况
- 实际游戏中,玩家在关卡中移动时:
- Surfel 会 持续生成和求解
- 但 不会从零开始 (除了最开头)
- 已缓存的 Surfel 持续提供光照
测试 1:4K 输出
| 指标 | 数值 |
|---|---|
| 输出分辨率 | 4K |
| 平均耗时 | 约 5.4 毫秒 |
| 对比压力测试 | 性能更好 |
测试 2:1800p + 棋盘格渲染
| 指标 | 数值 |
|---|---|
| 输出分辨率 | 1800p + Checkerboard Rendering |
| 平均耗时 | 约 3.4 毫秒 |
| 代表性 | 更接近实际游戏会使用的设置 |
Clipmap 探针开销
- 沿与自由漫游测试相同的路线
- 三层级 Clipmap 结构
- 开销 非常合理 :每隔几帧 轮流更新不同层级
- 第三层级最便宜(大部分光线为 Miss,因为覆盖远处空旷区域)
- 如果 全屏每像素采样探针(900p):约 0.2 毫秒
未来工作(Future Work)
程序化几何支持
- 目前 不显式支持程序化几何(Procedural Geometry)
- 程序化几何可以工作,但会 不断生成和回收 Surfel ,因为缺乏像蒙皮网格那样的 Transform 跟踪机制
高细节几何的过度生成问题
- 某些 高细节几何 可能导致 Surfel 过度生成(Overspawn)
- 可选方案之一:与屏幕空间全局光照(SSGI)结合
- 在 SSGI 效果好的区域 减少 Surfel 生成
- 限制需要求解的 Surfel 数量
- 限制 Surfel 和光线追踪的距离范围
光线引导的改进
- 正在研究 跨 Surfel 共享引导信息 ,以获得更全面的光照方向认知
- 正在研究使用 ReSTIR 类方法 来增强光线引导
多光源采样的优化方向
| 优化方向 | 说明 |
|---|---|
| 光源可见性存储 | 在网格单元或体素数据结构中存储光源可见性,大幅减少每次光线命中需考虑的光源数量 |
| 光线间共享光源 | 由于光线已按空间排序(Ray Binning),同一 Bin 内的光线可以 共享光源采样结果 |
| 蓄水池空间采样 | 利用排序后的光线进行空间蓄水池采样 |
| 时间蓄水池采样 | 将蓄水池存储在 网格单元结构或每个 Surfel 上 |
| Lightcuts 遍历优化 | 当前实现未充分利用 平台特定指令(Platform-Specific Intrinsics) |
| 光源采样调度 | 将光源采样从光追 Pass 中移出,将二次阴影光线调度到后续 Pass |
探针系统的未来方向
- 探针 Clipmap 方案仍处于 早期阶段
- 计划添加:
- 光线引导 支持
- 高光(Specular) 支持
- 计划通过 与 Surfel 光线调度共享和整合光线 来提升性能
总结(Wrap Up)
GIBS 技术的核心价值
- 场景的机会性 Surfel 化 提供了高效的 表面信息缓存机制
- 缓存机制具有三大特性:
- 持久化(Persistent) :跨帧保留,不因离开屏幕而丢失
- 全动态(Dynamic) :支持蒙皮角色、动态物体、动态光源
- 分辨率解耦(Decoupled from Output Resolution) :性能与输出分辨率无关
- 使用 Surfel 在 各种几何体和场景 上缓存辐照度
- 对于不适合 Surfel 的几何体(如透明物体),提供 探针 Clipmap 回退方案
性能总结
| 场景类型 | 典型耗时 |
|---|---|
| 大型室外(仅日光) | ~7 ms |
| 1400 灯压力测试 | ~11 ms |
| 中等灯光(~140) | ~5.5 ms |
| 自由漫游(4K) | ~5.4 ms |
| 自由漫游(1800p + CB) | ~3.4 ms |
| 探针采样(全屏 900p) | ~0.2 ms |