HDR(High Dynamic Range, 高动态范围)
引入HDR
- 你可能遇到过这种情况:当场景中大部分片段的颜色值超过1.0时,就会使渲染效果混成一片,难以分辨。这是因为:显示器被限制为只能显示0.0到1.0之间的颜色,并且颜色值在帧缓冲里也会被限制在0.0到1.0之间,因此当颜色值超过1.0时,都被截取到1.0,使场景显示出大量的白色,从而使细节分辨不清
- 仔细琢磨原因可知,虽然显示器和帧缓存对颜色值的范围有限制,但是片段的颜色值并没有被限制
- 在一般情况下,片段的颜色值也都是小于1.0的;但是当片段的颜色值大于1.0时,就称为高动态范围(HDR)
引入浮点帧缓冲
- 片段的颜色值大于1.0的情况可以是在片段着色器中经过计算得到,而更多时候是在客户端程序中直接往帧缓冲中传入,这时就需要一个特殊的帧缓冲来存储
- 浮点帧缓冲可以存储超过0.0到1.0范围的浮点数,所以他非常适合
创建浮点帧缓冲
- 当一个帧缓冲的颜色缓冲的内部格式被设定成了GL_RGB16F, GL_RGBA16F, GL_RGB32F 或者GL_RGBA32F时,就创建了浮点帧缓冲
glBindTexture(GL_TEXTURE_2D, colorBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_FLOAT, NULL);//注意数据的类型使float
- 默认的帧缓冲默认一个颜色分量只占用8位(bits),对于一个精度要求不是非常高的帧缓冲来说,使用GL_RGB16F就足够了
引入色调映射
- 前面引入HDR的概念使片段的颜色值超过1.0时,也能有该有的名分,不让人感觉奇怪
- 引入浮点帧缓冲,让片段的颜色值更方便的超过1.0
- 但是,显示器的限制我们无法改变,所以渲染结果分辨不清的问题仍然存在
- 为了解决这个问题,我们将所有的片段颜色通过某种映射统一变换到0.0到1.0之间,这样就会达到一种视觉平衡,使渲染的结果细节分明
- 这种映射变换就叫做色调映射(Tone Mapping)
色调映射
- 一个损失很小的转换浮点颜色值至我们所需的[0.0, 1.0]范围内的过程
- 他通常会伴有特定的风格的色平衡(Stylistic Color Balance)
- 有很多色调映射算法,这些算法致力于在转换过程中保留尽可能多的HDR细节。这些色调映射算法经常会包含一个选择性倾向黑暗或者明亮区域的参数
Reinhard色调映射
- Reinhard色调映射算法平均地将所有亮度值分散到[0.0, 1.0]上。
- 分散整个HDR颜色值到[0.0, 1.0]上,所有的值都有对应
- 为了更好的效果加上一个Gamma校正过滤
- 片段着色器
void main()
{
const float gamma = 2.2;
vec3 hdrColor = texture(hdrBuffer, TexCoords).rgb;
vec3 mapped = hdrColor / (hdrColor + vec3(1.0)); // Reinhard色调映射
mapped = pow(mapped, vec3(1.0 / gamma));// Gamma校正
color = vec4(mapped, 1.0);
}
曝光(Exposure)色调映射
- 与相机照相一样,通过设置不同的曝光度,让渲染结果表现出不同细节
- 曝光度越高,黑暗地方的细节表现的越多
- 曝光度越低,明亮地方的细节表现的越多
uniform float exposure;
void main()
{
const float gamma = 2.2;
vec3 hdrColor = texture(hdrBuffer, TexCoords).rgb;
vec3 mapped = vec3(1.0) - exp(-hdrColor * exposure); // 曝光色调映射
mapped = pow(mapped, vec3(1.0 / gamma));
color = vec4(mapped, 1.0);
}
- 曝光度从0.0到5.0
自动曝光调整(Automatic Exposure Adjustment)或者叫人眼适应(Eye Adaptation)技术
- 它能够检测前一帧场景的亮度并且缓慢调整曝光参数模仿人眼使得场景在黑暗区域逐渐变亮或者在明亮区域逐渐变暗
HDR作用
- 在明亮和黑暗区域无细节损失,因为它们可以通过色调映射重新获得
- 多个光照的叠加不会导致亮度被截断的区域的出现
- 在实时渲染中,HDR不仅允许我们设置的颜色超过[0.0, 1.0]的范围并且保留更多细节,同时还让我们能够根据光源的真实强度指定它的强度
- 在庞大和复杂的场景中应用复杂光照算法会被显示出来