GPU Gems 1 · Chapter 18. Spatial BRDFs
用紧凑的空间 BRDF 参数表示每个表面点的角度反射差异,并支持离散光与预卷积环境光的实时渲染。
学习目标
- 能区分空间变化与角度变化,并用自己的话解释为什么纹理与 BRDF 的组合比单独的颜色贴图更能描述真实表面
- 能实现 Lafortune representation 的 diffuse 加多个 specular lobe,并说明 Cx、Cy、Cz、n 和 albedo 各自控制什么
- 能把每个 texel 的 lobe 参数打包到纹理,在 local surface coordinates 中完成离散光评估
- 能解释预卷积环境图如何把 sharpness n 映射为 cube mipmap LOD,并在质量、带宽和精度之间做取舍
一张普通颜色纹理只回答“这个位置是什么颜色”,而一个普通光照模型只回答“这个位置在不同入射、出射方向下如何反射”。真实的木纹、刷痕、丝线和金属表面同时拥有这两种变化:不同位置的材质不同,同一位置从不同角度看也不同。GPU Gems 1 第 18 章把两者合并为 Spatial BRDF,并讨论用紧凑参数在硬件上实时评估。
↡把纹理映射的空间变化与表面 BRDF 的入射、出射角度变化组合起来的反射表示 为每个表面位置保存一组反射参数,而不是让整张物体共享一个材质。这里的空间坐标通常通过 UV 找到 texel,角度则在 shading 时由光向量和视向量提供。这样,同一个模型可以在不同位置混合粗糙、镜面、织物或带方向性的反射行为。
1. 从 BRDF 到可存储的空间表示
↡描述固定表面点在不同入射方向与出射方向之间反射比例的函数 关注一个点的角度响应;空间 BRDF 再为表面上的每个点提供不同的响应。最直接的方案是在每个 texel 保存完整 BRDF 参数,但参数数量必须足够紧凑,才能接受纹理带宽、缓存和 fragment shader 成本。
GPU Gems 采用 Lafortune 风格的和式:一个 diffuse reflectance 加上若干 specular terms。可以用下面的结构理解它,而不必把它当成对所有真实光传输的精确测量:
↡Lafortune 风格的空间 BRDF 近似,用少量可调 lobe 的 albedo、方向系数和 exponent 组成反射响应 把每个 term 看作一个可缩放、可偏转的高光形状。↡空间 BRDF 中描述一个局部镜面峰值的单个高光项,包含 albedo、shape 和 sharpness 参数 的 albedo 控制能量,C 系数控制峰值方向与各向异性,n 控制峰值宽窄。一个、两个或三个 lobe 通常足以表达常见材质的质量与速度折中。
当 Cx 与 Cy 相等时,响应更接近各向同性;不同符号和不同大小可以把峰值从法线反射方向拉开,表达 off-specular、retroreflection,或沿刮痕和丝线方向不同的 forward/back scattering。需要注意的是,这种灵活性来自参数近似,不能自动恢复测量数据中所有尖峰和遮挡。
2. local frame 与 lobe 评估
↡以切线 T、双切线 B 和法线 N 为轴,把光线和视线变换到表面局部坐标后评估 BRDF 的坐标系 将方向对齐到材质的切线框架:z 轴是法线,x、y 轴可以指向木纹、刷痕或织物主方向。顶点阶段应把 light vector 和 eye vector 以一致的坐标约定传给片元阶段,否则 C 系数的方向意义会错位。
离散光路径可以按以下顺序组织:
- 由 UV 读取 diffuse albedo、每个 lobe 的 albedo 和 shape 参数。
- 用 scale-bias 解码八位纹理里的 Cx、Cy、Cz 与 n;若使用浮点纹理,则减少量化误差但增加带宽。
- 归一化 local-space 的 to-light 与 to-eye,按 lobe 计算广义点积,再累加 diffuse 和 specular response。
- 将每个 light 的 incident radiance 乘上
max(0, N dot L),再把所有 light 的 exitant radiance 相加。
float3 EvaluateLobe(float3 wi, float3 wr, Lobe lobe) {
float3 shearedEye = lobe.C * wr;
float shape = max(0.0, dot(shearedEye, wi));
return lobe.albedo * pow(shape, lobe.n);
}
float3 ShadeSbrdf(float2 uv, float3 wi, float3 wr, float3 lightColor) {
float3 result = sampleDiffuse(uv);
for (int j = 0; j < LOBE_COUNT; ++j) {
Lobe lobe = decodeLobe(uv, j);
result += EvaluateLobe(normalize(wi), normalize(wr), lobe);
}
return result * lightColor * max(0.0, wi.z);
}这里的伪代码将 lobe 的形状和入射 irradiance 分开,便于检查能量是否被重复乘入。原始实现还会处理 HDR exposure、alpha 和多个 light;工程实现应保持参数解码、坐标变换和归一化的一致性。
八位纹理无法直接覆盖 Cx、Cy 的负值范围,因此需要约定 encoded = value * scale + bias,读取后再反解码。示例中可用固定 scale 与 bias 让数值落入可存储范围;实际平台应通过材质范围、压缩格式和误差测试校准,而不是把一个示例常数当成普适物理单位。
3. 纹理采样与质量预算
普通 diffuse albedo 通常可以使用线性过滤,因为颜色在空间上的平滑混合较自然;但 Cx、Cy、Cz 与 n 进入非线性 lobe 函数,线性插值可能产生视觉上不连续的峰值、sparkle 或错误各向异性。point sampling 是保守起点,代价是 zoom 或 mip 过渡时可能显得块状。
有三种常见取舍:
- point sample:保持每个 texel 的参数离散性,减少参数被混合到非法范围的机会;需要处理纹理放大和 mip 选择。
- 浮点参数纹理:减少 scale-bias 和八位量化,适合精细材料;带宽、缓存占用和平台支持会增加成本。
- 受控滤波:如果必须平滑参数,应在 lobe 评估之前做约束、重归一化或对 log-sharpness 等更合适的量进行滤波,并用极端 C 值测试。
不要只看高光中心截图。应同时检查镜头移动、纹理接缝、远近 mip、斜视角和各向异性方向;一个在静态近景看似准确的压缩策略,可能在动画中产生跳变。
4. 用预卷积环境图服务很多空间 BRDF
离散光只需要对有限个 light 评估每个 lobe;环境光则需要对整个入射半球积分。如果每个 texel 都有自己的 BRDF,为每个 BRDF 单独卷积环境图会不可行。↡已按一组反射 kernel 预先积分环境辐射,使运行时可以按方向和 sharpness 快速查表的环境纹理 的策略是预先卷积若干固定 sharpness 的 kernel,再让运行时用方向与 n 选择合适的层。
最简单的组织方式是把不同 n 的卷积结果放入 cube mipmap:较粗层保存宽 lobe,较细层保存尖 lobe。n 作为 LOD 的依据,lobe 的峰值方向 p(wr) 作为 cube lookup 方向;最后用 lobe albedo 和适当的 irradiance factor 组合结果。diffuse 环境项可以单独用法线方向索引的预卷积图。
预卷积的核心近似是把入射积分中的权重集中到 lobe 峰值附近。它减少了运行时成本,但要记住三点:卷积层只覆盖预设 n 集合,峰值方向需要从 local frame 正确变到 world space,且归一化与 irradiance falloff 不能被误当成同一个量。对 HDR 环境图,还要保留足够的动态范围,例如使用 RGBE 或浮点路径。
卷积本身也可能是离线成本。一个朴素实现遍历 cube map 每个面、每个 texel 和每个 incident direction,计算 kernel weight 后归一化;真正的工具链可以加入重要性采样、并行生成、缓存和质量分档,但必须用同一套 BRDF 参数验证离线与运行时方向约定。
5. 动手实验:在表现力与带宽之间选方案
先预测四个变化:增加 lobe 数量会提高材质表现力但带来更多纹理读取与 shader 工作;增大 n 会收窄高光并要求更细的预卷积层;把各向异性推离零会让峰值沿切线框架移动;把 shape 参数改成 linear filtering 可能让尖锐 lobe 出现 sparkle。
Spatial BRDF Lab
先猜:sharpness 增大,lobe 和纹理过滤会怎样?
观察:增加 lobes 能表达更多材质行为但增加纹理和 shader 成本;n 越大高光越尖,非线性 shape 参数用 linear filtering 更容易 sparkle;environment 模式把积分成本换成预卷积与 LOD。
实验图是确定性的示意模型,不代替 GPU profile。把它对应到实际材质时,至少记录 lobe 数量、参数纹理格式、采样方式、环境图卷积层和目标分辨率;这样当画质或带宽预算改变时,才知道要牺牲哪一层近似。
小结:让每个表面点拥有适度复杂的反射
- spatial BRDF 把纹理的空间变化与 BRDF 的角度变化组合起来,让不同 texel 拥有不同的反射模型。
- Lafortune representation 用少量 specular lobe 近似 diffuse 之外的复杂反射,albedo、C 系数和 n 分别控制能量、方向和宽窄。
- local surface coordinates 让各向异性方向进入 lobe 评估;坐标变换和归一化错误会直接表现为高光方向错误。
- 参数纹理需要处理负值范围、量化和非线性采样;point sampling 是保守起点,浮点纹理和受控滤波是更高成本选项。
- preconvolved environment map 把环境积分换成方向与 sharpness lookup,必须同时验证卷积层、LOD、HDR 动态范围和 irradiance 近似。
练习
问题 1|改 Demo 代码 一个材质当前只有一个 specular lobe,n=16。请把它扩展为两个 lobe:第一个保持宽而弱,第二个变为窄而亮;说明你会新增哪些纹理参数,以及如何避免两个 lobe 的能量被重复归一化。
问题 2|推导题 某个 texel 的编码 Cx 为 160,采用 value = encoded / 96 - 128 / 96 解码。计算 Cx,并判断它相对于零是正还是负;再说明如果误省略 bias 会怎样。
问题 3|独立实现题 为一面具有木纹各向异性的墙面选择离散光、预卷积环境光或混合方案。要求镜头移动稳定、近景高光有方向、远景带宽可控,请写出你的参数纹理和验证计划。
名词解释
本章出现的专业名词,用大白话再讲一遍。
- spatial BRDF
- 将表面空间变化与 BRDF 角度变化组合起来的反射表示。
- BRDF
- 描述固定表面点在入射方向和出射方向之间反射比例的函数。
- Lafortune representation
- 用 diffuse 与多个可调 specular lobe 近似空间 BRDF 的参数化表示。
- specular lobe
- 表示局部镜面峰值的高光项,包含 albedo、shape 和 sharpness。
- local surface coordinates
- 由切线、双切线和法线构成,用于在材质局部方向中评估 BRDF 的坐标系。
- preconvolved environment map
- 把环境辐射按反射 kernel 预先积分,运行时按方向和 sharpness 查表的环境纹理。