方向光照
一盏点光源只对应一个入射方向,环境则从四周提供光。环境贴图 Environment Map 将不同方向的入射辐亮度保存下来,使一个材质能够受到天空、窗户、墙面等方向结构的照明。这种利用图像描述光照的做法常称为基于图像的照明 Image-Based Lighting,IBL 。
左侧是森林的经纬展开,右侧是教堂环境的 Cube Map 展开。它们各自将方向对应到纹理位置,不是在给模型表面贴一张普通颜色图。两图来自不同环境,作用是说明表示方式,不能比较同一方向的数值。
基本模型将环境放在很远处,入射光只随方向变化,不随着色点位置变化。附近墙面、局部视差和随位置变化的遮挡不能完整装进这一张方向图。环境也常包含远高于普通显示白色的亮度,需要高动态范围 High Dynamic Range,HDR 数据;先截到 [ 0 , 1 ] [0,1] [ 0 , 1 ] 会丢失照明能量。
无阴影积分
先暂时忽略遮挡,令可见性 V = 1 V=1 V = 1 ,求环境在单个表面点上的反射:
L o ( ω o ) = ∫ Ω + L i ( ω i ) f r ( ω i , ω o ) ( n ⋅ ω i ) d ω i . L_o(\boldsymbol\omega_o)
=\int_{\Omega^+}L_i(\boldsymbol\omega_i)
f_r(\boldsymbol\omega_i,\boldsymbol\omega_o)
(\mathbf n\cdot\boldsymbol\omega_i)\,\mathrm d\omega_i.
L o ( ω o ) = ∫ Ω + L i ( ω i ) f r ( ω i , ω o ) ( n ⋅ ω i ) d ω i .
法线 n \mathbf n n 定义接收半球,ω o \boldsymbol\omega_o ω o 是从表面指向观察者的方向。入射方向 ω i \boldsymbol\omega_i ω i 也以离开表面的方向表示,用于查询光来自哪里;实际入射传播方向与它相反。
蒙特卡洛积分 Monte Carlo Integration 可以处理这个问题。按方向概率密度 p ω p_\omega p ω 取 N N N 个样本,计算
L ^ o = 1 N ∑ j = 1 N L i ( ω j ) f r ( ω j , ω o ) ( n ⋅ ω j ) p ω ( ω j ) . \widehat L_o=\frac1N\sum_{j=1}^N
\frac{L_i(\boldsymbol\omega_j)
f_r(\boldsymbol\omega_j,\boldsymbol\omega_o)
(\mathbf n\cdot\boldsymbol\omega_j)}{p_\omega(\boldsymbol\omega_j)}.
L o = N 1 j = 1 ∑ N p ω ( ω j ) L i ( ω j ) f r ( ω j , ω o ) ( n ⋅ ω j ) .
它通用,但每个片元都要查询许多方向。实时着色时,一个像素还可能混合多个局部环境探针,直接重复积分更贵。问题因此转向:哪些工作与当前像素无关,可以提前完成,运行时只查少量纹理?
这里讨论的是无阴影环境着色,不包含自发光与完整间接反弹。不能因为结果中有复杂环境反射,就认为所有局部遮挡也已经计算。
反射范围
理想镜面只反射一个方向;粗糙表面由许多不同朝向的微面组成,环境中一片方向都可能贡献到当前视线。反射瓣 Reflection Lobe 描述这份方向分布,粗糙度 Roughness 增大时通常使相关方向范围变宽。
对镜面,若观察方向为 v \mathbf v v ,查询环境的反射方向为
r = 2 ( n ⋅ v ) n − v . \mathbf r=2(\mathbf n\cdot\mathbf v)\mathbf n-\mathbf v.
r = 2 ( n ⋅ v ) n − v .
GLSL 中对应 reflect(-v, n);输入的向量方向约定不同,就会查到错误环境。粗糙反射则需要以相应反射方向为中心,收集一个方向范围,而不只取一束光。
图左侧黑色方向 i i i 经表面反射形成围绕 r r r 的方向族,右侧把这种范围贡献转成预过滤后的一次查询。借助 BRDF 的互易性,固定观察方向时也可以反过来分析哪些入射方向会贡献。它说明“方向范围平均与中心查询”的思路,并不表示任意粗糙反射都只是均匀圆形范围。
拆分积分
沿用乘积近似 ,记 K = f r ( n ⋅ ω i ) K=f_r(\mathbf n\cdot\boldsymbol\omega_i) K = f r ( n ⋅ ω i ) ,可以近似写成
∫ L i K d ω i ≈ L ‾ i ∫ K d ω i . \int L_iK\,\mathrm d\omega_i
\approx \overline L_i\int K\,\mathrm d\omega_i.
∫ L i K d ω i ≈ L i ∫ K d ω i .
L ‾ i \overline L_i L i 代表相关反射范围中经过适当过滤的环境光,第二项代表材质在该观察条件下的整体反射响应。光照与响应的相关性被近似分开,所以两者可以分别预计算。
窄反射瓣使有效方向范围较小;宽而平滑的材质响应则能降低对环境细节的敏感度。这些观察给近似提供动机,但不是“任意 BRDF 都能安全提出环境项”的证明。环境中一个非常亮的小窗恰被高光挑中时,相关性可能很强。
工程中积分通过求和实现,这种将两份求和分开预计算的方案称为 Split-Sum Approximation 。两个阶段分别处理环境图与 BRDF,不是把所有材质参数和环境变化一起塞进一张高维表。
环境预过滤
第一阶段为不同粗糙度预先生成环境的过滤结果。运行时用反射方向 r \mathbf r r 查对应层级,再在相邻粗糙度层之间插值。
左上树干和天空细节较清晰,中间范围平均后变模糊,右下只剩宽范围的亮暗与颜色结构。粗糙反射要收集更多方向,正是这种由局部细节向宽范围平均的变化,而非把已着色的物体图像再整体模糊。
这类层级常放在 Cube Map 的 mip 层中,但各层应按目标反射分布构造。GGX / Trowbridge–Reitz 统计不同朝向微面的面积,用较长尾部表达粗糙反射的方向范围,具体定义与推导见GGX长尾 。普通 2 × 2 2\times2 2 × 2 图像平均并不自动等于这种反射瓣的预过滤;球面方向面积、Cube Map 边缘和纹素立体角也会影响结果。
常见方案为控制数据维度,预过滤时采用 n = v = r \mathbf n=\mathbf v=\mathbf r n = v = r 的近似,只保留反射方向和粗糙度。真实微表面反射瓣还随观察角变化,掠射时可能被拉长,所以这一处理会丢掉部分方向形状。更换环境图后也需要更新预过滤数据,不能沿用旧环境的光照。
漫反射分流
漫反射可以更直接处理。Lambertian BRDF 为 ρ / π \rho/\pi ρ / π ,则
L o d i f f u s e ( n ) = ρ π E ( n ) , E ( n ) = ∫ Ω + L i ( ω ) max ( 0 , n ⋅ ω ) d ω . L_o^{\rm diffuse}(\mathbf n)
=\frac\rho\pi E(\mathbf n),
\qquad
E(\mathbf n)=\int_{\Omega^+}L_i(\boldsymbol\omega)
\max(0,\mathbf n\cdot\boldsymbol\omega)\,\mathrm d\omega.
L o diffuse ( n ) = π ρ E ( n ) , E ( n ) = ∫ Ω + L i ( ω ) max ( 0 , n ⋅ ω ) d ω .
预计算的是辐照度 Irradiance ,查询变量为法线 n \mathbf n n 。它不依赖观察方向,与沿镜面反射方向查粗糙高光的做法要分开。后面将用球谐函数高效表达这个较平滑的响应。
有些工程纹理保存 E E E ,有些保存 E / π E/\pi E / π ,组合时必须核对约定。漫反射和高光分量还要共享合理的能量预算,不能各按全部入射能量相加。
材质预积分
高光的第二阶段仍是积分:
I ( n , v , 材质 ) = ∫ Ω + f s ( ω i , v ) ( n ⋅ ω i ) d ω i . I(\mathbf n,\mathbf v,\text{材质})
=\int_{\Omega^+}f_s(\boldsymbol\omega_i,\mathbf v)
(\mathbf n\cdot\boldsymbol\omega_i)\,\mathrm d\omega_i.
I ( n , v , 材质 ) = ∫ Ω + f s ( ω i , v ) ( n ⋅ ω i ) d ω i .
若直接为全部颜色、粗糙度和方向组合建表,存储维度会很高。预积分 Preintegration 需要利用公式结构,先把能够单独组合的参数拆出来。
对常见单次微表面高光,令入射与观察方向都指离开表面,半程向量为 h = normalize ( ω i + v ) \mathbf h=\operatorname{normalize}(\boldsymbol\omega_i+\mathbf v) h = normalize ( ω i + v ) ,有
f s = D ( h ) G ( ω i , v ) F ( v ⋅ h ) 4 ( n ⋅ ω i ) ( n ⋅ v ) . f_s=\frac{D(\mathbf h)G(\boldsymbol\omega_i,\mathbf v)
F(\mathbf v\cdot\mathbf h)}
{4(\mathbf n\cdot\boldsymbol\omega_i)(\mathbf n\cdot\mathbf v)}.
f s = 4 ( n ⋅ ω i ) ( n ⋅ v ) D ( h ) G ( ω i , v ) F ( v ⋅ h ) .
D D D 是法线分布函数 Normal Distribution Function,NDF ,统计不同朝向微面的面积;G G G 描述微面间遮挡,F F F 是 Fresnel 界面反射率。完整的微表面讨论可复习材质模型 。这里先利用其颜色参数的可分离结构。
Fresnel拆色
Schlick 近似 把 Fresnel 写为
F ( c ) = F 0 + ( 1 − F 0 ) ( 1 − c ) 5 , c = v ⋅ h . F(c)=F_0+(1-F_0)(1-c)^5,
\qquad c=\mathbf v\cdot\mathbf h.
F ( c ) = F 0 + ( 1 − F 0 ) ( 1 − c ) 5 , c = v ⋅ h .
F 0 F_0 F 0 是正入射基础反射率,可以为 RGB;它不等于所有材料统一的“漫反射基色”。电介质常有较低的近灰色 F 0 F_0 F 0 ,金属反射可以带颜色。这里的角度是视线与微面法线 h \mathbf h h 的夹角,不是任意的 n ⋅ ω i \mathbf n\cdot\boldsymbol\omega_i n ⋅ ω i 。
令 F c = ( 1 − c ) 5 F_c=(1-c)^5 F c = ( 1 − c ) 5 ,改写为
F = F 0 ( 1 − F c ) + F c . F=F_0(1-F_c)+F_c.
F = F 0 ( 1 − F c ) + F c .
设 H = D G / [ 4 ( n ⋅ ω i ) ( n ⋅ v ) ] H=DG/[4(\mathbf n\cdot\boldsymbol\omega_i)(\mathbf n\cdot\mathbf v)] H = D G / [ 4 ( n ⋅ ω i ) ( n ⋅ v )] ,将它代入材质积分:
I = F 0 ∫ Ω + H ( 1 − F c ) ( n ⋅ ω i ) d ω i ⏟ A + ∫ Ω + H F c ( n ⋅ ω i ) d ω i ⏟ B . I=F_0\underbrace{\int_{\Omega^+}H(1-F_c)
(\mathbf n\cdot\boldsymbol\omega_i)\,\mathrm d\omega_i}_{A}
+\underbrace{\int_{\Omega^+}HF_c
(\mathbf n\cdot\boldsymbol\omega_i)\,\mathrm d\omega_i}_{B}.
I = F 0 A ∫ Ω + H ( 1 − F c ) ( n ⋅ ω i ) d ω i + B ∫ Ω + H F c ( n ⋅ ω i ) d ω i .
F 0 F_0 F 0 不随积分方向变化,因此可以提出。A , B A,B A , B 不再依赖基础反射颜色;改变金属颜色等参数时,不必重新计算这一方向积分。
二维查表
对给定的各向同性 D , G D,G D , G 模型,剩余响应主要由粗糙度和 N V = n ⋅ v N\!V=\mathbf n\cdot\mathbf v N V = n ⋅ v 决定。可以将 A , B A,B A , B 存成一张二维纹理的两个通道,称为 BRDF LUT Lookup Table 。
横轴是 cos θ v = N V \cos\theta_v=N\!V cos θ v = N V ,纵轴是粗糙度;红、绿表现两个预积分通道,不是材质的实际颜色。入射方向已经在生成表时被积分,不应再拿某条入射角作查询坐标。
常见粗糙度参数 r r r 与 NDF 的参数 α \alpha α 有映射关系,例如采用 α = r 2 \alpha=r^2 α = r 2 。预积分、环境预过滤和运行时材质必须使用一致的映射及同一 D , G D,G D , G 约定。各向异性材质还依赖切向方位等参数,不能无条件套用这张各向同性二维表。
重要性采样
预计算阶段仍可使用较多样本,成本只是移到运行之前。重要性采样 Importance Sampling 让样本较多地落在贡献大的方向,减少用有限样本估计这些积分的噪声。
以按 NDF 采样半程向量为例,p h ( h ) = D ( h ) ( n ⋅ h ) p_h(\mathbf h)=D(\mathbf h)(\mathbf n\cdot\mathbf h) p h ( h ) = D ( h ) ( n ⋅ h ) 。镜面反射把 h \mathbf h h 变为 ω i \boldsymbol\omega_i ω i ,方向 PDF 需要相应的 Jacobian:
p ω ( ω i ) = D ( h ) ( n ⋅ h ) 4 ∣ v ⋅ h ∣ . p_\omega(\boldsymbol\omega_i)
=\frac{D(\mathbf h)(\mathbf n\cdot\mathbf h)}{4|\mathbf v\cdot\mathbf h|}.
p ω ( ω i ) = 4∣ v ⋅ h ∣ D ( h ) ( n ⋅ h ) .
这个 4 ∣ v ⋅ h ∣ 4|\mathbf v\cdot\mathbf h| 4∣ v ⋅ h ∣ 来自半程方向与反射方向之间的立体角换元,不是随意归一化常数。将 f s f_s f s 、余弦和 PDF 相除,D D D 等项相消,得到权重
G v i s = G ( ω i , v ) ( v ⋅ h ) ( n ⋅ h ) ( n ⋅ v ) . G_{\rm vis}=\frac{G(\boldsymbol\omega_i,\mathbf v)
(\mathbf v\cdot\mathbf h)}
{(\mathbf n\cdot\mathbf h)(\mathbf n\cdot\mathbf v)}.
G vis = ( n ⋅ h ) ( n ⋅ v ) G ( ω i , v ) ( v ⋅ h ) .
于是预积分可用下述过程估计:
1 2 3 4 5 6 7 8 9 10 11 12 对每组粗糙度 r 和观察角 NoV: 构造单位法线 n 与满足观察角的单位视线 v 初始化 A = 0,B = 0 对 N 个半程向量样本: 按目标 NDF 的投影分布取 h wi = 2 * dot(v, h) * h - v 若 wi 在法线上半球且所需分母合法: 计算 Gvis 与 Fc = (1 - dot(v, h))^5 A += Gvis * (1 - Fc) B += Gvis * Fc 否则该样本贡献为 0 将 A / N、B / N 写入二维表
反射到接收半球外的样本贡献为零,仍按原总样本数归一化,不能改成只除以通过的样本数而不修正采样分布。零粗糙度的镜面极限与掠射退化需要相应分支,不能把零直接代入所有 NDF 分母。
运行组合
运行时查询粗糙度对应的环境层和 A , B A,B A , B 表,得到
L o s p e c u l a r ≈ L p r e f i l t e r ( r , r ) [ F 0 A ( N V , r ) + B ( N V , r ) ] . L_o^{\rm specular}
\approx L_{\rm prefilter}(\mathbf r,r)
\left[F_0A(N\!V,r)+B(N\!V,r)\right].
L o specular ≈ L prefilter ( r , r ) [ F 0 A ( N V , r ) + B ( N V , r ) ] .
RGB 与标量按通道组合。颜色和环境数据都应在线性空间,不能对 gamma 编码值直接执行能量运算。
1 2 3 4 5 6 求反射方向 r_dir = reflect(-v, n) 按材质粗糙度查询预过滤环境图,得到 RGB 光照 L 按 (NoV, roughness) 查询 BRDF LUT,得到 A、B specular = L * (F0 * A + B) 按材质能量分配组合 diffuse、specular 与其他分量 再执行相应的显示处理
它避免每个片元实时积分许多方向,但纹理查询、插值、局部探针混合以及数据更新仍有成本。“没有实时采样积分”不等于没有任何纹理采样。
误差与遮挡
上排是参考,下排是 Split Sum;从左到右反射逐渐从清晰变宽,两组在主要颜色与光照结构上接近。它展示该环境和材质条件下的近似效果,不能作为所有方向、所有环境逐像素一致的保证。
误差包括光照与 BRDF 的相关性被分离,预过滤中观察角变化被简化,以及有限层级、有限表分辨率和积分样本带来的误差。掠射拉长高光、极亮小光源或不符合建表模型的材质,都值得单独检查。
本篇始终设 V = 1 V=1 V = 1 。环境图有许多方向,若为每个方向分别建立阴影图,成本会随数量增长;若采样方向检查可见性,局部遮挡又可能具有高频细节。后续的环境阴影 将讨论主光源投影、AO、预计算传输和射线查询等预算取舍,这里的 Split Sum 本身不包含完整环境阴影。