Shader 图像处理1_ToneMap技术处理过曝

本文的是 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)显示设备,同时保留视觉细节并模拟电影工业的调色风格。与其他色调映射技术相比,实际应用中的优势

  1. 广泛集成:被Unity、Unreal Engine等主流引擎采用,成为游戏和影视渲染的行业标准之一。

  2. HDR与LDR的平滑衔接:在处理极端亮度时(如太阳、灯光),能自然过渡到白色,避免"硬截断"或颜色偏移。

  3. 艺术化控制:支持通过调整曲线斜率或添加后处理(如饱和度微调)实现风格化效果,例如模拟电影《银翼杀手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形曲线设计

  1. 三段式非线性压缩:Hable Filmic通过分段函数(线性段 + 过渡段 + 压缩段)实现动态范围压缩:

    1. 线性区:保留中间调的对比度。

    2. 过渡区:平滑衔接线性区与压缩区。

    3. 压缩区(肩部与趾部):抑制高光过曝并提升暗部细节。 这种设计比简单的全局S曲线(如Reinhard)更灵活,能精确控制不同亮度区域的映射。

  2. 高度可调的参数化:需手动调整参数(如线性区宽度、白点阈值、阴影提升强度),允许艺术家根据场景需求自定义对比度和动态范围分布,例如增强高光戏剧性或柔化阴影。

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));}

资料

  1. GM Shaders Mini: Tonemaps mini.gmshaders.com/p/tonemaps

  2. HDR关键技术:HEVC/H.265编码优化 cloud.tencent.com/developer/a...

  3. MLP Embedded Inverse Tone Mapping dl.acm.org/doi/pdf/10....

相关推荐
Senar15 分钟前
Web端选择本地文件的几种方式
前端·javascript·html
烛阴32 分钟前
UV Coordinates & Uniforms -- OpenGL UV坐标和Uniform变量
前端·webgl
姑苏洛言37 分钟前
扫码小程序实现仓库进销存管理中遇到的问题 setStorageSync 存储大小限制错误解决方案
前端·后端
烛阴1 小时前
JavaScript 的 8 大“阴间陷阱”,你绝对踩过!99% 程序员崩溃瞬间
前端·javascript·面试
lh_12541 小时前
ECharts 地图开发入门
前端·javascript·echarts
jjw_zyfx1 小时前
成熟的前端vue vite websocket,Django后端实现方案包含主动断开websocket连接的实现
前端·vue.js·websocket
Mikey_n2 小时前
前台调用接口的方式及速率对比
前端
周之鸥2 小时前
使用 Electron 打包可执行文件和资源:完整实战教程
前端·javascript·electron
我爱吃朱肉2 小时前
HTMLCSS模板实现水滴动画效果
前端·css·css3
机器视觉知识推荐、就业指导2 小时前
开源QML控件:进度条滑动控件(含源码下载链接)
前端·qt·开源·qml