本文的是 mini.gmshaders.com/p/tonemaps 的学习笔记
动态范围问题的来源
数字图像技术永远都在追求所谓的"沉浸感"和"真实感"。人眼将清晰度视为分辨率和对比度的组合。因此,当对比度增加时,人眼感知的清晰度也会增加。所以即使分辨率没有改变,图像也可能因为颜色范围扩大而对比度看起来更加清晰。
比如很贵的超高清电视(UHDTV) 通过高动态范围(HDR)与宽色域(WCG)相结合,可以使视频看起来更加清晰、逼真,与传统的标准动态范围视频相比,能够有效改善用户的视频观看体验。
那么回到 Shader中,Tone Mapping(色调映射)是将高动态范围(HDR)图像转换为低动态范围(LDR)的还能符合真实观看感受的技术。
HDR转为 LDR会带来的问题称之为动态范围问题。动态范围带来的问题和解法如下图所示。
ToneMap用于处理其中一个过曝(Over-Exposure)的问题. 对于过曝可以看下面两张图, 红框部分左边为过曝情况,右边为真实视觉。 由于过曝导致太阳周围也是一片白。


Shader中过曝
假设我们在 Shader种有一个红日 Shader, 如下面所示
scss
void mainImage( out vec4 fragColor, in vec2 fragCoord ){ vec2 uv = (2.*fragCoord - iResolution.xy)/iResolution.y; float d = length(uv) ; vec3 sun = vec3(1.0, 0.5, 0.2); float brightness = 1.0 / d; fragColor = vec4(brightness * sun,1.0);}

2504250631
在上图种最左边的 sun的三个颜色通道值为(1.0, 0.5, 0.2) 所以 red, green更加容易到达最大值 1.0. 到了 1.0 整个颜色就变成白色, 就是去了颜色与颜色之间的对比,产生过曝。 都是暖色的情况下还比较正常一些, 如果换成蓝色主导或者绿色主导就有点奇怪了。。 不过说实话,我挺喜欢蓝色。。。


Tonemaps
回到 ToneMaps,其核心就是讲一堆超过 1.0 的值回到 1.0 以内。 这个 Shader www.shadertoy.com/view/llXyWr 作者给出了 ToneMaps的一些算子, x轴表示 input brightness 而 y 轴表示output brightness
最上面的白线是 1.0 ,也就是屏幕输出最大的 brightness是 1.0. 灰色的区域是表示输入为[0, 1]. 可以看到基本上都有一个缓-平-缓的变化。 在接近于 0.的地方导数接近于 0., 在大于 1.的地方导数也接近于 0.
接下去就看看一些经典的 tonemaps算法吧. 首先给出原图与对比


ACES

2504252522
css
// Narkowicz 2015, "ACES Filmic Tone Mapping Curve"vec3 Tonemap_ACES(vec3 x){ const float a = 2.51; const float b = 0.03; const float c = 2.43; const float d = 0.59; const float e = 0.14; return (x * (a * x + b)) / (x * (c * x + d) + e);}
Narkowicz于2015年提出的ACES Filmic Tone Mapping Curve是一种广泛应用的色调映射技术,其设计目标是将高动态范围(HDR)内容映射到低动态范围(LDR)显示设备,同时保留视觉细节并模拟电影工业的调色风格。与其他色调映射技术相比,实际应用中的优势
-
广泛集成:被Unity、Unreal Engine等主流引擎采用,成为游戏和影视渲染的行业标准之一。
-
HDR与LDR的平滑衔接:在处理极端亮度时(如太阳、灯光),能自然过渡到白色,避免"硬截断"或颜色偏移。
-
艺术化控制:支持通过调整曲线斜率或添加后处理(如饱和度微调)实现风格化效果,例如模拟电影《银翼杀手2049》的高对比度科幻风格。
Hable

2504252622
css
// Hable 2010, "Filmic Tonemapping Operators"vec3 Tonemap_Uncharted2(vec3 x){ x *= 16.0; const float A = 0.15; const float B = 0.50; const float C = 0.10; const float D = 0.20; const float E = 0.02; const float F = 0.30; return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;}
John Hable在2010年提出的Filmic Tonemapping Operators(通常称为"Hable Filmic"或"Uncharted 2 Tonemapping")是一种经典的色调映射技术,最初为游戏《神秘海域2》开发,旨在将高动态范围(HDR)图像适配到低动态范围(LDR)显示设备,同时模拟电影胶片的高对比度视觉效果。其基于分段函数的S形曲线设计
-
三段式非线性压缩:Hable Filmic通过分段函数(线性段 + 过渡段 + 压缩段)实现动态范围压缩:
-
线性区:保留中间调的对比度。
-
过渡区:平滑衔接线性区与压缩区。
-
压缩区(肩部与趾部):抑制高光过曝并提升暗部细节。 这种设计比简单的全局S曲线(如Reinhard)更灵活,能精确控制不同亮度区域的映射。
-
-
高度可调的参数化:需手动调整参数(如线性区宽度、白点阈值、阴影提升强度),允许艺术家根据场景需求自定义对比度和动态范围分布,例如增强高光戏剧性或柔化阴影。
Unreal

2504252955
vbnet
vec3 Tonemap_Unreal(vec3 x){ // Unreal 3, Documentation: "Color Grading" // Adapted to be close to Tonemap_ACES, with similar range // Gamma 2.2 correction is baked in, don't use with sRGB conversion! return x / (x + 0.155) * 1.019;}
这个方法看起来简单舒服~
Tanh
这是作者原创自己找到的, 在他后面许多 shader种都有用到
scss
vec3 Tonemap_tanh(vec3 x){ x = clamp(x, -40.0, 40.0); return (exp(x) - exp(-x)) / (exp(x) + exp(-x));}
资料
-
GM Shaders Mini: Tonemaps mini.gmshaders.com/p/tonemaps
-
HDR关键技术:HEVC/H.265编码优化 cloud.tencent.com/developer/a...
-
MLP Embedded Inverse Tone Mapping dl.acm.org/doi/pdf/10....