学习资料链接
https://learnopengl-cn.github.io/05%20Advanced%20Lighting/06%20HDR/
https://learnopengl-cn.github.io/05%20Advanced%20Lighting/07%20Bloom/
HDR
一句话定义HDR:HDR是一个后期效果
一句话描述HDR:先渲染其他模型,并且过度曝光之,然后写入帧缓冲,然后通过HDR Shader进行修正。当然,Unity里面,后期是通过取出GPU中的数据,然后CPU里面计算,在写回GPU的方式,所以比较耗啦。
一句话说明HDR的目的:为了能够正确的显示那些亮度明显应该超过1.0的颜色的细节。
[1.1]小结:何时需要FrameBuffer
后期处理通常需要以下要求之一(才显得有必要):
(1)需要知道相邻其他像素的数据,这是正常渲染所不能提供的。
(2)需要扩展数据位,比如HDR需要扩展颜色范围,使之超过1。
[1.2]高动态范围与Tone Mapping
光照方程中,颜色值大于1是没错的,只是无法被显示出来而已。通过使片段的颜色超过1.0,我们有了一个更大的颜色范围,这也被称作 HDR(High Dynamic Range, 高动态范围)。转换HDR值到(LDRLow Dynamic Range,低动态范围)值得过程叫做色调映射(Tone Mapping),
[1.3]实现步骤
实际操作步骤简述:
1使用更大Size的FrameBuffer,为颜色提供更大的范围(颜色值超过1.0)
2在FrameBuffer中进行后期处理,使用函数进行色调映射(tone mapping)
Reinhard公式: finalcolor = hdrColor / (hdrColor + vec3(1.0)
曝光参数公式: finalcolor = vec3(1.0) - exp(-hdrColor * exposure);
高曝光值会使黑暗部分显示更多的细节,然而低曝光值会显著减少黑暗区域的细节,但允许我们看到更多明亮区域的细节。也有一些技巧被称作自动曝光调整(Automatic Exposure Adjustment)或者叫人眼适应(Eye Adaptation)技术,它能够检测前一帧场景的亮度并且缓慢调整曝光参数模仿人眼使得场景在黑暗区域逐渐变亮或者在明亮区域逐渐变暗。
e exp 0 = 1,e exp x(x<0) HdrColor越大,光强越强,exp(-HdrColor*exposure)的结果值越小。
Bloom
Bloom和HDR并没有必须与否的关系,但是由于HDR提供了更大的颜色范围,Bloom的效果将更精准,颜色值在[0,1],很多非发光体,也会被模糊化,模糊的阈值很难界定。
Bloom的原理是,将高亮部分提取出来做高斯模糊,然后在写回FrameBuffer。
具体操作略微复杂,步骤如下:
(1)准备一个FrameBuffer,绑定两个ColorBuffer。在一个FragmentShader里面,同时写入两个ColorBuffer。
一个写入基础片段信息(Fragment ColorBuffer)
另一个只写入高亮部分信息(Bloom ColorBuffer),写入高亮信息时需要一个颜色过滤器。
(2)另外准备两个FrameBuffer A(Horizontal)和B(Vertical),用作二次高斯运算。也就是先做水平高斯计算,在做垂直高斯计算,并且重复迭代这个过程,直到N次,N越大,模糊效果明显。
第一次将BloomFrameBuff写入FrameBufferA,这之后就在AB之前乒乓交换进行模糊值计算。
反复N次。
最后将两个FrameBuffer的片段的颜色值相加相加之后再通过Tone Mapping,将颜色值,根据一定的曝光度,映射到LDR颜色范围内。HDR+Bloom的后期特效就完成了!
其他问题
两个值得称道的地方
第一个问题是:用作过滤高亮颜色的Filter
//float brightness = dot(result, vec3(0.7152,0.2126, 0.0722));
float brightness = dot(result, vec3(0.333, 0.333, 0.333));
if(brightness > 1.0)
BrightColor = vec4(result, 1.0);
else
BrightColor = vec4(0.0, 0.0, 0.0, 1.0);
第一个倾向于让红色产生Bloom
第二个对于各种颜色的Bloom是均等的,但是要求亮度大于3才会产生Bloom
第二个问题是GaussBlur的Weight数组
uniform float weight[5] = float[] (0.2270270270, 0.1945945946, 0.1216216216, 0.0540540541, 0.0162162162);
参考:
if(horizontal)
{
for(int i = 1; i < 5; ++i)
{
result += texture(image, TexCoords + vec2(tex_offset.x * i, 0.0)).rgb * weight[i];
result += texture(image, TexCoords - vec2(tex_offset.x * i, 0.0)).rgb * weight[i];
}
}......
为了颜色的亮度守恒,需要保证:[0] + 2[1]+2[2]+2[3]+2[4] = 1.0f