Unity 颜色空间(Color Space)转换

很多小伙伴会遇到下面的问题,就是把UI切好的图片放到Unity中,会发现有些颜色的差异,尤其是透明度混合方面会有很大的变化,这些都是由于Unity中颜色空间的设置问题,先给大家看一下Unity中的效果。

        Unity 颜色空间(Color Space)转换_第1张图片

可以看到,在Gamma空间下,我是用PS输出的70%透明度的纯黑色的图片,和使用windows自带应用打开的图片没有任何区别,我们打开图片的设置,有一个sRGB选项(下文会详解sRGB选项),不管开启还是关闭这个选项,图片都不会有变。由于PS是使用Gamma空间进行图片制作和输出的,所以我们使用Gamma空间进行设置,可以得到一比一的效果。

Unity 颜色空间(Color Space)转换_第2张图片

        那既然我们显示都是正确的,我们为什么不直接使用Gamma空间,而是要把颜色空间设置为Linear呢,下面是Unity的官方解释。

线性或伽马工作流程 - Unity 手册

        其中有这样一句话

         所以我们在烘焙3d场景,或布置灯光时更倾向于选择Linear颜色空间得到最精确的结果,但是如果我们把颜色空间切换为Linear,就会发现UI有了一些不一样的变化,会发现颜色变浅了,效果如下

        Unity 颜色空间(Color Space)转换_第3张图片

         下面介绍一下ColorSpace中的Linear和Gamma两种工作流,这里引用一下知乎博主PZZZB的一张图片和Unity官方的一个解释

颜色空间 - Unity 手册

        

Unity 颜色空间(Color Space)转换_第4张图片

        对于我们PS输出的图片,如果我们不做特殊的设置,那么我们的图片就是在Gamma空间下进行输出和制作的,下面有一个很关键的知识点,就是两个颜色空间的透明度混合公式

Linear Color Space

ret = (srcColor^2.2 * srcAlpha + dstColor^2.2 * (1 - srcAlpha) ) ^(1/2.2)

Gamma Color Space

ret = srcColor * srcAlpha + dstColor * (1 - srcAlpha)

        对于美术人员来说,他们在PS中通过透明度混合得到的最终效果,是使用Gamma空间下的计算公式进行计算得到的结果,而Unity中的Linear Color Space下却是使用第一个公式进行处理的,我们要做的就是要是用Linear下的公式,通过一些变化,让他得到与Gamma下相同的结果。

        下面要介绍一个知识,就是图片属性上的sRGB,对于点了sRGB属性的图片,Unity会默认对其做一次变暗的操作,也就是2.2次幂,也就是流程图上写的Remove Gamma Correction,通过这个操作,会得到Gamma1.0空间下的颜色,然后将得到的颜色放入Shader中处理(Shader默认使用的是Linear Color Space的混合公式)

        经过对上图两个公式分析可得,如果我们把UI的图片的sRGB取消,那么就可以让Remove Gamma Correction过程忽略掉,也就是把Gamma0.45下的颜色直接带入的透明度公式,也就变成了下图公式

ret = (srcColor^0.45^2.2 * srcAlpha + dstColor^0.45^2.2 * (1 - srcAlpha) ) ^(1/2.2)
    =  (srcColor * srcAlpha + dstColor * (1 - srcAlpha) ) ^(1/2.2)

        

        通过比较可知,这个公式和Gamma空间下的透明度混合公式,只差一个2.2次方,所以我们只需要增加一个摄像机的后处理,将UI摄像机的结果进行一个2.2次方即可达到UI最终的效果。(PS:对于项目中的UI的Canvas我们会使用Scene Camera的模式,这样就可以在对应摄像机上添加后处理,也可以增加各种不同的UI特效

Unity 颜色空间(Color Space)转换_第5张图片

       

        现在把我们的脚本添加到我们的摄像机上 Unity 颜色空间(Color Space)转换_第6张图片

       

        使用的两个脚本如下所示

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UICameraLinearCorrect : MonoBehaviour
{
    private Material mat;
    private Material RenderMat
    {
        get
        {
            if (mat == null)
            {
                mat = Resources.Load("Materials/UILinearCorrectMat");
            }
            return mat;
        }
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, destination, RenderMat);
    }
}
Shader "MyShader/UILinearCorrectShader"
{
    Properties
    {
        _MainTex("Main Tex", 2D) = "white"{}
    }
        SubShader
    {
        Tags { "RenderType" = "Opaque" }
        LOD 100
        //Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            Cull Off ZWrite Off ZTest Always
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            sampler2D _MainTex;

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
            };


            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                fixed4 color = tex2D(_MainTex, i.uv);
                color = pow(color, 2.2);
                return color;
            }
            ENDCG
        }
    }
}

        但是由于我们把整体颜色调暗了,所以场景中的物体的颜色也会变暗,也就像下图

 Unity 颜色空间(Color Space)转换_第7张图片

 Unity 颜色空间(Color Space)转换_第8张图片

       

        可以发现场景中的UI颜色确实还原了,但是红色方块的颜色变化了 ,所以为了让场景颜色还原,我们需要再添加一个摄像机,将这个摄像机只看场景,原有的摄像机只看UI,同时将场景摄像机的颜色进行一个0.45次幂,还原他原来的颜色,操作如下

         Unity 颜色空间(Color Space)转换_第9张图片

Unity 颜色空间(Color Space)转换_第10张图片       

        对于场景摄像机,我们只需要看除了UI以外的所有层就可以 ,效果如下,可以发现颜色都得到了正确的显示

        Unity 颜色空间(Color Space)转换_第11张图片

         

        Scene Camera使用的代码如下

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SceneCameraLinearCorrect : MonoBehaviour
{
    private Material mat;
    private Material RenderMat
    {
        get
        {
            if (mat == null)
            {
                mat = Resources.Load("Materials/SceneLinearCorrectMat");
            }
            return mat;
        }
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, destination, RenderMat);
    }
}
Shader "MyShader/SceneLinearCorrectShader"
{
    Properties
    {
        _MainTex("Main Tex", 2D) = "white"{}
    }
        SubShader
    {
        Tags { "RenderType" = "Opaque" }
        LOD 100
        //Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            // 制作后处理shader的时候要把这几个属性设置上
            Cull Off ZWrite Off ZTest Always
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            sampler2D _MainTex;

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
            };


            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                fixed4 color = tex2D(_MainTex, i.uv);
                color = pow(color, 1.0 / 2.2);
                return color;
            }
            ENDCG
        }
    }
}

(PS:如果UI打了图集,图集的sRGB也要关闭掉) 

你可能感兴趣的:(杂项,unity)