浅谈Unity下的 HDR(高动态范围)

前言

所谓HDR( High dynamic range)是高动态范围的简称,这项技术的本意是使用具有更大动态范围的信息载体去描述颜色亮度(相对低动态范围LDR而言),以期获得更加真实,更加明亮的色彩观感。HDR技术原理非常简单,就是将原先每条颜色通道只有8-bit位宽的帧缓存纹理,升级为拥有更多bit位宽,且能够存储浮点数值的纹理缓存,使得纹素可以存放亮度超过1的数值,从而增加了颜色亮度表达范围。最后输出到显示器前,再通过tone mapping将颜色调节回满足显示设备需求的动态区间。

对Unity而言,HDR的含义分为两个方面,其一是HDR Rendering,主要专注于“颜色”计算过程中的高动态,让输出到帧缓存的画面看起来更加真实自然;其二是HDR Output,是Unity将原生HDR信号输出到具有HDR硬件支持显示设备上的技术。就一般手游项目而言,谈到Unity HDR,我们指的的HDR Rendering,因为HDR Output即便在最新版本的Unity工程上,Built-in和URP管线都不支持。具体参考2022版官方文档中的相关功能表单(链接):

table

支持的纹理格式

  • FP16模式是ARGBHalf,每个通道16bit,带Alpha通道。
  • R11G11B10模式一个像素32bit,R11bit,G11bit,B10bit,不带Alpha通道。

开启HDR Rendering的优点

  • 增加高亮度下颜色的对比度 (高亮度区间有更多可区分的颜色)
  • 减少低亮度光照区域的条带感 (低亮度区间有更多可区分的颜色)
  • 更好地支持Bloom和Emission效果

开启HDR Rendering的缺点

  • VRAM 使用量增加
  • 色调映射(tone mapping)产生额外的计算开销
  • 硬件抗锯齿不兼容 HDR 渲染

一些可能有用的Tips

1)在前向渲染模式下,仅当工程启用了后期处理效果时才可能正确支持 HDR。这是出于性能考虑的原因。如果没有添加后期处理效果,则不存在色调映射,部分颜色将会发生强度截断。参考如下HDR文档说明:

When using HDR rendering with SDR output, you must use tonemapping to convert the HDR image buffer to an SDR image for display. Unity provides tonemapping post-processing effects that let you do this (链接

实际测试后发现,你完全可以只勾选HDR,不开启后处理,Unity工程不会报错,截帧后发现帧缓存数据类型已经升级到HDR格式,只是图像画面会有灰白过曝的感觉。

2)延迟渲染的HDR 模式下,光照缓冲区会被分配为浮点缓冲区。即使没有后期处理效果,延迟渲染也支持HDR。

3)在Build-in管线下,Unity可以很方便得在ProjectSettings -> Graphics -> Tier Settings 页签下选择启用HDR模式,顺带选择承载HDR纹理的帧缓存格式:参考有FP16和R11G11B10两种。其中后者(既R11G11B10)只占用32-bit位宽,但代价是没有A通道。前者(既R16G16B16A16)占用高达64-bit带宽,带有A通道,动态范围也更大,同时也更费VRAM。


HDR settings

4)在URP管线下,需要去URPiplineAsset配置文件中定义是否开启HDR,同时通过脚本修改“Graphics.preserveFramebufferAlpha”或者 “PlayerSettings.preserveFramebufferAlpha”来确定实际使用的HDR纹理:设置为True代表需要保留Alpha通道,那么Unity为你安排FP16格式的大杯;反之Unity为你配置R11G11B10格式的小杯。

5)关于R11G11B10格式RT如何混合图层,经过实践后发现只要不涉及到DstAlpha的混合模式,都没有问题,一旦你要访问DstAlpha,那么返回值一律为1.0(既不透明)。

关于(5)点,实验设计是这样的:创建2张平面前后排列,如下图所示,让摄像机观察到两块平面以及它们的重叠部分。


TestAlpha

平面1的材质shader做如下设置:

Tags { "RenderPipeline" = "UniversalPipeline" "RenderType" = "TransparentCutout" "Queue" = "Transparent" }
Blend One Zero, One Zero 

逻辑是渲染100%自己的颜色和Alpha值到背景板(帧缓存),其中材质Alpha值被设置为0(完全透明)。

平面2的材质shader有如下设置:

Tags { "RenderPipeline" = "UniversalPipeline" "RenderType" = "TransparentCutout" "Queue" = "Transparent" }
Blend DstAlpha Zero

逻辑是让自己的颜色 * 背景板上Alpha的返回值,直接覆盖到背景板上。

(A)当Urp工程处于以下

  • 配置1)开启HDR,设置缓存格式为FP16 关闭HDR
  • 配置2)关闭HDR

得到如下图效果:可以看到在都有A通道的前提下,红色平面材质在计算重合部分像素的时候,可以正确从背景板中取到绿色平面预存入的Alpha值(为0.3),而后直接覆盖自身颜色时,重合处的颜色会乘以0.3,导致渲染结果更加暗。

compare1

(B)当Urp工程处于以下

  • 配置1)开启HDR,设置缓存格式为R11G11B10

从下图结果看到,重合处颜色为红色平面本色,这是由于取到的背景板Alpha值恒定为1所致。

compare2

多提一嘴 第一组对比图中左下方的摄像机视窗截帧图像看起来比较暗沉,其原因在于Unity是以线性读写模式存放HDR缓存的,而我们的输出显示屏自带remove gamma correction效果,会把真实亮度的图像(线性空间)下拉变暗,作为对比右下角的视窗图像(非HDR)是基于ARGB32_SRGB格式创建的,图像被存放在sRGB空间,从而保证了显示器输出的准确性。

Unity shader中的HDR方法

参考如下DecodeHDR方法参考,主要用于解码Unity自身创建的各类贴图纹理(如高动态IBL, SKY-BOX,Lightmap等),以上贴图一般存放在sRGB中,且单通道只有8-bit,因此在HDR品质设置不高的情况下需要使用一些技巧扩展其上颜色的上限,常见的编码类型有RGBM和dLDR。

// Decodes HDR textures
// handles dLDR, RGBM formats
inline half3 DecodeHDR (half4 data, half4 decodeInstructions)
{
    // Take into account texture alpha if decodeInstructions.w is true(the alpha value affects the RGB channels)
    half alpha = decodeInstructions.w * (data.a - 1.0) + 1.0;

    // If Linear mode is not supported we can skip exponent part
    #if defined(UNITY_COLORSPACE_GAMMA)
        return (decodeInstructions.x * alpha) * data.rgb;
    #else
    #   if defined(UNITY_USE_NATIVE_HDR)
            return decodeInstructions.x * data.rgb; // Multiplier for future HDRI relative to absolute conversion.
    #   else
            return (decodeInstructions.x * pow(alpha, decodeInstructions.y)) * data.rgb;
    #   endif
    #endif
}

HDR的开销

主要由HDR缓存本身带来的内存开销,以及最终校色所带来的消耗共两部分组成。一般而言前者带来的性能影响不大,特别是当选择R11G11B10格式时,就连采样数据所占用的IO带宽都与从前一致。所以可以认为HDR的主要消耗来自全屏校色相关的开销,它包括生成LUT和全屏Blit。

在工程开启了HDR,也追加了tonemapping等后处理逻辑后,Unity会执行2个特殊pass,其一叫“ColorGradingLUT”,参考下图:

HDRLUT1

该Pass内容比较琐碎,详细说明的话恐怕得新开一坑,简言之,这个Pass基于当前帧的参数,分别计算了后曝光、白平衡、对比度、颜色过滤、色调分离、通道混合、阴影中间调高光、色相偏移、饱和度,伽马校正等等各种映射关系和预计算表格,并存储到如下所示的多张LUT中:

HDRLUT2

在后处理阶段会执行如下图所示的“UberPostProcess”大杂烩Pass,通过读取预生成的LUT,逐像素调节并输出最终的颜色。

HDRLUT3

测试工程中我给tonemapping设置了ACES映射(一种HDR映射曲线),所以能在pass中看到对应Keywords。实际性能分析时可以详细关注这两个pass在整个渲染流程中的占比,从网上收集的一些信息来看,这两个pass在主流机配置下应当不高于1ms。

Ref

  1. HDR Offical Doc
  2. 关于URP的HDR格式
  3. UnityShader精要笔记
  4. Unity通用渲染管线(URP)系列(十二)—— HDR
  5. Unity分享85|资源管理方式|UI相机性能开销|HDR……
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容