大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE稳定放心使用
百人计划学习视频连接:【技术美术百人计划】图形 2.6 伽马校正
颜色空间
具体内容在之前的文章有讲:色彩空间介绍
- sRGB:微软在1996年发布的通用颜色标准
- DCI-P3:数字电影播放的颜色标准
- Rec-709、PAL:电视行业的颜色标准
图中可以看出sRGB和Rec-709表示的颜色范围很接近,三原色的位置也是相同的
那他们的区别在哪里呢?答案是 他们的传递函数的不同
什么是传递函数
当我们已经知道了颜色空间下三原色的值,但是我们需要将它显示到电子设备上,那就需要把它转换成一个视频信号。
这里就需要用到一个转换函数,也叫传递函数
一个传递函数包含2个部分:
- OEFT 把光转换为电子信号(例如:拍摄视频的时候,将场景中的光保存成信号)
- EOFT 把电子信号转换成光信号(例如:播放视频的时候,将视频中的信号转换成光强度)
而传递函数就是Gamma校正所使用的一个函数
Gamma校正
什么是Gamma校正?
就是指对线性三色值和非线性视频信号之间进行编码和解码的操作
也可以简单的定义为:
那为什么不直接用线性方式存储颜色信号呢?
首先我们知道了图像的来龙去脉:自然界捕获的图像 -> 图像存储和处理->显示器输出
而两头的颜色数量都是非常丰富的,但是中间的存储和处理出于存储容量和渲染时带宽的限制,24位色图片每个通道只有28个色阶,总共只能显示224种颜色。
如过是32位通道的图片,确实可以直接存储物理光强,因为存储空间足够;但是目前主流使用还是8位通道的图片
因此,我们得出结论
- 主要是为了优化存储空间和带宽,传递函数能更好的帮我们利用编码空间
- 人眼对暗部的变化更加敏感,为了充分的利用带宽,那么就需要使用更多位置存储暗部值。也就是说暗部使用更高精度保存,而亮部使用更低精度保存
韦伯定律
正常人会觉得是上面更均匀,但是实际下面才是均匀变化的。
将自然界线性增长的亮度和心理上感受到的亮度进行一个映射得到下面的曲线,就是Gamma编码的曲线
这种情况也符合一个定律,就是韦伯定律
小结
- 人眼对暗部的变化比亮部更加敏感
- 我们目前所使用的RGBA32,每个颜色通道只有8位用于记录信息,为了合理使用带宽和存储空间,需要进行非线性转换
- 目前我们所普遍使用的sRGB颜色空间标准,他的传递函数gamma值为2.2
CRT
早期的人们是如何修正人眼的视觉感受和物理亮度的差异
CRT与转换函数
中灰值
中灰值是什么?
中灰值并非是一个固定的具体数值,而是取决于视觉感受
线性工作流
为了保证我们在着色器中拿到的颜色值是线性空间下的颜色值
如果不在线性空间下进行渲染工作,会产生什么问题
Unity中的颜色空间
- 选择Gamma Space时,Unity不会做任何处理
- 当选择Linear Space时,引擎的渲染流程在线性空间计算,理想情况下项目使用线性空间的贴图颜色,不需要勾选sRGB,如果勾选了sRGB的贴图,会通过硬件特性采样时进行线性转换。
Unity目前主要通过以下两个硬件特性来支持
资源导出问题
当Substance的贴图导出时,线性的颜色值经过伽马变换,颜色被提亮了,所以需要在Unity中勾选sRGB选项,让它在采样时能还原回线性值。
如果使用线性空间,一般来说Photoshop可以什么都不改,导出的贴图只要勾上sRGB就可以了。如果调整PhotoShop的伽玛值为1,导出的贴图在Unity中也不需要勾选sRGB了。
关于Color profile
半透明效果不一致
Unity中的混合是线性混合,Photoshop的图层和图层之间做混合的时候,每个上层图层都经过了伽马变换,然后才做了混合。在设置中更改,选择“用灰度系数混合RGB颜色”,参数设置为1,这样图层才是直接混合的结果。
作业
手动尝试几种伽马校正的方法
1. 直接修改Unity3d的设置为Linear空间进行gamma校正
写了一个简单的带纹理的blin-phong模型
fixed4 frag (v2f i) : SV_Target
{
fixed3 MainTex = tex2D(_MainTex, i.uv) * _DiffuseColor.rgb;
float3 normalDir = normalize(i.worldNormal);
float3 lightDir = normalize(UnityWorldSpaceLightDir(_WorldSpaceLightPos0.xyz)) ;
float3 Diffuse = _LightColor0.rgb * MainTex.rgb * saturate(dot(lightDir, normalDir));
float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
float3 halfDir = normalize(lightDir + viewDir);
float3 Specular = _LightColor0.rgb * pow(saturate(dot(halfDir, normalDir)), _Gloss);
float3 Ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * MainTex.rgb;
float4 col = fixed4(Diffuse + Specular, 1);
return col;
}
Gamma空间:
Linear空间:
2. 在shader中手动进行gamma校正
fixed4 frag (v2f i) : SV_Target
{
fixed3 MainTex = tex2D(_MainTex, i.uv) * _DiffuseColor.rgb;、
//先将Gamma空间的颜色值进行一次转换,转换到Linear空间。
MainTex.rgb = GammaToLinearSpace(MainTex.rgb);
float3 normalDir = normalize(i.worldNormal);
float3 lightDir = normalize(UnityWorldSpaceLightDir(_WorldSpaceLightPos0.xyz)) ;
float3 Diffuse = _LightColor0.rgb * MainTex.rgb * saturate(dot(lightDir, normalDir));
float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
float3 halfDir = normalize(lightDir + viewDir);
float3 Specular = _LightColor0.rgb * pow(saturate(dot(halfDir, normalDir)), _Gloss);
float3 Ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * MainTex.rgb;
float4 col = fixed4(Diffuse + Specular, 1);
//输出的时候,再进行一次逆转换,从Linear空间转换回Gamma空间。
col.rgb = LinearToGammaSpace(col.rgb * unity_ColorSpaceDouble);
return col;
}
注:最后转换回Gamma Space的时候,我在Color上乘了一个Unity_ColorSpaceDouble。Unity_ColorSpaceDouble是一个Unity提供的与色彩空间相关的值,这个值在Gamma颜色空间时为2,在Linear Color Space时为4.594(2的2.2次方)。
对于这个值可以这样来理解。一般在Gamma颜色空间中将两个Color值相乘后,为了避免颜色变得很暗,会在后面乘以2。
也就是说,为了避免颜色变暗,应该扩大两倍,但是这个值在不同空间下不一样,所以要使用Unity_ColorSpaceDouble。
原效果:
加上gamma校正后的效果
参考学习:
《Unity+Shader入门精要》——冯乐乐著
https://blog.csdn.net/candycat1992/article/details/46228771
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/189830.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...