Gamma Correction/Gamma校正/灰度校正/亮度校正 - 部分 DCC 中的线性工作流配置

本文详细探讨了Unity中的伽马和线性颜色空间的工作流程,包括DCC软件中的颜色处理、纹理的sRGB选项以及帧缓冲的sRGB作用。解释了线性空间下纹理的后处理、sRGB编码以及着色器中的颜色转换。同时,提到了不同DCC软件如Photoshop、C4D、3DsMax中的线性工作流设置,并给出了Unity中BRP和URP管线的线性色彩处理。总结了伽马校正、线性空间的重要性以及它们在显示器上的表现差异。


纠正

之前自己写的乱七八糟,都是似懂非懂
直到有一天,要给项目中 linear 效果,要在 gamma space 下还原
后来发现以前自己写的这边文章,全写了一堆错误的理解,现在自己重新修改了很多出写错的地方


我们一般说的 gamma correction 简称 gamma,而 gamme correction 就是对 gamma2.2或是gamma0.45,也就是 pow(inputVal, 1.0/gamma),也就是 pow(inputVal, 1.0/2.2) 或是 pow(inputVal, 1.0/0.45)

注意,DCC软件中的 gamma 值的设置,一般都是这么使用,比如 3Ds max 中的 gamma 值:

  • outputVal=gammaCorrect(inputVal, gamma)
  • gammaCorrect(inputVal, gamma)=pow(inputVal, 1.0/gamma)
  • outputVal=pow(inputVal, 1.0/gamma)

下文有提到


OK,到这里,我们都不知道为何要有这个东西


因为经过前人验证过

如果现实世界中,我们看到的线性的颜色亮度,人眼看起来感觉不是线性的

如下图,看图感受:
在这里插入图片描述

  • 问题1:你觉得左边是线性值,还是右边是线性值?
  • 问题2:你觉得左边的还是右边的图中的黑和白的颜色过渡比较均匀?

那么下面回答:

  • 问题1 回答:左边是:非线性,右边才是:线性的值(这是前人使用过专业的光子平均面积亮度测量仪测出来的结果)
  • 问题2 回答:正常人都会觉得,左边的比较均匀,感觉中间就是刚刚好的黑与白中间的中灰

下面是右边的图的 中灰 位置
在这里插入图片描述

下面是左边的图的 中灰 位置
在这里插入图片描述


上图再次总结:

  • 人眼看到的亮度线性信息,大脑感知的是非线性的
  • 这个非线性呈:上拱曲线型,大概是:pow(inputVal, 1.0/2.2) 的输入输出曲线 (x轴:input,y轴:output)
  • 从上图的右图可得知,人眼看到的是暗部偏少(也就是说,暗部的值很快就往更亮的值靠近,所以才呈现:上拱形状)

使用 GGB 可以看到 pow(InputVal, 1.0/2.2) 是怎么个拱法:
在这里插入图片描述

为什么变亮了,如:输入线性值为 0.5,但是被上拱过之后,输出就变成了在 0.73 左右

所以原本 0.5 变成了 0.73 那肯定就更亮了
在这里插入图片描述

倒回这两个黑白颜色过渡图
在这里插入图片描述

如果我们想要将:右边的线性值,在人眼看到后在大脑呈现上拱(被提亮)过的信号,能像 左边那样看着比较舒服的信号

那么我们就需要之前开头说的:这里相对 humanEyeSeeLinear 之后的数据,做 类似 pow(inputVal, 2.2) - 压暗颜色 来处理 上拱过的数据 (压回限制感知强度)

注意,所谓的 gamma 校正 是 上拱的 pow(val, 1.0/2.2) 变成到 sRGB 空间下的颜色,因为以前的CRT显示器会 pow(val, 2.2) 的压暗处理

所谓的 degamma 是 下压的,就是将 sRGB 颜色还原到 Linear 颜色空间的过程,即:pow(val, 2.2)

还是使用 GGB 来查看效果
在这里插入图片描述

可以看到 humanEyeSeeLinear (没错就是 Cnglish,一般可以写为:linearAfterEyeSee 会更好一些)是人眼看到的线性感知后的上拱型,而我们需要的 gamma 是下压型来将 被拱 过的 压回 线性的

可以看到 经过 linear(x)=fixed(humanEyeSeeLinear(x)) 就是线性的结果(因此,现在各大渲染引擎,影视,都是按这套流程来渲染前处理颜色数据)


下面引用韩世鳞老师的一张图来说明(此图中的显示器都是 CRT为例),

DCC软件中编辑好的照片,在 CRT 显示器 中,感觉被 gamma=0.45454545; output=pow(input, 1.0/gamma); output=pow(input, 1.0/0.45454545);,导致亮度被压暗了,

因此需要提前 gamma=2.2; output=pow(input, 1.0/gamma); output=pow(input, 1.0/2.2);,也就是转到 sRGB 颜色空间的 sRGB 贴图,这样此图片在 CRT 上看着正常了

然后此贴图通过 internet 等传送方式到各个CRT设备上的显示时(这些图片都是经过:gamma=2.2; output=pow(input, 1.0/0.45454545); 处理过提亮了),

所以在 CRT 自带的 gamma=0.45454545; output=pow(input, 1.0/0.45454545) 的特性抵消好,刚好就是 linear 的结果

在这里插入图片描述


因为我们人眼在看到真实世界中的亮度值时

在眼球编码生物神经电信号给大脑中的灰度感知并非线性的(人眼会将 outputVal=pow(inputVal, 1.0/2.2) 处理(近似这样处理)

会将原来的线性颜色表现出来的亮度整体提升,这种视觉效果感觉很差,暗部色阶跳跃过大(暗部往亮提了,导致暗部灰阶细节降低,在 8bits 的表示 255 中,色阶跳跃过大,因此暗部细节表现就会丢失)

所以我们为了提高视觉效果更好,才会在颜色输出给人眼看到之前,先将压暗暗颜色,也就是outputVal=pow(inputVal, 2.2)

(注意:但是相对 CRT 显示器(会压暗,比如,输入电压0.5,输出亮度才 0.2+),因此我们需要给CRT显示器输入的颜色是要提亮的,也就是输入 sRGB 空间下的颜色,而不是压暗,下面的内容有提到)

这样将压暗过 pow(intputVal, 2.2) 的结果,这一升一降,刚刚好变成线性的

如果这时时候人眼看到线性颜色,肯定又是整体提亮的,所以需要再次 pow(inputVal, 2.2) 这时在人脑中的灰度信号是线性的,那么视觉上自然就会舒服很多,但是实际上真正的物理亮度是被压暗过的

CRT显示器是会降低亮度,近似:outputColor = pow(inputColor, 2.2) 这样的结果(如下图,参考:CRT显示器的亮度响应曲线图

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


也可以简单参考 SIGGRAPH 2010 的 PBS 的 Gamma-Correct

s2010_physically_based_shading_hoffman_b.pdf

下面是原理,和需要处理的部分:

In Theory, Just Need To (原理,需要处理的):
• Convert shader inputs to linear before shading (在 shader 的 shading 前,先将输入值转为 linear)
• Convert shader output to gamma at end (在 shader 输出前,将 linear to gamma,其实应该是 fbo 输出前而不是 shader 输出前,这里写的不够精准)
• “Free” (pre-convert constants & vertex colors, HW converts from textures / to frame buffer) (“免费” (预处理 一些常量值 和 顶点颜色,硬件处理 纹理采样转换,和 输出到 frame buffer 的转换))

下面是一些引起的其他问题,多是一些早期的硬件没考虑 颜色空间的问题导致硬件程序的处理不当导致的:

Complications
• Some HW does gamma blending incorrectly (某些硬件处理 gamma 混合不正确)
– Bad for multipass / deferred shading, transparencies (多pass,延迟着色,透明 等,渲染都是糟糕的)
• Some HW filters gamma textures incorrectly (某些硬件处理 texture filter 不正确)
– But you can at least generate MIP maps the right way (但是你至少可以在生成mipmap时是对的)
• Actual nonlinear space supported by HW varies (实际上,非线性空间的支持 是各不相同的)
– Especially bad for consoles (尤其是糟糕的主机设备)

下面是导致需要在工作流中做对应调整的部分:

Unintended Consequences (其他确定的结果)
• Changes light distance falloff, Lambert falloff, soft shadow edges, vertex interpolation, etc. (改变灯光衰减,兰伯特衰减,软阴影边缘,顶点插值,等)
– May require artist adjustment / retraining (可能需要美术调整 或是 重新培训)
– In some cases (like vertex interpolation) it might make sense to fix in the shader (在某些情况(比如顶点插值)是可以在 shader 中修复的)


Unity 中的 gamma、linear 工作流

比如:“A纹理是在 Photoshop Gamma 空间下生产的”

意思说:在 Photoshop 中默认(默认的颜色空间配置,默认是sRGB的)对 A 纹理制作生产过程中,这类颜色空间下的颜色都是 pow(inputVal, 1.0/2.2) 的,想要返回Linear 空间需要 压暗亮度pow(inputVal, 2.2)

参考 :

unity 纹理 sRGB 勾选项的作用:

参考:TextureImporter.sRGBTexture

  • gamma 空间上
    勾不勾 都无所谓,都当作 没有勾选 纹理格式一样是使用:GL_COMPRESSED_RGB8_ETC2,虽然 unity 引擎不会申请 sRGB 格式,硬件也不会有 sRGB To Linear 处理,但是贴图图像的颜色值是否经过 sRGB 编码的决定于DCC软件产出的贴图是否sRGB颜色空间
  • linear 空间上
    • 勾上,那么unity 引擎会申请 sRGB 纹理格式,硬件 在 sample 后,做 sRGB to Linear (硬件将此纹理提亮;纹理格式为:GL_COMPRESSED_SRGB8_ETC2 (压缩格式取决于 unity 引擎中对此纹理的格式设置,这一小段的内容,我们整理出了下面的表格,并以 ETC2 格式纹理为例))
    • 不勾,就是说,硬件把你的纹理直接当做 linear 数据,硬件不会 在 sample 后,不做 sRGB to Linear (不压暗亮度) ,直接给到 shader 着色计算,纹理格式为:GL_COMPRESSED_RGB8_ETC2

简单总结为:

  • linear space 下
    • 纹理的 sRGB 勾上,说明此说明是 sRGB颜色空间的贴图,此贴图为的是看着更舒服的线性颜色,实际是提亮过的,请 unity 帮我在显示这个贴图的时候 处理压暗 pow(color, 2.2),并且申请sRGB贴图格式,进行 sRGB 编码提亮,在为的是shader调用sample时,硬件返回颜色前经过 压暗 pow(color, 2.2) 处理的,再返回线性值,用于后续shading的正确性;因此我们看到 inspector 中,一般勾上 sRGB 的话,inspector 中显示贴图会就变暗 (sRGB编码的硬件自动处理过程)
    • 纹理的 sRGB 没勾,说明此贴图没有经过 sRGB 编码过, shader调用sample后不会有任何处理就返回

所以你现在应该知道 unity sRGB 的作用了

因此设置unity颜色空间的话,其实除了会影响纹理的 sRGB 的格式外,还会影响 FrameBuffer(下面简称 FB)的格式 (简单总结区别就是: Linear Color Space 下,够了 sRGB 的贴图格式就是 sRGB 的,FBO 的 color attachment 的 纹理格式也是 sRGB 的)

下面以 OpenGL Graphics API 在 RenderDoc 中抓帧分析的情况,使用一张表格来表示

Tabel Edit Platform Color Space sRGB Checked HDR TextureDefaultFormat FrameBufferFormat Comments
jave.lin OpenGL Gamma False False GL_COMPRESSED_RGB8_ETC2 GL_RGBA8 初始化…
jave.lin OpenGL Gamma True False GL_COMPRESSED_RGB8_ETC2 GL_RGBA8 在 Gamma 空间下,将 sRGB=True,发现 Texture, FB 的格式都没有变化
jave.lin OpenGL Gamma True True GL_COMPRESSED_RGB8_ETC2 GL_R11F_G11F_B10F 将 HDR=True,发现 FB 格式 RGBA8 变成了 R11FB11FG10F,A通道精度完全丢失,都放到RGB通道来来做High Range
jave.lin OpenGL Linear True True GL_COMPRESSED_SRGB8_ETC2 GL_SRGB8_ALPHA8 Color Space调整为Linear后 Texture 和 FB 的格式都有变化
jave.lin OpenGL Linear False True GL_COMPRESSED_RGB8_ETC2 GL_SRGB8_ALPHA8 sRGB=False,Texture格式变为:GL_COMPRESSED_RGB8_ETC2
jave.lin OpenGL Linear False False GL_COMPRESSED_RGB8_ETC2 GL_R11F_G11F_B10F
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值