中EN
概念页

光谱渲染

Spectral Rendering

光谱渲染 (Spectral Rendering) 在光传输中直接处理波长,而不是 RGB 三元组。pbrt 第 4 版在每条相机光线上随机采样少数几个波长(默认 4 个),把光谱积分并入渲染本来就在做的蒙特卡洛积分;最后用颜色匹配函数估计像素的 XYZ/RGB。输入端的 RGB 颜色则通过"sigmoid 二次多项式"转换成平滑光谱。本页按 PBRT 4e 4.5、4.6.4–4.6.6 节整理。

为什么要光谱渲染(PBRT 4.6.4)

色彩空间对加法封闭,但分量相乘的结果取决于所选色彩空间。PBRT 的例子:反射率和光照都是 sRGB 绿 (0,1,0)(0,1,0),在 sRGB 里相乘仍是 (0,1,0)(0,1,0);同一颜色在 ACES2065-1 中约为 (0.38,0.82,0.12)(0.38,0.82,0.12),相乘得 (0.14,0.67,0.01)(0.14,0.67,0.01),不但变暗,饱和度还高到超出可实现颜色的马蹄形。本页用标准原色与白点矩阵复算:sRGB 绿在 ACES2065-1(不做色适应)中为 (0.375,0.817,0.118)(0.375,0.817,0.118),平方为 (0.141,0.667,0.014)(0.141,0.667,0.014),与书一致。 另外,光谱表示能自然地处理色散、薄膜虹彩和表面微结构衍射等依赖波长的效果。

观点来源
RGB 渲染在多数情况下够用,只在特殊情况失败RTR4 第 8 章(见 色度学与色彩空间"用 RGB 渲染")
RGB 相乘的结果随色彩空间任意变化,即使最终输出 RGB 也应做光谱渲染PBRT 4e 4.6.4

游戏引擎几乎都在线性 RGB(常是 sRGB/Rec.709 原色,或 ACEScg)中做光照;了解 PBRT 的论证有助于理解为什么换工作色彩空间会让同一材质的多次反弹颜色发生变化。

光谱的表示(PBRT 4.5.1–4.5.3)

pbrt 的波长单位是 nm。Spectrum 接口用 TaggedPointer 实现运行时多态:指针里带类型标签,Dispatch() 根据标签调用具体类型的方法,接口是隐式的(实现类并不继承 Spectrum),这样 CPU 与 GPU 代码都能用,又避免虚函数表(细节在附录 B.4.4)。必须实现 operator()(λ) 和 MaxValue()(后者用于给光源功率求上界,以便按贡献采样光源)。

实现作用
ConstantSpectrum常值光谱,常用于表示"没有这类散射"的零光谱
DenselySampledSpectrum在整数波长区间上每 1 nm 存一个值,求值只需一次查表;覆盖可见光约需 2 kB
PiecewiseLinearSpectrum(λi,vi)(\lambda_i,v_i) 对之间线性插值,变化快的区域可加密;区间外为 0
BlackbodySpectrum归一化黑体光谱:用维恩定律求峰值波长,除以峰值使最大值为 1(总功率随 T4T^4 增长太快)
RGBAlbedoSpectrum / RGBUnboundedSpectrum / RGBIlluminantSpectrum由 RGB 转来的反射率、无界、光源光谱(见下文)

pbrt 二进制里内嵌了常用光谱(光源发射谱、导体的复折射率、各种玻璃的色散折射率),用 GetNamedSpectrum() 按名称取;Spectra::X/Y/Z() 返回 CIE 匹配曲线,Spectra::D() 返回给定色温的 D 光源。

点采样光谱:SampledSpectrum 与 SampledWavelengths(PBRT 4.5.4)

直接对光谱函数做运算会失控:两个分段线性光谱相乘变成分段二次,再乘次数继续升高,不同表示两两组合还要各写实现。pbrt 的做法是把波长也当作蒙特卡洛的一个积分维度。例如某波段的辐照度

E≈1n∑i=1nLi(p,ωi,λi)∣cos⁡θi∣pω(ωi) pλ(λi)(式 4.21)E\approx\frac1n\sum_{i=1}^n\frac{L_i(p,\omega_i,\lambda_i)|\cos\theta_i|}{p_\omega(\omega_i)\,p_\lambda(\lambda_i)}\quad(\text{式 4.21})

只需在采样到的离散波长上求被积函数。

  • SampledSpectrum:NSpectrumSamples 个值(默认 4,改动需重新编译),逐分量的加减乘除、SafeDiv(除数为 0 时得 0)、Lerp/Sqrt/Clamp/Pow/Exp、Min/MaxComponentValue、Average;可隐式转成 bool 判断是否全零,以便反射率为 0 时跳过追踪。
  • SampledWavelengths:存波长及其 PDF。SampleUniform() 只用一个随机数:第一个波长均匀取,其余按等间距 δ\delta 递增并在越界时回绕,得到分层的波长样本,且改变样本数不必改调用方代码。
  • 色散时只留一个波长:折射方向依赖波长时,一条光线无法再同时携带多个波长。TerminateSecondary() 把除 lambda[0] 外的 PDF 置 0,并把幸存者的 PDF 乘以 1/n1/n,原理同俄罗斯轮盘赌(见 蒙特卡洛积分与重要性采样)。总让 lambda[0] 幸存之所以正确,是因为每个波长都来自同一分布;因此 SampleUniform() 不能把 lambda[0] 固定放在第一个分层里。
  • 为什么两个类分开:把波长存进每个 SampledSpectrum 可以在运行时发现"混用不同波长"的 bug,但临时对象极多,GPU 路径同时存几百万个,所以要尽量小;实践中波长在路径开始时采样并沿路径不变,混用的 bug 很少见。pbrt 早期在调试版里带过检查,几个月没发现 bug 后删掉了。

每条光线带几个波长(PBRT 4.6.5)

每条光线只带一个波长时,要用一个光谱值估计 R、G、B 三个值,颜色噪声很大。光线路径通常与波长无关、而多算几个波长的光照很便宜,所以每条光线带多个波长是有效的分裂(splitting)。PBRT 的实验(只有点光源直接光照、散射项零方差、误差全部来自颜色匹配积分的场景):

  • 32 个波长比 1 个波长慢约 1.6 倍;独立采样时 MSE 按 O(1/n)O(1/n) 下降,分层采样误差低几个数量级且收敛更快;32 个分层波长的效率比 1 个高一百万倍以上,曲线还没走平。
  • 默认仍取 4 的三个理由:8 到 32 个波长误差虽差约 500 倍,但图像已无法分辨;每像素本来就要取多条路径,像素内的总波长数随之增加;最重要的是,真实场景里其他蒙特卡洛误差源占主导——在一个光照复杂的场景中,6 个以后 MSE 收益有限,8 个时效率最高。

从 RGB 到光谱(PBRT 4.6.6)

由于同色异谱,RGB→光谱是欠定问题;这也意味着可以挑一个简单高效的解。要区分三类光谱:光源光谱(非负、无界,从平滑到尖峰)、反射率光谱(在 [0,1][0,1] 内,通常平滑)、无界光谱(非负、无界但不发光,如折射率、介质系数)。目标:

  1. 恒等:RGB→光谱→RGB 回到原值;
  2. 平滑:光谱本身平滑,且 RGB 的微小变化只引起光谱的微小变化(否则换光源时纹理上会出现接缝);
  3. 能量守恒:[0,1][0,1] 的 RGB 得到 [0,1][0,1] 的光谱。

sigmoid 多项式模型(Jakob & Hanika 2019):

s(x)=12+x21+x2,S(λ)=s(c0λ2+c1λ+c2)(式 4.25、4.26)s(x)=\frac12+\frac{x}{2\sqrt{1+x^2}},\qquad S(\lambda)=s(c_0\lambda^2+c_1\lambda+c_2)\quad(\text{式 4.25、4.26})

二次多项式能表示常数、线性、中间单峰或两端高的曲线,覆盖真实反射率的常见形状(白/黑、近线性、单峰的绿黄、双峰的蓝紫);sigmoid 严格单调、把值压进 (0,1)(0,1) 保证能量守恒。全吸收/全反射需要多项式趋于 ±∞\pm\infty,会出现 ∞/∞\infty/\infty 的 NaN,代码单独处理。MaxValue() 只需比较 360/830 nm 两端与多项式驻点(sigmoid 单调)。

  • 系数求解:最小化往返误差 arg⁡min⁡c∥(r,g,b)−∫(R,G,B)(λ)S(λ,c)W(λ) dλ∥\arg\min_c\left\|(r,g,b)-\int(R,G,B)(\lambda)S(\lambda,c)W(\lambda)\,d\lambda\right\|,WW 为白点(因而灰色 RGB 映射到常数光谱);在 sRGB 这类紧凑色域内可达零误差,ACES2065-1 等含虚色的色域做不到,所以误差用感知均匀的 CIE76 ΔE\Delta E(在 CIELAB 中算 L2L_2 距离);求解用 Gauss–Newton,每次约几微秒,太慢,不能逐纹素现算。
  • 预计算表:编译 pbrt 时对 sRGB、DCI-P3、Rec2020、ACES2065-1 各算一张覆盖 [0,1]3[0,1]^3 的系数表(cmd/rgb2spec_opt.cpp)。不只按二维色度表(把 (1,0,0)(1,0,0) 的光谱缩放给 (c,0,0)(c,0,0)),因为高饱和色光谱必须窄峰,低饱和色可以更平滑,三维表能保住平滑性。系数在最大分量切换处突变,所以按 r/g/b 哪个最大分成三块分别列表;以 r 最大为例,坐标 (x,y,z)=(g/r, b/r, r)(x,y,z)=(g/r,\,b/r,\,r),最大分量总映射到 zz,使三个坐标都落在 [0,1][0,1];x,yx,y 等距,zz 用在 0 和 1 附近加密的非线性节点(二分查找),再做三线性插值(zz 方向虽非线性也按线性插值,误差很小)。表是五维数组:[最大分量][z][y][x][3 个系数]。三个分量相等时直接返回 c0=c1=0c_0=c_1=0 的常数光谱(反解 sigmoid 得 c2c_2),避免插值带来的轻微色偏。
  • 无界 RGB:先按最大分量缩放,但缩放到最大值 1/2 而不是 1:高饱和颜色在 1 附近会被能量守恒约束逼出怪异形状(书中 (0.95,0.05,0.025)(0.95,0.05,0.025) 的例子短波端能量偏多),留出余量后拟合更规整,再乘回比例。
  • RGB 光源:不去猜复杂的发射谱,而是用平滑光谱乘色彩空间白点的光谱(像透过彩色滤光片看白光源),保证白光照白物体得到白像素。等能光谱转到 RGB 通常不是中性白,因为色彩空间假定观察者适应了某种环境光(如 D65)。

在游戏开发中的取舍

  • 实时渲染很少做全光谱,但 PBRT 的结论可用于:选择工作色彩空间时意识到多次反弹的颜色会变;需要色散、薄膜、黑体色温光源时局部使用光谱计算再投影回 RGB(色温→RGB 本质是黑体光谱的 XYZ 积分)。
  • 离线烘焙器/路径追踪参考渲染若要与游戏画面对齐,必须与引擎使用同一工作色彩空间和白点。

原书问题(PBRT 4.6.5–4.7)

位置原书说法核对结果
4.6.5"图 4.26(a) 显示 8 到 32 个波长误差降低近 500 倍"按图注,4.26(a) 是渲染时间,MSE 在 4.26(b);应指 (b)
4.6.6"locating the maximum of the quadratic polynomial from Equation (4.25)"按 HTML 中的公式标签,式 4.25 是 sigmoid s(x)s(x),二次多项式在式 4.26 S(λ)=s(c0λ2+c1λ+c2)S(\lambda)=s(c_0\lambda^2+c_1\lambda+c_2) 中;应引用式 4.26(来源:https://pbr-book.org/4ed/Radiometry,_Spectra,_and_Color/Color )
4.7 参考文献"Walter, B., P. M. Hubbard, P. Shirley, and D. F. Greenberg. 1997"第四作者是 Donald P. Greenberg,应为 D. P. Greenberg(来源:ACM DL doi 10.1145/256157.256158 作者列表;书中 Meyer and Greenberg 1980 等条目也写作 D. P. Greenberg)

PBRT 的视角:胶片、传感器与可见光波长采样(PBRT 4e 5.4)

相机测量方程(5.4.1)

胶片上一点的辐照度可从辐亮度定义出发、把立体角换成后透镜切平面上的面积 AeA_e;胶片与透镜平行,θ=θ′\theta=\theta',∥p′−p∥=z/cos⁡θ\|p'-p\|=z/\cos\theta(zz 为轴向距离),于是

E(p)=1z2∫AeLi(p,p′) ∣cos⁡4θ∣ dAe(式 5.3)E(p)=\frac1{z^2}\int_{A_e}L_i(p,p')\,|\cos^4\theta|\,dA_e\quad(\text{式 5.3})

胶片相对 zz 较大时 cos⁡4θ\cos^4\theta 明显降低边缘亮度,也是暗角 (vignetting) 的成因之一,数码相机通常用预设系数补偿。对快门时间积分得辐射曝光量 HH(J/m²,式 5.4),再对像素面积积分得到像素接收的焦耳数,称为相机测量方程(式 5.5)。pbrt 只在 RealisticCamera 中计入这些因子,简单相机不计入,以便与其他渲染器对比。

传感器响应(5.4.2)

像素红色分量 r=∫s(λ)rˉ(λ) dλr=\int s(\lambda)\bar r(\lambda)\,d\lambda(式 5.6)。真实传感器是绿色像素数量为红蓝两倍的马赛克,需要去马赛克;主要噪声是服从泊松分布的散粒噪声;ISO 在数码相机里就是读出增益,会放大噪声。pbrt 不模拟马赛克、噪声、溢出 (blooming) 和卷帘快门,所以 ISO 可以随意设置以得到想要的曝光。PixelSensor 模拟三件事:曝光控制(快门时间与 ISO 合成"成像比")、基于实测相机光谱响应曲线的 RGB 响应(书中用 Canon EOS 5D 曲线渲染,色调比 XYZ 匹配函数更冷)、白平衡。传感器 RGB 到 XYZ 的 3×3 矩阵由二十多个标准色卡色块用线性最小二乘拟合(式 5.7),亮度按第二个匹配函数对光源的积分归一化,相当于按视觉重要性加权;由于两边用各自的光源计算,这个矩阵同时完成白平衡。传感器响应用蒙特卡洛估计:r≈1n∑iL(λi)rˉ(λi)/p(λi)r\approx\frac1n\sum_i L(\lambda_i)\bar r(\lambda_i)/p(\lambda_i)(式 5.8)。

白平衡(5.4.2)

人眼的色适应让物体颜色在不同光照下看起来不变,相机对应的处理叫白平衡。pbrt 实现 von Kries 变换:在与三种视锥细胞响应匹配的 LMS 空间中,除以场景光源的颜色、乘以目标白点的颜色(一个对角矩阵),前后各乘 XYZ↔LMS 矩阵。书中用 3000 K 色温给白炽灯照明的场景做白平衡。默认的 XYZ 传感器不做白平衡,留给后期(色彩空间与白点见 色度学与色彩空间)。

按传感器响应采样波长(5.4.2)

只按 CIE YY 采样会有过多色噪;按 X+Y+ZX+Y+Z 之和采样又在视觉上不重要的 400–500 nm 浪费样本。pbrt 用参数化分布

pv(λ)=f(λ)∫360830f(λ) dλ,f(λ)=1cosh⁡2(A(λ−B)),A=0.0072 nm−1, B=538 nm(式 5.9)p_v(\lambda)=\frac{f(\lambda)}{\int_{360}^{830}f(\lambda)\,d\lambda},\qquad f(\lambda)=\frac1{\cosh^2\big(A(\lambda-B)\big)},\quad A=0.0072\ \text{nm}^{-1},\ B=538\ \text{nm}\quad(\text{式 5.9})

本页验算:∫sech⁡2=tanh⁡/A\int\operatorname{sech}^2=\tanh/A,在 360–830 nm 上的归一化常数为 0.0039398,与代码中的 0.0039398042f 一致;逆变换采样 λ=538−138.89 artanh⁡(0.85691−1.82750u)\lambda=538-138.89\,\operatorname{artanh}(0.85691-1.82750u) 中的三个常数分别是 1/A1/A、−tanh⁡(A(360−B))-\tanh(A(360-B)) 和 (tanh⁡(A(830−B))−tanh⁡(A(360−B)))\big(\tanh(A(830-B))-\tanh(A(360-B))\big),也都对得上(逆变换采样见 随机变量变换与逆变换采样)。SampleVisible() 用一个随机数在 [0,1)[0,1) 上等间距地生成全部波长样本。与均匀采样相比色噪大幅降低,运行时间只增加约 1%。

RGBFilm 的工程细节(5.4.6–5.4.7)

  • 像素累加和用双精度:书中用每像素 400 万样本渲染参考图,32 位浮点的参考图在约 1000 spp 后 MSE 就不再下降(本应是斜率 −1 的直线),64 位则正常。用低精度参考图评估采样算法会得出错误结论。
  • 萤火虫钳制:maxComponentValue 把每个样本的传感器 RGB 钳到上限(默认无穷大)。能明显改善有太阳等难采样光路的图像,但损失能量、引入偏差;习题指出直接看到太阳的像素不该被钳,建议用 Zirr 等 (2018) 的方法。
  • AddSplat() 可能被多个线程同时写同一像素,必须用原子操作或互斥;浮点加法不满足结合律,因此 splat 结果不再确定性可重现(习题)。
  • GBufferFilm 另存位置、法线、反照率、深度对光栅坐标的偏导及颜色方差,供降噪或机器学习使用。

原书问题(PBRT 第 5 章,在线版)

位置原书说法核对结果
5.4.2"silver halide crystals produce silver bromide when they are exposed to light"溴化银本身就是卤化银的一种(胶片乳剂常用的卤化银正是溴化银或溴碘化银);曝光只在晶体中形成潜影,显影时被曝光的卤化银还原为金属银。应为"卤化银晶体曝光后(经显影)生成金属银"(来源:Wikipedia "Photographic film" 乳剂与显影部分,引 Fujita《Organic Chemistry of Photography》2004)
5.4.4"Light transport algorithms that sample paths starting from the light sources (such as bidirectional path require the ability to 'splat'…"括号未闭合且漏词,应为"(such as bidirectional path tracing) require…"(https://pbr-book.org/4ed/Cameras_and_Film/Film_and_Imaging ;读者已在 pbr-book-website issue #22 报告:https://github.com/mmp/pbr-book-website/issues/22 )
  1. PBRT 4e source excerpt: chapter 04 (Radiometry, Spectra, and Color)
  2. Pharr, Jakob, Humphreys《Physically Based Rendering: From Theory to Implementation》第 4 版(MIT Press, 2023)第 4 章 4.5、4.6.4–4.6.6 节,pbr-book.org/4ed/Radiometry,_Spectra,_and_Color
  3. PBRT 4e source excerpt: chapter 05 (Cameras and Film)
  4. Pharr, Jakob, Humphreys《Physically Based Rendering: From Theory to Implementation》第 4 版(MIT Press, 2023)第 5 章 5.4 节,pbr-book.org/4ed/Cameras_and_Film

相关页面

图形学色度学与色彩空间Colorimetry & Color Spaces色度学研究光谱功率分布与颜色感知的关系。人眼只有三种视锥细胞,所以三个数就能精确表示任意颜色刺激;CIE XYZ 是标准的三刺激值空间,各种 RGB 色彩空间由三原色和白点定义,可用 3×3 矩阵互相转换。实时渲染用 RGB 而非光谱计算,严格说是范畴错误,但对非预测性渲染效果出奇地好。图形学辐射度量学与光度学Radiometry & Photometry辐射度量学精确度量电磁辐射,是基于物理渲染的第一步;光度学用人眼灵敏度对它加权。渲染中最重要的量是辐射亮度 L:眼睛和相机测的就是它,着色方程要算的也是沿视线的辐射亮度。图形学蒙特卡洛积分与重要性采样Monte Carlo Integration & Importance Sampling渲染方程里的半球积分没有闭式解,只能数值积分。蒙特卡洛积分在定义域里随机取样、按概率密度加权求平均,期望值正好等于积分真值,误差随样本数 N 按 1/\sqrt{N} 下降,而且与积分维数无关,所以成了光线追踪类渲染的数学基础。降低误差的主要手段是让采样分布接近被积函数的形状,也就是重要性采样(GEA4 卷 II 12.6.2、12.6.4)。图形学色调映射Tone Mapping色调映射把场景参考的辐射亮度转换为显示参考的辐射亮度,目标不是恒等复现、也不是把所有信息"挤进"显示范围,而是在显示条件下尽量复现观看原场景时的感知印象。它通常是曝光缩放加一条 S 形曲线(全局映射),之后可做调色;最后由显示编码输出到 SDR 或 HDR 显示器。图形学随机光线追踪与路径追踪Stochastic Ray Tracing & Path Tracing光线追踪用投射光线同时解决可见性问题和光照问题。GEA 从最简单的"眼睛光线 + 着色方程"讲起,逐步加入阴影光线、透射光线、用蒙特卡洛估计渲染方程的随机光线追踪、递归间接光照,再到每次只走一条路径的路径追踪、双向路径追踪和 Metropolis 光传输,最后讨论降噪与光线投射效率(GEA4 卷 II 12.6.3–12.6.5)。游戏开发C++ 内存布局与对齐C++ Memory Layout & Alignment理解 C/C++ 程序在内存中的样子,是写高性能引擎代码的前提。主要内容包括:声明与定义、内部与外部链接;可执行映像的几个段(text、data、BSS、rodata)以及栈和堆;结构体成员的自然对齐和填充;继承和虚函数如何改变类的布局(vpointer 和 vtable)。GEA 也简要回顾了面向对象:多重继承容易产生致命菱形,所以通常只允许 mix-in;组合往往比继承更好。游戏数学随机变量变换与逆变换采样Random Variable Transforms & Inversion Sampling蒙特卡洛估计器要求能从指定的概率密度里抽样。最基本的工具是逆变换法 (Inversion Method):对 CDF 求逆,把 [0,1) 上的均匀随机数映射成目标分布;更一般地,随机变量经过一一映射后的密度由导数(多维时是雅可比行列式)决定;多维非独立分布则先采边缘密度、再采条件密度。本页按 PBRT 第 4 版 2.1.1、2.3、2.4 节整理,是 蒙特卡洛积分与重要性采样 的数学部分。图形学测量 BRDF 与数据驱动反射模型Measured BRDFs & Data-Driven Reflectance测量 BRDF 用实验室测得的真实材质数据代替解析模型,适合虹彩漆、织物、珠光膜等解析模型拟合不好的材质。难点是四维 BRDF 的维数灾难;PBRT 4e 的 MeasuredBxDF(Dupuy 与 Jakob 2018)把可见法线采样当作参数化,只需较稀疏的测量就能插值。本页对应 PBRT 4e 9.8 节及其延伸阅读。图形学菲涅耳反射Fresnel Reflectance菲涅耳方程描述光在两种物质的平坦界面上反射的比例 F,它取决于入射角和两侧折射率:垂直入射时为材质特有的 F0(特征镜面色),掠射时趋于 1。实时渲染用以 F0 为参数的 Schlick 近似。电介质 F0 低且无色,金属 F0 高且常带色,半导体介于其间;金属度参数化利用了这一点。光从物体内部射向表面时还可能发生全内反射。本页对应 RTR4 9.5 节。图形学纹理映射管线Texturing Pipeline纹理通过修改着色方程中使用的值(颜色、粗糙度、法线等)来高效表现表面材质的变化。通用纹理管线是:空间位置 → 投影函数得到纹理坐标 → 对应函数变换到纹理空间 → 取值 → 值变换 → 用于着色。本页还整理纹理坐标约定、纹理表示方式、程序纹理、纹理动画、材质贴图、alpha 贴图和纹理光源。图形学参与介质与体积散射Participating Media & Volumetric Scattering参与介质是充满粒子、通过吸收和散射参与光传输的体积,如空气、水、雾、烟、云;皮肤、蜡等致密材质也有强烈散射——"一切都是散射"。实时渲染主要考虑单次散射:沿视线积分透射率衰减后的内散射光,内散射由相位函数和对光源的可见性(不透明阴影 × 体积阴影)决定。通用实时方案把介质材质和散射结果体素化到相机视锥体素 (froxel) 中逐片累积(RTR4 14.1、14.3、14.8 节)。

输入关键词开始搜索

滚轮或双指缩放 · 拖动平移 · 双击放大 / 还原