一 什么是HDR?
了解HDR前需要先了解什么是LDR。通常受限制与人的视觉系统,通常显示器让显示器支持1670 万(24位)的颜色值,这些颜色值通常可以足够表示一个物体的颜色。显示器屏幕每个像素由3种颜色通道R、G、B组成,每种各占8位,组合起来就是24位,表示值的范围也就是0-255,那么对应的每个颜色通道的对比率范围也就是1:256。我们就称作这样的范围的颜色值叫做 LDR低动态范围 。
对比与LDR,HDR 是 Hight Dynamic Range 的缩写,中文名字叫高动态范围。最初来自摄影技术来自摄影技术,摄影师对一个场景采用不同曝光多张相片,捕捉大范围的色彩,这些图片最终合成为hdr图片。高动态范围可以表示的每个基础颜色的范围超出1:256,那么HDR就可以这样定义了通常把使用颜色对比度超出1:256的颜色值的渲染方式叫做HDR渲染。
二 为什么需要HDR或者说它有什么用 回答问题前先看一下图
左边的图片没有使用HDR看起来颜色很单调,后面的图片使用了HDR看起来很靓丽,亮的部分和暗的部分对比很明显。明亮的部分细节很丰富。
由于渲染着色中(使用shader)最终的颜色值被限制在0 -1 范围内,所以如果一部分颜色值超出1的值都会被渲染出纯白色,那么亮部的细节就会丢失了。所以为了保留更多的颜色细节尤其是明亮部分的我们需要使用HDR。
三 HDR使用方法
采样一个HDR图片,让后使用某种算法把颜色值限制到LDR输出。
流程图:
1. HDR 格式图片
Radiance:1985年,GregWard创建了Radiance文件格式。今天还在使用。这些文件使用RGBE格式,每像素32位。其概念是使用共享红色,绿色和蓝色指数。它可以处理非常明亮的像素而不损失精度的暗的颜色。可以使用简单的代码快速加载.hdr文件。
OpenEXR:于2003年由Industrial Light and Magic (ILM)作为开放标准发布。该HDR文件格式健壮、高效,主要用于电影生产。OpenEXR支持各种颜色格式,并打包了一套操作HDR图像的工具(库、查看器……)。
在后处理中我们通过帧缓冲的方式处理hdr颜色:
//定义一个帧缓冲
unsigned int hdrFBO;
//初始化帧缓冲
glGenFramebuffers(1, &hdrFBO);
// 生成一个包含16位的浮点类型信息的颜色值的图片(GL_RGBA16F),并把它作为hdr的帧缓冲的颜色附件
//颜色附件可以理解成帧缓冲可以包含的颜色信息的载体
unsigned int colorBuffer;
glGenTextures(1, &colorBuffer);
glBindTexture(GL_TEXTURE_2D, colorBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
不使用hdr时候我们通常生成图片使用数据格式为GL_RGB(A),在上面代码中我们生成图片的时候采用的图片数据类型为GL_RGBA16也可以为 GL_RGB16(不保留透明通道信息),每一个RGB颜色通道存储的类型为16位那么表示的范围就是0.0 -65 536.0 这么多范围已经足够了,此外opengl还支持每一个颜色通道值最大颜色范围为32位(GL_RGBA32F),一般情况下是不需要的因为它会占据很多的内存。
2. 色调映射
色调映射(Tone Mapping)是一个损失很小的转换浮点颜色值至我们所需的LDR[0.0, 1.0]范围内的过程,通常会伴有特定的风格的色平衡(Stylistic Color Balance)。
Reinhard色调映射
最简单的色调映射算法是Reinhard色调映射,它涉及到分散整个HDR颜色值到LDR颜色值上,所有的值都有对应。Reinhard色调映射算法平均得将所有亮度值分散到LDR上。我们将Reinhard色调映射应用到之前的片段着色器上,并且为了更好的测量加上一个Gamma校正过滤(包括SRGB纹理的使用)。OpenGL着色器代码如下:
void main()
{
const float gamma = 2.2;
//hdrBuffer 为hdr贴图
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);
}
带有可控曝光度的色调映射
//曝光度
uniform float exposure;
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);
}
结论:
曝光值越大,图片呈现越亮,暗部的细节越明显。
曝光值越小,图片越暗,亮部的细节越明显。
合理控制曝光度来显示丰富的图片信息。
Reference :https://sudonull.com/post/12412-Learn-OpenGL-Lesson-57-HDR
。
。