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....

相关推荐
袁煦丞1 分钟前
泰拉瑞亚远程联机魔法:cpolar内网穿透实验室第617个成功挑战
前端·程序员·远程工作
玲小珑5 分钟前
Next.js 教程系列(十二)API Routes:构建轻量级后端服务
前端·next.js
JinSo10 分钟前
EasyEditor AI 聊天助手:让低代码开发更简单
前端·javascript·github
答案answer14 分钟前
three.js 实现几个炫酷的粒子特效(火焰,烟雾,烟花)
前端·three.js
ObjectX前端实验室30 分钟前
三年写了很多代码,也想写写自己
前端·程序员
满分观察网友z35 分钟前
uniapp的navigator跳转功能
前端
江城开朗的豌豆39 分钟前
Vue组件DIY指南:手把手教你玩转自定义组件
前端·javascript·vue.js
无奈何杨43 分钟前
CoolGuard风控节假日完善,QLExpress自定义函数
前端·后端
CSR-kkk1 小时前
前端工程化速通——①ES6
前端·es6·速通