老张是“星夜舞厅”的灯光师。舞厅开幕当晚他傻了眼:头顶的射灯把光圈打在舞池中央,但舞池边缘、卡座背后、过道尽头全是黑洞洞的,把射灯调到最亮也没用,客人投诉“气氛阴森”。物理同事告诉他:真实世界的光不会只走直线。太阳光打在红墙上,墙吃掉一部分光线变成红色,剩下的光被“弹”回来,再去照亮旁边的沙发;沙发又把光弹到茶几底下……正是这种“光蹦蹦跳跳”的反弹,让房间里没有绝对的黑角。这种模拟光在场景中反复反弹、最终照亮整个空间的技术,就叫全局光照(Global Illumination,GI)。 难点只有一个字:快。现实里一束光可以反弹成百上千次,如果让电脑把每一条光线逐条追一遍,等算完舞厅早打烊了。老张的破解思路是“聪明的偷懒”:在地板和墙壁里埋“光线温度计”(Light Probe),把房间切成一块块“冰块盒”(体素 Voxel),再让温度计自己会移动、按帧更新(DDGI)。这套组合拳让光“弹”得又快又像,舞厅终于亮堂又流畅。这就是实时全局光照的核心秘密。

岗位关联

理解实时 GI 三大方案(Light Probe / Voxel GI / DDGI)的原理与性能取舍,是游戏引擎与实时渲染工程师的核心竞争力,直接决定落地项目的画质上限和帧率下限。

目录
  1. 可视化演示
  2. 一、光照反弹原理:光是舞厅里停不下来的客人
  3. 二、采样方式权衡:全算会死,抽样才能活
  4. 三、Light Probe 技术:埋设“光线温度计”
  5. 四、Voxel GI 方案:把舞厅切成冰块盒
  6. 五、DDGI 技术:会跑的探针加上时光滤波
  7. 六、GI 性能优化:给光的反弹定预算
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

动态演示实时全局光照怎么实现才快的核心机制,通过总览/传输/转换/输出四种模式展示关键概念与交互流程。

本节配有交互动效(进入视口后自动加载,约 3KB)。

实时全局光照怎么实现才快:实时全局 实时全局光照问题与技术谱系 Light Probe光照探针 核心机制可视化 实时全局光照怎么实现才快整体架构与关键组件

一、光照反弹原理:光是舞厅里停不下来的客人

舞厅里最让老张头疼的不是射灯不够亮,而是“背光侧”。他做过试验:把一盏 2000 瓦的追光灯正对粉色沙发,沙发背后依然黑得能藏一只猫。物理同事一句话点醒了他——你看到的沙发是粉色的,说明沙发把白光里“非粉色的部分”吃掉了,可剩下的粉色光并没有消失,而是被弹到沙发背后去了。光在房间里就像停不下来的舞客,撞到墙就换方向接着走,这种反弹出来的光就是间接光。

图形学把“一个点最终看到多亮”这件事写成一条公式,叫渲染方程。翻译成大白话:某个点的出射光,等于自己发的光(只有灯丝、霓虹灯管这类光源才有),加上从四面八方射过来、又被材质反射出去的光。麻烦就出在“四面八方”四个字上——严格来说要考虑半球面上每一个方向,这是一个无穷维的积分,电脑算到死也算不完。

渲染方程就是舞厅的“光线账本”:任何一点最后的亮度,都等于自己发光加上所有方向反弹过来的光。账本本身很简单,可怕的是“所有方向”这四个字。老张由此悟出一个原则:想快,就不能对“所有方向”认死理,得聪明地偷懒——不全算,挑着算。

Lo(x,ωo)=Le(x,ωo)+∫Ωfr(x,ωi,ωo) Li(x,ωi) cos⁡θ dωi\displaystyle L_o(x,\omega_o) = L_e(x,\omega_o) + \int_{\Omega} f_r(x,\omega_i,\omega_o)\, L_i(x,\omega_i)\, \cos\theta\, d\omega_i
一句话解释:一个点看到的光 = 自己发的光 + 所有方向弹过来的光的总和。
  1. 出射辐射 $L_o$点 $x$ 朝观察方向 $\omega_o$ 发出去的总光量,画面最终的颜色就来自这一项。
    Lo(x,ωo)L_o(x,\omega_o)
  2. 自发光项 $L_e$只有点 $x$ 自身是光源时才有值,普通墙面、地板这一项都是零。
    Le(x,ωo)L_e(x,\omega_o)
  3. 双向反射分布函数 $f_r$描述材质怎么反光——磨砂墙把光向各个方向糊开,镜面把光原路弹回。
    fr(x,ωi,ωo)f_r(x,\omega_i,\omega_o)
  4. 入射半球积分把点 $x$ 上半球所有方向 $\Omega$ 射来的光 $L_i$,乘以反射率 $f_r$ 和角度衰减 $\cos\theta$,全部加起来。
    ∫Ωfr(x,ωi,ωo) Li(x,ωi) cos⁡θ dωi\int_{\Omega} f_r(x,\omega_i,\omega_o)\, L_i(x,\omega_i)\, \cos\theta\, d\omega_i

二、采样方式权衡:全算会死,抽样才能活

老张想统计舞客们最喜欢的歌,他不可能问遍全场几千人,那会问到天亮。他只随机拉住 32 个客人问一问,结果也八九不离十。光线追踪也一样——把“所有方向”的积分换成“随机抽几个方向估算”,这就是蒙特卡洛思想,也是几乎所有实时 GI 方案的数学地基。

具体做法是:每个像素每帧只发射 N 条光线(实时里通常 N 只有 1 到 4 条),每条光线随机选方向射出去。代价是样本太少会产生噪点,就像抽样人数太少、统计结果会抖来抖去。画质与速度的矛盾,本质上是 N 这个数字的矛盾:N 大则准但慢,N 小则快但花。

抽样把“问遍全场”变成了“问 32 个人”,蒙特卡洛让实时 GI 在数学上成为可能。可老张很快发现新问题:抽样再快,每帧每像素都重新问一遍,累积起来还是贵。更聪明的做法是——别问个体,直接在场地里固定埋一批“光线温度计”,舞客走到哪,读附近温度计就行。

L^o≈1N∑j=1Nfr(x,ωj,ωo) Li(x,ωj) cos⁡θjp(ωj)\displaystyle \hat{L}_o \approx \frac{1}{N} \sum_{j=1}^{N} \frac{f_r(x,\omega_j,\omega_o)\, L_i(x,\omega_j)\, \cos\theta_j}{p(\omega_j)}
一句话解释:随机抽 N 个方向分别算贡献,再按“被抽中的概率”校正后取平均,就是积分的近似值。
  1. 采样总数 $N$每个像素打出的光线条数。N 越大结果越接近真值,但计算量线性上涨。
    NN
  2. 概率密度 $p(\omega_j)$表示方向 $\omega_j$ 被抽中的可能性有多大,抽到“稀有”方向时必须给大权重补偿。
    p(ωj)p(\omega_j)
  3. 除以概率校正每一项都除以 $p$,消除抽样不均匀带来的系统偏差,这是无偏估计的关键一步——否则结果会整体偏亮或偏暗。
    1p(ωj)\frac{1}{p(\omega_j)}
  4. 取平均把 N 个样本的结果累加求平均,作为无穷积分的近似值。
    1N∑j=1N(⋅)\frac{1}{N}\sum_{j=1}^{N}\left(\cdot\right)

三、Light Probe 技术:埋设“光线温度计”

老张在舞厅的地板、墙壁、卡座之间埋了一排排温度计。温度计自己不发光,只负责感知附近的光线环境,记录“这个位置大概能接收到多少来自哪个方向的光”。跳舞的人走到任意位置,系统读取附近几支温度计取一个加权平均,就能估计出那里的亮暗和颜色。这套东西在引擎里叫 Light Probe(光照探针)。

探针里存的不是照片,而是一组球谐函数(Spherical Harmonics,SH)系数。球谐函数可以理解为“标准方向波形”,用一小组数字就能近似描述“来自各个方向的光强分布”。查询时对周围探针的系数做权重插值,成本极低,非常适合静态场景的烘焙(离线预计算)后用。

温度计方案把“逐像素追光线”升级成了“读表加插值”,速度快得飞起。但它的软肋是温度计钉死在原地:如果舞厅把一个大沙发推到墙边,固定探针来不及反映变化,沙发附近就会出现一圈“测不准”的假光晕。老张挠头:又要快又要能变,怎么办?答案是——把房间切成格子。

I(ω)≈∑l=0n∑m=−llclm Ylm(ω)\displaystyle I(\omega) \approx \sum_{l=0}^{n} \sum_{m=-l}^{l} c_{lm}\, Y_{lm}(\omega)
一句话解释:把任意方向的光照分布,拆成若干块“标准积木”(球谐基函数)的加权组合,系数就是每块积木的用量。
  1. 球谐基函数 $Y_{lm}$一组预先定义好的“方向波形”,下标 $l$ 和 $m$ 决定波形的形状。
    Ylm(ω)Y_{lm}(\omega)
  2. 阶数 $l$精度档位。$l=0$ 只有一块“平均亮度”积木,$l=2$ 有 9 块,能描述柔和的方向差异;阶数越高越精细但存储越多。
    l=0,1,2,…l = 0,1,2,\dots
  3. 系数 $c_{lm}$每块积木的用量,探针实际保存的就是这些数字,通常只要 9 到 27 个浮点数,非常省内存。
    clmc_{lm}
  4. 求和重建把积木按用量拼起来,任意方向 $\omega$ 的入射光就近似复原了。
    ∑l=0n∑m=−llclm Ylm(ω)\sum_{l=0}^{n}\sum_{m=-l}^{l} c_{lm}\, Y_{lm}(\omega)

四、Voxel GI 方案:把舞厅切成冰块盒

老张弄来一块看不见的“大冰块”,把舞厅空间数学地切成了指甲盖大的小方块(体素),每个方块里记录“这个区域大概存了多少光”。光线穿过舞厅时,不需要真的去追每一束光的完整路径,只需要沿途查查方块里的数值,就像光线在一块半透明的冰里穿行,被内部的小气泡逐层照亮。这就是 Voxel GI(体素全局光照)。

具体做法是:先把场景实时或离线地体素化,生成一张 3D 纹理(可以想象成一个由小立方体组成的大数组),每个格子存该区域的辐照度近似值。渲染时从物体表面出发,沿一束“光锥”向前推进,每走一步查询沿途体素、加权累加,估算打进这个点的间接光。

冰块盒方案的精妙在于:查询体素是查表操作,不关心光线是从哪条路径来的,快得惊人。代价是分辨率有限——格子太大会让墙角的光晕发糊,格子太小内存会爆炸。老张盯着这些冰块盒想:既然体素费内存、探针又不够动态,能不能让温度计自己跑起来,再用上一帧的结果帮这一帧“去噪”?

A(r)=∑k=1Mwk Vτk ⁣(x+k Δr)\displaystyle A(r) = \sum_{k=1}^{M} w_k\, V_{\tau_k}\!\left(x + k\,\Delta r\right)
一句话解释:从表面点出发,沿锥体方向一步步前进,把沿途查到的体素光照值加权求和,就是该方向的间接光。
  1. 步长 $\Delta r$每一步前进的距离,通常取当前精度等级下一个体素的尺寸。
    Δr\Delta r
  2. 体素查询 $V$在体素网格中查找位置 $x + k\Delta r$ 存储的光照值,下标 $\tau_k$ 表示精度等级——远处用粗格子,近处用细格子。
    Vτk(x+k Δr)V_{\tau_k}(x + k\,\Delta r)
  3. 权重 $w_k$越靠前的体素离表面越近、遮挡越强,贡献越大;权重分布模仿真实光的距离衰减。
    wkw_k
  4. 累加 $M$ 步把沿途 $M$ 个体素的加权采样结果累加,得到该方向进入表面的间接光量。
    ∑k=1Mwk Vτk ⁣(x+k Δr)\sum_{k=1}^{M} w_k\, V_{\tau_k}\!\left(x + k\,\Delta r\right)

五、DDGI 技术:会跑的探针加上时光滤波

老张给温度计装上了滑轮和遥控器。探针不再钉死在地板上,而是悬在舞厅空中的关键位置——沙发挪了位置,探针也会跟着滑动补位。更妙的是,老张不让探针每次全都重新测量:过去一秒钟刚测过的数据,稍微旧一点也没关系,和新鲜数据一混合,既稳又新,画面不再闪烁。这套思路就是 DDGI(Dynamic Diffuse Global Illumination,动态漫反射全局光照)。

DDGI 在场景中维护一层稀疏的探针网格,每帧用极少量光线追踪更新每个探针的辐射度,然后靠时域滤波把这少量的采样结果“炖”成平滑画面。所谓时域滤波,就是让当前帧的结果和上一帧的历史结果做一个加权混合——历史数据负责压噪,新数据负责跟随场景变化。

移动探针加上时域滤波,老张终于同时拿到了快和准。他手里现在有三套工具:固定温度计(Light Probe)适合静态烘焙场景,冰块盒(Voxel)适合中等动态场景,会跑的探针(DDGI)专治大型开放世界。但工具再好也得算账——最后一节,老张开始精打细算每一毫秒的花销。

It=α I^t−1+(1−α) Iray\displaystyle I_t = \alpha\, \hat{I}_{t-1} + (1-\alpha)\, I_{\text{ray}}
一句话解释:本帧最终亮度 = 上一帧压过噪的历史值 × 权重 α + 本帧新采样的光线结果 × 权重 (1−α)。
  1. 当前帧结果 $I_t$探针这一帧要写回网格的最终亮度值。
    ItI_t
  2. 历史平滑值 $\hat{I}_{t-1}$上一帧产出并存储的结果。因为场景变化不会瞬间翻脸,它可作为“旧温度”压住噪点。
    I^t−1\hat{I}_{t-1}
  3. 新鲜采样 $I_{\text{ray}}$本帧新追踪到的几束光线的结果,带噪点但最“跟手”,能反映刚发生的动态变化。
    IrayI_{\text{ray}}
  4. 混合系数 $\alpha$$\alpha$ 越接近 1 越信任历史(更稳、更糊),越接近 0 越信任新采样(更准、更闪),工程上通常取 0.9 左右。
    α∈[0,1]\alpha \in [0,1]

六、GI 性能优化:给光的反弹定预算

舞厅开业一个月,老张收到电费单差点晕过去。老板说直接把灯关一半,客人不答应。老张的办法是“分级照明”:舞池中央的灯每帧都精打细算,照得又亮又细腻;走廊尽头的墙每 4 帧才更新一次,反正没人盯着角落发呆。全程不降亮度,只降“更新频率”和“计算精度”,客人的观感几乎没有变化。

实时 GI 的优化本质就是预算控制加多级细节(LOD)。常用的三板斧是:降分辨率计算——用 1/4 分辨率算间接光再上采样;空间共享——相邻像素复用同一束光线的结果;时域累积——用上一节的 α 滤波把噪点摊到历史上。还有一个关键武器是级联体素:近处用精细的小体素、远处用粗糙的大体素,把“内存爆炸”变成可控增长。

老张的“分级照明”让舞厅电费降了七成,客人根本没发现角落的灯光更新变慢了。实时 GI 的诀窍从来不是“算得更准”,而是“在该准的地方准、该省的地方省”。温度计、冰块盒、会跑的探针,配上一本精打细算的预算账本——这就是实时全局光照又快又真的全部秘密。

Dl=2 l×D0\displaystyle D_l = 2^{\,l} \times D_0
一句话解释:体素网格每一层的间距按 2 的幂次翻倍,离相机越远的层越粗,用指数增长换取无限远覆盖。
  1. 最细级间距 $D_0$离相机最近一层的体素边长,通常设 0.1 到 0.25 米,保证近景细节。
    D0D_0
  2. 级数 $l$第几层精度,$l=0$ 最细,$l=1$ 间距翻倍,$l=2$ 再翻倍,依此类推。
    l=0,1,2,…l = 0,1,2,\dots
  3. 翻倍因子 $2^{\,l}$每远一级体素边长翻倍,存储从 1 个单位变成约 1+8+64=73 个单位,用受控增长换大范围覆盖。
    2 l2^{\,l}
  4. 帧预算不等式间接光开销必须小于帧预算减去直接光开销,超了就自动降级——降分辨率、减级数、降采样数。
    CIndirect≤Bframe−CDirect\displaystyle C_{\text{Indirect}} \le B_{\text{frame}} - C_{\text{Direct}}

AI 应用场景

游戏引擎中的 GI 方案选型

阶段:算法选型与部署调优

解决的问题:传统引擎只靠离线烘焙光照贴图,场景一有动态物体就“露馅”。用 Light Probe 配合静态烘焙、再叠加实时探针,可以在角色移动、门开关时提供平滑的过渡间接光,避免画面闪烁。

具体案例:某开放世界手游的地窖和建筑背阴面,用 Light Probe 预烘焙低频间接光;日间草原走进地窖的瞬间,亮度过渡自然、无跳变,帧率稳定在 60fps。

LED 虚拟影棚的实时补光

阶段:实时渲染推理

解决的问题:LED 影棚里演员站在大屏幕前,屏幕上放虚拟场景,但演员脸上只有屏幕直射光,缺少环境反弹光,看起来像“贴上去的纸片人”。用 DDGI 思路布设动态探针,每帧捕捉屏幕光并回弹到演员面部,肤色立刻立体起来。

具体案例:国内外虚拟制片项目参考《曼达洛人》的 StageCraft 方案,在 LED 墙前用实时探针为演员补充间接光,后期实拍补光工作量减少约 40%。

建筑与数字孪生的实时漫游

阶段:性能优化与效果评估

解决的问题:建筑漫游要求 60fps 下呈现完整 GI,中端显卡吃不住全分辨率间接光。用 Voxel GI 的级联精度做“近细远粗”,配合降分辨率加时域累积,画质目视无损。

具体案例:某数字孪生园区项目在 WebGL 端把间接光计算分辨率降到 1/4,叠加上一帧时域累积,帧率从 22fps 提升到 58fps,评委肉眼难辨差异。

Python 演示

python53 行
import math  class VoxelGridGI:    """极简二维体素全局光照模拟器: 直接光照 + 多轮反弹扩散."""     def __init__(self, cols, rows, source):        self.cols = cols        self.rows = rows        self.source = source  # (col, row) 光源所在格        self.grid = [[0.0 for _ in range(cols)] for _ in range(rows)]     def direct_pass(self, intensity=1.0):        """阶段1: 只写直接光照, 亮度随距离衰减."""        sx, sy = self.source        for y in range(self.rows):            for x in range(self.cols):                dist = math.hypot(x - sx, y - sy)                self.grid[y][x] = max(0.0, intensity / (1.0 + dist * 0.8))     def bounce_pass(self, iterations=4, reflect=0.5):        """阶段2: 模拟间接光反弹, 每轮把光向四邻居扩散一份."""        for _ in range(iterations):            new_grid = [[0.0 for _ in range(self.cols)] for _ in range(self.rows)]            for y in range(self.rows):                for x in range(self.cols):                    val = self.grid[y][x]                    for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)):                        nx, ny = x + dx, y + dy                        if 0 <= nx < self.cols and 0 <= ny < self.rows:                            new_grid[ny][nx] += val * reflect / 4.0            for y in range(self.rows):                for x in range(self.cols):                    self.grid[y][x] += new_grid[y][x]     def sample(self, x, y):        """阶段3: 查询某格光照, 对应实时渲染读取体素."""        return self.grid[y][x]  def main():    sim = VoxelGridGI(cols=16, rows=10, source=(4, 5))    sim.direct_pass()    corner = sim.sample(13, 8)    sim.bounce_pass(iterations=4, reflect=0.5)    bright = sim.sample(13, 8)    print("角落格(13,8) 直接光后亮度: %.3f" % corner)    print("角落格(13,8) 反弹4轮后亮度: %.3f" % bright)    print("间接光贡献占比: %.1f%%" % ((bright - corner) / bright * 100))  if __name__ == "__main__":    main()
目的:用 2D 网格模拟“直接光照 + 4 轮反弹”的过程,量化展示间接光对暗部角落的贡献,对应正文的体素 GI 思想。
  1. dist = math.hypot(x - sx, y - sy)计算格点到光源的直线距离,距离越远光越弱,模拟射灯衰减。
  2. self.grid[y][x] = max(0.0, intensity / (1.0 + dist * 0.8))把直接光按距离衰减写进体素网格,就像老张的射灯只照亮光源附近。
  3. new_grid[ny][nx] += val * reflect / 4.0把格子的光分出 reflect 比例、均摊给上下左右四个邻居,模拟一次反弹。
  4. self.grid[y][x] += new_grid[y][x]本轮反弹光叠加回原网格,对应多次反弹持续累积间接光。
  5. sim.bounce_pass(iterations=4, reflect=0.5)反弹 4 轮、每轮保留一半能量,轮数越多角落越亮。
  6. corner = sim.sample(13, 8)查询最远离光源的角格亮度,用于对比反弹前后的变化。
  7. if __name__ == "__main__":把演示逻辑放进主入口,脚本可以直接运行。

常见误区

误区既然探针是采样点,那肯定是埋得越多越准。我直接往场景里扔几千个 Light Probe,覆盖得密不透风,画质肯定拉满。

正解探针数量线性增加内存和更新开销,且画质并非随探针数线性提升。插值错误主要发生在“探针之间跨遮挡墙体”的场景,比如两个探针中间隔着一堵墙,插值会把墙后的光错误地糊过来。正确做法是让探针避开遮挡边界、在关键转折处加密,数量控制在几百个级别,而不是无脑堆几千个。

为何常见:直觉上“采样点越多越接近真实”,但忽略了实时系统的预算约束和插值结构本身的误差来源。

误区Voxel GI 的格子切得越小,光晕越锐利。我把体素边长设成 1 厘米,画质肯定无敌。

正解体素网格的内存随边长按三次方膨胀,1 厘米体素覆盖一个舞厅会直接爆显存。而且实时 GI 处理的是低频间接光,墙角发糊一点肉眼根本注意不到。正确姿势是使用级联结构:近处 0.1 米、远处 1 米以上,把分辨率花在观众盯着看的地方。

为何常见:只看“精度越高越好”的单目标,忽略了内存和带宽的硬约束,这是新手最容易踩的工程大坑。

误区DDGI 又动态又实时,是终极方案。以前那些 Light Probe 烘焙、光照贴图全都可以删了,一劳永逸。

正解DDGI 对低频间接光表现出色,但对高频细节——比如小物件的接触阴影、细窄缝隙里的光线——采样率远远不够,而且每帧光追本身有固定开销。工业界主流做法是混合:静态烘焙负责高质量细节,DDGI 负责动态补充,各司其职。

为何常见:新技术发布时宣传话术容易让人产生“替代一切”的错觉,忘了每种方案都有分辨率与预算的物理极限。

误区既然时域滤波能压噪,我把 α 调到 0.99,历史权重拉满,画面肯定又稳又干净。

正解α 过大意味着几乎完全信任历史帧,一旦有角色跑过、灯光切换,旧画面会拖出长长的“鬼影”或残像。正确做法是结合运动向量和深度校验:运动剧烈的区域自动降低 α、甚至立刻重置历史,静止区域才放心地高 α 压噪。

为何常见:理解了“历史能压噪”却忽略了“历史会过期”,看不到动态与稳定之间的对立统一关系。

面试题

Q1. 渲染方程中哪一项表示“点自身发出的光”?

单选题 · easy

  • A.入射光辐照度项
  • B.自发光辐射项← 正确答案
  • C.反射分布函数项
  • D.余弦角度衰减项

答案:B。渲染方程中 $L_e$ 是自发光项,只有光源自身才有非零值。入射光、BRDF、cosθ 分别描述外界射来的光、材质反射特性、几何角度衰减,都不是“自身发出”。

Q2. 蒙特卡洛采样时,降低噪点最直接的方法是?

单选题 · easy

  • A.提高每个像素的采样数← 正确答案
  • B.降低屏幕输出分辨率
  • C.关闭所有直接光照
  • D.成倍提高光源的强度

答案:A。蒙特卡洛估计的方差与采样数 N 成反比,增加 N 是降低噪点的根本手段。降低分辨率、关光照、调强度都只是改变画面内容或输出,不能消除采样不足产生的方差抖动。

Q3. Light Probe 中实际长期保存的数据是?

单选题 · mid

  • A.场景的深度缓冲数据
  • B.光源的移动轨迹数据
  • C.球谐函数的紧凑系数← 正确答案
  • D.逐像素高动态范围图

答案:C。Light Probe 存的是球谐函数系数,通常只有 9 到 27 个浮点数,用紧凑的方式描述“各方向光强分布”。深度缓冲、光源轨迹、逐像素 HDR 图都不是探针的存储形态。

Q4. 关于体素化全局光照,下列说法正确的是?

多选题 · hard

  • A.内存随级联级数快速膨胀← 正确答案
  • B.查询体素是快速查表操作← 正确答案
  • C.体素越小内存开销越小
  • D.远近可用不同精度体素← 正确答案

答案:ABD。体素相邻级之间边长翻倍,存储量呈三次方膨胀,A 对;查询体素是直接读 3D 纹理,B 对;级联 LOD 近细远粗,D 对。C 说反了——体素越小意味着格子数量越多,内存开销反而越大。

Q5. DDGI 时域滤波中,混合系数 α 的典型取值为?

单选题 · mid

  • A.固定取值 1.0 效果最稳
  • B.取 2.0 可以加强压噪
  • C.取 0.1 更信任历史值
  • D.取 0.9 附近更信任历史← 正确答案

答案:D。α 是历史结果的权重,取 0.9 左右代表 90% 信任上帧、10% 信任新采样,压噪和跟手性取得平衡。1.0 完全不更新、2.0 超出 0~1 区间是非法取值,0.1 则是过度信任新采样、噪点压不住。

Q6. 时域滤波 α 取值过大会产生什么副作用?

单选题 · mid

  • A.动态物体拖出残影鬼影← 正确答案
  • B.静态画面开始持续闪烁
  • C.间接光整体亮度成倍翻
  • D.体素网格会从画面消失

答案:A。α 过大意味着旧画面权重极高,场景中高速运动的物体会在屏幕上留下“鬼影”拖尾。闪烁是 α 过小的典型表现,亮度翻倍和网格消失与时域滤波没有直接因果关系。

Q7. 以下属于实时 GI 性能优化手段的是?

多选题 · extreme

  • A.降低间接光渲染分辨率← 正确答案
  • B.让相邻像素复用采样结果← 正确答案
  • C.每像素每帧追一千条光线
  • D.使用上一帧结果参与滤波← 正确答案

答案:ABD。降分辨率再上采样、空间复用邻居采样、时域累积复用历史帧,是实时 GI 的三大优化武器。C 是离线渲染(如电影级 CGI)的做法,实时场景每像素追 1000 条光线会直接卡死。

Q8. 使用 Light Probe 查询某点光照时采用的方式是?

单选题 · easy

  • A.全场景所有探针取平均
  • B.随机抽一个探针读数
  • C.邻近几个探针加权插值← 正确答案
  • D.永远读取最近一个探针

答案:C。查询点周围几个探针按距离做加权插值,能得到连续平滑的光照过渡,这是探针方案的标准做法。全场景平均会糊成一片,随机抽一个会闪,只读最近一个会在探针边界出现明显接缝。

Q9. 关于球谐函数阶数 l,下列正确的是?

多选题 · hard

  • A.零阶只能表达平均亮度← 正确答案
  • B.阶数越高表达细节越多← 正确答案
  • C.阶数越高存储开销越小
  • D.二阶已够多数低频间接光← 正确答案

答案:ABD。l 越大,球谐基函数越多、能描述的高频细节越丰富,但系数数量是 $(l+1)^2$ 量级上涨,存储开销随之增大,所以 C 说反了。间接光是典型的低频信号,实际工程中常用 2 到 3 阶。

Q10. 关于 DDGI 探针的特性,描述正确的是?

单选题 · hard

  • A.探针固定在场景中离线烘焙
  • B.探针仅保存深度与法线数据
  • C.探针必须逐像素地密集排布
  • D.探针每帧更新并做时域滤波← 正确答案

答案:D。DDGI 的探针在网格上动态分布,每帧用稀疏光线追踪更新辐射度,并用时域滤波压噪,这与“固定离线烘焙”的 Light Probe 本质不同。探针存的是光照数据而非深度法线,且是稀疏排布而非逐像素。