GPU Gems 1 · Chapter 16. Real-Time Approximations to Subsurface Scattering
用 wrap lighting、光线穿透厚度估计与纹理空间 diffusion 近似皮肤、大理石等半透明材质的次表面散射外观。
学习目标
- 能从 entry、内部散射、exit 三个阶段解释皮肤和大理石的柔化、邻域 bleed 与偏红阴影过渡
- 能实现 wrap lighting 和 LUT 近似,并说明它没有显式模拟材料内部的真实传播
- 能用 light depth map 的 d_o − d_i 估计穿透距离,并识别凸物体、深度精度和投影边缘限制
- 能把模型展开到唯一 UV 的纹理空间,用低分辨率 light map 和 separable blur 近似 diffusion
大多数实时 shading 只在表面交点处理光:法线、光向量和材质颜色决定当前像素。但皮肤、蜡、大理石等材料会让光从一个表面点进入,在内部散射和吸收,再从另一个点离开。GPU Gems 1 第 16 章不追求完整的物理求解,而是把这种外观拆成几种可编程硬件能够承受的近似。
为什么次表面散射看起来不一样
↡光进入材料后在内部传播、散射、吸收并从另一个表面位置离开的光传输现象 的三个外观线索是:整体光照对比被柔化,局部光会 bleed 到相邻区域,细薄区域和明暗交界会出现更明显的颜色变化。皮肤的血液和组织会让背光过渡略偏红;耳朵、鼻翼等薄处尤其明显。
光传播越远,能量越被 attenuation 和 diffusion;这意味着“厚度”既影响亮度,也影响颜色和边缘宽度。实时近似的关键不是制造一张泛白的图,而是让传播距离、方向和材质颜色至少有一个可解释的对应关系。
1. 低成本的 wrap lighting
↡把 Lambert diffuse 的明暗截止向背光侧延伸,以降低硬对比的经验性光照近似 修改 dot(L, N) 的映射:标准 Lambert 在点积为负时直接归零,wrap 则使用一个 0 到 1 的 wrap 参数把曲线向背光侧延伸。
float diffuse = max(0, dot(L, N));
float wrapDiffuse = max(0, (dot(L, N) + wrap) / (1 + wrap));wrap lighting 不能告诉我们光从哪个点进入、在内部走了多远,也不能处理真实的多次散射;它只是降低 diffuse 对比的 cheap approximation。章节还建议把点积映射编码进 1D 或 2D LUT:纹理可以同时加入过渡区的红色 tint、diffuse color 和 specular power。
LUT 的优势是把多个经验曲线合成一次 texture lookup;代价是需要预先定义参数范围,并注意 dot(N,L) 和 dot(N,H) 的归一化。适合先做视觉风格探索,再决定是否需要更高成本的厚度或纹理空间方案。
2. 用 depth map 估计穿透距离
↡用光源到表面进入点的距离 d_i 与光源到离开点的距离 d_o 相减,估计光在材料内行进距离的量 是比 wrap 更具方向性的近似。第一遍从光源视点渲染,把进入表面的距离存入 light depth texture;正常 shading 时,把当前点投影回这张纹理得到 d_i,再计算当前点到光源的距离 d_o。
可以把 s 直接查到 artist-created 1D scatter texture,也可以使用指数吸收:
float s = trace(point, lightTexMatrix, lightMatrix, lightDepthTex);
float transmission = exp(-s * sigma_t);
return transmission * lightColor;这种方法把入射方向保留在 depth map 里,比静态 painted thickness 更适合动画模型;但单层深度只记录一个可见表面,孔洞和复杂内部结构会被错误解释。解决方向包括 depth peeling、额外的 front/back layers,或接受它作为 convex approximation。
深度精度同样是质量预算:旧硬件的 depth texture 读取可能只提供很少的有效位,需要 floating-point texture 或把 32-bit float pack 到 RGBA8。浮点纹理又可能不支持 filtering,放大投影时会出现 block artifacts;投影边缘还可能把背景采样到物体边缘,因此 depth pass 常会沿顶点法线做轻微 grow。
如果 shader 还需要入射点的 normal 或 surface color,可以把它们作为额外 render target 写出;支持 multiple render targets 的路径可以把 depth、normal 和其他信息合并到同一 pass。
3. 从单次散射到 diffusion
↡假设光在介质中只发生一次散射,通过沿折射光线步进估计向相机散射光子的近似模型 用 phase function 描述方向分布,并可在入射、离开处考虑 Fresnel。它能表达更具体的路径,但步进成本和参数复杂度会快速上升。
↡把强散射介质的多次散射累积效果用扩散过程近似的模型 更适合皮肤这类高度散射介质;本章把它作为更复杂的模型方向,并把可实时实现的重点放在 wrap、depth map 和 texture-space diffusion 上。
这些模型的边界应写进产品决策:若目标只是让角色在实时光照中不显得像塑料,wrap + LUT 可能已足够;若需要方向性、厚度和薄处的透射,应加入 depth map;若需要稳定的邻域柔化且拥有良好 UV,则 texture space 更可控。
4. texture-space diffusion
↡把模型按 UV 展开,在二维 light map 上计算和模糊光照,再重新投影回模型的扩散近似 将 3D 表面参数化为二维图像:顶点 shader 把 UV 的 0 到 1 映射到 NDC,先在纹理空间渲染 diffuse lighting,再对 light map 做 blur,最后像普通纹理一样应用回 3D 模型。
它的优势是 shading 复杂度由屏幕分辨率解耦到纹理 texel;很多 image-processing 操作在 2D 上更高效。若 UV 参数化相对均匀,纹理空间邻近可以近似世界空间邻近。低分辨率 light map 本身也会提供一些平滑效果。
blur 应拆成 horizontal 与 vertical 两个 pass,利用 separable filter 和 bilinear filtering 减少采样。皮肤的颜色传播还可以对 RGB 使用不同权重:红色通道用更宽的 kernel,绿色和蓝色更窄,以模拟波长依赖的吸收和散射。
half4 diffusion(float2 uv, sampler2D lightTex) {
half4 sum = 0;
sum += tex2D(lightTex, uv + float2(-5.5, 0)) * weight0;
sum += tex2D(lightTex, uv + float2(-1.5, 0)) * weight1;
sum += tex2D(lightTex, uv) * weight2;
sum += tex2D(lightTex, uv + float2(1.5, 0)) * weight3;
sum += tex2D(lightTex, uv + float2(5.5, 0)) * weight4;
return sum;
}如果把 shadow 也写进 light map,blur 会自然地产生软阴影;但最终材质仍应把 diffused lighting 与原始高分辨率 color map 合成,避免把所有纹理细节都抹掉。对于内部密度变化明显的物体,表面 UV 方法仍然不如体积渲染准确。
5. 动手实验:分辨 wrap、厚度与纹理空间
先预测:把 material thickness 拉高时,transmission 会如何变化?把 scatter width 拉宽时,邻域 bleed 是否增强?把 mode 从 texture 切到 wrap,哪些 entry/exit 信息会消失?最后提高 red diffusion,观察阴影边缘的色相而非只有亮度。
Subsurface Scattering Lab
先猜:厚度增加时,亮度和红色 bleed 会怎样?
观察:厚度增加会增强吸收;较宽的散射会模糊邻域;红色通道更宽时,阴影过渡更偏红。wrap 便宜但不记录真实 entry/exit。
实验中的 entry 与 exit 用来提醒你:wrap 只改照明曲线,depth 估计 d_o - d_i,texture mode 则将 light map 在 UV 空间扩散。四个滑杆不是物理参数求解器,而是把“厚度、传播宽度、颜色通道差异和近似模式”拆开观察。
小结:用最小模型换取可信外观
- 次表面散射让光从 entry 点进入、在材料内部传播后从另一个 exit 点离开,形成柔化、bleed、吸收和偏红过渡。
- wrap lighting 便宜且适合 LUT 风格控制,但不表达真实穿透距离。
- depth map 通过
s = d_o - d_i估计光程,需面对凸物体、深度精度、过滤和投影边缘限制。 - single scattering 和 diffusion approximation 是更复杂的路径模型;texture-space diffusion 用 UV light map 与 separable blur 近似邻域传播。
- 唯一且均匀的 UV、合理的低分辨率、分通道 blur 权重和最终与原始 color map 合成,是可发布实现的质量护栏。
练习
问题 1|改 Demo 代码 将 wrap 参数从 0.2 提高到 0.8,同时保持原始 diffuse 和 LUT 的 specular alpha 不变。请说明你会观察哪些视觉变化,以及为什么不能把结果称为真实 subsurface scattering。
问题 2|推导题 当前点到光源的离开距离 d_o 为 3.4,light depth texture 中的进入距离 d_i 为 2.6,吸收系数 sigma_t 为 1.2。计算 penetration depth 和指数 transmission,并说明厚度增加的趋势。
问题 3|独立实现题 为一张带动画的人脸选择方案:只要求柔和光照时使用什么;需要薄耳朵透射时增加什么;要做稳定的邻域 diffusion 时还要验证什么?
名词解释
本章出现的专业名词,用大白话再讲一遍。
- subsurface scattering
- 光在材料内部散射、吸收并从不同表面点离开的次表面光传输。
- wrap lighting
- 将 diffuse 明暗曲线向背光侧延伸的低成本光照近似。
- penetration depth
- 由离开距离减去进入距离得到的材料内光程估计。
- single scattering
- 假设光只发生一次内部散射的近似模型。
- diffusion approximation
- 用扩散过程近似强散射介质多次散射累积效果的模型。
- texture-space diffusion
- 在唯一 UV 的二维 light map 上计算、模糊并重新应用光照的扩散方法。