OpenGLSL中处理HDR.ToneMapping(HLG)

紧跟前提文章,没有HDR.ToneMapping知识基础的同学请先阅读之前的文章:

HDR in Android 专栏

这篇文章主要是介绍在GLSL中处理HLG(hybrid-log-gamma 混合对数伽马曲线)的HDR tonemap SDR的渲染流程。

先看看Wiki有关HLG的概述:

混合对数伽马 (HLG) 传递函数是 BBC 和 NHK 联合开发的用于高动态范围 (HDR) 显示的传递函数。它向后兼容 SDR(伽马曲线)的传递函数。它被无线电工业和商业协会 (ARIB) 批准为 ARIB STD-B67。 它还在 ATSC 3.0、数字视频广播 (DVB) UHD-1 第 2 阶段和国际电信联盟 (ITU) Rec. 2100中被定义。

HLG 传递函数和 HLG 格式都是免版税的。当接收器与 BT.2020 颜色容器兼容时,向后兼容性允许它们与现有传输标准一起使用,从而降低设备制造商和内容分发商的复杂性和成本。[1][10][9] 它们受 HDMI 2.0b、HEVC、VP9 和 H.264/MPEG-4 AVC、支持,并被 BBC iPlayer、DirecTV、Freeview Play、和YouTube。

HLG 被设计为更适合电视广播,其中其他 HDR 格式所需的元数据不能向后兼容非 HDR 显示器,消耗额外的带宽,并且还可能在传输中变得不同步或损坏。 HLG 定义了一个非线性光电传递函数,其中下半部分信号值使用伽马曲线,上半部分信号值使用对数曲线。在实践中,标准动态范围显示器将信号解释为正常(尽管能够在高光中显示更多细节),但 HLG 兼容显示器可以正确解释信号曲线的对数部分以提供更宽的动态范围。 与其他 HDR 格式相比,它不使用元数据。

HLG 传递函数向后兼容 SDR 的伽马曲线。 但是, HLG通常与 Rec.2020色域 一起使用。 在不兼容的设备上产生具有可见色调变化的去饱和图像。 因此,HLG 向后兼容 SDR-UHDTV,在仅支持 Rec.709色域 的常见 SDR 设备上显示,色彩会有所失真。

(详细Wiki:https://en.wikipedia.org/wiki/Hybrid_log%E2%80%93gamma#Description)

三个关键重点已加粗:

1、详细标准在ITU.BT2100中定义;

2、HLG曲线的本质;上半部分信号值使用对数曲线,下半部分信号值使用伽马曲线。

3、HLG要与BT.2020色域一起食用。

直接上GLSL,fragment shader代码:

#version 320 es
precision highp float;
uniform int bitMark;
uniform float maxLuminance;
uniform lowp float imgWidth;
uniform lowp float imgHeight;
uniform highp usampler2D tex_unsigned_y;
uniform highp usampler2D tex_unsigned_uv;
in  vec2 vTextureCoord;
out vec4 _FragColor;

highp vec3 medialook_YuvConvertRGB_BT2020(highp uvec3 yuv, int normalize) {
    highp vec3 rgb;
    highp int y = highp int(yuv.x);
    highp int u = highp int(yuv.y);
    highp int v = highp int(yuv.z);
    float r = float(y - 64) * 1.164384                             + float(v - 512) * 1.717000;
    float g = float(y - 64) * 1.164384 - float(u - 512) * 0.191603 - float(v - 512) * 0.665274;
    float b = float(y - 64) * 1.164384 + float(u - 512) * 2.190671;
    rgb.r = r;
    rgb.g = g;
    rgb.b = b;
    if (normalize == 1) {
        rgb.r = r / 1024.0;  // [64, 960]
        rgb.g = g / 1024.0;
        rgb.b = b / 1024.0;
    }
    return rgb;
}

const float A3 = 0.15f;
const float B3 = 0.50f;
const float C3 = 0.10f;
const float D3 = 0.20f;
const float E3 = 0.02f;
const float F3 = 0.30f;
highp vec3 hableToneMapping(highp vec3 color) {
    return (color * (color * A3 + B3 * C3) + D3 * E3) / (color * (color * A3 + B3) + D3 * F3) - E3 / F3;
}
highp float hableF(highp float inVal) {
    return (inVal * (inVal * A3 + B3 * C3) + D3 * E3) / (inVal * (inVal * A3 + B3) + D3 * F3) - E3 / F3;
}

float ARIB_B67_A = 0.17883277f;
float ARIB_B67_B = 0.28466892f;
float ARIB_B67_C = 0.55991073f;
highp float arib_b67_inverse_oetf(highp float x)
{
    x = max(x, 0.0f);
    if (x  (b) ? (a) : (b))
#define FFMAX3(a,b,c) FFMAX(FFMAX(a,b),c)
void main() {
    float samplerPosX = vTextureCoord.x * imgWidth;
    float samplerPosY = vTextureCoord.y * imgHeight;
    highp uint unsigned_y = texelFetch(tex_unsigned_y, ivec2(int(samplerPosX), int(samplerPosY)), 0).x;
    highp uint unsigned_u = texelFetch(tex_unsigned_uv, ivec2(int(samplerPosX / 2.0), int(samplerPosY / 2.0)), 0).r;
    highp uint unsigned_v = texelFetch(tex_unsigned_uv, ivec2(int(samplerPosX / 2.0), int(samplerPosY / 2.0)), 0).g;
    highp uvec3 yuv10bit = uvec3(unsigned_y >> bitMark, unsigned_u >> bitMark, unsigned_v >> bitMark);
    highp vec3 rgb10bit = medialook_YuvConvertRGB_BT2020(yuv10bit, 1);
    // 电 转线性光信号
    highp vec3 fragColor = 2.5 * vec3(arib_b67_eotf(bfiler(rgb10bit.r)), arib_b67_eotf(bfiler(rgb10bit.g)), arib_b67_eotf(bfiler(rgb10bit.b)) );
    // HDR线性 ToneMapping映射转成 SDR线性
    highp float sig;
    highp float sig_orig;
    sig = FFMAX(FFMAX3(fragColor.r, fragColor.g, fragColor.b), 1e-6);
    sig_orig = sig;
    float peak = maxLuminance / 100.0f;  //  MaxCLL / REFERENCE_WHITE(100);
    sig = hableF(sig) / hableF(peak);
    fragColor.r = fragColor.r * (sig / sig_orig);
    fragColor.g = fragColor.g * (sig / sig_orig);
    fragColor.b = fragColor.b * (sig / sig_orig);
    // 逆线性光信号,变回电
    fragColor = vec3(arib_b67_oetf(fragColor.r), arib_b67_oetf(fragColor.g), arib_b67_oetf(fragColor.b));
    _FragColor = vec4(fragColor.r, fragColor.g, fragColor.b, 1.0);
}

流程和PQ曲线没有区别(流程分析参考 仿照FFmpeg在GLSL中处理HDR.Tonemapping)差异就是在eotf电光转换 和 oetf光电转换 的传输函数上的区别。参数公式都是参考zimg模块中的源码提示。

也可以参照ITU.BT2100标准详细扣细节:https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2100-2-201807-I!!PDF-E.pdf

主要还是一下两个转换函数的table。

OpenGLSL中处理HDR.ToneMapping(HLG)

OpenGLSL中处理HDR.ToneMapping(HLG)

几处细节要解析一下:

步骤一(电转线性光信号)的bfiler对应的 β 的操作,Lb和Lw是暂时还不懂原理,个人感觉是一组魔术数(magic number)w3c组织研究过这一组参数的取值(具体研究链接:https://www.w3.org/Graphics/Color/Workshop/slides/Cotton2.pdf

还是步骤一(电转线性光信号)后乘以2.5,这个对应的to_linear_scale,也是一个魔术数(magic number)这个不是固定值,看主观效果的喜好调整。

步骤三(逆线性光信号,变回电)这一次不是在想PQ感知量化曲线那样,用rec_709_oetf,因为HLG全程都是配套BT.2020色域使用的,所以就按照ARIB STD-B67标准的就好了。

That is all,欢迎讨论。

Original: https://blog.csdn.net/a360940265a/article/details/124671992
Author: Mr_Zzr
Title: OpenGLSL中处理HDR.ToneMapping(HLG)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/532583/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球