把材质反射率设为 0.6,像素就应该写成 0.6 吗?同一块表面放在不同照明下,显然不会保持相同的显示颜色。问题在于材质参数、到达表面的光、离开表面的光和屏幕码值分别代表不同的量。

本篇选择一个能手算的场景:不发光、不透射的漫反射平面,法线上方一个圆锥内有恒定入射辐亮度,其余方向为零。先算物理量,再用确定性求积核对,最后才把结果压缩到灰度图。它是已知入射条件下的局部积分,不是通用间接光照求解器。

功率落到面积上,还缺一个方向

辐射通量 Φ 表示单位时间传递的辐射能量,单位 W。把入射通量按表面积分配,得到辐照度 E=dΦ/dA,单位 W/m²。它回答一小块表面总共收到多少功率,没有保留这些光分别来自哪些方向。

方向分布由辐亮度 L 描述。通过面积 dA、沿某个小立体角 dω 传播的功率满足:

dΦ=LcosθdAdω.d\Phi=L\cos\theta\,dA\,d\omega.

θ 是该方向与表面法线的夹角,面积投影为 cosθ dA。L 的单位为 W/(m²·sr),sr 是立体角单位球面度。一个方向范围在单位球面上覆盖的面积就是其立体角,完整球面为 4π sr,半球为 2π sr。

同样一块表面,从倾斜方向看见的投影面积较小,因此通量表达式含余弦。这不是材质凭空变暗的经验参数,而是面积投影产生的几何因素。

将入射方向上的辐亮度积分,得到辐照度:

E(x)=Ω+Li(x,ωi)(nωi)dωi.E(x)=\int_{\Omega^+}L_i(x,\omega_i)(n\cdot\omega_i)\,d\omega_i.

这里 n 和 ωᵢ 都是单位向量,Ω⁺ 为 n 所在的正面半球。沿用表面散射中的方向约定,ωᵢ 指向光来自的方向,也就是从表面向外;真实入射传播方向与它相反。ωₒ 也从表面向观察方向,不把两者写成一入一出的向量再直接套同一符号。

辐亮度保留方向,辐照度累加方向。即使单位中都有 W/m²,也不能互换它们,更不能把法线点积直接称为辐亮度。第 09 篇将光强常量折入经验系数的做法,在这里需要展开量纲。

BRDF 是响应函数,不是最终颜色

双向反射分布函数 BRDF 定义微小入射辐照度对某个出射方向辐亮度的贡献:

fr(x,ωi,ωo)=dLo(x,ωo)dEi(x,ωi).f_r(x,\omega_i,\omega_o)=\frac{dL_o(x,\omega_o)}{dE_i(x,\omega_i)}.

因此 fᵣ 的单位是 sr⁻¹。它描述材质如何把不同入射方向的光分配到出射方向,不是无量纲的“像素颜色”。把所有入射贡献相加,就得到反射积分。

Lambert 模型选择 fᵣ=ρ/π,其中 ρ∈[0,1] 是无量纲反射率。为什么要除 π?固定入射方向后,对出射半球积分 fᵣ cosθₒ dωₒ,总反射份额应为 ρ,而余弦加权半球积分等于 π。

如果漏掉这个归一化,使用 fᵣ=ρ,即使 ρ=0.6 仍可能让积分反射份额大于 1。单看材质参数落在 [0,1] 并不能证明反射模型守恒。另一方面,一般 BRDF 在很窄的方向峰值可以大于 1 sr⁻¹,守恒约束针对积分,不是简单逐点裁剪。

Lambert 出射辐亮度不随正面观察方向改变。这不表示等立体角方向携带相同通量;通量还乘出射面积的余弦投影。也不表示所有真实哑光材料都精确遵循这个模型,尤其掠射等情况可能偏离。

当前 lambert_brdf 只实现正面反射:入射和出射余弦都大于零时返回 ρ/π,否则返回零。掠射边界按零处理,不影响普通积分的测度。反射率、余弦都要求有限且在合法范围内,它们仍是普通 double,没有静态单位类型系统。

BSDF 与渲染方程各自增加了什么

BRDF 只讨论反射。BSDF 统称表面散射分布,还可以包含透射。对于不透射的单面表面,积分在正面半球上即可;玻璃会把光带到另一侧,需要不同的散射项、介质与折射约定,不能把同一个 Lambert 半球积分直接称为玻璃模型。

对本篇的不透射表面,出射辐亮度由自身发射和反射贡献组成:

Lo(x,ωo)=Le(x,ωo)+Ω+fr(x,ωi,ωo)Li(x,ωi)(nωi)dωi.L_o(x,\omega_o)=L_e(x,\omega_o)+ \int_{\Omega^+}f_r(x,\omega_i,\omega_o) L_i(x,\omega_i)(n\cdot\omega_i)\,d\omega_i.

量纲依次为 sr⁻¹、W/(m²·sr)、无量纲余弦、sr,乘积积分后仍是 W/(m²·sr),与左边相同。漏掉余弦或重复乘余弦不会被量纲检查全部识别,还需要几何推导和数值例子。

式子的难点在 Lᵢ。真实场景中,它可能来自其他物体反射,而那里的出射又依赖更多位置的入射。第 17、18 篇提供沿方向找到最近表面的能力,并没有自动算出那个表面向这里发出多少光。

在无遮挡、无参与介质且折射率不变的自由传播段,辐亮度沿射线保持不变。距离改变会影响有限光源在接收点张开的立体角,不能因此给每条辐亮度射线再随意乘一次距离平方衰减。点光源强度到辐照度的关系与此采用不同的量和几何表达。

本篇直接指定 Lᵢ,把未知的全场景耦合变成可解的小问题。后续路径积分才求多次反射,当前结果不能作为颜色渗透或全局光照的验证。

一个圆锥照亮平面,积分是多少

令法线沿 +Z,入射辐亮度在 θ∈[0,α] 内恒为 L,所有方位角 φ∈[0,2π) 都有光,圆锥外为零。α 在 0 到 π/2 之间。球面坐标的立体角微元为 dω=sinθ dθ dφ,因此:

E=L02π0αcosθsinθdθdϕ=πLsin2α.E=L\int_0^{2\pi}\int_0^\alpha \cos\theta\sin\theta\,d\theta\,d\phi =\pi L\sin^2\alpha.

不发光 Lambert 表面于是得到 Lₒ=(ρ/π)E=ρL sin²α。这里的 sinθ 来自积分测度,cosθ 来自投影,它们不能互相替换。

取 L=2 W/(m²·sr)、ρ=0.6。α=90° 时整个半球入射,E=2π W/m²,Lₒ=1.2 W/(m²·sr)。α=30° 时 sin²α=1/4,Lₒ=0.3;α=60° 时为 3/4,Lₒ=0.9。

半球不是简单把 L 乘 2π。方向越接近地平线,投影余弦越小,所以余弦加权立体角为 π。忽略这点会把入射总量算大一倍。

同一个半球例子若漏掉 BRDF 中的 π,错误结果为 0.6×2π≈3.769911。若余弦重复乘一次,积分变成 2πL/3,错误 Lₒ 为 0.8。检查器打印这两个解析反例值;它们是手算表达式的数值展示,没有冒充两套错误渲染器的运行比较。

数值求积怎样验证解析值

cone_irradiance 在 θ 与 φ 方向均匀划分区间,用每个 θ 小格中心的 cosθ sinθ 乘 ΔθΔφ 后累加。当前被积函数与 φ 无关,方位角循环只是重复相同权重;程序另检查 1 份和 32 份方位角结果一致。

对每个 α,固定方位角份数 32,把极角份数依次设为 4、16、64、256。没有随机数、种子或俄罗斯轮盘,终止由固定网格规模决定。这是中点求积收敛检查,不是蒙特卡洛误差统计。

α 4 份绝对误差 16 份绝对误差 64 份绝对误差 256 份绝对误差
30° 0.00449485 0.000280401 0.0000175230 0.00000109518
60° 0.0542639 0.00336608 0.000210281 0.0000131422
90° 0.164444 0.0101046 0.000630869 0.0000394267

表中误差是 E 的误差,单位 W/m²;不是 PNG 灰度差。每次将极角份数扩大四倍,误差约缩小十六倍,与光滑函数上中点公式的二阶行为相符。这个特定结果不能保证含遮挡跳变的积分也有相同误差规律。

在 256×32 网格上,半球 E=6.28322473387245,对应 Lₒ=1.20000752994367。程序要求三种角度的 E 最终绝对误差小于 4×10⁻⁵ W/m²,并核对六种材质/入射配置的 Lₒ 误差小于 2×10⁻⁵ W/(m²·sr)。阈值针对本次有界数值实验,不当作任意场景的精度承诺。

从物理量到屏幕码值

反射率为 0.6 并不妨碍 Lₒ 大于 1。前者是份额,后者是带单位的辐亮度;当半球 L 从 2 增到 4,Lₒ 从 1.2 增到 2.4,仍然只反射入射能量的 60%。把 Lₒ 裁成 1 会破坏这个关系。

显示阶段先选择参考辐亮度 Lref=1 W/(m²·sr),得到无量纲 u=Lₒ/Lref,再沿用第 04 篇的 T(u)=u/(1+u),最后执行 sRGB 编码和 8 位量化。参考值是本次曝光尺度的选择,不代表显示器已完成辐射标定。

每块为 128×128,按从左到右、从上到下编号。它们是实际积分输出的均匀平面样本,不是三维场景截图。标量结果复制到三个 RGB 通道形成灰阶,不把这三个相同数字称为完整光谱。

位置 Lᵢ ρ α 解析 Lₒ 实际 Lₒ 8 位码值
上左 2 0.6 90° 1.2 1.20000753 195
上中 2 0.6 30° 0.3 0.30000021 132
上右 2 0.6 60° 0.9 0.90000251 183
下左 2 0 90° 0 0 0
下中 2 1 90° 2 2.00001255 213
下右 4 0.6 90° 2.4 2.40001506 219

Lᵢ、Lₒ 两列单位均为 W/(m²·sr)。同样的 ρ=0.6 在前三块显示为不同灰阶;下右还说明入射翻倍后,物理 Lₒ 翻倍,经过压缩的码值却不会翻倍。

原始数值 CSV 保存 Lᵢ、ρ、圆锥角、E、未截断 Lₒ、解析值、显示线性值和码值。换显示曲线可以重新处理 CSV,无需重算积分;只有最终 PNG 则无法恢复被量化的信息。

复跑与练习

1
2
make -C examples/computer-graphics check
examples/computer-graphics/build/radiometry_check examples/computer-graphics/build

代码是 radiometry.hppradiometry_check.cppwriting-plans/computer-graphics/evidence/19-cpu.txt 保存全部网格结果和拒绝样本计数。方向正反面、掠射边界、方位划分一致性、零圆锥及非法输入均有检查;单位由接口约定、推导与 CSV 表头表达,没有虚构编译器的量纲验证能力。

练习一:L=3 W/(m²·sr),ρ=0.4,入射覆盖整个正面半球。E 与 Lₒ 各是多少?若把反射率改成 1,Lₒ 应是多少?

答案:E=3π W/m²,Lₒ=1.2 W/(m²·sr)。ρ=1 时 Lₒ=3,而不是被裁成 1。反射率限制总反射份额,不限制带单位的辐亮度数值。

练习二:圆锥半角从 30° 改为 60°,保持 L 和 ρ 不变,Lₒ 增加几倍?为什么不能只按角度翻倍计算?

答案:sin²60°/sin²30°=3,因此增加三倍。积分同时包含球面测度和投影余弦,面积与角度不是线性关系。

练习三:若把 BRDF 改成 ρ,量纲表仍给它标 sr⁻¹,量纲核对能发现漏 π 吗?

答案:不能,π 不改变单位。还需要反射半球积分与解析场景核对。量纲是必要检查,不能代替归一化和数值验收。

系列导航与资料

前篇:18:怎样减少求交次数系列入口。下一篇:20 随机样本怎样估计积分,尚未完成。

PBRT 第四版 §4.1 核对通量、辐照度和辐亮度;§4.3 核对反射函数及方向约定;§9.2 核对 Lambert 归一化;§13.1 核对发射、散射与场景耦合。实际读取于 2026-09-20,后两页写作时再次打开;独立推导的圆锥输入与本地结果见本篇代码和记录。