HDR

1)什么是HDR

现实真正存在的亮度差,即最亮的物体亮度,和最暗的物体亮度之比为10^8, 而人类的眼睛所能看到的范围是10^5左右,但是一般的显示器,照相机能表示的只有256种不同的亮度。

但是我们可以多拍几张照片,2张,3张……甚至几十张,这些照片的曝光依次增大,随着照片曝光的增大,照片会依次变亮,换一种角度,照片所表示的细节会由亮处向暗处改变。
根据上面的原理,如果我们将照相机拍摄的很多张图片合成,结果图片的数量级一般为甚至更多。

根据上面的原理,如果我们将照相机拍摄的很多张图片合成,结果图片的数量级一般为10^16甚至更多。这样问题就出现了,或者更高数量级的亮度只能存在电脑里,而一般的显示器只能表示256个亮度数量级,用256个数字来模拟所能表示的信息,这种模拟的方法就是HDR技术核心内容之一,学名叫Tone-Mapping(色调映射)。用Tone-mapping压缩以后,合成的HDR影像就能很好地在显示器上显示。

2)phong模型中的颜色计算问题

在我们之前的光照计算中,对于phong模型,我们最终的结果是result = ambient + diffuse + specular; 这样相加的计算会导致一个结果,就是result的结果值可能会超过1,或者接近1,这样会导致第一个结果:图像太亮了,我们只能看到一堆的亮度,而看不到图像的内容。这是因为我们在framebuffer中,亮度和颜色的值会限定在0.0到1.0之间,当颜色值大于1.0的话,会强制设置为1.0,这样就导致了图像的过度曝光。
hdr

3)解决方案

我们可以在计算颜色时,只管直接计算就好了,不用管他们是否大于1,然后我们对最终的结果做一下Tone-Mapping,将其范围约束到0-1就好了。

4) Tone-Mapping

色调映射(Tone Mapping)是一个损失很小的转换浮点颜色值至我们所需的LDR[0.0, 1.0]范围内的过程
最简单的色调映射算法是Reinhard色调映射:

1
2
3
4
5
6
7
8
9
10
11
12
void main()
{
const float gamma = 2.2;
vec3 hdrColor = texture(hdrBuffer, TexCoords).rgb;

// Reinhard色调映射
vec3 mapped = hdrColor / (hdrColor + vec3(1.0));
// Gamma校正
mapped = pow(mapped, vec3(1.0 / gamma));

color = vec4(mapped, 1.0);
}

曝光色调映射算法:

1
2
3
4
5
6
7
8
9
10
11
12
void main()
{
const float gamma = 2.2;
vec3 hdrColor = texture(hdrBuffer, TexCoords).rgb;

// 曝光色调映射
vec3 mapped = vec3(1.0) - exp(-hdrColor * exposure);
// Gamma校正
mapped = pow(mapped, vec3(1.0 / gamma));

color = vec4(mapped, 1.0);
}

5)后记

本章内容比较简单,就是用不同的算法将颜色值限定在0-1的范围内。但是HDR的内容可不是这么简单的,他有很多具体复杂的算法来实现调色,以实现更好的效果。

参考资料
https://learnopengl-cn.github.io/05%20Advanced%20Lighting/06%20HDR/