前言
本篇将介绍什么是色调映射,为什么需要色调映射,HDR,HDRI,当前流行的色调映射算法
HDR和色调映射
-
在谈及色调映射前我们先来看看平时经常听到的HDR和HDRI
- HDR: High-Dynamic Range(高动态范围)的缩写,可以理解为一个 CG 的概念,常出现在计算机
图形学与电影、摄影领域中 - HDRI: High-Dynamic Range Image 的缩写,即 HDR 图像,高动态范围图像
现代照相机是无法捕捉到人眼能看到的所有细节,这意味着即使使用分辨率最高的照片,所拍摄的场景也不能真实地还原。为了解决这一问题,需要拍摄同一场景的多张照片,并将它们合并。但是不能合并同一张照片,需要拍摄一张曝光均衡的照片,然后拍摄更多曝光不足和曝光过度的照片。然后合并这些照片,最终即可得到更多的细节都在高光和阴影的图片。这一合并过程就是色调映射
动态色调映射是用来使图像在显示器上看起来很真实。色调映射会到降低图像中的色调值,使其适合在显示器上观看(因为屏幕的颜色范围是[0,255])。例如,具有100,000:1动态范围的 HDR 照片需要进行色调映射,以便色调值落在1和255之间
那为什么不能简单地复制呢?因为在帧缓冲(Framebuffer)中时,亮度和颜色的值是默认被限制在[0,1],若将一个高范围的HDR照片简单的复制到帧缓冲中,会导致大于1范围的被限制为1。可能的效果如下图所示,可以看到大部分区域都是白色,这并不是我们想要的效果
- HDR: High-Dynamic Range(高动态范围)的缩写,可以理解为一个 CG 的概念,常出现在计算机
-
色调映射的定义:色调映射(Tone mapping)又称色调复制(tone reproduction)是将
宽范围
的照明级别拟合到屏幕有限色域内
的过程。色调映射与表示高动态范围的 HDR 和 HDRI 密切相关 -
渲染中的色调映射:HDR渲染和合成照片很相似,用更大范围的颜色值进行渲染从而获取大范围的黑暗与明亮的场景细节,最后将所有HDR值转换成在[0.0, 1.0]范围的LDR(Low Dynamic Range,低动态范围),且它可以在渲染过程的任何阶段完成.因为显示器的颜色范围在[0,1],我们肯定要做一些转换从而使得当前的HDR颜色值符合显示器的范围。简单地取平均值重新转换这些颜色值并不能很好的解决这个问题,因为明亮的地方会显得更加显著,所以我们能做的是用一个不同的方程来转换这些HDR值到LDR值
-
使用色调映射的原因:进行大幅度的
对比度衰减
将场景亮度变换到可以显示的范围,同时要保持图像细节与颜色等表现原始场景的重要信息
-
根据应用的不同,色调映射的目标可以有不同的表述。在有些场合,生成“好看”的图像是 主要目的,而在其它一些场合可能会强调生成尽可能多的细节或者最大的图像对比度。在实际的渲染应用中可能是要在真实场景与显示图像中达到匹配
色调映射算法
-
色调映射算法大致分为两种
-
全局映射
对于图片中任一像素我们不考虑它处于明部还是暗部,所有像素的映射方式是相同的。这种算法的好处是很快,但图像较为平坦
-
局部映射
局部映射会考虑图像的亮区或暗区中像素的位置,根据像素的空间特性对其进行处理,从而得到更多的细节。该算法会慢点,但效果更好
-
以下我们介绍两种流行的算法
Reinhard
Reinhard色调映射算法将所有亮度值平均地分布到LDR,增加了亮度值较低的像素的亮度,同时为高亮度像素提供了较低的对比度。我们将Reinhard色调映射应用到之前的片段着色器上,并且为了更好的测量加上一个Gamma校正过滤(包括SRGB纹理的使用)
函数:$\large $$\large V_{out} = \frac{V_{in}}{V_{in} + 1}$
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);
}
该算法是倾向明亮的部分,暗的部分不会那么精细
伽马压缩(Gamma Compression)
HDR图片包含不同曝光(图像的明暗程度)等级的细节,若需要控制图像的明亮程度,我们可以实现伽马压缩(曝光参数
).如有一个场景要展现日夜交替,在白天使用低曝光,在夜间使用高曝光,就像人眼调节方式一样
函数:$\large V_{out} = AV_{in}^\gamma,A > 0 \space and \space 0 < \gamma < 1 $.该函数基于非线性的伽马压缩技术
uniform float exposure; //默认为1
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);
}
通过改变曝光因子,我们可以看见场景的很多细节,而这些细节可能在LDR渲染中都已经被丢失。比如说隧道尽头,在正常曝光下木头结构隐约可见,但用低曝光木头的花纹就可以清晰看见了。对于近处的木头花纹来说,在高曝光下会能更好的看见
reference
Understanding HDR and Tone Mapping (skylum.com)
[Tone mapping | Indigo Renderer](https://www.indigorenderer.com/documentation/manual/rendering-with-indigo/camera/tone-mapping#:~:text=Tone mapping changes the brightness,immediately to the rendered image.)
[HDR - LearnOpenGL CN (learnopengl-cn.github.io)](https://learnopengl-cn.github.io/05 Advanced Lighting/06 HDR/)
Tone Mapping To Achieve High Dynamic Range (HDR) | by Vincent Tabora | High-Definition Pro | Medium
标签:HDR,映射,色调,vec3,mapped,曝光 From: https://www.cnblogs.com/chenglixue/p/17276471.html