文章目录
- 纠正
- 也可以简单参考 SIGGRAPH 2010 的 PBS 的 Gamma-Correct
- Unity 中的 gamma、linear 工作流
- Unity BRP 管线中的 shaderlab 提供的 gamma2linear, linear2gamma
- Unity中线性空间配置
- DCC 中的 gamma 值是怎么计算的?
- Photoshop 的线性工作流
- AE 中的 Linear 工作流
- C4D 中的 Octane 渲染器 的 Linear 工作流
- 3Ds Max VRay 中的线性工作流
- 线性空间工作流程图
- Docs
- URP下的 UI alpha blend 的 gamma to linear
- BRP下的 UI alpha blend 的 gamma to linear
- 总结 unity 中 linear 下 tex的后处理以及srgb编码 和 shader sample srgb to linear 和 fbo output linear to srgb 的处理细节
- 总结 sRGB 空间,sRGB shader中采样, gamma correct, degamma,CRT,LCD,早期需要 gamma correct 等,相对理解
- Project
- References
纠正
之前自己写的乱七八糟,都是似懂非懂
直到有一天,要给项目中 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 |

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

3280

被折叠的 条评论
为什么被折叠?



