Bloom
全库目录221
本文目录10
小亮点只模糊一次,容易得到单一大小的光圈。Bloom先从HDR颜色构建多尺度图,再从粗层向细层合成,让光晕同时保留近处亮核和远处扩散。每级过滤与能量权重共同决定形状和闪烁。
高亮信号
Bloom 模拟强光在镜头、传感器和视觉系统中向周围扩散。它应该由 HDR 高亮驱动。
阈值与曝光
根据亮度或颜色阈值提取高亮。Soft Knee 在阈值附近平滑过渡,避免亮度稍微变化时 Bloom 突然出现。
也可以让全部线性HDR颜色参与扩散,保留弱光贡献。带阈值的方案需要明确阈值是在场景尺度还是曝光尺度定义:若输入已乘预曝光 ,场景阈值 和过渡半宽 也应分别换成 与 ,否则曝光变化会改变入选的光源。
下面的HLSL函数输入有限、非负的线性RGB,用最大RGB分量定义亮度 ,保持提取后的RGB比例。阈值 ,过渡半宽 ,均与输入处于同一线性尺度。在 内构造二次过渡, 时退化为硬阈值。
float3 ExtractBloom(float3 color, float threshold, float knee)
{
float brightness=max(color.r,max(color.g,color.b));
if (brightness<=0.0) return float3(0,0,0);
float soft=0.0;
if (knee>0.0) {
float q=clamp(brightness-threshold+knee,0.0,2.0*knee);
soft=q*q/(4.0*knee);
}
float contribution=max(max(brightness-threshold,soft),0.0);
return color*(contribution/brightness);
}在上边界 ,二次项的值为 、导数为1,正好接上线性项 ;下边界处值与导数都为0。半宽不超过阈值,使曲线从非负亮度开始过渡,并保持提取量不超过输入亮度。零输入直接返回黑色;正输入使用自身亮度作为除数,颜色、阈值和半宽同时缩放时,输出随同一倍率缩放。固定的绝对亮度下限会破坏极低预曝光尺度下的这个关系。它是美术提取模型,并非镜头散射的光学定律。
多尺度扩散
提取图已有亮核,直接把它合回原图只会让原位置更亮。光晕需要把这份信号送到邻近像素。大半径卷积能做到这一点,但半径增大时,逐像素采样的成本也随之增长。金字塔先缩小图像,让粗层的一个纹素代表原图的一片区域;再把粗层结果逐级展开,就能用小核产生大范围的扩散。
降采样与采样位置
先固定一个可复现模型:输入宽高为偶数,下一层各减半,纹素中心位于整数索引加0.5的位置。目标像素(x,y)覆盖源图(2x,2y)到(2x+1,2y+1)的四个纹素,把它们平均就得到最小的2×2盒式降采样。
def down2(src):
h, w = len(src), len(src[0])
if h % 2 or w % 2:
raise ValueError("本模型要求偶数尺寸")
return [[sum(src[2*y+j][2*x+i] for j in range(2) for i in range(2))/4
for x in range(w//2)] for y in range(h//2)]这里每个值代表线性HDR颜色的一个分量,RGB逐分量执行同样运算。四个权重相加为1,所以全图为4时,下一层仍为4。孤立的16则变成一个4;它对应的像素面积扩大四倍,因此面积加权总量仍是16。把这些值误当作应当直接相加的总能量,会在每次降采样时错误补偿亮度。
盒平均便宜,但它只提供有限的低通能力。亚像素亮点移动时,输入光栅覆盖率和滤波相位都可能变化;粗层会把变化扩大成明显闪烁。扩大首层低通核可以削弱这种相位敏感性,代价是近处亮核更软。若用非线性的亮度权重抑制极亮点,还会改变本页线性模型的能量关系,须单独检查。
奇数尺寸的右侧与下侧无法按完整2×2块配对。工程实现可使用向上取整尺寸、边界钳位与明确的采样中心,但这会让边缘纹素被重复计入。参考模型主动拒绝奇数尺寸,使数值例子的覆盖范围保持精确;目标工程应单独用右下角亮点检查所选边界策略。
上采样核与扩散半径
粗层已有远距离信息,还需要把它连续地重建到细层。令目标中心的归一化坐标为uv,粗层尺寸为(W,H)。对应粗层索引坐标为(uv.x*W-0.5, uv.y*H-0.5)。双线性重建先取这个坐标周围四个粗层纹素,再按小数部分加权;边界在本模型中钳位到最近纹素。
只做双线性重建可以平滑块边界,但扩散形状仍容易显出网格。这里在重建前增加一个可分离的帐篷核,沿每个轴使用[1,2,1]/4,二维权重是两轴的乘积:
1 2 1
2 4 2 × 1/16
1 2 1中心贡献1/4,四个轴向邻居各贡献1/8,四个对角各贡献1/16。九个权重相加为1,因而保持常量。这个核是方形可分离近似;重复扩大半径时仍可能出现轴向形状,不能把它当成真实镜头的点扩散函数。
下面的HLSL是机制片段。Source是当前粗层的线性HDR纹理,LinearClamp使用线性过滤和边界钳位,sourceTexelSize为粗层纹素尺寸倒数。偏移使用粗层单位;若误用输出细层的倒数,粗层越小,实际核就越窄,远处光晕会收缩。每个样本的双线性插值完成重建,外层九点权重完成帐篷过滤。
float3 TentUpsample(float2 uv, float2 sourceTexelSize)
{
float3 result = 0;
const float weights[3] = { 0.25, 0.5, 0.25 };
for (int y=-1; y<=1; ++y)
for (int x=-1; x<=1; ++x)
result += weights[x+1]*weights[y+1]*
Source.SampleLevel(LinearClamp,
uv+float2(x,y)*sourceTexelSize,0).rgb;
return result;
}高斯核同样可以拆成横向和纵向两遍,核宽由标准差决定;Kawase类方法通过多次偏移采样扩大支撑范围。选择它们会改变频率响应、轴向形状和采样数。本文的完整数值链固定使用上面的帐篷核,读者可以先追踪一种确定的实现,再更换核比较亮点响应。
尺度混合与光晕形状
设完整提取图为,连续降采样得到到。最低层从开始,逐级计算:
表示用帐篷核重建到第层尺寸,是本模型的尺度混合系数。它只决定粗细层的比例,最终强度另由控制。展开三级链可得:
当时,三个尺度的系数为0.5、0.25、0.25;当时,系数为0.2、0.16、0.64。粗层贡献增加,能量分布向更远处移动,亮核占比下降。权重之和始终为1,所以增加层数不会把常量场直接加亮;直接逐层相加则会随层数增加常量输出。
以远离边缘的16亮点为例,D0中亮点为16,D1中对应块为4,D2为1。帐篷核把D2的1分到中心1/4、轴向1/8和对角1/16的位置,再通过双线性插值铺到D1。在最外侧,D0、D1都为零,仍能收到最粗层的贡献;靠近亮核则会叠加细层的局部信息。因此只显示最低层再放大,能看见大光晕,却会损失中间尺度的形状。
每个粗层纹素覆盖的原图宽度按2、4、8倍增长。更深的层数扩展最大支撑范围,改变这些范围的相对权重。这两个参数不能互相替代。有限图像的钳位边界会重复边缘样本,常量保持并不保证边缘孤立亮点的面积加权总量守恒;能量测试应同时放置中心与边缘亮点。
合成与资源执行
最终采用时,原始颜色完整保留,光晕是额外增加的美术贡献。若目标是把一部分光从亮核分散出去,可对无阈值输入使用,其中。这要求滤波及边界满足所需归一化,阈值提取与边缘钳位还会改变总量;“保持常量”本身不能证明每个有限图像都严格守恒。
合成在线性HDR空间、色调映射之前进行。若先把亮点压到显示范围,再提取高亮,原来强度16与强度4的差别就会被压缩,光晕无法表达这份亮度差。曝光关系见 HDR与曝光,显示映射见 Tone Mapping。
实际纹理链按下面顺序提交。每个D在生成之后供下一级读取;每个U写到另一张纹理,避免同一子资源一边被采样一边被覆盖。最低层可以直接作为只读的Um,其余Ui等最后消费者完成后才回收。
Extract(C) → D0
Down2(D0) → D1 → Down2(D1) → D2
U2 = D2
Mix(D1, Tent(U2), s) → U1
Mix(D0, Tent(U1), s) → U0
Compose(C, U0, g) → output动态分辨率改变时,重新计算尺寸、层数和每次采样的源纹素倒数。若仅调整输出视口而继续使用旧纹理尺寸,边缘可能采到未写区域;若读写次序声明遗漏,则关闭某级后也可能留下上帧数据。把每级D和U单独显示,比只观察最终光晕更容易定位这些问题。
验证方法
固定曝光,先用常量图核对每一级的归一化,再用中心亮点核对扩散支撑范围。逐级关闭粗层贡献,检查光晕是否按预计收缩;把亮点缓慢移过纹素边界,观察降采样相位造成的变化。边缘亮点与动态分辨率单独检查。
test_post.py 检查零输入、软阈值中点、预曝光尺度不变性及不同层数的常量保持;正文提取与帐篷上采样函数通过DXC着色器模型6.0像素入口编译,编译封装见probe.hlsl。GPU纹理链、滤波伪影和性能尚未测量。
reference.py是Python3标准库的完整标量图像参考实现,展开盒平均、边界钳位、双线性与帐篷重建和尺度混合。test_reference.py检查常量场、16亮点、尺度权重、奇数尺寸拒绝和粗层扩散范围。命令为python -m unittest discover -s 知识库/examples/mechanism_depth/bloom -p test_reference.py。它验证本文数值模型,不模拟GPU纹理精度与真实镜头。
相关主题
参考资料
- Filament:Bloom,曝光相对自发光与Bloom输入。
- LearnOpenGL 作者示例:Physically based bloom,多尺度降采样与上采样的进一步实现入口;本页使用显式给出的简化归一化模型。