OpenGLSL中处理HDR.ToneMapping(HLG)

article/2025/8/16 9:03:57

紧跟前提文章,没有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 <= (1.0f/2.0f))x = (x * x) * (1.0f / 3.0f);elsex = (exp((x - ARIB_B67_C) / ARIB_B67_A) + ARIB_B67_B) / 12.0f;return x;
}
highp float arib_b67_ootf(highp float x)
{return x < 0.0f ? x : pow(x, 1.2f);
}
highp float arib_b67_eotf(highp float x)
{return arib_b67_ootf(arib_b67_inverse_oetf(x));
}
highp float arib_b67_oetf(highp float x) 
{x = max(x, 0.0f);if (x <= (1.0f / 12.0f))x = sqrt(3.0f * x);elsex = ARIB_B67_A * log(12.0f * x - ARIB_B67_B) + ARIB_B67_C;return x;
}float Lb = 0.1f;
float Lw = 302.0f;
float sys_gamma = 1.001f;
highp float bfiler(highp float x) {float b = sqrt(3.0 * pow((Lb/Lw), (1.0/sys_gamma)) );return max(0.0, ((1.0-b)*x + b) );
}#define FFMAX(a,b) ((a) > (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。

几处细节要解析一下:

步骤一(电转线性光信号)的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,欢迎讨论。


http://chatgpt.dhexx.cn/article/rqkAEby1.shtml

相关文章

HDR中HLG与PQ曲线的互转

HDR视频中由于电光转换曲线的不同&#xff0c;技术标准也大致分为了杜比视界、HDR10、HDR10、HLG等&#xff0c;目前常用的主要是以PQ曲线的HDR10和HLG。这两者之间的区别在HDR视频编码参数中进行过详细的说明。一般的HLG适用于广电&#xff08;根据用户设备的峰值亮度动态的调…

使用DataList吧

这是个具有明显煽动性的标题&#xff0c;没错&#xff0c;在这里就是提议大家使用DataList。在MSDN中文站有一篇关于repeater&#xff0c;datalist和datagrid的性能测试的文章&#xff0c;我也把这篇文章转到了我的站的“转载嘿嘿 希望你接受我的教训 一定早做打算 精华”里&am…

html5-datalist标签-定义选项列表

datalist使用 datalist使用需与input关联起来&#xff0c;input定义list属性&#xff0c;同时datalist定义id属性&#xff0c;两者的值必须相同。 实例代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><m…

常用数据集合—List

1、List、Map、Set、Queue、Array 2、数据结构&#xff1a;数组、链表 3、实现List方法的类Ctrl T&#xff08;设置的是Eclipse快捷键&#xff09;或右键查找 4、这里就简单看下最常用的ArrayList 5、构造方法一目了然 6、添加数据&#xff0c;一条条添加或者添加集合 良好…

DataList的数据绑定

8.4.1 DataList的数据绑定 DataList控件中通过自定义模板来设置数据的显示样式&#xff0c;它支持如下模板类型&#xff1a; ItemTemplate&#xff1a;包含一些 HTML元素和控件&#xff0c;将为数据源中的每一行呈现一次这些HTML元素和控件。 AlternatingItemTemplate&#xf…

datalist标签

学习笔记&#xff0c;仅供参考&#xff0c;有错必纠 参考自&#xff1a;w3school 关于datalist标签 <datalist> 标签定义选项列表&#xff0c;应该与 <input>标签配合使用该&#xff0c;它定义 input 可能的值&#xff1b; <datalist> 及其选项不会被显示出…

datalist 元素

效果图&#xff1a; 代码如下&#xff1a; <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>datalist 元素</title> </head> <body><form action"#" method"post">请输入用户…

<datalist>标签

<datalist>标签是HTML5新增的元素&#xff0c;用于input的标签可选值。 用法&#xff1a;input标签的list属性指定自定义的datalist可选值&#xff0c;datalist的id于list值相同即可完成绑定。 可选值: <input type"text" list"myDatalist">…

数据列表DataList模板之实例

1&#xff0c;数据列表DataList与重复列表Repeator很类似&#xff0c;但是DataList应用更广泛&#xff0c;因为他可以选择和修改数据项的内容。 DataList的数据显示和布局与Repeator控件一样都是通过“模板”控制的。 &#xff08;注&#xff1a;模板至少要定义一个“数据项模…

DataList详细用法

DataList控件与Repeater控件一样由模板驱动,与Repeater控件不同的是: DataList控件默认输出是一个HTML表格.DataList在输出时已经在相应的模板上套上了表格标签,而Repeater则是模板是什么样,输出就是什么样. 1. DataList显示数据 例1:使用DataList显示数据 Code <as…

DataList 用法详解

<% Page Language"C#" AutoEventWireup"true" CodeBehind"DataList.aspx.cs" Inherits"FileUpload自动上传文件.DataList" %><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w…

datalist标签使用

jsp使用datalist标签写可查询可输入下拉框样式 jsp使用datalist标签写可查询可输入下拉框样式。需求用法 jsp使用datalist标签写可查询可输入下拉框样式。 需求 jsp页面实现可选择,可查询,可输入的下拉选择框, 一段废话 本来想用select看看有什么属性可以实现,但基本上接触的…

DataList内容详解

DataList是另一种显示数据控件&#xff0c;它与GridView不同的是&#xff0c;它全部使用模板进行设计&#xff0c;并且DataList的模板是对整行设置&#xff0c;而不是像GridView那样只对某一列进行模板设计。 正是由于它使用模板进行设计&#xff0c;所以它的灵活性比GridView更…

DataList控件详细用法(一)

使用DataList控件 本章内容&#xff1a; 1、理解事件冒泡 2、使用模板 3、在DataList中显示数据 4、在DataList中创建多列 5、捕获DataList控件中产生的事件 6、选择DataList中的项 7、使用DataList控件中的DataKeys集合 8、编辑DataList中的项 本章介绍在ASP.NET框架中功能…

(13)<datalist> 标签

一、<datalist>标签的作用 <datalist> 标签规定了<input> 元素可能的选项列表。<datalist>元素包含了一组<option>元素&#xff0c;这些元素表示预定义可选值&#xff0c;在<input>元素输入过程中&#xff0c;会自动响应<option>元…

Hibernate缓存的evict、clear和flush方法

evict()、clear()和flush()方法是Hibernate缓存的3种基本操作方法&#xff0c;本文主要介绍这3种方法的使用方式和具体区别。 Company表&#xff1a; Company实体类&#xff1a; import java.util.Set;public class Company {private int companyId;private String companyName…

注册中心日志输出_Running the evict task with compensationTime 0ms_频繁输出这句_SpringCloud工作笔记161

可以在:application.properties 中配置一下不停的打印日志,太烦人了.##Running the evict task with compensationTime 0ms不停的输出这个太烦人了可以关闭 logging.level.com.netflixwarn技术交流QQ群【JAVA,C,Python,.NET,BigData,AI】&#xff1a;170933152 开通了个人技术微…

SpringCloud Eureka注册中心日志输出问题:Running the evict task with compensationTime 0ms

动Eureka注册中心后&#xff0c;控制台一直输出 — [a-EvictionTimer] c.n.e.registry.AbstractInstanceRegistry : Running the evict task with compensationTime 0ms 这段日志&#xff0c;间隔时间与你的配置有关。强迫症看着很烦&#xff0c;那么在开发过程中如何关闭这条日…

HashMap中的putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict)解读

在面试中我们会经常遇到关于HashMap的问题&#xff0c;这里我写了我对HashMap里面一个挺重要的方法 putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict)的理解&#xff0c;下面就是我对这个方法的理解。 其实putVal(int hash, K key, V value, boolean o…