優化實現Mobile Diffuse動態直接光照shader

項目中美術使用了Unity自帶的Mobile/Diffuse這個shader製做了一部分場景素材,這個shader會依賴場景中的動態實時光源,比較耗費。工具

因而本身手動重寫一份,簡化shader的消耗,但同時保持美術已經制做場景的效果。spa

Shader "Mobile/Diffuse"
{
    Properties
    {
        _MainTex ("Base (RGB)", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 150

        CGPROGRAM
        #pragma surface surf Lambert noforwardadd nolightmap noshadow novertexlights nodynlightmap nodirlightmap 

        sampler2D _MainTex;

        struct Input
        {
            float2 uv_MainTex;
        };

        void surf (Input IN, inout SurfaceOutput o) {
            fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }

    Fallback "Mobile/VertexLit"
}

我在原始shader上添加了一些編譯選項用來關閉一些特性,但編譯出來的shader仍是有不少非必要的運算。code

手動實現一份noforwardadd的(只有一個pass)版本:orm

Shader "James/Scene/Mesh Lighting"
{
    Properties
    {
        _MainTex ("Base (RGB)", 2D) = "white" {}
    }
    
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry" }
        LOD 200
        
        Pass
        {
            Tags { "LightMode"="ForwardBase" }
            // Lighting Off
            
            CGPROGRAM
            #pragma fragmentoption ARB_precision_hint_fastest
            
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase
            #pragma multi_compile_fog
            
            #include "UnityCG.cginc"

            float4 _LightColor0;

            uniform sampler2D _MainTex;
            uniform half4 _MainTex_ST;

            struct vertexIN_base
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
            };
            
            struct v2f_base
            {
                float4 pos : SV_POSITION;
                fixed3 vertexLight : COLOR;
                half2  uv : TEXCOORD0;
                float3 normal : TEXCOORD1;
                float3 lightDir : TEXCOORD2;
                UNITY_FOG_COORDS(3)
            };

            v2f_base vert(vertexIN_base v)
            {
                v2f_base o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.normal = v.normal;
                o.lightDir = ObjSpaceLightDir(v.vertex);

                half3 worldNormal = UnityObjectToWorldNormal(v.normal);
                float3 shlight = ShadeSH9(float4(worldNormal, 1.0));
                o.vertexLight = shlight;
                #ifdef VERTEXLIGHT_ON
                    o.vertexLight += Shade4PointLights (
                        unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
                        unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
                        unity_4LightAtten0, worldPos, worldNormal
                        );
                #endif

                UNITY_TRANSFER_FOG(o,o.pos);
                return o; 
            }
            
            fixed4 frag(v2f_base i) : COLOR
            {
                i.lightDir = normalize(i.lightDir);
                i.normal = normalize(i.normal);

                float diffuse = max(0, dot(i.normal, i.lightDir));

                fixed4 mainColor = tex2D(_MainTex, i.uv);
                fixed4 clr = mainColor * _LightColor0 * diffuse;
                clr.rgb += mainColor.rgb * i.vertexLight;

                UNITY_APPLY_FOG(i.fogCoord,clr);

                return clr;
            }
            ENDCG
        }
    }
    FallBack Off
}

上述shader和Mobile Diffuse效果基本一致(場景中光照並不複雜),而且默認的效果也是不帶forward add的。htm

但這個shader仍是依賴了場景中的實施光源數據。對象

因而乎,進一步,將場景中的實時光源所有移除,並將光源的顏色和方向信息直接寫在shader的屬性中,獲得了下面的去光源版本:blog

Shader "James/Scene/Mesh Diffuse"
{
    Properties
    {
        _MainTex ("Base (RGB)", 2D) = "white" {}

        _MainLightColor("主光顏色", Color) = (1,1,1,1)
        _MainLightDir("主光方向", Vector) = (1,1,0,0)

        _SecondLightColor("輔光顏色", Color) = (1,1,1,1)
        _SecondLightDir("輔光方向", Vector) = (1,1,0,0)
        _SecondLightBrightness ("輔光強度", Range(0, 10)) = 0.9
    }
    
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry" }
        LOD 200
        
        Pass
        {
            Tags { "LightMode"="ForwardBase" }
            Lighting Off
            
            CGPROGRAM
            #pragma fragmentoption ARB_precision_hint_fastest
            
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog
            
            #include "UnityCG.cginc"

            float4 _MainLightColor;
            float4 _MainLightDir;
            float4 _SecondLightColor;
            float4 _SecondLightDir;
            float _SecondLightBrightness;

            uniform sampler2D _MainTex;
            uniform half4 _MainTex_ST;

            struct vertexIN_base
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
            };
            
            struct v2f_base
            {
                float4 pos : SV_POSITION;
                half2  uv : TEXCOORD0;
                float3 normal : TEXCOORD1;
                float3 lightDir : TEXCOORD2;
                float3 lightDir2 : TEXCOORD3;
                UNITY_FOG_COORDS(4)
            };

            v2f_base vert(vertexIN_base v)
            {
                v2f_base o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.normal = v.normal;

                o.lightDir = mul(unity_WorldToObject, _MainLightDir).xyz;
                o.lightDir2 = mul(unity_WorldToObject, _SecondLightDir).xyz;

                UNITY_TRANSFER_FOG(o,o.pos);
                return o; 
            }
            
            fixed4 frag(v2f_base i) : COLOR
            {
                i.lightDir = normalize(i.lightDir);
                i.lightDir2 = normalize(i.lightDir2);
                i.normal = normalize(i.normal);

                float diffuse = max(0, dot(i.normal, i.lightDir));
                float diffuse2 = max(0, dot(i.normal, i.lightDir2));

                fixed4 mainColor = tex2D(_MainTex, i.uv);
                fixed4 clr = mainColor * _MainLightColor * diffuse;
                clr += _SecondLightBrightness * (mainColor * _SecondLightColor * diffuse2);

                UNITY_APPLY_FOG(i.fogCoord,clr);

                return clr;
            }
            ENDCG
        }
    }
    FallBack Off
}

  這個版本支持兩個光源信息,而後按照一樣的Lambert光照方式來計算光照信息,其中輔光給了一個強度的調節因子。ip

  注:這裏沒有徹底按照默認的計算方式來計算主光之外的光照信息,是由於half3 ShadeSH9 (half4 normal)因此來的unity_SHAr unity_SHAg unity_SHAb的計算方式不明確。ci

    // normal should be normalized, w=1.0
    half3 SHEvalLinearL0L1 (half4 normal)
    {
        half3 x;

        // Linear (L1) + constant (L0) polynomial terms
        x.r = dot(unity_SHAr,normal);
        x.g = dot(unity_SHAg,normal);
        x.b = dot(unity_SHAb,normal);

        return x;
    }

    // normal should be normalized, w=1.0
    half3 SHEvalLinearL2 (half4 normal)
    {
        half3 x1, x2;
        // 4 of the quadratic (L2) polynomials
        half4 vB = normal.xyzz * normal.yzzx;
        x1.r = dot(unity_SHBr,vB);
        x1.g = dot(unity_SHBg,vB);
        x1.b = dot(unity_SHBb,vB);

        // Final (5th) quadratic (L2) polynomial
        half vC = normal.x*normal.x - normal.y*normal.y;
        x2 = unity_SHC.rgb * vC;

        return x1 + x2;
    }

    // normal should be normalized, w=1.0
    // output in active color space
    half3 ShadeSH9 (half4 normal)
    {
        // Linear + constant polynomial terms
        half3 res = SHEvalLinearL0L1 (normal);

        // Quadratic polynomials
        res += SHEvalLinearL2 (normal);

    #   ifdef UNITY_COLORSPACE_GAMMA
            res = LinearToGammaSpace (res);
    #   endif

        return res;
    }

  Unity是怎麼計算出unity_SHAr unity_SHAg  unity_SHAb這幾個變量的,又知道的小夥伴能夠告知一下哈。 it

  下面是基於以上三個shader的實時渲染效果,其中"James/Scene/Mesh Diffuse"不依賴場景光源。

  

  讓美術直接在材質球上面手動輸入light dir,其實很是的不直觀,因而寫了一個工具,直接把場景中的lighting對應的shader中的方向值給打印出來:

    private void WriteLightingDir()
    {
        Object obj = Selection.activeGameObject;
        if (obj is GameObject == false) return;
        Debug.Log(-(obj as GameObject).transform.forward);
    }

  選中場景中的Lighting對象,而後執行上面的編輯期代碼便可打印出方向值。

   添加環境光:

  若是隻有場景光照,模型會有一些暗角,所以還須要加上自定義環境光的光照亮度。

  首先經過代碼設置全局的環境光,方便shader訪問:

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

[ExecuteInEditMode]
public class GlobalShaderSetting : MonoBehaviour
{
    public Color GlobalAmbientColor = new Color(0.5f, 0.5f, 0.5f, 1f);
    public float GlobalAmbientBrightness = 1;
    public Vector4 GlobalWindDirection = new Vector4(0.5f, 0.5f, 0.5f, 1f);

    [ContextMenu("Set")]
    private void Awake()
    {
        Shader.SetGlobalColor("_GlobalAmbientColor", GlobalAmbientColor);
        Shader.SetGlobalFloat("_GlobalAmbientBrightness", GlobalAmbientBrightness);
        Shader.SetGlobalVector("_GlobalWindDirection", GlobalWindDirection);
    }
}

  而後添加一個cginc的通用文件:

#ifndef JAMES_LIGHTING_H
#define JAMES_LIGHTING_H

float4 _GlobalAmbientColor;
float _GlobalAmbientBrightness;
float4 _GlobalWindDirection;

#define AMBIENT_COLOR _GlobalAmbientColor * _GlobalAmbientBrightness

#endif

  在須要添加環境光的shader中添加環境光的影響便可:

clr += mainTex * AMBIENT_COLOR;
相關文章
相關標籤/搜索