Unity & PS Linear Workflow - Unity 和 PS 的线性工作流实践 - 简单配置示例(后续补上渲染差异图)


目的

因为 新的 Unity 项目人物走写实PBR风格
所以铁定基于 Linear Workflow 比基于 Gamma Workflow 的渲染效果更好
但是 Linear Workflow 下对 美术工作流不太友好,下面就实验并总结一些方案的优缺点
供大家选取

先看看不同 Color Space 下的 PBR 选择差异有多大

在 Unity Linear Color Space 渲染质量接近 SP (Substance Painter) 等选择 DCC 软件的 PBR 渲染效果

[此处放一些截图,先占位,后续补上]

可以看到,在 Unity Gamma Space 真的差别很大

至于为何有差别,可以参考我之前的一篇:Gamma Correction/Gamma校正/灰度校正/亮度校正(已更正) - 部分 DCC 中的线性工作流配置


问题

在 Linear Workflow 下3D渲染效果是好了好多

但是 UI特效 美术同学生产资源仍然是基于 Gamma Workflow(blog是在:2022/09/02写的,安装好的 Photoshop 默认都是在 Gamma Workflow 的颜色空间配置的,以后的 Photoshop 版本可能会变,等 sRGB 过时了,可能会变)

所以 基于 Gamma Workflow 生产出来的资源,放在 Unity Linear Workflow 下使用,肯定会有显示问题的


解决方案1


PS 颜色空间配置

我让 UI 特学给了一张 gamma 空间下制作的特图
在这里插入图片描述

然后我将这张 Gamma 空间的图放到 调整好的颜色设置好之后的 PS 下,效果如下

(没有了 Gamma Correct,即:pow(inputVal, 2.2),感觉过亮,并且灰蒙蒙的,相当于Non-sRGB 下就是这样的效果)

在这里插入图片描述


Unity纹理设置

我将上面的 gamma.png 图,导出为:linear.png,然后将纹理的 sRGB 的勾去掉(即:sRGB=false)

bg.png 也是 sRGB = false
在这里插入图片描述

然后放两个图叠在一起,显示如下,和 Photoshop 中一模一样了

在这里插入图片描述


GIF效果对比

请添加图片描述


优缺点

  • 有点:效果还原度 100%
  • 缺点:PS色板色相不均衡、灰度丢失,而且如果你用PS的颜色吸管吸取 PS 颜色,你会发现不对,特别是吸取 PS 程序窗口意外的其他程序窗口内容的颜色值

下面细说缺点:

但是这种方式,UI 和 特效美术在使用 PS 生产过程中有些不方便的地方:

  • 如果是项目初期,直接使用这种工作流的方式还好
  • 如果是项目中途,那么需要将以往的所有色相,饱和度,都统统需要修改,这会让美术同学不可接受的
  • 而且,部分灰度,在 PS 的色板中也会丢失进度(即:部分灰度无法选择而使用)

在这里插入图片描述

在这里插入图片描述


解决方案2


PS gamma Correct 保持 2.2 & 灰度混合系数 1.0 勾上

在这里插入图片描述


Unity 然后纹理 sRGB = false 并使用自己的 CustomShader来做 Gamma Correct

在这里插入图片描述


CustomShader - UI/Default_Ext 源码

// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
// jave.lin : 2022/09/03 基于 unity 2020.3.37f1 的 builtshader 的 UI/Default Shader 修改而来

Shader "UI/Default_Ext"
{
    
    
    Properties
    {
    
    
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {
    
    }
        _Color ("Tint", Color) = (1,1,1,1)

        _StencilComp ("Stencil Comparison", Float) = 8
        _Stencil ("Stencil ID", Float) = 0
        _StencilOp ("Stencil Operation", Float) = 0
        _StencilWriteMask ("Stencil Write Mask", Float) = 255
        _StencilReadMask ("Stencil Read Mask", Float) = 255

        _ColorMask ("Color Mask", Float) = 15

        [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
        // jave.lin : 自己添加一个是否需要使用 gamma correct 的开关
        [Toggle(GAMMA_CORRECT_ON)] _GammaCorrectOn ("GammaCorrectOn", Float) = 0
    }

    SubShader
    {
    
    
        Tags
        {
    
    
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Stencil
        {
    
    
            Ref [_Stencil]
            Comp [_StencilComp]
            Pass [_StencilOp]
            ReadMask [_StencilReadMask]
            WriteMask [_StencilWriteMask]
        }

        Cull Off
        Lighting Off
        ZWrite Off
        ZTest [unity_GUIZTestMode]
        Blend One OneMinusSrcAlpha
        ColorMask [_ColorMask]

        Pass
        {
    
    
            Name "Default"
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 2.0

            #include "UnityCG.cginc"
            #include "UnityUI.cginc"

            #pragma multi_compile_local _ UNITY_UI_CLIP_RECT
            #pragma multi_compile_local _ UNITY_UI_ALPHACLIP
            // jave.lin : 使用一个全局变体 keyword,便于外部脚本 Shader.EnabledKeyword() 的方式来全局控制
            #pragma multi_compile _ GAMMA_CORRECT_ON

            struct appdata_t
            {
    
    
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f
            {
    
    
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                float2 texcoord  : TEXCOORD0;
                float4 worldPosition : TEXCOORD1;
                float4  mask : TEXCOORD2;
                UNITY_VERTEX_OUTPUT_STEREO
            };

            sampler2D _MainTex;
            fixed4 _Color;
            fixed4 _TextureSampleAdd;
            float4 _ClipRect;
            float4 _MainTex_ST;
            float _UIMaskSoftnessX;
            float _UIMaskSoftnessY;

            v2f vert(appdata_t v)
            {
    
    
                v2f OUT;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                float4 vPosition = UnityObjectToClipPos(v.vertex);
                OUT.worldPosition = v.vertex;
                OUT.vertex = vPosition;

                float2 pixelSize = vPosition.w;
                pixelSize /= float2(1, 1) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy));

                float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
                float2 maskUV = (v.vertex.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy);
                OUT.texcoord = TRANSFORM_TEX(v.texcoord.xy, _MainTex);
                OUT.mask = float4(v.vertex.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_UIMaskSoftnessX, _UIMaskSoftnessY) + abs(pixelSize.xy)));

                OUT.color = v.color * _Color;
                return OUT;
            }

            fixed4 frag(v2f IN) : SV_Target
            {
    
    
                half4 color = IN.color * (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd);

                // jave.lin : 我们自己来添加 gamma correct
                #ifdef GAMMA_CORRECT_ON
                // jave.lin : 试过好几种数值,就这个是最接近的了

                // jave.lin : 写法1
                // // jave.lin : step1 : rgba 整体 pow(val, 2.2) 压暗
                // color.rgba = pow(color.rgba, 2.2);
                // // jave.lin : step2 : 单独对 a 通道 pow(val, 0.5) 曲线提升一些 alpha 值
                // color.a = pow(color.a, 1.0/2.0);

                // jave.lin : 写法2,在写法1基础上简化
                // color.rgb = pow(color.rgb, 2.2);
                // color.a = pow(color.a, 2.2 * 0.5);

                // jave.lin : 写法3,在写法2基础上,优化:充分利用 SIMD 4分量并行指令
                color.rgba = pow(color.rgba, half4(2.2, 2.2, 2.2, 2.2 * 0.5));

                #endif

                #ifdef UNITY_UI_CLIP_RECT
                half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw);
                color.a *= m.x * m.y;
                #endif

                #ifdef UNITY_UI_ALPHACLIP
                clip (color.a - 0.001);
                #endif
                
                color.rgb *= color.a;

                return color;
            }
        ENDCG
        }
    }
}


GIF效果对比

除了字体 alphablend 的稍微有些不同(这个到时再做实验)
请添加图片描述


优缺点

  • 优点:这种方式,UI、特效美术 几乎是不用修改的 工作流的,只要将:alpha blend 1.0 勾上,而且对 PS 颜色吸管的功能可以保持原有功能效果,所以对美术工作流友好最大
  • 缺点:每个 UI 或是 特效的 Shader 都要添加 gamma correct(一个 pow运算),如果 overdraw 很多时会放大这个点的性能消耗,但是对于现代显卡来说,应该可以忽略不计

总结

显而易见

想在 linear workflow 中,既要 有更好的 PBR 3D 光照效果,又要 有兼容就版本的 gamma workflow 的 UI 和 特效效果,那么最好使用:方案2,的方式,前提是:那么一丢丢的性能损耗是可接受的

如果 你们项目美术可以接受 方案1 的灰度,和色相范围丢失 的情况下,可以选用 方案1,shader 也不用处理 gamma correct


Project

backup project(备份用)
Testing_UI_Default_CustomShaderGammaCorrect_in_Linear_workflow


References

猜你喜欢

转载自blog.csdn.net/linjf520/article/details/126672005
今日推荐