动态全局光照与 Lumen
本篇是系列讲座的开篇,主要内容并非深入 Lumen 的技术细节,而是为整个学习路径“定调”。讲师阐述了为何要从更宏观的全局光照(GI)历史讲起,并分享了本次课程的独特学习方法。
一、 课程与社区动态
在进入核心技术主题前,讲师分享了一些关于课程和开源社区的最新进展,这有助于我们理解课程的实践背景。
-
引擎更新:GPU 粒子系统
- 核心观点: 课程配套的开源引擎新增了 GPU Particle 功能。这虽然在商业引擎中是基础功能,但讲师希望通过在小引擎中实现它,来证明现代渲染技术并非遥不可及。
- 关键术语:
- Pico Engine: 课程配套的、用于学习和实践的轻量级自研引擎。
- GPU Particle: 在 GPU 上进行模拟和渲染的粒子系统,能够高效处理海量粒子,是现代游戏中常见的特效技术。
- Playground: 讲师希望引擎能成为社区成员的“游乐场”,鼓励大家动手实践、共同贡献。
-
社区成果展示与鼓励
- 核心观点: 讲师展示了学员们基于引擎的出色作业,例如实现了专业的材质调整工具。这表明即使是简单的开源引擎,也能在社区的共同努力下逐步完善,并鼓励大家积极参与贡献。
二、 核心主题:动态全局光照与 Lumen
这部分是本讲座的重点,讲师阐明了本次课程的核心思路和结构调整,这对于后续理解 Lumen 的设计哲学至关重要。
1. 学习前提:为何要先讲全局光照 (GI)?
讲师坦言,课程准备从“只讲 Lumen”临时调整为“系统性地讲解 GI 发展史,再引出 Lumen”。
-
核心观点: 直接学习 Lumen 会陷入海量且孤立的技术细节中,难以理解其设计的根本原因。Lumen 并非凭空出现,而是站在了过去几十年 GI 研究的肩膀上。
-
关键类比:
- Lumen 是一个“算法的集大成者”。它的许多模块和设计思想,都可以在之前的 GI 算法中找到影子。例如,它的某些部分借鉴了 Voxel Cone Tracing,另一些部分可能受到了 Light Propagation Volumes 的启发。
- 不了解 GI 的历史,就像直接阅读一部巨著的最后一章,无法理解人物关系和情节的来龙去脉。
-
学习路径:
- 理解 GI 的宏观结构: 首先要明白 GI 技术主要分为哪几大类,各自试图解决什么问题,以及它们的优缺点。
- 追溯算法渊源: 学习 Lumen 的各个组件时,能够将其与历史上的经典 GI 算法联系起来。
- 洞悉设计哲学: 最终,我们才能明白 Epic Games 的工程师们为什么这样组合不同的技术来构建 Lumen,理解其在效果、性能和动态性之间的权衡。
2. 课程结构调整与学习方法论
基于以上思考,课程的最后部分将聚焦于 Unreal Engine 5 的两大核心技术,并采用一种特殊的教学模式。
-
课程焦点:
- Lumen: 动态全局光照与反射系统。
- Nanite: 虚拟化微多边形几何体系统。
- 相关技术: 在讲解这两大系统时,会穿插介绍相关的基础技术,如 Cluster-based Rendering, Visibility Buffer, Mesh Shaders 等。
-
教学模式:Paper Reading (论文研讨会)
- 核心观点: 本次课程更像是一场由讲师带领的 “Paper Reading”,而非单向的知识灌输。讲师也是近期才深入研究和消化这些前沿知识,甚至需要深入源码来验证细节。
- 这种模式的特点:
- 新鲜出炉: 内容非常前沿,是讲师团队最新研究和理解的成果。
- 共同探索: 讲师将分享自己消化和理解的过程,帮助听众降低理解门槛,这是一种共同学习和探讨的模式。
- 注重宏观: 目标是帮助大家理解算法的整体结构和设计思路,而不是纠结于每一个可以轻易查阅的琐碎实现细节。
本节总结: 这一部分是整个 Lumen 学习之旅的“地图”和“指南”。它告诉我们,不要急于求成地去啃 Lumen 的源码或孤立的技术点。相反,我们应该跟随讲师的步伐, 首先建立一个关于全局光照(GI)技术的完整知识框架。只有站在这个宏观的视角上,我们才能真正看懂 Lumen 的精妙之处,并将其思想内化,为我们自己的渲染开发工作提供启发。
全局光照 (Global Illumination) 导论:从渲染方程到蒙特卡洛
这是系列课程的第二部分,我们将深入探讨全局光照(GI)的核心概念。理解 GI 的宏观结构,是理解现代高级渲染技术(如 Lumen)的基石。本篇笔记将从最根本的物理和数学原理出发,解释 GI 的重要性、实现难度以及经典的求解思路。
一、 万物之源:渲染方程 (The Rendering Equation)
所有现代物理渲染技术,无论多么复杂,最终都是为了求解或近似求解一个核心公式——渲染方程。它由 James Kajiya 在 1986 年提出,是整个计算机图形学领域的基石,其地位堪比物理学中的 E=mc²。
-
核心观点: 渲染方程用一个简洁的数学形式,描述了光能在场景中稳定分布的物理本质。它告诉我们,从一个点(
p)沿某个方向(ω_o)射出的光(Radiance),等于该点自身发出的光,加上所有从其他方向(ω_i)射入该点并被反射到ω_o方向的光的总和。 -
关键术语:
- 渲染方程 (Rendering Equation): 描述光照物理现象的积分方程。
- 全局光照 (Global Illumination, GI): 求解渲染方程,从而模拟完整光照效果(包括直接和间接光照)的过程。
-
公式详解:
- : 出射光(我们最终看到的光)。
- : 物体自身发出的光(自发光项,如灯泡)。
- : 对以点
p为中心的整个半球进行积分,这是 GI 计算复杂度的核心来源。 - : BRDF (双向反射分布函数),描述了材质的反射特性。
- : 入射光,即从各个方向射向点
p的光。 - : 入射光与法线的夹角,即 Lambertian 因子。
尽管公式本身很优美,但要在实时应用(如游戏)中精确求解这个积分方程,其计算量是天文数字,至今仍是图形学界的“圣杯”。我们目前所有的工作,都是在无限逼近这个目标。
二、 为什么全局光照 (GI) 如此重要且困难?
如果一个游戏引擎只计算直接光照,那么场景中没有被光源直接照射的地方将是一片漆黑,画面会显得非常“假”和“塑料感”。GI 的目标就是补全这部分缺失的光照信息。
1. GI 的核心:间接光照 (Indirect Lighting)
-
核心观点: GI 的本质是模拟间接光照。当光源发出的光线照射到物体表面后,该表面会吸收部分能量,并将剩余能量向四周反射出去。此时,这个被照亮的表面本身就成为了一个新的、次级的光源,继续照亮场景中的其他物体。
-
关键术语:
- 直接光照 (Direct Lighting): 光源直接照射到物体表面的光。计算相对简单。
- 间接光照 (Indirect Lighting): 光线经过一次或多次反射后才到达物体表面的光。这是 GI 的主要模拟对象。
- 单次反弹 (Single Bounce): 光线只经过一次表面反射。
- 多次反弹 (Multi-bounce): 光线经过多次表面反射,效果更真实,但计算量呈指数级增长。
2. 经典案例:康奈尔盒子 (Cornell Box)
康奈尔盒子是用于测试和验证 GI 算法最经典的场景。它结构简单(一个封闭盒子,内置几个物体和一个面光源),却能清晰地展示出复杂的 GI 现象。
- 核心观点: 康奈尔盒子直观地展示了 GI 的关键效果,尤其是 光色溢出 (Color Bleeding)。
- 现象分析:
- 盒子顶部的面光源照亮了整个场景。
- 光线照射到左侧的红色墙壁后,反射出的光线也带上了红色。
- 这些红色的间接光照射到中间的白色方块和地面上,使其表面也染上了一层淡淡的红色。
- 同理,右侧的绿色墙壁也会对周围环境产生绿色的光色溢出。
- 这些微妙的色彩交互,正是 GI 带来真实感的关键。
图:一个典型的康奈尔盒子渲染结果,清晰地展示了光色溢出效果。
3. GI 对真实感的决定性作用
在现代游戏中,我们追求的目标是跨越“恐怖谷”,让画面达到以假乱真的程度。GI 在其中扮演了决定性角色。
- 没有 GI: 场景缺乏深度和氛围。过去我们常用一些“Hack”手段来模拟,例如 环境光 (Ambient Light),但这只是一种全局提亮的粗糙方法,效果很差。
- 拥有 GI: 场景光影过渡柔和自然,暗部细节丰富,物体与环境融为一体。无论是阳光穿过窗户洒满房间,还是手电筒在洞穴中照亮周围的岩壁,这些生动的场景都离不开 GI 的支持。
三、 求解之道:蒙特卡洛方法 (Monte Carlo Method)
既然渲染方程中的积分那么难算,我们该如何着手呢?一个强大而通用的数学工具就是蒙特卡洛积分。
1. 核心思想:用随机采样近似积分
-
核心观点: 对于一个难以解析求解的复杂积分,我们可以通过在积分域内进行大量随机采样,然后计算这些采样点函数值的平均值,来近似得到积分结果。采样数量越多,结果就越接近真实值。
-
关键术语:
- 蒙特卡洛积分 (Monte Carlo Integration): 一种基于概率统计的数值积分方法。
-
简化理解: 想象一下要求一个不规则图形的面积。我们可以把它放在一个面积已知的矩形里,然后向矩形内随机撒下大量的沙子。最后,通过计算落在不规则图形内的沙子数量占总数量的比例,就能估算出它的面积。
-
基本公式:
其中, 是在
[a, b]区间内的随机采样点。
2. 应用:蒙特卡洛光线追踪 (Monte Carlo Ray Tracing)
将蒙特卡洛方法应用于渲染方程,就诞生了蒙特卡洛光线追踪,也就是我们常说的 路径追踪 (Path Tracing)。
-
核心观点: 从摄像机(眼睛)出发,为屏幕上的每个像素发射一条光线。当光线击中物体表面时,根据其材质(BRDF)在半球方向上随机采样一个新的方向,继续发射光线,如此递归下去,直到击中光源或达到最大反弹次数。将整条路径上的光照贡献累加起来,就得到了该像素的颜色。
-
算法流程:
- 从视点为像素发射主光线 (Primary Ray)。
- 光线击中场景中的点
p。 - 在点
p的半球方向上,根据蒙特卡洛方法随机采样一个或多个方向,发射次级光线 (Secondary Rays)。 - 递归追踪次级光线,累加它们带回的光能。
- 重复多次采样并取平均值,得到最终像素颜色。
3. 根本性难题:指数级复杂度
蒙特卡洛光线追踪虽然在理论上能够完美求解渲染方程,但其计算成本极高。
-
核心观点: 每一次光线反弹,都需要进行一次半球积分的蒙特卡洛采样。如果每次反弹都分裂出 N 条新的采样光线,那么经过 M 次反弹后,光线总数将达到 N^M,这是一个指数级的增长。
-
现实挑战:
- 单次反弹 (Single Bounce): 计算量尚可接受。
- 多次反弹 (Multi-bounce): 计算量会爆炸式增长,对于需要每秒渲染 60 帧的实时游戏来说,这几乎是不可接受的。
因此,如何在保证效果的前提下,用有限的计算资源高效地模拟多次反弹的间接光照,是实时 GI 技术(如 Lumen)需要解决的核心挑战。
全局光照(GI)的核心挑战与优化:蒙特卡洛路径追踪与重要性采样
一、 蒙特卡洛路径追踪 (Monte Carlo Path Tracing) 的核心困境
讲座的这一部分深入探讨了基于蒙特卡洛方法的路径追踪在实现全局光照(GI)时面临的根本性难题。
1. 指数级计算复杂度
- 核心观点: 全局光照需要模拟光线在场景中的多次弹射(Multi-bounce),而每一次弹射都会导致需要追踪的光线数量呈指数级增长,从而带来巨大的计算开销。
- 关键术语:
- 多弹射 (Multi-bounce): 光线在击中一个表面后,反射出去并继续与其他表面相交的过程。这是实现真实间接光照的关键。
- 指数级扩张 (Exponential Expansion): 如果每次光线弹射都分裂成 N 条新的光线,那么经过 M 次弹射后,总光线数将是 N^M,计算量急剧膨胀。
2. 采样效率低下与噪声 (Noise)
- 核心观点: 在许多真实场景中,光源(如一扇小窗)的面积远小于整个场景。随机、均匀地投射光线(采样)会导致绝大多数光线无法击中光源,造成大量计算资源的浪费,最终在画面上形成难以接受的 噪点 (Noise)。
- 关键术语:
- 采样 (Sampling): 在这里指从某个点向周围空间投射光线,以“询问”来自四面八方的光照信息。讲座中强调, 采样是蒙特卡洛路径追踪的“一生之敌”。
- SPP (Samples Per Pixel): 每个像素的采样数量。提高 SPP 可以有效降低噪声,使画面更平滑,但这会直接增加渲染时间。从讲座的例子可以看出,从 1 SPP 到 65536 (2^16) SPP,图像质量才变得平滑,但计算成本是天文数字。
(图片示意:随着每个像素采样数(SPP)的增加,图像的噪点逐渐减少)
二、 解决方案:重要性采样 (Importance Sampling)
为了解决上述困境,图形学界引入了“重要性采样”这一核心技术,其思想是从“均匀撒网”的蛮力方法转变为“重点捕捞”的智能方法。
1. 核心概念:概率密度函数 (PDF)
- 核心观点: 与其在所有方向上均匀采样,我们应该在那些对最终结果贡献更大的方向上(即信号更强的方向)投入更多的采样光线。这种指导采样分布的数学工具就是 概率密度函数 (Probability Distribution Function, PDF)。
- 关键术语:
- 重要性采样 (Importance Sampling): 一种蒙特卡洛积分的优化技术,其核心思想是根据被积函数本身的大小来调整采样点的概率分布,从而在采样数量不变的情况下,获得更精确、更稳定的积分结果(即更低的噪声)。
- PDF (Probability Distribution Function): 在此上下文中,它描述了在不同方向上投射采样光线的概率。一个好的 PDF 应该与光照贡献的分布(例如 BRDF 的形状、光源的位置等)高度匹配。
2. 实践中的应用:如何选择 PDF
根据表面的材质特性,我们可以选择不同的 PDF 来指导采样,以达到更好的效果。
-
均匀采样 (Uniform Sampling):
- 这是最基础的方法,PDF 在所有方向上都是常数。
- 效果最差,相当于“盲猜”,在同样采样数下噪声最严重。
-
基于余弦的采样 (Cosine-Weighted Sampling):
- 适用场景: 理想的 漫反射 (Diffuse) 表面(遵循兰伯特余弦定律)。
- 原理: 对于漫反射表面,来自正上方的光(法线方向)贡献最大,而来自掠射角度的光贡献最小。因此,我们的 PDF 应该与余弦值成正比,将更多采样光线集中在法线方向附近。
- 公式表达:
其中PDF ∝ cos(θ)θ是采样方向与表面法线的夹角。 - 效果: 同样是 256 SPP,使用余弦采样相比均匀采样,噪点显著减少。
-
基于 BRDF 的采样 (BRDF-based Sampling):
- 适用场景: 高光/镜面 (Glossy/Specular) 表面,例如金属、塑料等。
- 原理: 对于高光表面,光照贡献主要集中在镜面反射方向周围的一个很小的锥角内。此时,PDF 应该模拟材质的 BRDF 形状,例如常用的 GGX 分布。
- GGX 特性: 讲座中提到 GGX 的特点是“高频足够尖,低频足够宽”,这意味着它能很好地模拟出清晰明亮的高光核心,同时在周围保留一个柔和衰减的“光晕”(尾部),非常适合作为高光材质采样的 PDF。
三、 核心思想总结
- 全局光照(GI)算法的核心技术点在于找到一个高效的采样策略 (Sampling Strategy)。
- 我们的目标是用尽可能少的光线(计算资源),去捕捉和表达一个点周围复杂的光场信息。
- 关键不在于采样的数量,而在于采样的质量 ——即是否将有限的采样资源用在了“刀刃”上(光照贡献最大的方向)。
四、 承上启下:从离线到实时 GI
讲座最后指出,以上讨论的蒙特卡洛路径追踪和重要性采样等技术,在过去几十年主要应用于电影等 离线渲染 (Offline Rendering) 领域。而近十几年,业界的巨大挑战和目标是将这些高质量的 GI 效果实时化,应用在游戏等交互式场景中。Lumen 算法正是这一探索方向上的重要成果。
接下来,讲座将介绍一个堪称实时 GI“老祖宗”的算法—— 反射阴影图 (Reflective Shadow Maps, RSM),以此为起点,逐步展开实时 GI 技术的演进。
第四部分:实时全局光照的基石 - Reflective Shadow Map (RSM)
1. 实时全局光照(Real-Time GI)的早期探索
讲座的这一部分开始追溯实时GI技术的源头。在十几年前,游戏中的全局光照(GI)因其巨大的性能开销而被认为是不可能实时化的。然而,一些前瞻性的研究开始挑战这一难题,而 Reflective Shadow Map (RSM) 正是这一系列探索中的开山之作。
- 核心观点: RSM (2005) 是一项里程碑式的工作,它首次向业界证明了实时GI的可能性,并启发了后续包括Lumen在内的大量相关技术。它的核心是解决一个根本问题: 如何高效地将光源的能量“注入”到场景中,以计算间接光照。
2. 开山之作:Reflective Shadow Map (RSM)
2.1 思想溯源:与光子映射(Photon Mapping)的异曲同工
在深入RSM之前,讲者首先回顾了经典的离线GI算法,以建立一个概念框架。
-
两种经典的GI方法:
- 蒙特卡洛路径追踪 (Monte Carlo Path Tracing): 从 相机/眼睛 的视角出发,反向追踪光线路径。这是一种“Gather”(收集)的过程。
- 光子映射 (Photon Mapping): 从光源的视角出发,正向发射大量光子(Photons),记录它们在场景物体表面的“落点”。渲染时,再从这些落点处收集光照信息。这是一种“Shoot/Cast”(投射)的过程。
-
核心观点: 讲者认为,RSM及其后续发展(直至Lumen)在哲学思想上与光子映射非常相似。它们都采用了从光源视角出发的策略来解决光照问题,而不是传统的从相机视角出发。
2.2 核心思想:复用并扩展Shadow Map
RSM的巧妙之处在于它基于一个我们已经非常熟悉的技术: Shadow Map。
-
关键洞察 (Observation):
- 传统的渲染是从相机视角看世界。
- 而生成Shadow Map的过程,本质上就是从光源视角去“看”世界。
- 在Shadow Map中,所有能被光源直接看到的表面(即第一次被照亮的表面)都会被记录下来。
-
RSM的扩展:
- 传统的Shadow Map只存储了深度信息(Depth)。
- RSM则在此基础上,在生成Shadow Map的pass中,额外存储了更多有用的表面信息,例如:
- 世界坐标位置 (World Position)
- 表面法线 (Normal)
- 直接光照的颜色/通量 (Direct Illumination / Flux)
-
核心观点: RSM将传统的 Shadow Map 升级为了一个信息更丰富的 G-Buffer。讲者认为,称其为 “光照图” (Illumination Map) 或 “辐射度图” (Radiance Map) 可能比原名"Reflective Shadow Map"更能准确地描述其本质。这张图上的每一个像素,都可以被看作是一个微小的、发光的 虚拟点光源 (Virtual Point Light, VPL),它们是间接光照的来源。
2.3 算法流程:收集间接光照
有了这张从光源视角生成的“光照图”后,计算间接光照的流程变得直观:
- 生成RSM: 从主光源的视角渲染场景,将每个像素的 位置、法线、直接光照颜色 等信息存入一张或多张Render Target中。
- 主渲染Pass: 正常从相机视角渲染场景。
- 计算间接光: 对于屏幕上的每一个需要着色的点
x,遍历RSM中的所有像素(即所有虚拟点光源xp),将它们对x的光照贡献累加起来。
这个累加过程本质上是在做一个积分,模拟了点 x 从场景中所有被直接照亮的表面接收到的光线。
2.4 能量收集与公式解析
对于任意着色点 x,它从一个虚拟点光源 xp 接收到的辐射度(Radiance)贡献可以用以下概念公式表达:
-
: 存储在RSM中的来自
xp的光通量(Flux)。 -
:
xp点的BRDF,描述了光如何从xp散射到x。 -
: 几何项,主要包含了距离衰减(通常是距离的平方反比 )。
-
: 可见性函数(Visibility),判断
x和xp之间是否有遮挡。 -
一个有趣的技术细节(关于距离衰减的“四次方”之谜):
- 讲者提到,原论文中的公式看起来像是距离的四次方衰减(),这在物理上是反直觉的。
- 经过分析发现,这其实是一个实现上的技巧。公式中的向量
(xp - x)没有被归一化。 - 因此,
向量 / |向量|^4实际上等价于normalize(向量) / |向量|^2。 - 这样做可能是为了在Shader中避免一次开方和除法运算,直接使用
dot(v, v)的平方作为分母,从而提升性能。这对于引擎开发者来说是一个很好的启发。
2.5 性能瓶颈与优化思路
RSM的朴素实现存在一个致命的性能问题。
-
性能瓶颈: 对于屏幕上的每一个像素,都需要遍历RSM中的所有像素(例如 512x512 个VPLs)。这是一个
O(屏幕像素数 * RSM像素数)的算法,在实时渲染中是完全不可接受的。 -
早期的优化尝试:
- 为了解决这个问题,原论文提出了一种近似的优化方法。
- 它并非遍历所有VPL,而是从当前着色点
x出发,向随机方向进行类似 锥形追踪 (Cone Tracing) 的采样。 - 通过随机采样少数几个RSM中的VPL来估算总体的间接光照贡献。这种方法虽然在当时是一种“Hack”,但它开启了后续通过有限采样来近似积分的优化思路。
本部分小结: Reflective Shadow Map (RSM) 是实时GI领域的一块重要基石。它通过巧妙地扩展Shadow Map,将其变为一个存储了大量虚拟点光源的“光照图”,从而将复杂的间接光照问题转化为一个相对直观的“多点光源”问题。尽管其朴素实现存在严重的性能瓶颈,但它提出的 “从光源视角生成VPLs,再由场景点收集光照” 的核心思想,深刻地影响了后续十几年的实时GI技术发展。
从 RSM 到 LPV——间接光照的早期探索
在上一部分的基础上,我们继续深入探讨全局光照(GI)在实时渲染中的早期实现。本次笔记的核心是回顾 Reflective Shadow Maps (RSM) 的精妙思想,并引出下一个里程碑式的技术: Light Propagation Volumes (LPV)。
一、Reflective Shadow Maps (RSM) 的回顾与深度解析
RSM 不仅仅是一个简单的技术,它引入的几个核心思想对后来的实时 GI 算法产生了深远的影响。讲座中对其几个关键创新点进行了深入剖析。
1. 核心思想:用 Cone Tracing 模拟蒙特卡洛采样
RSM 的初衷是解决如何从每个像素点收集来自四面八方的间接光照。
- 核心观点: RSM 提出了一种近似的蒙特卡洛方法,通过在 RSM 贴图上进行随机采样,来模拟从场景中无数个微小光源(讲座中称为 "billions of lighthouses")接收光照的过程。
- 关键术语: Cone Tracing (锥体追踪)
- 这是一种 "hack" 式的实现,并非物理精确的光线追踪。
- 从着色点向 RSM 上的采样点发射一个锥体。锥体的扩散程度(大小)与采样点距离着色点的远近相关。
- 距离远的采样点,其代表的光源面积更大,因此锥体也更大,在计算中的权重也更高。
- 原论文指出,使用合适的分布方法(如泊松盘采样),大约 400 次采样 就能得到一个可接受的单次反弹(First Bounce)GI 效果。
2. RSM 的关键优化与思想演进
RSM 的原始思想虽然巧妙,但性能开销依然较大。后续的演进中,诞生了几个极其重要的优化思路。
优化一:利用 Mipmap 实现高效 Cone Tracing
- 核心观点: 可以对 RSM 纹理生成 Mipmap,从而根据材质的粗糙度进行重要性采样,极大地提升了 Cone Tracing 的效率和效果。
- 实现方式:
- 对于 高光/镜面反射 (Glossy) 的表面,需要精确的光照方向,因此在 Mipmap 的高层级(高分辨率) 进行采样,模拟窄锥体。
- 对于 漫反射 (Diffuse) 的表面,对光照方向不敏感,可以在 Mipmap 的低层级(低分辨率) 进行采样,模拟宽锥体,一次采样就能覆盖更大的区域。
优化二:利用间接光的低频特性进行稀疏采样
这是 RSM 时代一个非常了不起的发现,至今仍在现代 GI 方案中被广泛应用。
- 核心观点: 间接光照(Indirect Illumination)是低频信号。在屏幕空间中,相邻像素接收到的间接光照强度和颜色变化非常缓慢。
- 关键术语: 稀疏采样 (Sparse Sampling)
- 实现方式:
- 我们不需要为屏幕上的每一个像素都执行昂贵的 400 次采样。
- 可以在一个更低分辨率的网格上计算间接光照,例如 每 2x2 或 4x4 像素的区域只计算一次。
- 在最终着色时,每个像素通过插值其周围的低分辨率采样结果来获取间接光照。
- 深远影响: 这个思想是现代屏幕空间 GI 方案(如 Lumen 中的 Screen Probe)的基石。Lumen 之所以可以用 16x16 的 Probe 网格生效,其理论源头便在于此。
优化三:处理插值失效的 Fallback 机制
稀疏采样和插值虽然高效,但在某些情况下会产生明显的瑕疵(Artifacts)。
- 核心观点: 大部分像素通过插值都能获得良好结果,但总有少数像素会失效。我们只需识别出这些“坏点”,并为它们提供一种 备用(Fallback)方案。
- 失效场景:
- 采样点与当前像素的 空间位置(深度)差异过大。
- 采样点与当前像素的法线方向差异过大。
- 解决方案:
- 错误检查 (Error Check): 在插值前,检查周围采样点与当前像素的深度、法线等信息。
- 标记与补全: 如果判断为无效插值,则将该像素标记出来。
- 完整采样: 对这些被标记的、数量极少(可能仅占屏幕的 1‰)的像素,执行一次完整的、昂贵的 RSM 采样。
- 深远影响: 这种“大部分用快速近似,小部分用精确计算”的混合求解思路,同样是现代复杂渲染系统中(如 Lumen)处理各种边缘情况的核心策略。
3. RSM 的启发与历史意义
讲座作者对 RSM 给予了高度评价,认为它是一个极具启发性的工作。
- 主要贡献:
- 概念简单,易于理解。
- 首次在实时渲染领域实现了将光源信息“注入”到场景中,形成虚拟点光源(VPLs)的思想。
- 开创了在低分辨率屏幕空间中采集间接光,再通过插值和错误检查来重建高质量图像的范式。
- 局限性:
- 仅能处理单次反弹 (Single Bounce) 的光照。
- 采样过程中 不进行可见性检测 (Visibility Check),会导致漏光等问题。
二、Light Propagation Volumes (LPV) 的引入
RSM 解决了“光如何注入场景”的问题,但没有解决“光注入后如何在场景中传播和多次反弹”的问题。LPV 正是为了解决这个问题而诞生的。
- 核心思想: 将整个场景 体素化 (Voxelize),即划分成无数个三维格子(Volume)。光照的计算和传播都在这个体素网格中进行。
- 关键术语: Light Propagation Volumes (LPV),光照传播体积。
- 工作流程:
- 注入 (Injection): 使用类似 RSM 的方法,将直接光照产生的 VPLs 注入到对应的体素格子中。
- 传播 (Propagation): 模拟光在相邻体素之间的传递和扩散,实现多次反弹的效果。
- 收集 (Gathering): 最终渲染时,像素从其所在位置周围的体素中收集累积的光照信息。
LPV 的核心挑战:如何表示和传播光?
- 光的表示: 在每个体素中,光是从四面八方而来的,其分布可以用一个 球面函数 (Spherical Function) 来描述。
- 传播的难题: 直接存储和传递完整的球面函数非常复杂且耗费性能。如何用一种紧凑、高效的方式来表示和计算这个球面函数,并让它在体素网格中传播,是 LPV 算法的关键。
- 解决方案预告: 讲座在此处卖了个关子,引出了一个强大的数学工具—— 球谐函数 (Spherical Harmonics, SH),它将成为解决这个问题的钥匙。
早期全局光照的探索——从 LPV 到 SVOGI
在全局光照(Global Illumination, GI)的早期探索中,业界提出了多种创新思路来解决实时渲染中的间接光照问题。本次笔记将聚焦于两种具有里程碑意义的、基于体素(Voxel)的技术: 光线传播体积 (Light Propagation Volumes - LPV) 和 稀疏体素八叉树全局光照 (Sparse Voxel Octree Global Illumination - SVOGI)。
1. 光线传播体积 (Light Propagation Volumes - LPV)
LPV 是最早将空间体素化与球谐光照(Spherical Harmonics)结合用于实时 GI 的技术之一。它的核心思想非常朴素和直接。
核心观点
- 空间离散化: 将场景空间划分为一个 三维的均匀网格(Voxel Grid)。
- 光照信息的缓存: 在每个体素(Voxel)的中心,缓存该位置接收到的来自 四面八方的光照信息(Radiance)。
- 使用球谐函数 (Spherical Harmonics - SH) 编码:
- 直接存储球面上的无数个光线方向和强度是不现实的。
- LPV 巧妙地使用 SH 来编码每个体素中心点的球面光照分布函数。SH 只需要少量系数(通常是二阶,即9个浮点数),就能近似表示一个低频的球面函数,非常适合存储和计算漫反射等低频光照。
- 优势: SH 使得来自不同方向的光照贡献可以很方便地进行加权累加,最终用一组固定的 SH 系数来表示整个体素的光照环境。
光照传播 (Propagation)
- LPV 的关键步骤在于模拟光在体素网格中的传播。
- 其基本思想是,每个体素中的光照(以 SH 系数表示)会向其相邻的体素进行 扩散(Propagate)。
- 通过迭代这个扩散过程,光照效果就能从光源体素传播到整个场景。
讲座中的批判性思考与历史价值
- 物理准确性疑虑:
- 能量守恒问题: 讲座提到,LPV 的传播方式似乎不完全符合能量守恒定律。光线从一个体素传播出去后,该体素内部的能量是否会相应衰减,原文实现并不清晰。
- 有限光速的错觉: 迭代式的传播过程,其效果范围与迭代次数相关,给人一种“光在体素间以有限速度传播”的感觉,这与物理现实不符。
- 历史贡献 (Legacy):
- 开创性思想: LPV 是第一个提出将空间体素化用于缓存和传播光照信息的主流算法。
- SH 的应用: 它开创性地在体素中 使用 SH 存储光照分布,这个思想影响深远。这与后来 PBR 中广泛使用的 Image-Based Lighting (IBL) 技术异曲同工——将环境光(如天空盒)预计算成 SH,以便与 BRDF 高效地进行卷积计算。
小结: LPV 作为一个较早期的算法,虽然在物理精确性上存在一些模糊和争议,但它提出的“空间体素化 + SH 缓存”的核心框架,为后续的 GI 技术发展提供了宝贵的思路。
2. 稀疏体素八叉树全局光照 (SVOGI)
SVOGI 是 NVIDIA 在 LPV 之后提出的一个更“科学”、更精细的方案。它直面了 LPV 均匀网格带来的巨大浪费和精度问题。
核心观点
- 问题: 均匀的体素网格效率低下。场景中的大部分空间是空的,而物体表面才需要高精度的光照信息。分的太粗,细节丢失;分的太细,内存和计算开销爆炸。
- 解决方案: 使用稀疏的层级数据结构来表达空间,即 八叉树 (Octree)。
- 八叉树: 一种树状数据结构,每个节点可以分裂为八个子节点,对应于空间中的三个轴向的二分。
- 稀疏性 (Sparse): 只在有物体表面的地方进行细分,在空旷区域则使用非常粗糙的父节点来表示,极大地节省了存储空间。
关键技术细节
-
场景体素化 (Voxelization)
- 为了将场景中的三角面片精准地转换到八叉树结构中,SVOGI 使用了一项关键硬件特性: 保守光栅化 (Conservative Rasterization)。
- 作用: 保证一个三角形哪怕只覆盖了像素的一小部分,这个像素也会被判定为被覆盖。在体素化应用中,这意味着 即使是非常薄或微小的三角形,也至少能被映射到一个体素中,确保了场景几何的完整性,不会出现“漏掉”的表面。
-
创新的光线追踪方式:圆锥追踪 (Cone Tracing)
- 这是 SVOGI 最具启发性的思想,极大地提升了查询效率和质量。
- 传统光线追踪 (Ray Tracing): 投射一根无限细的射线,一次只能查询一个点的信息。为了抗锯齿和获得柔和效果,需要投射大量光线。
- 圆锥追踪 (Cone Tracing):
- 从着色点出发,沿着特定方向(如法线方向、反射方向)投射一个 圆锥体 (Cone)。
- 这个圆锥体会随着距离的增加而变粗,这与八叉树的层级结构完美契合:
- 在近处,圆锥较细,会与八叉树中 精细的、高分辨率的叶子节点 相交,获取精确的局部光照。
- 在远处,圆锥变粗,会与八叉树中 粗糙的、低分辨率的父节点 相交。这些父节点本身就存储了其子空间内光照的平均值或聚合值。
- 优势: 一次 Cone Tracing 就相当于一次带范围查询的、具有 LOD (Level-of-Detail) 特性的采样。它用极少的采样次数,就能获得一个经过预过滤的、柔和且无锯齿的光照结果,效率极高。
讲座中的批判性思考与历史价值
- 实现的复杂性:
- 惊人的数据结构: SVOGI 的八叉树节点不仅存储自身信息,为了方便进行三线性插值(Trilinear Interpolation)和滤波,它还缓存了周围邻居节点的信息(例如,将 1x1x1 的查询扩展到 3x3x3 的邻域)。
- GPU 不友好: 在当时的 GPU 架构下(约2011-2012年),这种 依赖指针/索引进行层层跳转 的树状结构(Pointer Chasing)性能非常差,难以高效实现。
- 历史贡献 (Legacy):
- 稀疏八叉树的应用: SVOGI 证明了稀疏八叉树是表达场景空间信息的强大工具,这一思想在现代引擎(如 Lumen)中被广泛采纳和发扬。
- Cone Tracing 思想: Cone Tracing 作为一种高效的、利用层级结构进行范围查询的理念,对后来的光线追踪、环境光遮蔽等诸多技术产生了深远影响。
小结: SVOGI 尽管因其在当时硬件上的实现过于复杂而未能普及,但它提出的 “稀疏八叉树” 和 “圆锥追踪” 两大核心思想,展现了解决实时 GI 问题的正确方向,其理念至今仍在现代渲染技术中闪耀。
Voxel Global Illumination (VXGI) 与 Clipmap
在探讨了多种全局光照(GI)技术后,本次讲座的重点转向了在业界产生深远影响的 Voxel Global Illumination (VXGI)。这一部分的核心在于解释为什么 VXGI 能够取代像稀疏体素八叉树(SVO)这样的复杂方案,并深入剖析其背后的核心数据结构与算法思想。
一、 从 SVO 到 VXGI:一场思想的革命
1.1 为什么淘汰了稀疏体素八叉树 (SVO)?
讲座首先回顾了早期基于 SVO 的 GI 方案(如 SVOGI),并指出了其被业界放弃的核心原因:
- GPU 不友好:SVO 是一种指针式的数据结构,包含大量的索引跳转(一层 index 指向另一层 index),这在 GPU 这种大规模并行处理架构上效率低下,难以优化。
- 数据结构复杂:为了保证数据一致性,每个节点(Node)可能需要存储其邻居的冗余数据,导致管理和更新变得极为复杂和 hacky。
- 全场景表达的负担:SVO 的设计初衷是表达整个场景,这种“一视同仁”的思路对于实时渲染来说,开销巨大且没有必要。
1.2 VXGI 的核心思想:拥抱 视点依赖 (View-Dependent)
VXGI 的出现彻底改变了游戏规则,其核心思想转变是: 从“表达整个世界”转变为“只精细表达我所看到的”。
- 核心观点:对于实时渲染而言,离摄像机近的区域远比远的区域重要。我们只需要对近处进行高精度采样,远处则可以接受较低的精度。
- 优势:这种方法极大地减少了需要处理的数据量,完美契合了 GPU 的工作模式,实现起来也更清晰、简单、直接。
二、 核心数据结构:体素 Clipmap
为了实现视点依赖的体素化,VXGI 引入了 Clipmap 这一经典思想,并将其应用于三维空间。
2.1 Clipmap 的直观理解
讲座中提到了一个非常形象的比喻来解释 Clipmap:
Clipmap 就像用一个回形针(Clip)将一沓大小不一的纸(Map)别在一起。 最上面的是一张名片大小的图(最高精度),下面依次是A5、A4纸大小的图(精度递减)。你一提溜,就能把整个层级结构(LOD)都带走。
这个结构的核心是 一组以摄像机为中心、分辨率不同、覆盖范围递增的嵌套网格。
2.2 Voxel Clipmap 的工作原理
在三维空间中,Voxel Clipmap 表现为一系列嵌套的 3D Voxel Grid。
- 近处高精度,远处低精度:
- Level 0 (最近): Voxel 尺寸可能为 0.5m x 0.5m x 0.5m,覆盖相机周围几十米的范围。
- Level 1: Voxel 尺寸变为 1m x 1m x 1m,覆盖更远的范围。
- Level 2: Voxel 尺寸变为 2m x 2m x 2m,覆盖范围再翻倍。
- ...以此类推。
- 视点依赖的密度分布:这种结构天然形成了一种 以观察者为中心的、由近及远、由密到疏的体素密度分布。
- 屏幕空间感知:虽然远处的 Voxel 尺寸很大,但由于透视效应,它们在屏幕上占据的像素面积与近处的小 Voxel 看起来相差不大,从而在视觉上保持了相对一致的观感。
2.3 应对相机移动: 循环寻址技巧 (Wrapping Addressing)
一个关键问题是:当相机移动时,整个 Clipmap 需要跟随移动,如果直接在显存中进行大规模数据拷贝,开销将是毁灭性的。
-
解决方案:采用一种 循环寻址 (或环形缓冲区) 的技巧。在将世界坐标
(x, y, z)映射到 3D 纹理的(u, v, w)坐标时,使用取模运算:// 伪代码 tex_coord.x = world_coord.x % texture_width; tex_coord.y = world_coord.y % texture_height; tex_coord.z = world_coord.z % texture_depth; -
核心优势:当相机移动时,大部分已有的体素数据在 3D 纹理中的相对位置保持不变。我们 只需要更新因相机移动而“卷入”可视范围的新区域,而无需移动整个数据块。这极大地降低了更新开销,是实现高效动态场景的关键。
三、 VXGI 的渲染流程
3.1 Voxelization:构建体素场景
在渲染开始前,需要先将场景几何体“光栅化”到 Voxel Clipmap 结构中。
- 体素存储内容:每个 Voxel 存储的不是简单的“有/无”二进制信息,而是更复杂的 方向性不透明度 (Directional Opacity)。
- 核心观点:一个 Voxel 内部可能只被物体部分占据。因此,需要计算光线从不同方向(如X/Y/Z三个主轴)穿过该 Voxel 时被遮挡的比例。例如,从左右看遮挡了30%,从上下看遮挡了70%。
- 作用:这使得光线追踪时可以实现半透明的遮挡效果,光线可以穿过部分被占据的 Voxel 继续向后传播,这对于模拟光线在复杂几何体(如树叶)中的散射至关重要。
3.2 渲染管线:两步走
VXGI 的 GI 计算主要分为两个核心阶段:
第一步:光照注入 (Light Injection)
- 目标:将直接光照信息“注入”到 Voxel Clipmap 中,使其本身变成一个包含场景光照信息的“3D灯光贴图”。
- 方法:通常使用 Reflective Shadow Maps (RSM) 等技术。计算出场景中哪些 Voxel 能够被光源直接照射到,并将接收到的光照颜色和强度(Radiance)存入对应的 Voxel 中。
- 结果:此时,Voxel Clipmap 中只有被直接光照亮的 Voxel 存储了有效的 Radiance 值,其他处于阴影中的 Voxel 则是暗的。
第二步:光照收集 (Light Gathering) 与 Cone Tracing
-
目标:对于屏幕上的每一个像素,计算其接收到的间接光照。
-
方法:从像素对应的世界坐标点出发,向 Voxel Clipmap 中发射若干个 光锥(Cone) 来收集光照,这个过程被称为 Cone Tracing。
-
材质依赖的 Cone Tracing:Cone 的形态和方向取决于表面材质属性:
- Diffuse 表面:发射一个宽角度的 Cone,方向大致沿着表面法线,以模拟从四面八方接收漫反射光照。
- Specular 表面:发射一个窄角度的 Cone,方向沿着视线的反射方向,以模拟镜面反射。
- Roughness (粗糙度):表面的粗糙度决定了 Cone 的张开角度。 越粗糙,Cone 越宽;越光滑,Cone 越窄。
-
与 Clipmap 的完美结合:Cone Tracing 与 Clipmap 的多级分辨率结构是天作之合。
- 当 Cone 沿着其路径前进时,它会不断变粗。
- 在靠近起始点时,Cone 较细,可以从 Clipmap 的高精度层级(Level 0, 1)中采样。
- 随着距离增加,Cone 变粗,此时可以自然地切换到 Clipmap 的低精度层级(Level 2, 3...)进行采样。
- 这是一种 高效的、近似的、硬件友好的范围查询,完美地模拟了不同粗糙度下的光照积分,同时利用了LOD结构来大幅提升性能。
高级全局光照技术 - 从体素到屏幕空间
本部分承接上一部分对 Voxel Cone Tracing 的讨论,深入探讨了其针对不同表面材质的优化、核心技术挑战,并引入了一种全新的、思路截然不同的技术:屏幕空间全局光照 (SSGI)。
一、Voxel Cone Tracing 的深化与实践 (VXGI)
VXGI (Voxel Global Illumination) 可以看作是基于体素的 GI 思路的一个集大成者。它通过对基础的 Voxel Cone Tracing 进行优化,使其能够处理更复杂的现实世界光照现象。
1. 模拟可变粗糙度的锥形追踪 (Cone Tracing for Variable Roughness)
-
核心观点:为了模拟不同光滑度的表面反射, Cone Tracing 的“锥形”大小(立体角)需要动态调整。
- 光滑表面 (Specular):对于镜面反射,光线能量集中。此时,追踪的“锥形”应该非常窄,几乎等同于一条光线,以获取高频、清晰的间接光照细节。
- 粗糙表面 (Diffuse):对于漫反射,光线能量发散。此时,追踪的“锥形”应该非常宽,以在更大范围内采样,获得柔和、模糊的间接光照。
-
关键技术:利用层级化体素结构 (Clipmap/Mipmap)
- VXGI 利用了体素结构的 层级特性 (Mipmap) 来高效实现可变宽度的锥形追踪。
- 当需要一个窄锥形时,算法会在 精细的、高分辨率的 Mipmap 层级 中进行追踪。
- 当需要一个宽锥形时,算法会切换到 粗糙的、低分辨率的 Mipmap 层级 进行追踪,这样一次采样就能覆盖更广阔的空间区域,天然地实现了模糊和过滤效果。
2. 沿光线路径的透明度累积 (Opacity Accumulation)
- 核心观点:在 Cone Tracing 过程中,光线并不会在击中第一个非空体素后就停止,而是会 模拟光线穿透半透明介质的过程,持续累积不透明度 (Opacity)。
- 类比有序透明度渲染:这个过程非常类似于渲染一串前后有序的透明物体。光线每穿过一个体素,就根据该体素的 Alpha/Opacity 值,将后方传来的光线能量进行衰减,并叠加上当前体素贡献的光照。
- 算法流程:
- 光线沿方向步进,采样路径上的体素。
- 将每个体素贡献的 Radiance 与其 Opacity 相乘。
- 累积路径上所有体素的 Opacity。
- 当 累积的 Opacity 达到或超过一个阈值 (例如接近 1.0,代表完全不透明)时,追踪提前终止,以节省性能。
3. VXGI 的优势与地位
- 实装价值:VXGI 是一个经过验证、可以实际部署在产品中的算法(例如,曾作为虚幻引擎的插件存在)。这证明了它在效果和性能之间取得了良好的平衡。
- 技术迭代:在讲者看来,VXGI 是对早期更复杂的层级体素 GI 方案(如 SVOGI)的一种有效替代。它在概念上更清晰,工程实现上可能更具优势。
4. 核心挑战:光线泄漏 (Light Leaking)
- 核心观点:光线泄漏是所有基于空间离散化(如体素化)的 GI 算法的天生顽疾。
- 产生原因:
- 体素化近似:将连续的几何体(如三角形)量化到离散的体素网格中,会丢失精度。一个体素可能只被一个薄薄的表面部分占据,但整个体素会被赋予一个平均的 Opacity 值,这个值通常小于 1。
- 遮挡信息丢失:光线在追踪时,无法知道体素内部的精确遮挡情况。即使在现实中光路已被完全阻挡,但在体素世界里,光线仍可能因为一系列 Opacity 小于 1 的体素而“泄漏”过去。
- 常见场景:
- 薄墙或薄片物体:当物体的厚度小于一个体素的尺寸时,光线泄漏尤为严重。
- 远距离物体:物体在距离摄像机很远时,会被映射到更粗糙的 Mipmap 层级,导致其体素化表示非常不精确,更容易发生泄漏。
二、屏幕空间全局光照 (Screen-Space Global Illumination - SSGI)
在探讨完以体素为代表的“世界空间”GI 方案后,讲座转向了另一条截然不同的技术路线——在“屏幕空间”中计算 GI。
1. 核心思想:利用屏幕像素作为光源
- 核心观点:SSGI 的思想极其淳朴和巧妙—— 将当前屏幕上已经渲染好的、可见的像素,直接当作间接光照的微小光源 (Virtual Light Sources)。
- 思想来源:它源于 屏幕空间反射 (Screen-Space Reflection, SSR) 的扩展。SSR 能为镜面材质计算反射,它只追踪一条反射光线。SSGI 则将这个想法泛化,为一个点向多个方向(例如,沿法线方向的半球)发射光线,收集来自屏幕上其他像素的“光照贡献”。
- 提出者:该技术最早由寒霜(Frostbite)引擎在 2015 年 GDC 上提出。
2. 基本原理与实现
- 输入数据:渲染完成的场景颜色缓冲 (Color Buffer) 和深度缓冲 (Depth Buffer / G-Buffer)。
- 计算过程:对于屏幕上的每一个需要计算间接光的像素点 P:
- 在其法线方向的半球内,发射若干条采样光线。
- 每一条光线都在 屏幕空间(实际上是在视图空间,通过深度图投影) 中进行步进 (Ray Marching)。
- 如果光线步进过程中,找到了一个交点 Q(其深度值表明它在光线路径上),则将 Q 点在颜色缓冲中的颜色值,作为贡献给 P 点的间接光照。
- 累加所有采样光线的贡献,得到 P 点最终的间接光照颜色。
3. 屏幕空间中的光线步进 (Ray Marching in Screen Space)
如何在屏幕空间高效地寻找光线与场景的交点,是 SSGI 的技术关键。
-
朴素方法:固定步长的光线步进 (Fixed-Step Ray Marching)
- 从起始点出发,沿着光线方向,每次前进一个固定的步长。
- 在每个步进点,比较当前光线的深度与该屏幕位置上深度缓冲记录的场景深度。
- 如果光线深度大于场景深度,说明光线已经“穿过”了场景表面,即找到了一个有效的交点。
- 缺点:步长太长容易漏掉交点(“stepping over”),步长太短则性能开销巨大。
-
高效加速:层级深度缓冲 (Hierarchical-Z Buffer - Hi-Z)
- 核心观点:Hi-Z 是一种利用图像金字塔 (Mipmap) 思想来加速光线步进的技术。
- Hi-Z 构建:
- 对原始的深度缓冲(Z-Buffer)生成一系列 Mipmap。
- 与普通 Mipmap 不同的是,在降采样时,每个父层级的像素值 不是子层级四个像素的平均值,而是最小值 (Min) 或最大值 (Max)。在光线步进中,通常使用最小值,代表该区域内离摄像机最近的深度。
- 加速原理:
- 光线步进从 最粗糙的 Hi-Z Mipmap 层级 开始。
- 如果当前光线段完全没有与某个粗糙层级像素所代表的区域(一个大的屏幕矩形及其深度范围)相交,那么就可以安全地跳过这一大片区域,从而实现大幅加速。
- 如果相交,则下降到更精细的 Mipmap 层级,重复此过程,直到在最精细的层级找到精确的交点。
- 本质:这是一种经典的空间层次结构加速思想,将原本需要逐像素进行的相交测试,变为了高效的、由粗到细的层级测试。
屏幕空间全局光照 (SSGI) 的加速与优化
本部分内容深入探讨了屏幕空间全局光照(SSGI)中一项至关重要的加速技术—— Hierarchical-Z (Hi-Z) Ray Marching,并引申出实时GI中常用的 采样点复用 (Sample Reuse) 思想。这些技术不仅极大地提升了SSGI的性能,也为后续理解更复杂的系统(如Lumen)奠定了基础。
一、 Hierarchical-Z (Hi-Z) Ray Marching: 加速屏幕空间追踪
传统的屏幕空间光线步进(Ray Marching)采用均匀步长,在需要跨越较大空旷区域时效率低下。为了解决这个问题,业界引入了基于层级深度图的加速方案。
核心观点
Hi-Z (Hierarchical-Z) 本质上是深度缓冲 (Depth Buffer) 的 Mipmap。它通过构建一个层级结构,使得光线步进可以根据当前位置与场景几何体的距离,动态地调整步进的长度,从而实现快速的远距离穿越和精细的近距离检测。
关键术语与实现
-
Hi-Z 的构建:
- 这是一个多层级的纹理结构,每一层都是下一层分辨率的一半。
- 与普通的Mipmap不同,Hi-Z的生成规则是: 每一层级的像素存储其对应的下一层级(更精细层级)中四个像素的最小深度值(最靠近相机的值)。
- 这使得每一层级的Hi-Z都构成了其下一层级几何体的一个 保守的深度边界(Conservative Depth Bound)。如果一条光线没有与高层级的Hi-Z深度相交,那么它也一定不会与该区域内任何更精细的几何体相交。
-
加速光线步进 (Accelerated Ray Marching) 的流程:
- 起始: 从一个较小的步长开始。
- 加速: 如果当前光线位置未与场景相交,则 提升到Hi-Z的更高层级(更粗糙的层级) 进行深度测试。例如,在Mip 1级测试,步长相当于原始空间的2个像素;在Mip 2级测试,步长相当于4个像素。这样可以实现步长的指数级增长,快速跳过大片空白区域。
- 命中与细化: 一旦光线在某个较粗糙的层级(如Mip 2)上检测到相交,说明光线进入了某个几何体的大致范围。
- 回退与精调: 此时,将光线 回退一步,并降低到Hi-Z的更低层级(更精细的层级),如Mip 1或Mip 0,进行更精确的步进,直到找到准确的交点。
-
性能优势:
- 这种自适应步长的策略,将传统线性复杂度的Ray Marching优化为了对数复杂度。
- 复杂度:
其中 N 是光线需要行进的屏幕空间像素距离。相比之下,Uniform Ray Marching 的复杂度为 。这意味着即使在高分辨率下,Hi-Z也能在十几步内完成远距离追踪,而均匀步进可能需要数十甚至上百步。
二、 采样点复用 (Sample Reuse): 空间与时间的效率提升
在实时全局光照中,为屏幕上的每一个像素发射大量的采样光线是性能的巨大瓶颈。因此,复用计算结果成为了一个至关重要的优化方向。
核心观点
相邻像素的采样结果可以被共享和复用。在全局光照的语境下,所有被照亮的表面都可以看作是无数个微小的光源(讲座中称为“小灯泡”)。如果一个像素A通过光线追踪找到了远方的一个“小灯泡”,那么对于它旁边的像素B而言,这个“小灯泡”同样是一个有效的光源信息,可以被直接利用,从而避免了重复的光线追踪开销。
实现要点
- 复用什么: 主要复用的是 命中点的位置 (Hit Point Position) 和 其自身的辐射度 (Radiance)。
- 如何复用: 当像素B要复用像素A找到的光源信息时,它不能简单地直接使用其光照贡献。因为B点的位置、法线、材质(BRDF)都与A点不同。正确的做法是:
- 获取A命中的光源位置。
- 在B的着色计算中,将该光源视为一个直接光源,根据B点自身的信息(法线、观察方向、BRDF等)重新计算其光照贡献。
- 扩展: 这种复用思想不仅限于 空间维度(相邻像素),也可以扩展到 时间维度(Temporal Reuse,即复用前一帧的结果),是几乎所有实时GI方案都会采用的核心优化策略。
三、 SSGI 算法总结:优势与局限
讲座中提到的基于Hi-Z加速的SSGI方法,是一个非常实用且强大的技术,其优缺点非常鲜明。
核心优势
- 高质量的接触阴影 (Contact Shadows): 由于Hi-Z提供了高精度的深度信息,SSGI能够非常准确地处理物体接触面或狭窄缝隙中的遮蔽关系,效果远超传统的基于探针(Probe-based)或体素(Voxel-based)的方法。
- 精确的命中点 (Accurate Hit Points): 相比于一些近似的GI方案,基于Hi-Z的追踪能找到更准确的几何交点。
- 场景复杂度无关 (Scene Complexity Agnostic): SSGI完全在后处理阶段对G-Buffer进行操作,它不关心场景的几何复杂度(例如,是否使用Nanite)、材质的复杂性或物体的数量。它眼中只有一张包含了深度、颜色、法线等信息的“小灯泡图”。
- 原生支持动态物体 (Handles Dynamic Objects): 因为每一帧都基于最新的G-Buffer进行计算,所以场景中的动态物体及其产生的间接光照效果可以被自然地处理。
主要局限
- 屏幕空间依赖 (Screen-Space Dependency): 这是所有SS(Screen-Space)技术的通病。 任何在屏幕上不可见的物体,都无法对间接光照做出贡献。
- 典型Artifact: 一个悬浮在地面上方的物体,其底面的反射在地面上是无法被正确计算的,因为摄像机看不到那个底面,导致G-Buffer中没有对应信息。
四、 承前启后:理解 Lumen 的基础
讲座花大量篇幅介绍SSGI等前期技术,是为了构建一个完整的知识体系。理解这些技术的优缺点,是理解Lumen为何如此设计的关键。
- Lumen是集大成者: Lumen的设计融合了多种技术的思想,它的许多复杂设计都是为了 解决像SSGI这类先前算法的局限性(如屏幕空间依赖问题),同时借鉴其成功之处(如处理高频细节和动态物体的能力)。
- 工程实现的挑战: 将一个科研算法转化为一个能在复杂游戏场景中稳定运行、效果鲁棒的核心引擎特性(Feature),其难度和工作量是巨大的。Lumen正是经历了多年持续迭代和优化的成果。
通过学习这些前导知识,我们能更好地欣赏和理解Lumen在设计上的权衡与创新。
Lumen - 核心思想与光线追踪加速
在本节中,讲座的重心正式转向了 Unreal Engine 5 的核心全局光照(GI)技术——Lumen。内容首先剖析了 Lumen 诞生的背景,即实时光线追踪(Real-Time Ray Tracing)在实践中遇到的瓶颈,然后阐述了 Lumen 解决问题的核心思想,并深入讲解了其技术实现的第一阶段:如何利用 SDF (Signed Distance Fields) 在任意硬件上实现高效的光线追踪。
一、 Lumen 的设计动机:为什么不用纯硬件光线追踪?
在硬件光线追踪(RTX/RDNA)日益普及的今天,一个核心问题是:为什么还需要 Lumen 这样复杂度的软件解决方案?讲座给出了几个关键原因:
-
极高的性能开销:要达到高质量的间接光照效果,尤其是在室内场景,可能需要极高的光线采样率(例如,讲座中提到理想情况需要每像素 500 条光线)。而实时渲染预算极为有限,通常只能负担得起 每像素半条光线(0.5 ray/pixel)。这种巨大的差距使得纯硬件光线追踪难以在性能和质量之间取得平衡。
-
广泛的硬件兼容性:Lumen 的设计目标之一是能够在各种硬件上运行,包括那些不支持或支持不佳硬件光追的平台(如旧款显卡和部分主机模式)。它需要一套不完全依赖于特定硬件特性的方案。
-
棘手的采样与降噪问题:在极低采样率下,光线追踪会产生严重的 噪点 (Noise)。当采样点远离光源时(例如,在房间深处远离窗户),噪点会演变成大片的 色块 (Color Splotches),即使是先进的降噪器(Denoiser)也无力回天。这是实时路径追踪面临的普遍且根本性的难题。
核心观点: Lumen 的诞生并非为了取代硬件光追,而是为了在当前硬件性能和算法限制下,提供一个更具普适性、性能更可控的高质量动态全局光照解决方案。
二、 Lumen 的核心思想:Screen Space Probes
Lumen 的核心思想可以概括为一种巧妙的“降维打击”:它认识到间接光照(Indirect Lighting)本身是低频信息,不需要在全分辨率下进行昂贵的计算。
其核心工作流如下:
- 在屏幕空间放置探针 (Probes):Lumen 不在世界空间中预设光照探针,而是在 屏幕空间(Screen Space) 的稀疏网格上(例如每 16x16 像素)放置探针。
- 探针紧贴物体表面:这些探针会“吸附”到其所在像素对应的场景几何体表面,从而精确地捕获该位置的光照信息。
- 稀疏采样,全分辨率重建:Lumen 对这些稀疏的探针进行光线追踪,计算它们接收到的间接光。然后,在全分辨率渲染时,每个像素会根据自身位置、法线等信息,智能地从周围的探针中插值获取光照。
- 结合高频细节:最终,将这个相对平滑的低频间接光照与物体表面的高频细节(如法线贴图、材质属性)结合,从而在视觉上重建出细节丰富、物理正确的全局光照效果。
核心观点: 通过在屏幕空间对场景表面进行稀疏光照采样,再将低频的光照结果与高频的几何细节在全分辨率下进行重建,Lumen 在性能和质量之间取得了绝佳的平衡。
三、 Lumen 技术拆解: 基于 SDF 的快速光线追踪
为了实现上述思想,Lumen 必须能快速回答一个基本问题:“从一个点射出一条光线,它会打到什么?” 这就是光线追踪。为了不依赖硬件光追,Lumen 采用了一套基于 符号距离场 (Signed Distance Fields, SDF) 的软件光线追踪方案。
1. 关键术语:SDF (Signed Distance Field)
SDF (符号距离场) 是一种空间数据结构,它描述了空间中任意一点到某个几何体表面的最近距离。
- 符号 (Sign) 的含义:
- 点在物体外部,距离为正。
- 点在物体表面,距离为零。
- 点在物体内部,距离为负。
2. SDF 的重要性:几何体的“对偶表示”
讲座特别强调了 SDF 在现代图形学中的重要性,并将其称为传统三角网格(Triangle Mesh)的一种 对偶表示 (Dual Representation)。
| 特性 | 传统三角网格 (Triangle Mesh) | 符号距离场 (SDF) |
|---|---|---|
| 表示方式 | 离散的 (Discrete):由点、线、面构成,需要索引缓冲来建立拓扑关系。 | 连续的 (Continuous):是一个定义在整个空间中的场函数,天然包含体积信息。 |
| 数据结构 | 非均匀,处理邻接关系复杂。 | 均匀 (Uniform),易于在 GPU 上处理。 |
| 数学性质 | 几何处理复杂,不易求导。 | 可微的 (Differentiable),这为现代基于梯度的优化算法(如神经渲染)打开了大门。 |
核心观点: SDF 将几何体的描述从离散的表面片元转换为了连续的空间场,这种数学上的“升维”带来了优良的性质(如连续性、可微性),使其成为现代渲染引擎中一个极具潜力的基础构建模块。
3. Lumen 中的 SDF 应用:Per-Mesh SDF
直接为整个庞大的游戏场景生成一个全局 SDF 是不现实的,因为其内存开销和计算成本会是天文数字。因此,Lumen 采用了一种更务实的工程方案:
- Per-Mesh SDF:Lumen 不为整个场景,而是为 场景中的每一个独立的网格(Mesh) 在预处理阶段生成一个 SDF。这些 SDF 存储在各自的局部坐标系中。
- 动态组合:在运行时,通过坐标变换,这些独立的 Per-Mesh SDF 可以被组合起来,共同构成整个场景的距离场表达,从而实现对动态场景的高效光线步进(Ray Marching)。
这个阶段为 Lumen 提供了在任何硬件上都能运行的、高效的软件光线追踪能力,是其后续所有光照计算的基石。
符号距离场 (SDF) - 场景表达与光线追踪的利器
本部分深入探讨了 符号距离场 (Signed Distance Fields, SDF) 作为一种强大的场景表达方式,及其在现代实时渲染中的核心应用,尤其是在光线追踪和全局光照领域的价值。
一、SDF 的基本理念:从单个物体到整个场景
传统方法是为整个场景空间建立一个巨大的SDF,但这会浪费大量空间存储无效信息(空旷区域)。一个更高效、更直观的思路是模块化处理。
-
核心观点:基于图元 (Per-Object) 的SDF组合
- 我们不为整个场景直接生成一个SDF,而是为场景中的 每一个独立的Mesh(物体)生成其自身的局部SDF。
- 一个复杂的游戏场景通常由成百上千个物体构成,但这些物体往往是少量基础模型(例如,几种树、几种石头)通过 平移、旋转和缩放 变换(Transform)实例化而来的。
- 因此,我们只需要存储那几百个 基础模型的SDF,再加上每个实例的变换信息,就可以 动态地、高效地重建出整个场景的SDF。
-
关键技术细节与挑战
- 变换处理:平移、旋转和 等比缩放 (Uniform Scaling) 可以很直接地应用到SDF上,数学变换简单。
- 非等比缩放 (Non-Uniform Scaling):这是一个棘手的数学问题。如果一个物体在X、Y、Z轴上进行了不同比例的拉伸,其SDF的精确表达会变得非常复杂,是当前研究中的一个难点。
二、SDF 的工程实践与核心优势
一旦拥有了场景的SDF表达,许多复杂的图形学问题都将迎刃而解。
1. 处理极薄物体 (Handling Thin Geometry)
- 问题: SDF本质上是对空间进行离散采样。如果一个物体的表面(如一张纸、一面墙)比两个采样点之间的最小距离还要薄,SDF采样可能会完全“错过”这个物体,导致其两侧的distance值都为正,从而无法检测到该表面。
- 工程解法: 在生成SDF时, 人为地将过薄的物体“撑开”一点,确保它至少能跨越一个采样单元的厚度,从而保证其能够被SDF正确地表达和检测到。这是一种“瑕不掩瑜”的实用技巧。
2. 高效的光线步进 (Efficient Ray Marching)
SDF彻底改变了在空间中进行光线追踪的方式,解决了传统光线步进(Ray Marching)最大的难题——步长选择。
-
核心算法:球体步进 (Sphere Tracing)
- 从光线上的一个点
p出发,查询该点的SDF值d = SDF(p)。 - 这个值
d的物理意义是:从点p到场景中最近表面的距离。 - 因此,我们可以沿着光线方向安全地前进
d的距离,而不会穿过任何物体。 - 这个过程不断迭代,光线会以指数级速度快速逼近物体表面。
// 伪代码 p_next = p_current + ray_direction * SDF(p_current) - 从光线上的一个点
-
关键优势
- 高效 (Efficient): 相比固定步长或基于包围盒的试探,SDF的步进非常快,尤其是在开阔空间,一步就可以跨越很长的距离。
- 鲁棒 (Robust): 即使某一步由于精度问题稍微“走过头”进入了物体内部,SDF值会变为负数。这个负值可以指导光线“弹回”或通过更精细的步进(如二分法)精确找到交点,不易出错。
3. 锥体追踪与软阴影 (Cone Tracing & Soft Shadows)
SDF不仅能处理线性的光线,还能很好地近似处理锥形的范围查询。
- 核心思想: 在进行光线步进时,每个点
p的SDF值d不仅定义了安全步长,还定义了一个半径为d的空心球。 - 应用:软阴影近似
- 当计算某点到面光源的遮挡时,我们可以从该点向光源方向步进。
- 在步进过程中的每一点,SDF值
d可以用来估算在该距离上,多大范围的锥体是未被遮挡的。 - 通过比较这个“空心锥体”和光源的张角,可以得出一个遮挡比例的近似值,从而实现效果不错的软阴影。
- 虽然这个近似在物理上并不精确,但在实时渲染中是一个效果好且性能高的方案。
4. 强大的数学属性
-
法线计算 (Normal Calculation): SDF是一个在空间中连续可导的函数。它的 梯度 (Gradient) 方向就是该点表面的法线方向。这意味着我们无需存储法线信息,可以在任意表面点上即时、精确地计算出来。
-
统一的隐式表达 (Implicit Representation): SDF用一个统一的、连续的函数表达了一个无限精度的表面,集距离、内外判断、法线信息于一身,避免了传统Mesh表达的离散化问题。
三、SDF 的存储优化与LOD
-
稀疏化存储 (Sparse Representation)
- 在SDF体素网格中,大部分空间是远离物体表面的“空旷”区域。
- 我们可以设定一个距离阈值,将所有SDF值大于该阈值的体素标记为空,从而不必存储这些数据,极大地减少了内存占用。
- 潜在权衡: 稀疏化可能会影响光线步进的效率。原本可以一步跨越的巨大空旷区域,现在可能因为数据结构的限制需要分多步进行查询和跳转。
-
天然的LOD支持 (Level of Detail)
- SDF是基于体素网格的,为其生成不同细节层次(LOD)非常简单,本质上就是对三维数据进行 降采样 (Downsampling)。
- 这远比传统的 网格简化 (Mesh Simplification) 算法(如Simplygon)要简单和高效得多。
- 结合流式加载(Streaming),可以实现远处物体使用低精度SDF,靠近时无缝切换到高精度SDF,有效控制显存消耗。
四、全局SDF:应对复杂场景的加速结构
SDF的强大能力使其成为UE5 Lumen 等现代实时全局光照系统的基石,尤其是在没有硬件光追或硬件性能不足时,SDF提供了一个高质量的软件光追方案。
-
面临的挑战: 在一个包含成千上万个物体的复杂场景中,如果让一条光线去依次测试与每一个物体的SDF求交,计算开销将是灾难性的。
-
解决方案:全局SDF (Global SDF)
- 将所有 独立的、高精度的Per-Object SDF, 合并(Composite) 到一个覆盖整个场景的、 低精度的全局SDF 中。
- 这个全局SDF就像一个宏观的加速结构。光线首先在低精度的全局SDF中进行快速步进,快速跳过大片空白区域,直到接近某个物体表面。
- 当光线步进到全局SDF的物体附近时,再切换到该区域对应的高精度Per-Object SDF进行精确的求交计算。
-
遗留问题:动态更新 (Dynamic Updates)
- 当场景中的物体移动、增加或删除时,如何高效地 更新这个全局SDF 是一个巨大的挑战,涉及到复杂的算法和数据结构设计,是该技术领域的一个核心难题。
全局距离场 (Global SDF) 与 Lumen 的混合加速结构
在前面的部分我们已经了解了如何为单个网格(Mesh)生成SDF。这一部分将深入探讨 Lumen 是如何将这些独立的SDF整合成一个覆盖整个场景的 全局SDF (Global SDF),并以此作为不依赖硬件光追的、高效的光线投射加速结构。
一、 全局SDF:从独立物体到整个场景
核心观点:将场景中所有物体的独立 网格SDF (Mesh SDF) 合成一个统一的、覆盖整个场景的 全局SDF,是实现高效软件光线追踪的关键。
-
合成与更新 (Composition & Update)
- 合成过程:通过一系列数学变换,将每个物体的局部空间SDF融合到世界空间的全局SDF网格中。这本质上是在一个巨大的三维网格(Volume Texture)中,为每个体素(Voxel)计算其到场景中最近表面的距离。
- 动态场景处理:为了支持动态场景(如物体的移动、创建和销毁),必须配套一套高效的 更新算法 (Update Algorithm)。当物体发生变化时,只需更新其在全局SDF中影响的区域,而非重建整个SDF。
-
全局SDF的巨大优势:加速光线投射 (Ray Casting)
- 传统方法的瓶颈:若没有全局SDF,一次光线投射需要对场景中大量的物体进行逐一的相交测试(例如,测试光线与成百上千个物体的包围盒或三角形),计算开销巨大。
- SDF的加速原理:全局SDF将整个场景的几何信息编码成一个连续的距离场。进行光线步进(Ray Marching)时,每一步都可以安全地前进“当前点到最近表面的距离”那么远。这使得光线可以在空旷空间中进行大步长跳跃,极大地减少了求交测试的次数,从而实现数量级的性能提升。
- 效果对比:讲座中提到,未使用全局SDF时,一次追踪可能需要进行超过200次的物体测试;而使用全局SDF后,测试数量会急剧下降。
二、 Lumen的混合策略:全局与局部的权衡
核心观点:Lumen 并不只使用全局SDF,而是采用了一种 “粗粒度全局SDF + 精细化网格SDF” 的混合策略,以平衡性能与精度。
-
全局SDF的局限性:
- 存储与精度:全局SDF需要巨大的存储空间。为了将其控制在合理范围内(如几十到几百MB),其 分辨率必然是有限的、相对粗糙的。它无法像单个物体的网格SDF那样捕捉到非常精细的几何细节。
-
Lumen的解决方案:
- 远距离追踪 (Broad-Phase):首先利用 粗糙的全局SDF 进行光线步进,快速定位到与场景大致的相交区域。
- 近距离求精 (Narrow-Phase):当光线步进到靠近某个表面时,再切换到该表面所属物体的 高精度网格SDF 进行精确的求交计算。
这种混合方法,既利用了全局SDF快速跳过大片空间的能力,又保留了网格SDF在细节表现上的优势。
三、 性能优化:基于摄像机的Clipmap结构
核心观点:为了在广阔的开放世界中高效地管理全局SDF,Lumen采用了以摄像机为中心的 Clipmap 结构,实现了距离相关的 LOD (Level of Detail)。
-
什么是Clipmap:
- Clipmap是一种多分辨率的体积纹理结构,它由多个以摄像机为中心的、尺寸相同但覆盖范围和分辨率不同的嵌套网格组成。
- 近处高精度,远处低精度:离摄像机最近的网格分辨率最高,随着距离变远,网格覆盖的范围越来越大,但分辨率则相应降低。
- 示例:
- 0-50米:最高精度SDF
- 50-100米:次高精度SDF
- 100-200米:中等精度SDF
- 200-400米:低精度SDF
-
为何SDF适合Clipmap:
- SDF是一种 统一的(Uniform) 空间表达,与体素(Voxel)类似。这种规则的网格结构天然地支持降采样和滤波操作,使其能够完美地与Clipmap/MIP-map这类多级细节技术结合。
四、 理论升华:SDF作为一种优秀的几何表达
核心观点:从数学表达的层面看,SDF(作为一种隐式曲面)相比于传统的三角形网格或体素,在进行渲染相关的几何计算时具有天然的优势。
-
不同几何表达的对比:
- 三角形网格 (Triangle Mesh):
- 缺点:是 离散的 (Discretized) 且 不规则的 (Irregular)。这种表达方式在进行空间查询和连续性计算时非常复杂和低效。
- 体素/点云 (Voxel/Point Cloud):
- 优点:是 统一的 (Uniform),便于进行滤波等规则化处理。
- 缺点:仍然是 离散的 (Discretized),丢失了表面的连续性信息。
- SDF (隐式曲面):
- 优点:是一种连续的数学表达。它不仅定义了表面(值为0的位置),还定义了空间中任意一点到表面的关系。这使得它可以被无限精细化,并且易于求导(获取法线),为各种图形学计算提供了极大的便利。
- 三角形网格 (Triangle Mesh):
-
结论:虽然SDF本身不直接用于最终的着色渲染(最终还是要找到表面材质信息),但它作为一种加速渲染计算的中间表达,其数学上的优越性是显而易见的。它为Lumen摆脱对硬件光追的依赖、实现高性能的全局光照提供了坚实的理论基础。
Lumen 光线注入与场景的参数化表达
这份笔记总结了 Lumen 引擎中,在计算间接光照之前,如何将场景信息进行预处理和参数化的核心思想。讲座内容颠覆了传统的讲解顺序,从最源头的“光线如何注入场景”开始,引入了 Mesh Card 和 Surface Cache 这两个关键概念。
1. 全局光照的核心挑战:如何表达整个世界?
在计算全局光照(GI)时,我们面临一个根本性问题:场景中任何一个表面,无论是否在当前视锥内,都有可能成为一个间接光源,影响最终画面。
- 核心观点: 全局光照的计算不能只依赖于相机可见的像素。我们需要一种方法来高效地获取和查询整个场景中任意表面的光照信息,包括那些在相机背后、被遮挡或远离相机的物体。
- 传统方法的局限: 类似 Reflective Shadow Maps (RSM) 的技术虽然能提供一些间接光信息,但存在诸多限制,难以处理复杂的几何细节和全场景的光路。
2. Lumen 的解决方案:Mesh Card - 为物体拍摄“光照快照”
为了解决上述问题,Lumen 引入了一种名为 Mesh Card 的创新数据结构,用于对场景中的物体进行参数化表达。
- 核心观点: Mesh Card 是对场景中每一个物体实例(Instance)从六个轴向方向(上/下/左/右/前/后)拍摄的一组“光照属性快照”。它将一个复杂的三维几何体,简化成了一组二维的纹理信息,极大地便利了后续的光线追踪查询。
Mesh Card 的特性
-
生成方式:
- 对每个物体实例,沿着其 轴对齐包围盒(AABB) 的六个面,向内进行一次类似正交投影的“拍摄”。
- 这种方式虽然简单,但足以捕获物体在各个方向上的表面信息。
-
捕获内容:
- 每一次“拍摄”都会生成一组类似 G-Buffer 的数据,存储在纹理中:
- Albedo (反照率): 物体本身的颜色。
- Normal (法线): 物体表面的朝向。
- Depth (深度): 从包围盒表面开始计算的深度,用于重建表面位置。
- Emissive (自发光): 如果物体是自发光体,记录其发光信息。
- 每一次“拍摄”都会生成一组类似 G-Buffer 的数据,存储在纹理中:
-
LOD (Level of Detail) 机制:
- Mesh Card 的分辨率是动态的,与物体距离相机的远近相关。
- 近处的物体,对间接光照贡献更显著,其 Mesh Card 的分辨率更高,细节更丰富。
- 远处的物体,影响较小,其 Mesh Card 的分辨率更低,从而节约存储和计算资源。
3. 信息的存储与管理:Surface Cache
所有物体的 Mesh Card 并不会独立存在,而是被统一打包到一个巨大的纹理图集中进行管理,这个图集被称为 Surface Cache。
- 核心观点: Surface Cache 是一个全局的、动态更新的纹理集合,它将整个场景中所有物体的 Mesh Card 紧凑地打包在一起,为后续的 GI 计算提供了一个统一、高效的场景数据查询接口。
Surface Cache 的特性
- 本质: 一个巨大的纹理图集(Texture Atlas)。讲座中提到其典型尺寸为 4096x4096。
- 结构: 它不是一张单独的纹理,而是一个纹理数组或集合,分别存储着所有 Mesh Card 的 Albedo、Normal、Depth 等不同属性。
SurfaceCache.Albedo(4096x4096)SurfaceCache.Normal(4096x4096)SurfaceCache.Depth(4096x4096)- ...
- 动态管理:
- 这是一个名副其实的“缓存”。随着相机的移动,系统会动态地将进入视野或靠近相机的物体的 Mesh Card 换入 (Swap In),并将远离的物体 换出 (Swap Out)。
- 由于 GI 对信息的实时性要求不如直接光照苛刻,短暂的延迟或信息的缺失是可以接受的。
- 数据压缩: 为了在有限的显存(例如,一个 4K 的 RGBA8 纹理就需要 64MB)中容纳尽可能多的信息,Surface Cache 中的数据会使用硬件支持的纹理压缩格式进行存储。
4. 最终目标与遗留问题:将 Radiance “固化”到 Surface Cache
构建 Mesh Card 和 Surface Cache 的最终目的是为了简化和加速后续的 GI 计算。
- 核心观点: Surface Cache 的本质,是将复杂、离散的场景几何体,转化成了一个统一、连续的光照信息表达。 我们可以把它理解成一种现代版的 Photon Map,它将场景表面接收光照的潜力“固化”在了这张巨大的纹理上。
- 优势: 当进行光线追踪计算间接光时,光线不需要再与原始的、复杂的三角面片求交,而是可以直接在 Surface Cache 这个简化表示上进行采样,查询任意点的表面属性,极大地提升了效率。
接下来要解决的问题
讲座到此,成功地构建了场景的参数化表达,但留下了两个关键问题,这也是后续内容将要探讨的:
- 如何计算 Radiance? 我们现在只在 Surface Cache 中存储了物体的基本材质属性(Albedo, Normal)。如何计算出每个点在直接光照下的 辐射度(Radiance),特别是如何高效地处理阴影?
- 如何计算多弹跳(Multi-Bounce)? 目前的流程只为计算第一次光线弹射(直接光照 → 表面)提供了数据基础。如何利用 Surface Cache 来计算第二次、第三次乃至更多次的弹射,从而实现完整的全局光照效果?
Surface Cache 光照计算
在上一部分我们理解了 Lumen 如何通过 Mesh SDF 和 Global SDF 构建场景的几何表达后,这一部分的核心是解决一个关键问题: 如何为 Surface Cache 上的每一个点计算出准确的光照?
这不仅仅是简单的直接光照,更包含了 Lumen 实现实时动态全局光照(GI)的精髓——间接光照。
一、核心挑战:从几何到光照
当我们拥有了包含 Albedo、Normal、Depth 等信息的 Surface Cache 后,计算光照面临两大难题:
- 直接光照与阴影 (Direct Lighting & Shadows): Surface Cache 上的一个点(Pixel)在主光源的照射下究竟有多亮?它和光源之间是否被其他物体遮挡(处于阴影中)?
- 间接光照 (Indirect Lighting): 直接光照只是一次弹射(1-bounce)。一个点接收到的光还包含从场景中其他物体表面反弹过来的光。对于这些无数次的弹射,我们该如何高效地计算和收集?
为了解决这些问题,Lumen 的最终目标是生成一张至关重要的贴图:Surface Cache Final Lighting。这张图记录了 Surface Cache 上每个点最终的入射光强度,是后续所有光照计算的基础。
二、核心流程:三步计算最终光照
讲座中将整个光照计算过程拆解为三个核心步骤,这是一个巧妙的、利用时间换取空间和性能的循环过程。
(注:根据描述绘制的流程示意图)
-
计算直接光照 (Calculate Direct Lighting):
- 对 Surface Cache 上的每个点,计算来自场景中所有光源的直接光照贡献,并处理阴影。
-
体素化场景光照 (Voxelize Scene Lighting):
- 将场景中的光照信息(主要是上一帧的间接光照结果)烘焙到一个 世界空间下的体素化表达 (World-Space Voxelized Representation) 中。这个结构可以被看作是分布在场景中的大量微型光照探针。
-
计算间接光照 (Calculate Indirect Lighting):
- 对于当前帧的 Surface Cache,通过向场景发射光线,采样并收集来自上一帧的体素化光照信息(步骤2的产物),以此来计算当前帧的间接光照。
这三步构成了一个循环:当前帧计算出的 Final Lighting(直接光+间接光),会经过处理,更新到那个世界空间的体素化表达中,为下一帧的间接光照计算提供数据。
关键思想:时间累积 (Temporal Accumulation) 实现多弹射 (Multi-Bounce)
这个流程最巧妙之处在于它“左脚踩右脚”式的时间累积机制,用以低成本地模拟光线的多级弹射。
- 第 0 帧 (F0): 只有直接光照(1次弹射)。计算出的光照结果被存入体素结构中。
- 第 1 帧 (F1):
- 计算自己的直接光照(1次弹射)。
- 通过采样 F0 的体素结构,获得了来自 F0 的1次弹射光照。
- 两者叠加,F1 的结果实际上已经包含了 2次弹射 的光照信息。
- 第 2 帧 (F2):
- 计算自己的直接光照(1次弹射)。
- 通过采样 F1 的体素结构(已包含2次弹射信息),获得了更丰富的间接光。
- 两者叠加,F2 的结果包含了 3次弹射 的光照信息。
核心观点:通过在时间维度上复用上一帧的光照结果,Lumen 用单次弹射的计算成本,逐帧累积出多弹射的全局光照效果。 这种思想在其他技术(如 SSGI)中也有应用。
三、深入解析三大步骤
第一步:直接光照 (Direct Lighting) 与阴影 (Shadows)
这一步相对直接,但实现方式非常高效。
- 核心方法: 对于 Surface Cache 上的每个点,我们已知其世界坐标、法线和 Albedo。要计算阴影,只需从该点向光源方向发射一条光线,判断是否被遮挡。
- 关键技术: Lumen 使用在第一部分中构建的 SDF (Signed Distance Fields) 来进行光线追踪,即 SDF Ray Tracing。通过在 SDF 中步进,可以极快地判断光路是否通畅。这比传统的 Shadow Map 方法在处理大量动态光源时更具优势。
- 优势:原生支持多光源 (Natively Supports Multiple Light Sources):
- 由于光照计算是逐点(Per-Texel)进行的,并且阴影检测成本低廉,Lumen 可以非常简单地遍历场景中的所有光源,将它们的光照和阴影效果累加到
Final Lighting贴图上。 - 这对美术师的创作自由度至关重要,因为游戏场景中往往存在大量光源来营造氛围。
- 由于光照计算是逐点(Per-Texel)进行的,并且阴影检测成本低廉,Lumen 可以非常简单地遍历场景中的所有光源,将它们的光照和阴影效果累加到
第二步与第三步:间接光照 (Indirect Lighting) 与光照的体素化表达
间接光照是 GI 的核心,也是最复杂的部分。其核心在于如何高效地“收集”来自四面八方的反弹光线。
-
面临的挑战: 从物体表面向半球方向发射大量光线来收集间接光,计算开销巨大。并且,光线能走多远,能获取多精确的信息,是一个难题。
-
Lumen 的解决方案:远近场分离 (Near/Far Field Separation)
- 近场 (Near Field): 对于离表面点较近的区域,Lumen 可以负担得起成本较高的、精确的光线追踪。光线可以击中详细的 Mesh SDF,从而获取被击中物体的精确表面属性(Albedo, Normal),计算出高质量的间接光。
- 远场 (Far Field): 当光线走得很远时,再进行精确求交的成本太高。如果光线只与 Global SDF 求交,我们只能知道击中点的位置和粗略法线,无法得知其材质信息,也就无法计算反弹光。
-
远场光照探针:体素化 Clipmap (Voxelized Clipmap) 为了解决远场光照问题,Lumen 引入了一个关键的数据结构,即讲座中提到的“World-Space Voxelized Representation”。
- 核心概念: 创建一个以相机为中心的多层级 体素网格 (Voxel Grid),它像一个巨大的 3D 纹理,覆盖了相机周围的广阔空间。这个网格的作用是缓存场景的光照信息,充当远场光照的查询表(Lookup Table)。
- 术语: 这种以观察者为中心、动态加载细节的层级结构,在图形学中通常被称为 Clipmap。
- 具体参数:
- 结构: 4 个层级 (LODs)。
- 分辨率: 每层为 64x64x64 个体素 (Voxel)。
- 体素大小: 约 0.78 米。
- 工作原理: 当一条用于计算间接光的远场光线击中了 Global SDF 时,Lumen 不再尝试寻找具体的物体材质。而是直接查询这条光线终点位置在 体素化 Clipmap 中对应的 Voxel,读取其中存储的 近似辐射度 (Radiance)。这个辐射度值就是上一帧在该空间点的光照结果,现在被当作一个“小灯泡”贡献给当前点的间接光照计算。
通过这种远近场结合的方式,Lumen 在保证近处 GI 质量的同时,也用一种高效的近似手段处理了远处的环境光,实现了性能与效果的平衡。
Voxel GI 与时序反馈循环
在上一部分我们了解了 Lumen 的核心数据结构 Surface Cache 之后,这一部分将深入探讨 Lumen 是如何利用这个缓存来构建世界空间的全局光照,并解决多弹反弹(Multi-bounce)问题的。其核心在于一个巧妙的、基于体素(Voxel)的 时序反馈循环(Temporal Feedback Loop)。
一、 空间表达:Voxel Clipmap
为了在世界空间中存储和查询间接光照,Lumen 需要一个高效的空间数据结构。它没有选择对整个场景进行全局体素化,而是采用了以摄像机为中心的 Clipmap 结构。
-
核心观点: Lumen 使用一个多层次的 Voxel Clipmap 来存储摄像机周围区域的 入射光照(Incoming Radiance) 信息,作为间接光照的来源。
-
关键参数:
- 结构: 4 层 Clipmap。
- 分辨率: 每层为 64x64x64 个体素(Voxel)。
- 尺度: 讲者根据源码反推,每个 Voxel 大约为 0.78 米,使得最精细的一层 Clipmap 覆盖约 50x50x50 米的范围。这是一种对性能和精度的权衡。
-
数据存储:
- 每个 Voxel 的 6 个面 分别存储一个 亮度值(Luminance)。
- 这个结构本质上是记录了空间中每个微小面片“被照亮”的程度,它将作为光源,用于后续的间接光照计算。
二、 空间体素化 (Voxelization) 的实现
传统的体素化(如 VXGI)通常依赖于保守光栅化(Conservative Rasterization)。但 Lumen 既然已经为整个场景构建了 Mesh SDF,就选择了一条更高效、更统一的路径。
-
核心观点: Lumen 利用 Mesh SDF 进行光线追踪 来完成场景的体素化,而不是使用传统的硬件光栅化管线。这使得整个流程更加自洽和高效。
-
关键算法:
- 空间划分: 将 Clipmap 空间划分为更小的图块(Tile),例如 4x4x4 个 Voxel 组成一个 Tile。
- 物体筛选: 在每个 Tile 内部,由于空间局部性,包含的物体实例(Mesh Instance)数量非常有限(例如只有几个)。
- SDF 光线投射: 从 Voxel 的边界向内部发射随机光线。
- 求交与填充: 使用高效的 Mesh SDF 求交 来判断光线是否击中物体。
- 如果击中,则该 Voxel 被标记为“非空”,并记录下表面的 法线、颜色、深度 等信息。
- 如果未击中,则 Voxel 为空。
- 增量更新: 这个过程不是每一帧都对整个 Clipmap 进行,而是 只更新“脏”的区域 (例如摄像机移动或物体移动影响的区域),大大降低了计算开销。
-
优势: 这种方法的效率极高,因为它将光线求交的范围限制在了一个非常小的局部空间和极少数的物体上。
三、 Voxel Lighting 的计算:巧妙的“鸡生蛋”循环
这是 Lumen 实现多弹反弹 GI 的精髓所在。它解决了一个经典问题:要计算间接光,需要知道周围表面的亮度;但要计算周围表面的亮度,又需要包含间接光。Lumen 通过一个跨帧的反馈循环解决了这个“鸡生蛋”问题。
-
核心观点: Lumen 在 Surface Cache (屏幕空间) 和 Voxel Lighting (世界空间) 之间建立了一个时序反馈循环。
Surface Cache的最终光照结果被用来“点亮”Voxel Lighting,而Voxel Lighting又在下一帧作为间接光源来计算Surface Cache。 -
循环流程分解:
-
第 0 帧 (初始化):
- 此时,
Voxel LightingClipmap 是全黑的,因为还没有间接光信息。 - 计算当前帧的
Surface Cache。由于没有间接光来源,其Final Lighting仅包含直接光照。
- 此时,
-
光照注入 (Lighting Injection):
- 遍历场景中被体素化后的有效 Voxel 表面。
- 对于每个 Voxel 表面,找到它在世界空间中对应的原始表面位置。
- 查询该位置在
Surface Cache中的Final Lighting值(此刻只有直接光)。 - 将这个亮度值写入(注入)到 Voxel Clipmap 对应面的亮度存储中。
- 至此,我们完成了 第 1 次光线反弹 的能量传播:
Voxel Lighting(Frame 0) ←Surface Cache(Frame 0, Direct Light Only)。
-
第 1 帧 (反馈循环开始):
- 开始计算第 1 帧的
Surface Cache。 - 此时,当需要计算某个像素的间接光照时,它可以向世界空间发射光线,采样我们刚刚填充好的
Voxel LightingClipmap。 - 这样,
Surface Cache的Final Lighting就包含了 直接光照 + 1 次反弹的间接光照。 - 这一帧的
Surface Cache结果会再次被用来更新Voxel LightingClipmap。
- 开始计算第 1 帧的
-
持续收敛:
- 这个过程在后续帧中不断重复。每一帧,
Voxel Lighting都承载了上一帧更完整的光照信息,并将其贡献给下一帧的Surface Cache计算。 - 经过数帧到十几帧的累积,光线能量在场景中充分“反弹”,最终收敛到一个稳定且包含多弹反弹效果的自然结果。
- 这个过程在后续帧中不断重复。每一帧,
-
-
视觉表现: 这种时序累积的特性,也解释了为什么在 Lumen 场景中快速改变光源时,会观察到一个光照效果逐渐传播和变亮的过渡过程(大约零点几秒)。
四、 概念辨析:Voxel Lighting vs. World Space Probes
Lumen 中有两个看似相似的世界空间结构,但其作用完全不同,讲者在此特别进行了区分,这一点对于理解系统至关重要。
-
Voxel Lighting (本节讨论的核心)
- 作用: 描述一个表面 “被照得有多亮” (Incoming Radiance)。
- 数据: 每个 Voxel 的六个面只存一个标量亮度值。
- 用途: 作为下一帧
Surface Cache计算的间接光来源,是时序反馈循环的关键一环。
-
World Space Probes (后续会讲到)
- 作用: 描述空间中一个点的 “光场分布”,即从该点能看到的所有方向的光照信息 (Radiance Field)。
- 数据: 存储的是更复杂的光场信息(如球谐函数或 CubeMap)。
- 用途: 负责 “照亮别人”,主要用作一种覆盖范围更广、但频率较低的 GI 后备方案(Fallback),用于处理
Surface Cache无法覆盖的区域(如反射中的物体、半透明材质等)。
简单来说, Voxel Lighting 是“我被照亮了”,用于信息传递; World Space Probes 是“我能照亮你”,用于最终着色。
World Space Lighting 与间接光照计算
在上一部分我们了解了 Surface Cache 作为场景几何和直接光照的缓存。这一部分,我们将深入探讨 Lumen 如何利用这套系统计算 多弹射的间接光照 (Multi-bounce Indirect Lighting),并引入一个与之相辅相成的关键数据结构: 世界空间光照缓存 (World Space Lighting Cache)。
一、 Lumen 的核心照明数据结构:双层缓存系统
Lumen 的实时全局光照(GI)并非依赖单一的数据结构,而是巧妙地设计了一套双层缓存系统,分别处理不同尺度和精度的光照信息。
1. Surface Cache (表面缓存) - 高精度近场表达
- 核心观点: Surface Cache 不仅缓存了物体的表面属性,更重要的是它为场景提供了一套 为光照计算服务的“Imposter”。它将复杂的几何体简化为六个方向的参数化图集,极大地简化了后续的光线求交与采样过程。
- 类比: 你可以把它想象成游戏远景优化中常用的 Imposter 技术,但这里的 Imposter 是专门为存储和查询光照信息而设计的。
2. World Lighting (世界空间光照) - 低精度远场表达
- 核心观点: 为了实现多弹射 GI 和处理大范围场景,Lumen 引入了一套更粗糙但覆盖范围更广的全局光照表达方式。讲座中提到的 “WORKALIZATION” 或 “World Voxelization” 指的就是这个。
- 关键术语: World Voxel Lighting Grid (世界体素光照网格)。这是一个覆盖了数百米范围的、相对稀疏的 3D 网格(Voxel Grid)。
- 主要作用:
- 存储间接光照: 它存储了整个空间中的间接光照信息,是多弹射光线反弹的能量来源。
- 远距离查询: 当光线射出很远,超出了高精度 Surface Cache 的范围时,就在这个低精度网格中进行查询。
- Global SDF 的后备方案: 当光线追踪命中了 Global SDF 而非具体的 Mesh SDF 时,我们无法得知精确的表面信息。此时,只能从这个 World Voxel Lighting Grid 中查询对应位置的光照,作为光线命中的结果。
小结: Surface Cache 负责“我(物体表面)是什么样的”,而 World Voxel Lighting Grid 负责“我周围的环境光是什么样的”。两者协同工作,构成了 Lumen GI 的基石。
二、 间接光照(Indirect Lighting)的计算流程
有了双层缓存系统,计算某个表面(具体来说是 Surface Cache 上的一个点)接收到的间接光就变得有章可循了。
核心算法流程: 计算 Surface Cache 上的间接光照,是通过从其表面发射光线,与 World Voxel Lighting Grid 求交来完成的。
以下是具体的步骤分解:
-
采样单元 (Sampling Unit)
- 在 Surface Cache 的图集(Atlas)上,以 8x8 像素 为一个 瓦片(Tile) 进行处理。
- 注意: Lumen 中 “Tile” 这个词在不同上下文中有不同含义,这里的 Tile 指的是 Surface Cache 上的计算单元。
-
光线追踪 (Ray Tracing)
- 在每个 8x8 的 Tile 内,选择 4 个抖动(Jittered)的采样点。
- 从每个采样点出发,向半球方向发射 16 根光线。
- 关键点: 这些光线 不与其他的 Surface Cache 求交,而是直接与 低精度的 World Voxel Lighting Grid 进行求交,查询该方向上的环境光照。
-
结果存储 (Storing the Result)
- 将 16 根光线采样到的光照信息,编码并压缩成 球谐光照(Spherical Harmonics, SH)。
- 最终,每个 8x8 的 Tile 存储了 4 个采样点的 SH 数据。
-
着色与插值 (Shading & Interpolation)
- 当需要计算 Tile 内 64 个像素中任意一个像素的间接光时,无需重新发射光线。
- 直接通过对周围 4 个采样点的 SH 数据进行双线性插值,快速重建出该像素点的间接光照环境。
- SH 的优势: SH 是一种低频信号,非常适合进行平滑插值,用极少的采样点就能重建出柔和、自然的间接光效果,计算开销极低。
-
最终合成 (Final Composition)
- 将计算出的 间接光照(Indirect Lighting) 与之前得到的 直接光照(Direct Lighting) 相结合,就得到了最终的 GI 效果。
三、 该架构的巧妙之处:解决自发光(Emissive Lighting)难题
传统的实时渲染中,处理自发光物体(尤其是面光源)对周围环境的照亮效果是一个公认的难题。而 Lumen 的这套架构,通过一种“自举”或“反馈循环”的方式,优雅地解决了这个问题。
-
传统难题: 一个自发光的灯带,它本身是一个面光源。如果想让它照亮旁边的墙壁,通常需要非常复杂且昂贵的渲染技术(如 VPL, Virtual Point Lights),很难做到实时。
-
Lumen 的解决方案 (左脚踩右脚):
- 第 N 帧: 自发光物体(如灯带)被渲染。它的自发光属性被记录在它自己的 Surface Cache 中,作为它的“直接光照”。
- 光照整合阶段: 系统将所有 Surface Cache 上的光照信息(包括这个灯带的自发光)“注入”或“Splat”到 全局的 World Voxel Lighting Grid 中,更新整个世界的光照环境。
- 第 N+1 帧: 当旁边的墙壁在计算它的间接光照时,它会向外发射光线。这些光线会与刚刚被灯带“点亮”的 World Voxel Lighting Grid 求交,从而采样到来自灯带的光。
- 结果: 经过一到两帧的延迟,墙壁就会被灯带自然地照亮,并且由于是从低精度的 Voxel Grid 采样,天然地带有柔和的阴影效果。
核心思想: Lumen 将自发光视为一种特殊的直接光照,通过 Surface Cache → World Voxel Lighting Grid → Surface Cache 的反馈循环,将局部光照信息传播到全局,实现了高效且效果自然的区域光照效果。
四、 总结与思考
讲座的这一部分揭示了 Lumen GI 系统的核心——一套为性能和效果精心设计的双层缓存架构。
- 复杂性与收益: 虽然为每个网格物体维护一套 Surface Cache 听起来非常复杂(涉及图集管理、内存分配等),但它带来的收益是巨大的。它不仅统一了场景的几何表达,还通过与 World Voxel Lighting Grid 的联动,巧妙地解决了多弹射 GI 和自发光等棘手问题。
- 工程的艺术: Lumen 的成功之处在于它并非发明了某个单一的革命性算法,而是将多种现有技术(SDF、Voxel GI、Imposter、SH 等)以一种极其精妙的方式组合、优化,并解决了无数工程细节(如讲座中提到的 Terrain、参与性介质等特殊情况),最终实现了一个在当前硬件上可用的、效果惊人的实时全局光照系统。
从光照缓存到探针系统
在上一部分我们理解了 Lumen 如何通过 Surface Cache 将场景几何和光照信息“体素化”。现在,我们将深入探讨这一设计的哲学思想、更新策略,以及如何利用这些缓存数据,通过 光照探针 (Lighting Probes) 来真正实现最终的像素着色。
一、 Surface Cache 的设计哲学与更新策略
1. 核心设计思想:化繁为简 (Irregular to Regular)
讲座中提到,初看 Surface Cache 会觉得为每个 Mesh 维护六个面的 Atlas 贴图非常繁琐。但其背后蕴含着一个非常深刻且重要的图形学思想:
- 核心观点: 将场景中 不规则 (Irregular) 的、复杂的网格几何体,通过
Surface Cache和体素化的方式,转化为 规整、统一 (Uniform & Regular) 的数据表达。 - 带来的优势: 一旦数据变得规整,后续所有操作(如积分、卷积、采样)都会变得极其简单和高效。这与 SDF 的思想一脉相承,都是为了简化后续复杂的计算。
- 类比: 这就像将一个形状任意的物体放入一个标准化的盒子里,虽然前期打包费事,但后续的存储、堆叠、运输(计算)都变得标准化和高效。
2. 缓存更新的性能考量 (Update Strategy)
Surface Cache 的更新开销巨大,不可能每一帧都完全重新计算。因此,Lumen 采用了一种基于预算的增量更新策略。
- 核心观点: 每一帧只更新
Surface Cache的一小部分,以分摊计算开销。 - 更新预算 (Budget):
- 直接光照 (Direct Lighting): 每帧最多更新 1024x1024 Texels。
- 间接光照 (Indirect Lighting): 开销更大(需要在场景中进行光线追踪),因此预算更低,每帧最多更新 512x512 Texels。
- 更新调度:
- 系统需要决定优先更新哪些部分。这通常基于物体是否移动、光源变化、以及离摄像机的远近等因素。
- Lumen 内部实现了一套复杂的调度系统,包括 优先级队列 (Priority Queue) 和 分桶排序 (Bucket Sort) 等算法,来管理哪些
Mesh Card需要被更新。
- 概念类比: 这个过程可以理解为“冻结光照 (Freeze the Light)”。就像经典的 Photon Mapping 算法将光子“固定”在物体表面一样,
Surface Cache将着色结果“固定”在虚拟的纹理表面上,为后续查询提供稳定的数据源。
二、 光照应用的桥梁:光照探针 (Lighting Probes)
我们已经通过 Surface Cache 获得了场景中各个表面的光照信息。但这还不足以直接对着色点的最终颜色进行计算。
- 核心问题: 渲染屏幕上的一个像素点(Shading Point),需要知道其 上半球所有方向的入射光 (Incoming Radiance)。而
Surface Cache只告诉我们其他物体的表面亮度是多少。 - 解决方案: 使用 光照探针 (Lighting Probes)。探针是一个位于空间中的采样点,它负责采集周围环境的球面光照信息,并存储起来,供附近的像素点查询使用。
1. 传统世界空间探针 (World Space Probes) 的局限性
传统 GI 方案(如 Light Propagation Volumes, Voxel GI)通常在世界空间中均匀地放置探针网格。
- 问题:
- 无法适应几何细节: 固定的探针间距(如每米一个)无法捕捉到复杂的几何结构附近的光照剧烈变化。
- 视觉效果平庸: 这种方式很容易导致光照细节丢失,产生一种不自然的、我们行话里常说的“平 (Flat)”的感觉。
- 资源浪费: 在空旷区域放置大量探针是浪费,在细节丰富区域又显得不足。
2. Lumen 的解决方案:屏幕空间探针 (Screen Space Probes)
Lumen 采用了一种更为激进和巧妙的方法,主要在屏幕空间中动态放置探针。
- 核心观点: 在屏幕空间的网格上放置探针,而不是在世界空间。
- 具体实现:
- Lumen 默认 每 16x16 像素的图块 (Tile) 放置一个探针。
- 这意味着探针的分布密度会自适应于观察视角:离摄像机近的物体,在屏幕上占据更多像素,因此会获得更高密度的探针,从而捕捉更多光照细节。
- 理论依据:
- 间接光照是低频信号: 间接光照通常是柔和、变化缓慢的。因此,在一个小的屏幕区域内(如 16x16),光照变化不大,可以安全地共享同一个探针的数据,并通过上采样(Upsampling)应用到每个像素。
- 高频细节分离: 场景中的高频细节(如精细的法线贴图、材质纹理)由物体自身的材质属性来贡献,与低频的间接光照分离处理。
三、 探针数据的存储与参数化:八面体映射 (Octahedral Mapping)
每个探针都需要存储来自整个球面的光照信息(Radiance 和 Hit Distance)。如何高效地将这些球面数据存入一张 2D 纹理中,是一个经典问题。
1. 问题:如何将球面信息映射到 2D 纹理
- 传统经纬度映射 (Lat-Long Mapping) 的缺陷: 这是最直观的方法,但它会导致 极点(Poles)处的严重扭曲和采样冗余,而在赤道(Equator)附近采样又相对稀疏,分布非常不均匀。
2. 八面体映射 (Octahedral Mapping) 的优势
Lumen 和许多现代 GI 算法都采用八面体映射来解决这个问题。这是一种将球面投影到一个正八面体,再将八面体展开成 2D 正方形的技术。
-
核心优势:
- 分布相对均匀 (Relatively Uniform Distribution): 相比经纬度映射,它极大地改善了采样点在球面上的分布均匀性,减少了纹理资源的浪费。
- 计算简单 (Simple Computation): 从一个三维方向向量到二维 UV 坐标的转换,以及其逆运算,都只需要非常简单的数学运算,在 Shader 中计算开销极低。
- 利于插值 (Good for Interpolation): 在 2D 纹理空间中进行线性插值(Bilinear Interpolation),其结果能很好地近似于在球面上进行插值。这对于纹理过滤和mipmap生成至关重要。
-
关键术语: Octahedral Mapping / Octahedron Environment Map 是所有现代 GI 渲染管线中都值得深入研究和掌握的一项基础技术。
小结: 这一部分我们理解了 Lumen 如何巧妙地管理和更新 Surface Cache,并创造性地使用屏幕空间探针来桥接缓存数据与最终着色。同时,通过八面体映射技术,高效地解决了探针数据的存储问题。接下来,讲座的第四部分将会讲解如何利用这些已经放置好并填充了数据的探针,来完成最终的像素着色。
屏幕空间探针与自适应采样
在上一部分的基础上,我们继续深入 Lumen 的核心组件。本章将聚焦于 Lumen 如何在屏幕空间中高效地采集和组织光照信息,特别是其极具开创性的 自适应采样(Adaptive Sampling) 机制。
一、Screen Probe 的基础:八面体球面映射 (Octahedral Mapping)
为了在探针(Probe)中存储来自四面八方的光照信息(Radiance),Lumen 需要一种高效的球面参数化方法。
-
核心需求: 需要一种能将球面方向(3D向量)与二维纹理坐标(2D UV)快速相互转换,并且在纹理空间中进行线性插值(Bilinear Interpolation)时,其结果能很好地近似于在球面上的插值,避免产生明显的接缝或扭曲。
-
Lumen 的选择: 八面体球面映射 (Octahedral Mapping)。
-
关键优势:
- 计算简单: 从任意方向向量到 UV 坐标的转换计算量极小,可以高效地在 Shader 中实现。
- 插值友好: 在纹理图谱上相邻的纹素(Texel),其对应的球面方向也基本是相邻的。这使得硬件的插值能够产生平滑且视觉上正确的结果。
- 分布相对均匀: 相比其他如经纬图(Lat/Long Map)等方式,八面体映射能让采样点在球面上分布得更均匀。
-
实现细节: 在 Lumen 中,每个屏幕空间探针(Screen Probe)使用了一个 8x8 分辨率的纹理来存储通过八面体映射编码的球面光照信息。
二、核心思想:屏幕空间的自适应探针采样 (Adaptive Probe Sampling)
这是 Lumen 在屏幕空间 GI 方案中最为精妙和创新的部分。它解决了传统屏幕空间方法的一个核心痛痛点:如何处理几何不连续的区域。
1. 基础采样策略:稀疏的屏幕空间网格
Lumen 首先在屏幕空间上放置一个稀疏的探针网格。例如, 每隔 16x16 像素的区域(Tile)放置一个探针。这个探针负责采集并存储该区域的光照信息。
2. 核心挑战:屏幕邻近性 ≠ 世界空间邻近性
这个基础策略存在一个致命问题:在屏幕上看起来很近的两个像素,在三维世界空间中可能相距十万八千里。
- “视星等”类比: 讲座中用了一个非常形象的天文学概念—— 视星等(Apparent Magnitude)。夜空中的两颗星看起来像是挨在一起的伴星,但实际上它们之间的真实距离可能是几百上千光年。
- 渲染中的问题: 同样,在渲染场景中,一个角色的鼻尖像素可能紧挨着它背后远方山脉的像素。如果强制使用相同的几个探针来为这两个点提供间接光照并进行插值,结果必然是错误的,会导致 漏光(Light Leaking) 和细节模糊。
3. 解决方案:自适应细分 (Adaptive Refinement)
当 Lumen 检测到一个 16x16 的 Tile 内部几何或法线变化剧烈,导致基础探针不足以准确表达光照时,它会进行自适应细分。
- 细分层级:
- Level 0: 16x16 像素共享一个探针。
- Level 1: 如果 Level 0 不够精确,则细分为 8x8 像素区域,并采样新的探针。
- Level 2: 如果 Level 1 仍不够,则继续细分为 4x4 像素区域。
核心观点: Lumen 不采用固定的、统一的采样密度,而是根据画面的复杂性动态调整采样密度,在需要细节的地方投入更多的计算和存储资源。
4. 细分决策:基于误差度量的有效性判断
Lumen 如何判断一个 Tile 是否需要细分?它设计了一套基于“插值有效性”的误差度量方法。
- 前提: 对于一个 Tile 内的某个像素,它的光照信息应该由包围它的四个角落的探针插值得到。
- 判断逻辑:
- 取当前像素的 法线 (Normal),构建一个局部平面。
- 将周围四个探针的 世界空间位置 (World Position) 投影到这个局部平面上。
- 计算一个 误差项 (Error Term)。这个误差项综合了探针的世界空间距离以及它们在像素局部平面上的投影距离。
- 讲座中提到一个复杂的函数来计算这个误差,其形式大致如下:
其中包含Error = f(world_distance, projected_distance_on_plane)exp指数运算和一些经验常数,用以放大误差。
- 决策:
- 如果对于 Tile 内的大量像素,计算出的累计误差都超过了一个预设的 阈值 (Threshold),就意味着这四个角上的探针对于该区域内的表面来说是“无效”的(比如它们来自完全不相关的几何体)。
- 此时,Lumen 就认为当前采样精度不足,需要 请求一次细分 (Refinement)。
核心观点: 通过将探针位置投影到像素的局部平面上,Lumen 能非常有效地判断出某个探针的光照信息对于当前着色点是否“相关”,从而决定是否需要更精细的采样。
5. 巧妙的存储:利用纹理集图谱 (Texture Atlas) 进行打包
自适应采样会产生数量不定的额外探针,如何高效存储它们是一个挑战。Lumen 的解决方案堪称一绝。
- 背景: 屏幕通常是宽屏的(如 16:9),而 GPU 申请的纹理(Texture)为了内存对齐和寻址效率,往往是正方形的(如 2048x2048)。
- 存储策略:
- 基础层探针: 首先,将所有基础的(16x16 级别)探针数据依次存入这个正方形的纹理图谱中。由于屏幕是长方形,纹理图谱的下方会留出大量未使用的空间。
- 细分层探针: Lumen 将所有需要细分的、额外生成的探针(8x8 和 4x4 级别) 打包 (Packing) 到纹理图谱下方这片未使用的空间里。
- 链接: 在基础层的探针数据中,会存储一个索引或标记,指向其在图谱下方对应的、更精细的子探针。这样就形成了一个层级结构。
核心观点: Lumen 通过复用纹理图谱中的“边角料”空间,几乎零额外显存开销地实现了一套强大的自适应采样系统,这是其性能和效率的关键。
三、自适应采样可视化
讲座中展示了一张“土法炼钢”的可视化图,直观地证明了该算法的有效性。
- 红色点: 代表基础的、最粗糙的 16x16 级别的探针。
- 黄色点: 代表被细分后的 8x8 或 4x4 级别的探针。
- 观察结论: 黄色点(高精度采样)密集分布在场景中几何结构复杂、轮廓边缘、以及深度急剧变化的区域。这完全符合算法设计的初衷,证明了误差度量函数能够准确地识别出需要更多光照细节的地方。
四、锦上添花:抖动采样 (Jittering)
为了避免采样网格的规律性带来的瑕疵(如条带状的摩尔纹),Lumen 在采样时引入了 抖动 (Jittering)。
- 作用: 在每个 Tile 内对探针的采样位置进行微小的、随机的偏移。
- 时序上的好处: 随着时间(帧)的推移,抖动的采样点不断变化。通过时间累积(Temporal Accumulation),这种多帧的抖动采样等效于进行了一次 多重采样抗锯齿 (MSAA),进一步提升了光照的平滑度和质量。
Probe 的重要性采样 (Importance Sampling)
在上一部分中,我们了解了 Screen Space Probe 的布局和基本工作方式。这一部分,我们将深入探讨 GI 渲染中至关重要的一步:如何智能地投射采样光线,以在有限的性能预算内获得高质量的间接光照效果。这就是 重要性采样 (Importance Sampling) 的核心议题。
一、GI 采样的核心挑战:为何均匀采样不可取?
当我们为一个 Probe 采集周围环境光照时,最朴素的想法是在其所代表的半球或球面上进行均匀采样,例如投射 8x8=64 条均匀分布的光线。
-
核心问题: 这种“盲目”的均匀采样方式没有考虑到环境中光源的位置和强度。在一个室内场景中,绝大部分间接光都来自于一扇明亮的窗户。如果我们的采样光线没有集中射向窗户,而是在墙壁、地板等较暗的区域浪费了大量样本,那么最终计算出的 GI 结果将会是:
- 充满噪点:明暗区域估算不准,产生斑驳、不均匀的色块。
- 能量损失:无法准确捕捉到主要光源的贡献,导致场景整体偏暗。
- 收敛缓慢:需要天文数字级别的采样数量才能获得一个可看的稳定结果,这在实时渲染中是不可接受的。
-
解决方案: 我们必须采用重要性采样。其核心思想是: 将有限的采样预算(光线)集中投射到对最终结果贡献最大的方向上。
二、构建重要性采样:两大关键因子
在渲染方程中,某个方向对最终着色结果的贡献主要由两个函数相乘决定:入射光 和物体表面的 BRDF。因此,我们的重要性采样也需要从这两个方面来构建概率密度函数 (PDF),让采样方向的概率分布尽可能地逼近 光照函数 × BRDF 的乘积分布。
Lumen 通过巧妙的方法分别估算这两个因子。
2.1 光照重要性:利用上一帧信息 (Temporal Reuse)
如何知道当前帧哪个方向的光更亮?Lumen 的一个非常聪明的技巧是假设光照在帧与帧之间变化不大,从而利用上一帧的结果来指导当前帧的采样。
- 核心观点: 从上一帧的 Probe 数据中近似估算当前的光照分布。
- 技术名称: Approximate Radiance Importance from Last Frame's Probes。
- 实现流程:
- 对于当前帧的某个 Probe,找到其在屏幕空间上邻近的、属于上一帧的 Probe。
- 读取这些旧 Probe 存储的光照信息(例如球谐函数 SH 或其他形式)。
- 将这些信息整合起来,重建出一个代表当前位置光照强度预估的球面图(例如一个 8x8 的亮度图)。
- 这张图上更亮的区域,就意味着我们在当前帧应该朝这些方向投射更多的采样光线。
这种时间上的复用是现代实时 GI 系统中一个非常关键且常见的优化思路。
2.2 几何重要性:从单一法线到法线分布函数 (NDF)
BRDF 的贡献主要与表面法线相关,一个很自然的想法是沿着着色点的法线方向进行余弦加权采样 (Cosine-weighted Sampling)。
-
朴素想法的陷阱: 直接使用 G-Buffer 中单个像素的法线是错误的。
- 频率不匹配: 单个像素的法线可能来自非常精细的法线贴图,是高频细节。而一个 Screen Space Probe 覆盖的是一个较大的像素区域(例如 16x16 或 32x32),它需要代表的是这片区域 宏观的、平均的几何朝向。
- 代表性不足: 用一个点的法线来决定整个区域的采样方向,会完全忽略区域内其他所有像素的几何信息。
-
正确思路: 为每个 Probe 的影响区域构建一个 法线分布函数 (Normal Distribution Function, NDF),它描述了该区域内所有表面法线的统计分布情况。
-
构建 NDF 的流程 (包含大量工程 Hack):
- 确定影响域: 一个 Probe 的值通常通过双线性插值影响周围像素,其最大影响范围可达约 32x32 像素 的区域。
- 区域采样: 遍历这
32x32 = 1024个像素来统计法线分布,开销巨大。因此,Lumen 在这个区域内随机选取 64 个点 作为代表。 - 深度剔除 (Depth Test): 为了避免将前景物体(如角色)的法线与远处背景(如墙壁)的法线错误地混合在一起,采样时会进行深度检测。只采纳那些 深度值与 Probe 自身深度值相近 的像素,确保统计的法线在几何上是连续且相关的。
- 整合分布: 将采集到的 64 个法线各自看作一个余弦波瓣 (Cosine Lobe)。每个波瓣都可以用一组 球谐函数 (SH) 系数来表示。通过 将所有 64 组 SH 系数累加,最终得到一个能代表整个区域法线分布的、平滑的 SH。这个最终的 SH 就是我们需要的 NDF。
三、智能射线预算:固定射线总数下的自适应采样
现在,我们通过上一帧数据和 NDF 分别得到了光照和几何的重要性分布图。最后一步就是如何利用这些信息来指导光线投射。
-
核心约束: 每个 Probe 的总采样光线数量是固定的(例如 64 条),以保证稳定的性能开销。
-
自适应采样策略:
- 合并重要性: 将光照重要性图和几何 NDF 结合起来,得到一个最终的采样概率分布图 (PDF)。
- 重定向光线: 根据这个最终的 PDF,重新分配 64 条光线的方向。在概率高的方向(例如,又亮、法线又朝向该方向),我们可能会投射多条光线(比如 4 条)。而在概率低的方向,可能一条也不投射。
- 结果合并与存储: 虽然在重要方向投射了多条光线,但在存储结果时,可以将这些方向相近的光线的返回结果合并成一个样本,存入 Probe 的一个 Texel 中。
- 最终效果: 实现了 总光线数量不变,但采样质量显著提升 的效果。光线被智能地用在了“刀刃上”,极大地提高了 GI 的收敛速度和最终质量。
这个过程体现了实时渲染中的一个核心思想:在严格的性能预算下,通过各种空间、时间上的近似和统计学方法,最大化渲染质量。
自适应采样与探针滤波
在上一部分的基础上,我们继续深入 Lumen 的核心优化策略。本节将聚焦于 Lumen 如何在固定的性能开销下,通过智能的算法来最大化光线追踪的质量,并解决由此产生的噪声问题。
一、 Importance-Driven Ray Budgeting: 智能光线预估与分配
在光线追踪中,并非所有方向的光线都同等重要。Lumen 采用了一种极为巧妙的自适应采样策略,确保计算资源被用在“刀刃”上。
-
核心观点: 在 总采样光线数量(Ray Budget)固定 的前提下,将计算力从不重要的采样方向,动态地“转移”到最重要的采样方向上,实现对关键光照信息的 超级采样(Super Sampling)。
-
关键步骤:
-
构建重要性函数 (Importance Function):
- Lumen 的重要性评估并非只看一个因素,而是将两个关键的概率密度函数(PDF)进行 卷积(Convolution):
- BRDF PDF: 基于物体表面材质和法线分布,决定了哪些方向更容易反射光线。
- 光照 PDF (Lighting PDF): 基于上一帧的场景光照信息,决定了哪些方向存在更强的光源。
- 通过结合这两者,Lumen 能精准地预测出哪些方向的采样“性价比”最高。
- Lumen 的重要性评估并非只看一个因素,而是将两个关键的概率密度函数(PDF)进行 卷积(Convolution):
-
排序与筛选:
- 系统会计算出所有潜在采样方向(例如 64x64 个)的综合重要性分数,并进行从高到低的排序。
-
光线预算重分配 (Ray Re-allocation):
- 设定一个重要性阈值。
- 系统会识别出那些重要性分数低于该阈值的“最不重要”的方向。
- 然后,将这些被“牺牲”掉的光线采样机会, 重新分配给排名最靠前、最重要 的那个方向。
- 举例: 假设总预算是 64 条光线。系统发现有 3 个方向几乎不可能贡献有效光照,于是它取消了这 3 次采样,并将这 3 次机会全部赋予了最重要的方向。最终,最重要的方向获得了
1 (原始) + 3 (额外) = 4次采样机会,实现了局部的超级采样,从而可以得到更精确、更稳定的结果。
-
-
效果:
- 这种方法极大地提升了在具有 高动态范围(HDR)光照 场景下的 GI 质量,尤其是在处理“窗户”这类室内场景的头号难题时。
- 光线会自动集中在场景中更亮、对最终画面贡献更大的区域,显著减少了噪声,而总光线数量和性能开销保持不变。
二、 Inter-Probe Spatial Filtering: 严谨的探针间信息共享
即使经过了自适应采样,单个屏幕空间探针(Screen-Space Probe)的数据仍然充满噪声。一个自然的想法是让探针向其邻居“借鉴”信息进行滤波降噪。但简单的均值混合会引发严重的光线泄漏(Light Leaking)问题。Lumen 设计了一套非常周到(用讲座原话说)的滤波规则。
-
核心观点: 探针在借鉴邻居探针的光线信息时,必须通过严格的有效性验证,以防止因 视差(Parallax) 和遮挡关系不同而导致的错误数据污染。
-
关键算法: 两步验证法 当一个探针(Probe A)试图使用其邻居探针(Probe B)在某个方向上的光线数据时,必须通过以下两项检查:
-
角度一致性检查 (Angular Coherency Check):
- 问题: 由于探针 A 和 B 在屏幕空间中的位置不同,对于同一个方向的射线,它们实际观察到的场景内容可能完全不同。
- 规则: 比较两个探针在该方向上射线的夹角。如果夹角过大(例如,硬编码阈值为 10度),则认为邻居的这条光线数据与自身场景不符,直接丢弃。
if (angle(ray_A, ray_B) > 10.0) { discard ray_B_data; }
-
命中距离一致性检查 (Hit Distance Coherency Check):
- 问题: 即使两条射线的角度非常接近,它们命中的物体也可能一个极近,一个极远。例如,探针 A 的射线被面前 5 米的墙挡住,而探针 B 的射线则穿过窗户射向了 100 米外的天空。直接混合会导致天空的亮度“泄漏”到墙内。
- 规则: 比较两条射线各自的 命中距离(Hit Distance)。如果两者距离差异过大,同样认为邻居的这条光线数据无效,予以丢弃。
if (abs(hit_dist_A - hit_dist_B) > threshold) { discard ray_B_data; }
-
-
效果:
- 这套“宁缺毋滥”的滤波策略,有效地避免了因盲目插值而产生的 光斑、噪声和光线泄漏 问题。
- 它确保了只有在几何上和逻辑上都高度相关的邻居信息才会被采纳,极大地提升了 GI 结果的鲁棒性和视觉正确性。
三、 Hybrid Probe System: 屏幕空间与世界空间的协同
屏幕空间探针虽然密集,但让它们每一个都去追踪非常远的光线,性能开销是巨大的。光线追踪的成本不仅与光线数量有关,还与光线行进的距离和场景复杂度正相关。
-
核心观点: 采用两级探针系统,将GI计算任务分解,以应对不同距离的光照贡献,实现性能与质量的最佳平衡。
-
系统构成:
-
屏幕空间探针 (Screen-Space Probes):
- 职责: 负责处理 近场(Near-field) 、高频的 GI 细节。
- 特点: 密集、与视口绑定、动态更新。它们只追踪较短距离的光线。
-
世界空间探针 (World-Space Probes):
- 职责: 负责处理 远场(Far-field) 、低频的全局光照。
- 特点: 在世界空间中稀疏分布,可以预计算或较慢地更新。它们 缓存(Cache) 了来自远方(如天空、远景)的光照信息。
-
-
协同工作流:
- 当一个屏幕空间探针需要某个方向的光照信息时,它首先进行短距离的光线追踪。
- 如果光线没有在近处命中物体,而是射向了远方,它不会继续无限追踪下去。
- 取而代之,它会查询路径上附近的世界空间探针,直接从中采样已经缓存好的远场光照数据。
-
优势:
- 性能优化: 避免了大量屏幕空间探针进行昂贵的长距离光线追踪,将这部分计算压力转移给了稀疏且可缓存的世界空间探针系统。
- 关注点分离: 近处的细节由高密度的屏幕探针保证,远处的环境光由高效的世界探针提供,结构清晰,易于优化。
本节小结:
Lumen 的强大之处在于其高度的实战化和工程化思维。它并没有一味地追求物理上的“完美”,而是在有限的性能预算内,通过一系列精心设计的算法(自适应采样、启发式滤波、混合探针系统)来最大化视觉效果。这些技术环环相扣,共同构成了一个既高效又鲁棒的动态全局光照解决方案。
World-Space Radiance Cache
在前面的部分我们已经了解了 Lumen 的核心——基于屏幕空间的 Probe 系统。然而,纯粹的屏幕空间方案有其固有的局限性,尤其是在处理离屏物体、远距离光照以及相机快速移动时的稳定性问题。为了解决这些痛点,Lumen 引入了第二层级的光照缓存: 世界空间辐射缓存 (World-Space Radiance Cache)。
一、 超越屏幕空间:引入世界空间辐射缓存的动机
核心观点
纯粹的 Screen-Space Probe 系统在相机移动时极不稳定,每一帧都需要大量更新。为了获得更稳定、更高效的远距离全局光照,Lumen 引入了一个在世界空间中部署的、相对静态的 Probe 系统。
关键优势
- 稳定性 (Stability): 对于静态场景和光源(如太阳),World-Space Probe 的数据可以长时间保持有效,即使相机移动,也无需大规模重新计算。
- 效率 (Efficiency): 当相机移动时,只需在视野边缘增删少量 Probe,大部分 Probe 的数据可以复用,更新开销远低于需要每帧重建的 Screen-Space 系统。
- 远距离光照 (Far-Field Lighting): 它的核心目标就是解决远距离光照的采集问题,为 Screen-Space Probe 提供它们自身难以触及的 GI 数据。
二、 World-Space Probe 的设计与特性
核心观点
World-Space Probe 是 Screen-Space Probe 的“老大哥”和“最终后盾”。它被设计得 更远、更密、更精确,作为远距离光照信息的权威数据源。
关键术语
- Clipmap 结构: World-Space Probe 同样采用 Clipmap 的方式进行组织和管理。讲座中提到一个示例配置:
- 分辨率:
64 x 48 x 3 - 层级: 4 层
- 覆盖范围: 约 50 米
- 分辨率:
- 高密度采样 (High-Density Sampling): 为了能给任意方向的查询提供高质量数据,World-Space Probe 的采样密度远高于 Screen-Space Probe。
- Screen-Space Probe: 约
8x8个方向 - World-Space Probe: 高达
32x32(约1000+) 个方向
- Screen-Space Probe: 约
这种设计确保了无论 Screen-Space Probe 从哪个角度来“请教”,World-Space Probe 都有足够精度的光照数据可以提供。
三、 核心机制:连接光线 (Connected Rays)
核心观点
Lumen 最巧妙的设计之一,就是将 Screen-Space 的近场追踪和 World-Space 的远场查询无缝地“嫁接”起来,实现了高效的全距离光线追踪。这个过程被形象地比喻为 “中医接骨”。
算法流程
光线的旅程被分为两段:
-
Screen-Space Trace (近场):
- 从 Screen-Space Probe 出发,光线只追踪一小段距离。
- 这个距离不是固定的,而是动态计算的。其长度通常是该位置对应的 World-Space Probe Voxel 对角线长度的两倍。
- 这意味着在近处(World-Space Voxel 密集),追踪距离短;在远处(World-Space Voxel 稀疏),追踪距离长。
-
World-Space Query (远场):
- 当 Screen-Space 的光线追踪达到其最大距离后,它并不会终止,而是会查询其行进方向上最近的 World-Space Probe。
- 它直接“借用”该 World-Space Probe 在对应方向上已经采集好的光照数据,作为光线后续路径的结果。
双向优化
为了避免重复采样和资源浪费,这个机制是双向的:
- Screen-Space Probe: 只负责近场,然后将远场任务“外包”给 World-Space Probe。
- World-Space Probe: 在进行自身的光线追踪时,会 跳过 (Skip) 自身 Voxel 对角线长度的距离。它假设这部分近场区域已经被 Screen-Space Probe 负责了。
这种协同工作模式,极大地提升了整体光线追踪的效率。
四、 问题与对策:光线弯曲 (Ray Bending) Hack
问题描述:漏光 (Light Leaking)
由于 Screen-Space Probe 和 World-Space Probe 的位置和采样起点不同,可能会出现一种漏光现象:Screen-Space Ray 本应被一个近处的物体A挡住,但它追踪一小段后去查询的 World-Space Probe 恰好位于物体A的另一侧,导致这个 World-Space Probe 看不到物体A,从而返回了错误的、未被遮挡的光照信息。
- SS-Probe发出Ray,本应被遮挡物阻挡。
- 但它提前结束追踪,去查询WS-Probe。
- WS-Probe从自己中心发出Ray,绕过了遮挡物,导致漏光。
- Ray Bending通过连接WS-Probe中心和SS-Probe的Hit Point,强制光路“拐弯”,修正可见性。
解决方案:光线弯曲 (Ray Bending)
核心观点
为了解决上述可见性不匹配的问题,Lumen 采用了一种非常规但有效的 Hack 手段:让光线“拐弯”,以确保可见性判断的连续性。
算法步骤
- Screen-Space Ray 追踪到一个 查询点 (Query Point)。
- 找到最近的 World-Space Probe。
- 不直接使用 World-Space Probe 中与 Screen-Space Ray 同方向的数据。
- 而是重新构建一个方向:从 World-Space Probe 的中心 指向 Screen-Space Ray 的查询点。
- 沿着这个新的、“弯曲”后的方向去查询 World-Space Probe 的数据。
虽然这不符合物理规律,但它巧妙地将可见性判断的“锚点”拉回到了 Screen-Space 的追踪路径上,有效修复了大量因此产生的漏光瑕疵。
五、 性能优化:按需更新的 World-Space Probe
核心观点
尽管 World-Space Probe 采样密度极高,但 Lumen 通过一套精巧的 按需更新 (On-Demand Update) 机制,避免了每帧都更新所有 Probe 的巨大开销。
关键机制
- 最终着色依赖于 Screen-Space Probe: 场景的最终 GI 着色,直接使用的是屏幕空间的 Probe 数据。World-Space Probe 只是一个数据提供者。
- 标记系统 (Marking System):
- 一个 Screen-Space Probe 在进行插值计算时,会用到其周围的 8 个 World-Space Probe。
- 此时,它会给这 8 个 World-Space Probe 打上一个 “被需要”(Marked) 的标记。
- 选择性更新:
- 在每一帧, 只有被标记为 "Marked" 的 World-Space Probe 才需要被更新 (即执行
32x32的光线追踪)。 - 那些没有被任何 Screen-Space Probe 使用的 World-Space Probe (例如,在空旷区域或视锥之外) 则会被完全跳过,不产生任何计算开销。
- 在每一帧, 只有被标记为 "Marked" 的 World-Space Probe 才需要被更新 (即执行
- 时间缓存 (Temporal Caching): 结合之前提到的稳定性,如果场景和光源没有变化,即使一个 World-Space Probe 被标记了,只要它之前的数据仍然有效,也无需更新。
六、 效果对比与总结
通过结合 World-Space Radiance Cache,Lumen 的 GI 效果得到了质的飞跃。
- 无 World-Space Cache: GI 效果仅限于很近的范围(如2米),远处的物体无法对近处产生正确的间接光照,导致场景偏暗、漏光、效果不自然。
- 有 World-Space Cache: 远处的明亮表面可以将其光线贡献到场景的各个角落,形成了完整、准确且视觉效果自然的全局光照,这才是我们真正追求的 GI 效果。
总结来说,Lumen 的两层 Probe 架构是一个精妙的权衡:用高动态、高密度的 Screen-Space Probe 处理近场细节,用高稳定、高精度的 World-Space Probe 解决远场光照,并通过“连接光线”和“按需更新”两大核心机制将它们高效地融合在一起,最终在实时渲染中实现了媲美离线追光的效果。
最终着色与混合光线追踪策略
在经历了前面复杂的世界空间光照信息收集(Mesh Card、World Space Probe 等)之后,我们终于来到了 Lumen 工作流的最后阶段——将这些光照信息应用到屏幕像素上,并深入理解 Lumen 作为一套“集大成”系统的核心设计哲学。
一、 最终着色 (Final Shading)
核心观点:回归屏幕空间,利用球谐函数平滑光照
尽管 Lumen 在中间步骤中大量使用了世界空间的数据结构和追踪方法,但其最终的着色阶段,依然回归到了在屏幕空间进行。它将前面所有复杂计算的结果,最终“汇总”到屏幕空间的探针中,再进行最后的着色。
-
最终着色单元:Screen Space Probes
- 工作流程:在经历了 Mesh Card 光栅化、世界空间探针布局与光照注入、自适应采样和补漏等一系列操作后,Lumen 会在屏幕空间密集地放置探针(Screen Space Probes)。
- 信息汇总:这些屏幕空间的探针负责从已经计算好的世界空间光照数据(如 Surface Cache)中收集最终的入射光信息,为屏幕上的每一个像素提供间接光照。
-
利用球谐光照 (SH) 进行平滑处理
- 问题:即使经过了重要性采样,直接使用探针从各个方向采集到的光照信息(Radiance)可能仍然存在噪声和不稳定的闪烁(Jitter)。
- 解决方案:为了解决这个问题,Lumen 将每个探针采集到的入射光信息投影到 球谐函数(Spherical Harmonics, SH) 上。
- 本质作用:在这里,SH 本质上扮演了一个 低通滤波器(Low-pass Filter) 的角色。它有效地滤除了光照信息中的高频噪声,将原始的、可能带有瑕疵的光照信号,平滑成一个低频、柔和且稳定的光照环境。
- 最终效果:使用经过 SH 处理后的光照信息进行着色,可以得到非常柔和、自然的间接光照效果,大大提升了画面的稳定性和视觉质量。
二、 Lumen 核心思想:混合光线追踪策略 (Hybrid Tracing Strategy)
Lumen 之所以强大且能在实时渲染中落地,关键在于它并非依赖单一的“银弹”技术,而是根据不同情况,智能地选择最优的光线追踪方法,实现成本与效果的极致平衡。这正是它被称为 GI 思想“集大成者”和“缝合怪”的原因。
核心观点:成本与精度的分层权衡
Lumen 建立了一套光线追踪方法的成本“金字塔”,从最快但精度最低的方法,到最慢但精度最高的方法,形成一个优先级队列。
- 光线追踪方法的成本与精度层级 (由快到慢,由粗到精):
- 全局 SDF (Global SDF) 追踪:速度最快,但精度最低,适用于远距离追踪。
- 屏幕空间追踪 (Screen Space Trace - HZB):速度很快,利用深度缓存(HZB)在屏幕空间进行步进,适用于追踪屏幕内已有的几何体。
- 网格 SDF (Mesh SDF) 追踪:速度较快,在中近距离提供比全局 SDF 更精确的表面信息,是 Lumen 的核心追踪技术之一。
- 硬件光线追踪 (Hardware Ray Tracing):精度最高,但成本也最大。Lumen 可以利用硬件光追来追踪原始场景几何体或 Surface Cache (作为 Imposter)。
解读 Lumen 的“缝合怪”本质:可视化分析
讲座中提到了一张著名的、用红绿蓝三色来可视化 Lumen 所用追踪方法的图片,这张图直观地揭示了其混合策略的本质。
-
颜色含义:
- 红色区域: 主要使用 屏幕空间追踪 (Screen Space Trace)。
- 绿色区域: 主要使用 网格 SDF 追踪 (Mesh SDF Trace)。
- 蓝色区域: 主要使用 全局 SDF 追踪 (Global SDF Trace)。
-
关键洞察:权重混合而非单一选择
- 一个常见的误解是,每个像素或探针只会选择一种追踪方法。
- 但从图中平滑的颜色渐变可以看出,事实并非如此。讲师推测,对于一个探针需要发出的多条采样光线(例如 64 条),Lumen 会根据每条光线的具体情况, 动态地、按权重地混合使用 上述多种追踪方法。
- 例如,一个探针的某些光线方向可能在屏幕空间就找到了遮挡物(红色),另一些则需要深入场景,通过 Mesh SDF 找到(绿色),还有一些射向远方的光线则依赖 Global SDF(蓝色)。
Lumen 的光线追踪“瀑布” (Ray Tracing Cascade)
为了实现上述的混合策略,Lumen 为每一条光线都设计了一套清晰的“瀑布式”或“降级式”(Fallback)的追踪逻辑。
-
第一步:尝试屏幕空间追踪 (Screen Space Trace)
- 方法: 基于 HZB (Hierarchical Z-Buffer) 进行追踪。
- 限制: 最多步进 50 步。
- 结果: 如果命中,则直接采用该结果,这是最快的路径。
-
第二步:降级至网格 SDF 追踪 (Mesh SDF Trace)
- 触发条件: 屏幕空间追踪失败(未命中或超出步数限制)。
- 适用范围:
- 光线追踪距离相对较近 (例如,讲师提到代码中约为 1.8 米)。
- 追踪点位于相机附近一定范围内 (例如 40 米 内)。
- 优势: 能够返回精确的命中信息(如 Mesh ID, Hit Position, Normal),从而可以高效地从 Surface Cache 中查询详细的光照。
-
第三步:降级至全局 SDF 追踪 (Global SDF Trace)
- 触发条件: 上述两种方法均失败(例如,光线追踪距离超过了 Mesh SDF 的有效范围)。
- 适用范围: 用于处理更远距离的追踪需求 (例如,最远可达 200 米)。
这个逐级降级的策略,确保了 Lumen 能够用最低的性能开销,为每一条光线找到一个“足够好”的遮挡信息,是其实现高性能实时全局光照的关键所在。
一、核心追踪策略:Lumen 的多层次光线投射与着色
Lumen 的光线追踪并非单一方法,而是一个 基于距离和数据可用性的、高度优化的混合策略。它通过分层查询来平衡性能与质量,确保在不同场景和距离下都能获得合理的光照结果。
1. 近距离追踪:最高精度的细节层
当光线追踪的距离非常近时(例如讲座中提到的 1.8米 内),Lumen 采用最高质量的方案。
- 核心观点:在近距离,视觉细节最为重要,因此不惜成本使用最精确的数据源。
- 追踪目标: 网格SDF (Mesh Signed Distance Fields)。这是对单个物体几何细节的精确表达。
- 着色数据源:一旦命中,直接从 表面缓存 (Surface Cache) 中采样光照信息。Surface Cache 存储了场景表面高质量的光照快照。
- 返回数据:此路径可以返回详尽的命中信息,包括
Mesh ID,Hit World Position,Normal等,用于后续精确着色。
2. 中远距离追踪:性能与范围的平衡层
当光线行进距离超出近场范围,或者追踪起点本身就离摄像机很远时(例如讲座中提到的起点在 40米 外,或追踪距离为 200米),Lumen 会切换到更注重性能的全局方案。
- 核心观点:在中远距离,细节不再是首要矛盾,覆盖范围和追踪效率变得更加重要。
- 追踪目标: 全局SDF (Global Signed Distance Field)。这是一个覆盖整个场景的、相对粗糙的距离场,用于快速进行长距离遮挡判断。
- 着色数据源:命中后,从 体素光照 (Voxel Lighting) 中采样。这可以理解为存储在全局探针(Probes)或体素化结构中的、分辨率较低的环境光信息。
- 关键依赖: 体素光照 (Voxel Lighting) 在这个层级至关重要,它是远距离 GI 的主要能量来源。
3. 终极回退方案:天空光照
如果上述所有追踪(无论是网格SDF还是全局SDF)都失败了,意味着光线射向了无穷远。
- 核心观点:未命中任何场景几何体的光线,其能量来源就是天空。这是一个物理上正确且效果上至关重要的回退机制。
- 追踪目标: 天空盒 (Skybox) / 天球 (Sky Sphere)。
- 着色数据源:直接采样天空盒的颜色,即 天光 (Skylight)。
- 重要性:讲座强调这 并非简单的 Hack。天光是真实世界中一个非常重要的光源,尤其是在开阔场景中。如果缺少这一步,场景的整体亮度和氛围会大打折扣。
总结:Lumen 的追踪决策流程可以简化为以下逻辑:
// pseudo-code for Lumen's tracing logic
function TraceLumenRay(ray):
// 1. 近距离高质量追踪
if (ray.length < NEAR_DISTANCE_THRESHOLD):
hit = TraceAgainstMeshSDFs(ray)
if (hit.found):
return SampleFromSurfaceCache(hit.position, hit.normal)
// 2. 中远距离追踪
// (条件可能更复杂,比如结合起点位置)
hit = TraceAgainstGlobalSDF(ray)
if (hit.found):
return SampleFromVoxelLighting(hit.position)
// 3. 终极回退
return SampleFromSkybox(ray.direction)二、SSGI:不可或缺的高频细节补充
讲座特别强调了屏幕空间技术在 Lumen 中的重要性。
- 核心观点:Lumen 并非一个纯粹的“世界空间”GI 系统,它巧妙地融合了屏幕空间技术来弥补自身短板。
- 关键术语: 屏幕空间全局光照 (Screen Space Global Illumination, SSGI)。
- 作用:SSGI 极其擅长处理 近距离、高频 的光照细节,例如精细的接触阴影和屏幕空间反射。这些是基于SDF的追踪(尤其是全局SDF)难以精确捕捉的。
- Lumen 的集大成:Lumen 的强大之处在于它不是单一技术的胜利,而是 集过去十几年实时GI研究之大成 的产物。它将 SSGI、SDF、Voxel Probes 等多种思想有机地结合在一个统一、稳健的框架下。因此,批评 Lumen "只是SSGI" 是不准确的,但承认 SSGI 是其不可或缺的一部分则是事实。
三、工程奇迹:性能、效果与行业标杆
Lumen 不仅在算法上创新,更在工程实现上取得了巨大成功,真正实现了在主机平台上的落地。
1. 性能表现 (PS5)
- 全分辨率 (Screen Probes):约 3.74ms。
- 1/4 分辨率 (Screen Probes):可优化至约 2.15ms。
- 性能与质量的权衡:通过降低屏幕空间探针的分辨率,可以显著提升性能,但会牺牲一些光照细节的精度。讲座中提到的
16x16像素的探针规格,是 Epic Games 在大量测试后找到的、在 UE5 复杂场景中表现最佳的 甜点值 (sweet spot)。
2. 视觉效果与行业影响
- 效果标杆:Lumen 的效果已经接近甚至达到了过去离线渲染(如建筑效果图、动画电影)的水平,这为实时渲染树立了新的质量标杆。
- 未来引擎的标配:Lumen 的成功,标志着 实时动态全局光照 (Real-time Dynamic GI) 将成为下一代顶级游戏引擎的标配功能。
- 开山鼻祖:Lumen 被誉为这一代实时GI解决方案的开山鼻祖,它为未来十年实时渲染技术的发展奠定了坚实的基础,其创造者必将载入图形学史册。
四、总结与展望:渲染方程的漫漫征途
- 终极目标:所有渲染技术,包括 Lumen,都是为了求解 Kajiya 的渲染方程 (The Rendering Equation)。Lumen 只是攻克了其中 GI 的部分。
- 未来的挑战:透明/半透明材质 (Translucency)、毛发 (Fur) 等复杂效果的实时渲染依然是巨大的挑战。
- 对开发者的启示:游戏引擎开发是一个深度极深的领域,它融合了计算机科学最前沿的技术。作为引擎开发者,需要有啃硬骨头的准备和对前沿技术的敬畏之心。
待续问题:
讲座最后引出了一个深刻的问题,这也是当前行业正在积极探讨的:
在硬件光追飞速发展的今天,像 Lumen 这样复杂的软件光追方案与硬件光追在未来的引擎中将如何共存?它们都是必须的吗?
讲座总结与 Q&A:Lumen 的未来与实践挑战
本篇笔记总结了关于 Lumen 技术讲座的最终问答环节。讲师针对硬件光追的未来、Lumen 系统的复杂性以及实时 GI 的行业趋势等核心问题,给出了深刻的见解和前瞻性的思考。
Q1: 硬件光追 vs. 软件光追(Lumen SDF)的未来
核心观点
短期内,软件光追(如 Lumen 的 SDF方案)与硬件光追将长期共存,混合方案是未来值得探索的方向,但挑战巨大。 纯硬件光追的性能目前仍未达到完全取代高质量软件方案的程度,尤其是在多平台和性能受限的设备上。
关键要点
-
硬件光追的现状与瓶颈:
- 尽管硬件光追性能(如 NVIDIA 30、40 系显卡)在快速增长,但对于高质量、多次弹射的实时全局光照(GI)来说,其性能仍然不足。
- 讲师提到,30 系显卡约
10 billion rays/second的性能,对于 GI 而言依然捉襟见肘。 - 受限于摩尔定律的物理边界,未来 5-10 年硬件光追性能可能只会增长一个数量级,这不足以从根本上解决实时 GI 的性能难题。
-
软件光追的必要性:
- 平台兼容性:对于硬件更新换代较慢的平台,如游戏主机和移动端,一套高效的软件光追方案(Software Ray Tracing)是实现动态 GI 的关键。
- 当前最优解:Lumen 基于 距离场(SDF) 的方案,被认为是目前业界找到的、效果与性能平衡得最好的软件光追方案之一。
-
未来的探索方向:混合方案 (Hybrid Approach)
- 一个极具潜力的方向是:在支持硬件光追的设备上, 用硬件光追替换 Lumen 管线中的部分软件光追模块 (例如场景遍历和求交)。
- 巨大挑战:Lumen 是一个高度复杂且精细调校过的系统,任何模块的替换都可能破坏整体的稳定性和效果。它本身也存在很多 边缘情况(Broken Cases),引入新变量会让系统维护变得更加困难。
- 讲师对此保持 开放心态(Open-minded),认为这是一个值得尝试但极具挑战的研发方向。
Q2: Lumen 系统的复杂性、维护性与开发决策
核心观点
Lumen 是一个极其复杂的系统,完整复现和维护的成本极高。对于多数团队而言,更务实的做法是理解其核心思想,并根据项目需求“取其精华”,实现部分算法和效果。
关键要点
-
系统复杂性与开发门槛:
- 完整实现 Lumen 全套算法,对团队的 编程、数学和系统设计功底 有非常高的要求。
- 讲师建议,开发者可以不必追求完整复现,而是模块化地实现其部分关键技术(如 Screen Probe, Surface Cache 等),同样能获得非常出色的视觉效果。
-
实战化挑战 (Practical Challenges):
- 从技术演示(Tech Demo)到 可发售(Shippable) 的产品级功能,Lumen 面临着大量来自真实游戏场景的挑战。
- 维护性是其未来面临的巨大难题。
- 具体的挑战案例:
- 大规模地形 (Terrain): 地形无法有效生成 Mesh Card,导致其难以直接利用 Lumen 的核心数据结构 Surface Cache,需要特殊处理。
- 参与介质 (Participating Media): 如何处理场景中的雾、烟、体积光等效果,是 Lumen 需要解决的复杂问题。
- 高速动态场景 (Fast-moving Scenes): 在角色和镜头高速移动的游戏(如《索尼克》)中,Lumen 依赖的各种缓存和时间累积(Temporal Accumulation)技术能否保持稳定和高效,是一个巨大的考验。
-
行业展望:
- 讲师预测,未来一两年的 GDC 等技术会议上,会出现大量关于 在实际项目中落地 Lumen 所做的妥协、优化和定制化开发 的分享。这些实践经验对于业界将具有极高的参考价值。
Q3: 实时动态 GI 是否会成为次世代游戏标配?
核心观点
答案是肯定的(Yes)。实时动态 GI 将成为下一代 3A 游戏的标配,因为它从根本上提升了画面的真实感和沉浸感,一旦玩家适应了这种视觉质量,就很难再接受没有 GI 的画面。
关键要点
-
重塑玩家的视觉习惯:
- 人眼一旦被高质量的 GI 效果“训练”之后,就很难再回到过去那种缺乏真实光影互动的“塑料感”画面。
- GI 的有无,是区分“次世代”画面与传统画面的决定性因素之一。
-
广泛的艺术应用:
- GI 不仅限于写实风格渲染。它同样能极大地丰富 非写实渲染(NPR / 卡通渲染) 的表现力,创造出前所未有的光影和色彩效果。
-
技术发展的必然趋势:
- 尽管实现难度和性能开销巨大,但在未来 5-10 年,随着硬件算力的持续增长,实时动态 GI 将成为游戏开发的主流方向。
- Lumen 并非实时 GI 问题的终极答案,但它无疑是游戏图形史上一次 “关键性的、里程碑式的一步”。