GPU Gems 1 · Chapter 21. Real-Time Glow
用 glow source mask、低分辨率 render target、水平与垂直 separable convolution 以及 additive blend,在实时场景中构造可控的光晕与 after-image。
学习目标
- 能把 real-time glow 拆成正常场景、source mask、降采样、水平与垂直 blur、additive blend 五个可验证阶段
- 能用纹理 alpha 或对象属性隔离 glow source,并解释亮度、颜色和 mask 如何影响最终光晕
- 能实现 neighbor sampling 与 separable convolution,比较二维 d² 成本和两次一维 2d 成本
- 能处理低分辨率 aliasing、half-texel、fog、after-image 反馈和参数动画等发布边界
光晕和 halo 是强光源的视觉证据:屏幕和印刷品的动态范围有限,周围扩散的亮度会让观察者相信中心光源比普通表面更强。真实世界中的 glow 来自大气或眼内散射;实时渲染可以用屏幕空间后处理近似这种线索,既增强写实感,也能塑造科幻、雾气和高能量场景的风格。
↡通过隔离发光区域、模糊其屏幕空间图像并以加法合成回场景来实时模拟光晕的后处理效果 的核心是额外的图像通道,而不是修改原物体的几何轮廓。正常场景照常渲染,source pass 只写发光 RGB 与 mask;后续 blur 扩散亮度,最后使用 additive alpha blending 把 aura 加回 back buffer。
1. source pass:决定什么会发光
↡只包含 glow source、其他区域为黑色的屏幕空间纹理,用于后续 blur 与合成 可以由整件物体、材质属性、对象 flag 或纹理局部区域决定。一个适合资产管线的做法是复用 diffuse texture 的 alpha:正常渲染使用 RGB,glow pass 使用 RGB × alpha。alpha 为零处不产生 source,逐渐增大时同时提高源光颜色与强度。
source texture 通常写入 off-screen render target。可以重新渲染整个场景,让非发光对象变黑;也可以把 glow mask 写入普通 back buffer 的 alpha,再用一次 copy 或 StretchRect 复制并降采样到 source target。后一种方式避免复杂场景的第二次几何渲染,但要确认 back buffer alpha 不会影响正常颜色路径。
source 的 RGB 与亮度直接决定 glow 的颜色和能量,顶点、对象或时间参数可以再乘一个强度来做动画。source pass 若需要单独 fog,也应把它作为“只影响 glow”的艺术控制写清楚:黑 fog 会压低远景 source,能减弱低分辨率带来的闪烁,但也会让远景物体不再发光。
2. neighbor sampling 与屏幕空间卷积
↡为每个输出像素读取其周围固定偏移的 source texel,并按 kernel 权重组合的 GPU 图像处理方式 不把纹理坐标设为像素自身的单点,而是让每个 sampler 取不同 texel offset。对每个邻居乘以权重并累加,就得到一个局部 convolution;输入是全屏矩形,输出是 render-target texture。
float4 BlurRow(float2 uv, float2 texel, sampler2D source) {
float4 sum = 0.0;
sum += tex2D(source, uv - 3.0 * texel.xx) * weight0;
sum += tex2D(source, uv - 1.0 * texel.xx) * weight1;
sum += tex2D(source, uv) * weight2;
sum += tex2D(source, uv + 1.0 * texel.xx) * weight3;
sum += tex2D(source, uv + 3.0 * texel.xx) * weight4;
return sum;
}如果直接使用直径 d 的二维 kernel,读取数量近似与 d 的平方成正比。对于 50×50 的 blur,每个输出像素可能需要约 2500 个 texel reads;对可分离的近似,先沿水平方向得到 intermediate texture,再沿垂直方向读取它,只需要约 50 加 50 次读取。
↡将二维 blur 拆成一个水平一维 kernel 和一个垂直一维 kernel,使用中间 render target 近似大范围卷积的技术 不要求最终二维形状严格等于某个 Gaussian。只要两条一维 profile 的组合产生合适的宽底、中心 spike 或 diffraction-like 形状,就能用两次一维处理替代昂贵的二维采样。
实际管线通常是:source target → horizontal blur target → vertical blur target → back buffer。每次 render-to-texture 都要确认输入和输出不是同一 surface,避免读写冲突;半径扩大时,可以通过更低分辨率、较少采样、预合并权重或多次小 blur 维持帧预算。
3. render target 分辨率、texel 坐标与 aliasing
blur 结果主要是低频特征,因此 glow source 和 intermediate target 可以使用全屏三分之一或四分之一的分辨率,以减少填充和带宽。降低分辨率也会自然扩大光晕的屏幕覆盖范围;但 source 本身的 aliasing 会被 blur 扩散,形成运动中的闪烁。
对策应按成本排序:使用硬件 bilinear 过滤下采样 source,保留足够的 source resolution,给远景 source 添加仅用于 glow 的黑 fog,或让艺术家降低细线 source 的强度。不能只把 blur radius 拉大来掩盖 aliasing,因为这会同时增加 halo 和带宽。
在 Direct3D 风格的 texel corner 约定中,采样精确 texel center 需要加 half-texel offset;OpenGL 的坐标约定不同。跨 API 或工具链共享全屏 quad 时,应准备一张单像素网格测试图,检查水平、垂直和对角线 offset 是否落在预期 texel 中。
4. additive blend 与材质控制
↡将模糊后的 glow 颜色按 alpha 或强度加到正常 back buffer,以产生亮度扩散的合成方式 是最终合成的简单路径:scene 保留真实物体细节,glow texture 只贡献额外亮度。source RGB 决定色相,source alpha 或独立强度决定贡献;强光材质可以用宽底 profile,霓虹边缘可以叠加更窄的 spike。
source mask 也可以来自 render style table:正常 context 使用原始材质,glow context 使用 artist 指定的发光样式。这样同一件模型的不同部位可以有不同颜色和亮度,而不必拆出新的几何。若屏幕效果要模拟玻璃、镜头或皮肤,则仍要说明 glow 只是后处理近似,并结合 scene depth、mask 或其他通道防止穿透不该发光的物体。
5. after-image、fog 与参数动画
↡将上一帧 source 以衰减系数加入当前 source,再进行 blur 以形成拖尾和渐隐的时间反馈效果 可以让移动物体的 glow 逐帧淡出,隐藏 source aliasing,并制造速度、醉态或濒死等风格。稳定做法是保存 blur 前的 source,下一帧使用 current + previous × decay;decay 大,拖尾短;decay 小,拖尾长。
不要递归地无条件累加上一帧的最终 glow。因为最终 glow 已经扩散,继续把它作为 source 会让光晕每帧向外扩张;如果权重不足以衰减,系统会逐渐饱和。替代方案可以省掉 source buffer,但必须给 previous glow 设置严格的衰减和亮度上限。
source-only fog 是另一个艺术和工程控制:远处细线在低分辨率 target 中更容易抖动,把远景压成黑色会减少 aliasing,代价是远景 glow 被削弱。blend weight、fog color 和 decay 也可以随时间 ramp,做脉冲或不稳定的氛围,但所有参数动画都应避免突然跨越造成可见跳变。
6. 动手实验:找出 glow 的视觉与成本平衡
先预测五个变化:brightness 增大只应增强源和 halo;blur radius 增大会拓宽 aura 并增加读取;source resolution 降低会节省成本却提高 shimmer;decay 降低会拉长拖尾;fog 增大能压制远景 aliasing 但让远处光源消失。
Real-Time Glow Lab
先猜:降低 source resolution,为什么可能更闪?
观察:blur radius 增大能形成宽 halo 但增加成本;source resolution 过低会让单个 texel 被放大并闪烁;decay 控制拖尾,fog 可抑制远景 aliasing。
这个实验用确定性的示意光源表达参数关系,不替代真实 GPU pass profile。发布前要在目标分辨率、相机运动、细线几何、远景灯光、HDR 或 LDR framebuffer、不同 blend weight 下检查:source 是否准确、halo 是否溢出、运动是否闪烁、after-image 是否失控、额外 render target 和带宽是否可接受。
小结:用两个一维 blur 做出可控的光晕
- real-time glow 先隔离 glow source,再把它作为屏幕空间图像处理;不要直接 blur 整个场景。
- glow source mask 可由对象、材质 alpha、render context 和亮度控制,RGB 决定颜色,alpha 决定区域与强度。
- neighbor sampling 把固定偏移的 texel 送入 shader,separable convolution 将二维 d² 读取降为水平与垂直两次约 2d。
- 低分辨率 render target 能省成本并放大光晕,但会放大 source aliasing;需要下采样过滤、fog 和分辨率之间的取舍。
- additive alpha blending 负责合成,after-image 负责时间拖尾;任何时间反馈都必须有衰减和亮度上限。
练习
问题 1|改 Demo 代码 请把一个直接 25×25 二维 blur 改成水平和垂直两次一维 blur。需要哪些 render target,最终读取数量大约从多少降到多少?
问题 2|推导题 某帧使用当前 source 强度 0.8,上一帧 source 强度 0.5,after-image decay 为 0.3。忽略空间 blur,合成后的 source 强度是多少?如果 decay 改为 0.8,视觉趋势是什么?
问题 3|独立实现题 为一个包含远近霓虹细线的第一人称场景设计 glow 管线。要求远景不明显闪烁、近景有宽 halo、移动物体有短拖尾,请说明 source、分辨率、blur、fog 和验证计划。
名词解释
本章出现的专业名词,用大白话再讲一遍。
- real-time glow
- 隔离发光源、模糊其屏幕空间图像并加法合成回场景的实时光晕效果。
- glow source mask
- 只保留发光区域的屏幕空间纹理或 alpha 掩码。
- neighbor sampling
- 按固定 texel 偏移读取输出像素邻域并应用 kernel 权重的图像处理方法。
- separable convolution
- 将二维卷积拆成水平和垂直两个一维卷积的高效近似。
- additive alpha blending
- 将模糊 glow 颜色按强度加回正常场景的合成方式。
- after-image
- 衰减地加入上一帧 source 以产生拖尾和渐隐的时间反馈效果。