Пишем шейдеры кодом в Unity LWRP

Введение


Здравствуй, Хабр. Сегодня хочется рассказать немного о том, как можно быстро и безболезненно (почти) начать писать классические текстовые шейдеры в Unity с использованием Lightweight Rendering Pipeline (LWRP) — одним из примеров конвейера Scriptable Rendering Pipeline (SRP).

А как же Shader Graph?


Shader Graph — это удобное и быстрое средство прототипирования или написания простых эффектов. Однако, порою, требуется написать нечто сложное и комплексное и вот тогда — количество нод, кастомных функций, суб-графов неимоверно увеличивается, отчего даже самый матёрый программист графики начинает путаться во всём этом бардаке. Все мы понимаем, что автоматически генерируемый код априори не может быть лучше написанного вручную — за примерами ходить далеко не нужно, ибо любая ошибка в планировке нод может привести к тому, что уже известный результат вычислений в вершинном шейдере будет посчитан повторно во фрагментом. Бывают и люди, которым просто удобнее работать с кодом, а не с нодами. Причины могут быть разными, но суть одна — долой ноды, да здравствует код!

2020-01-19-17-07-35

Проблематика


Итак, в чём же проблема сесть и написать обычный текстовый шейдер под LWRP? А проблема в том, что всеми любимые Standard Surface Shader-ы не поддерживаются в LWRP.
При попытке его использования мы получаем следующие:

2020-01-19-17-16-16

Код шейдера
Shader "Custom/NewSurfaceShader"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        sampler2D _MainTex;

        struct Input
        {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}


Тогда на ум приходит попробовать написать обычный анлит шейдер с вертексной и фрагментной частью. И к нашему счастью, всё работает:

2020-01-19-17-22-46

Код шейдера
Shader "Unlit/NewUnlitShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

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

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}


Однако как тут можно не грустить — мы словно остались голыми на обочине без света, теней, лайтмап и любимого PBR, без которого и жизнь не мила.
Конечно, можно написать всё руками:
Да будет свет!

2020-01-19-17-55-56

Код шейдера
Shader "TheProxor/Simple Lit"
{
    Properties
    {
        _MainTex("Texture", 2D) = "white" {}
    }

    SubShader
    {
        Tags { "RenderType" = "Opaque" }
        LOD 100

        Pass
        {
             CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc" 
            #include "UnityLightingCommon.cginc" 

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
                fixed4 diffuse : COLOR0;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                half3 worldNormal = UnityObjectToWorldNormal(v.normal);
                half nl = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
                o.diffuse = nl * _LightColor0;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
               // apply fog
               UNITY_APPLY_FOG(i.fogCoord, col);
               col *= i.diffuse;
               return col;
            }
        ENDCG
    }
    }
}


Вроде бы всё работает, но этого всего лишь дифузное освещение. Что же делать дальше? Можно и дальше возвращать всё руками, но это долго и муторно, да и PBR никак не получится вернуть и всех фишек LWRP мы лишаемся. Поэтому нам ничего не остаётся как ковырять LWRP, чтобы одним волшебным взмахом всё вернуть.

Решение


Итак, как известно, LWRP построен на базе Forward-рендера, а значит имеет свои LitForwardPass-ы, которые должны быть где-то описаны. Очевидно, что где-то есть CGInclude, в котором всё это описано. Вот за ним мы и отправляемся в весёлое приключение по адресу:
%localappdata%\Unity\cache\packages\packages.unity.com\com.unity.render-pipelines.lightweight@(версия LWRP)\

Собственно, придя по указаному адресу и зайдя в папку Shaders мы можем обнаружить один интересный шейдер, с названием Lit.shader. Собственно, можно сказать наши поиски окончены, вот он — заветный шейдер. Зайдя внутрь — мы обнаружим следующие содержание:

Lit.shader
Shader "Lightweight Render Pipeline/Lit"
{
    Properties
    {
        // Specular vs Metallic workflow
        [HideInInspector] _WorkflowMode("WorkflowMode", Float) = 1.0
        
        [MainColor] _BaseColor("Color", Color) = (0.5,0.5,0.5,1)
        [MainTexture] _BaseMap("Albedo", 2D) = "white" {}

        _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5

        _Smoothness("Smoothness", Range(0.0, 1.0)) = 0.5
        _GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0
        _SmoothnessTextureChannel("Smoothness texture channel", Float) = 0

        [Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
        _MetallicGlossMap("Metallic", 2D) = "white" {}

        _SpecColor("Specular", Color) = (0.2, 0.2, 0.2)
        _SpecGlossMap("Specular", 2D) = "white" {}

        [ToggleOff] _SpecularHighlights("Specular Highlights", Float) = 1.0
        [ToggleOff] _EnvironmentReflections("Environment Reflections", Float) = 1.0

        _BumpScale("Scale", Float) = 1.0
        _BumpMap("Normal Map", 2D) = "bump" {}

        _OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
        _OcclusionMap("Occlusion", 2D) = "white" {}

        _EmissionColor("Color", Color) = (0,0,0)
        _EmissionMap("Emission", 2D) = "white" {}

        // Blending state
        [HideInInspector] _Surface("__surface", Float) = 0.0
        [HideInInspector] _Blend("__blend", Float) = 0.0
        [HideInInspector] _AlphaClip("__clip", Float) = 0.0
        [HideInInspector] _SrcBlend("__src", Float) = 1.0
        [HideInInspector] _DstBlend("__dst", Float) = 0.0
        [HideInInspector] _ZWrite("__zw", Float) = 1.0
        [HideInInspector] _Cull("__cull", Float) = 2.0

        _ReceiveShadows("Receive Shadows", Float) = 1.0        
        // Editmode props
        [HideInInspector] _QueueOffset("Queue offset", Float) = 0.0
        
        // ObsoleteProperties
        [HideInInspector] _MainTex("BaseMap", 2D) = "white" {}
        [HideInInspector] _Color("Base Color", Color) = (0.5, 0.5, 0.5, 1)
        [HideInInspector] _GlossMapScale("Smoothness", Float) = 0.0
        [HideInInspector] _Glossiness("Smoothness", Float) = 0.0
        [HideInInspector] _GlossyReflections("EnvironmentReflections", Float) = 0.0
    }

    SubShader
    {
        // Lightweight Pipeline tag is required. If Lightweight render pipeline is not set in the graphics settings
        // this Subshader will fail. One can add a subshader below or fallback to Standard built-in to make this
        // material work with both Lightweight Render Pipeline and Builtin Unity Pipeline
        Tags{"RenderType" = "Opaque" "RenderPipeline" = "LightweightPipeline" "IgnoreProjector" = "True"}
        LOD 300

        // ------------------------------------------------------------------
        //  Forward pass. Shades all light in a single pass. GI + emission + Fog
        Pass
        {
            // Lightmode matches the ShaderPassName set in LightweightRenderPipeline.cs. SRPDefaultUnlit and passes with
            // no LightMode tag are also rendered by Lightweight Render Pipeline
            Name "ForwardLit"
            Tags{"LightMode" = "LightweightForward"}

            Blend[_SrcBlend][_DstBlend]
            ZWrite[_ZWrite]
            Cull[_Cull]

            HLSLPROGRAM
            // Required to compile gles 2.0 with standard SRP library
            // All shaders must be compiled with HLSLcc and currently only gles is not using HLSLcc by default
            #pragma prefer_hlslcc gles
            #pragma exclude_renderers d3d11_9x
            #pragma target 2.0

            // -------------------------------------
            // Material Keywords
            #pragma shader_feature _NORMALMAP
            #pragma shader_feature _ALPHATEST_ON
            #pragma shader_feature _ALPHAPREMULTIPLY_ON
            #pragma shader_feature _EMISSION
            #pragma shader_feature _METALLICSPECGLOSSMAP
            #pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
            #pragma shader_feature _OCCLUSIONMAP

            #pragma shader_feature _SPECULARHIGHLIGHTS_OFF
            #pragma shader_feature _ENVIRONMENTREFLECTIONS_OFF
            #pragma shader_feature _SPECULAR_SETUP
            #pragma shader_feature _RECEIVE_SHADOWS_OFF

            // -------------------------------------
            // Lightweight Pipeline keywords
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
            #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
            #pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
            #pragma multi_compile _ _SHADOWS_SOFT
            #pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE

            // -------------------------------------
            // Unity defined keywords
            #pragma multi_compile _ DIRLIGHTMAP_COMBINED
            #pragma multi_compile _ LIGHTMAP_ON
            #pragma multi_compile_fog

            //--------------------------------------
            // GPU Instancing
            #pragma multi_compile_instancing
            
            #pragma vertex LitPassVertex
            #pragma fragment LitPassFragment

            #include "LitInput.hlsl"
            #include "LitForwardPass.hlsl"
            ENDHLSL
        }

        Pass
        {
            Name "ShadowCaster"
            Tags{"LightMode" = "ShadowCaster"}

            ZWrite On
            ZTest LEqual
            Cull[_Cull]

            HLSLPROGRAM
            // Required to compile gles 2.0 with standard srp library
            #pragma prefer_hlslcc gles
            #pragma exclude_renderers d3d11_9x
            #pragma target 2.0

            // -------------------------------------
            // Material Keywords
            #pragma shader_feature _ALPHATEST_ON

            //--------------------------------------
            // GPU Instancing
            #pragma multi_compile_instancing
            #pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

            #pragma vertex ShadowPassVertex
            #pragma fragment ShadowPassFragment

            #include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.lightweight/Shaders/ShadowCasterPass.hlsl"
            ENDHLSL
        }

        Pass
        {
            Name "DepthOnly"
            Tags{"LightMode" = "DepthOnly"}

            ZWrite On
            ColorMask 0
            Cull[_Cull]

            HLSLPROGRAM
            // Required to compile gles 2.0 with standard srp library
            #pragma prefer_hlslcc gles
            #pragma exclude_renderers d3d11_9x
            #pragma target 2.0

            #pragma vertex DepthOnlyVertex
            #pragma fragment DepthOnlyFragment

            // -------------------------------------
            // Material Keywords
            #pragma shader_feature _ALPHATEST_ON
            #pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

            //--------------------------------------
            // GPU Instancing
            #pragma multi_compile_instancing

            #include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.lightweight/Shaders/DepthOnlyPass.hlsl"
            ENDHLSL
        }

        // This pass it not used during regular rendering, only for lightmap baking.
        Pass
        {
            Name "Meta"
            Tags{"LightMode" = "Meta"}

            Cull Off

            HLSLPROGRAM
            // Required to compile gles 2.0 with standard srp library
            #pragma prefer_hlslcc gles
            #pragma exclude_renderers d3d11_9x

            #pragma vertex LightweightVertexMeta
            #pragma fragment LightweightFragmentMeta

            #pragma shader_feature _SPECULAR_SETUP
            #pragma shader_feature _EMISSION
            #pragma shader_feature _METALLICSPECGLOSSMAP
            #pragma shader_feature _ALPHATEST_ON
            #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

            #pragma shader_feature _SPECGLOSSMAP

            #include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitMetaPass.hlsl"

            ENDHLSL
        }
        Pass
        {
            Name "Lightweight2D"
            Tags{ "LightMode" = "Lightweight2D" }

            Blend[_SrcBlend][_DstBlend]
            ZWrite[_ZWrite]
            Cull[_Cull]

            HLSLPROGRAM
            // Required to compile gles 2.0 with standard srp library
            #pragma prefer_hlslcc gles
            #pragma exclude_renderers d3d11_9x

            #pragma vertex vert
            #pragma fragment frag
            #pragma shader_feature _ALPHATEST_ON
            #pragma shader_feature _ALPHAPREMULTIPLY_ON

            #include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitInput.hlsl"
            #include "Packages/com.unity.render-pipelines.lightweight/Shaders/Utils/Lightweight2D.hlsl"
            ENDHLSL
        }


    }
    FallBack "Hidden/InternalErrorShader"
    CustomEditor "UnityEditor.Rendering.LWRP.ShaderGUI.LitShader"
}



Остаётся только развернуть его для удобства редактирования, избавившись от include-ов. Ну и немного модифицировать на свой лад.

Получаем нечто подобное:

2020-01-19-18-28-13

Финальный убер-шейдер

Shader "TheProxor/LWRP/Dissolve + Vertex Offset"
{
            Properties
            {
                // Specular vs Metallic workflow
                [HideInInspector] _WorkflowMode("WorkflowMode", Float) = 1.0

                [MainColor][HideInInspector] _BaseColor("Color", Color) = (0.5,0.5,0.5,1)
                [MainTexture][HideInInspector] _BaseMap("Albedo", 2D) = "white" {}

                [HideInInspector]_Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5

                [HideInInspector]_Smoothness("Smoothness", Range(0.0, 1.0)) = 0.5
                [HideInInspector]_GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0
                [HideInInspector]_SmoothnessTextureChannel("Smoothness texture channel", Float) = 0

                [Gamma][HideInInspector] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
                [HideInInspector]_MetallicGlossMap("Metallic", 2D) = "white" {}

                [HideInInspector]_SpecColor("Specular", Color) = (0.2, 0.2, 0.2)
                [HideInInspector]_SpecGlossMap("Specular", 2D) = "white" {}

                [HideInInspector][ToggleOff] _SpecularHighlights("Specular Highlights", Float) = 1.0
                [HideInInspector][ToggleOff] _EnvironmentReflections("Environment Reflections", Float) = 1.0

                [HideInInspector]_BumpScale("Scale", Float) = 1.0
                [HideInInspector]_BumpMap("Normal Map", 2D) = "bump" {}

                [HideInInspector]_OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
                [HideInInspector]_OcclusionMap("Occlusion", 2D) = "white" {}

                [HDR][HideInInspector]_EmissionColor("Color", Color) = (0,0,0)
                [HideInInspector]_EmissionMap("Emission", 2D) = "white" {}

                // Blending state
                [HideInInspector] _Surface("__surface", Float) = 0.0
                [HideInInspector] _Blend("__blend", Float) = 0.0
                [HideInInspector] _AlphaClip("__clip", Float) = 0.0
                [HideInInspector] _SrcBlend("__src", Float) = 1.0
                [HideInInspector] _DstBlend("__dst", Float) = 0.0
                [HideInInspector] _ZWrite("__zw", Float) = 1.0
                [HideInInspector] _Cull("__cull", Float) = 2.0

                [HideInInspector]_ReceiveShadows("Receive Shadows", Float) = 1.0
                // Editmode props
                [HideInInspector] _QueueOffset("Queue offset", Float) = 0.0

                // ObsoleteProperties
                [HideInInspector] _MainTex("BaseMap", 2D) = "white" {}
                [HideInInspector] _Color("Base Color", Color) = (0.5, 0.5, 0.5, 1)
                [HideInInspector] _GlossMapScale("Smoothness", Float) = 0.0
                [HideInInspector] _Glossiness("Smoothness", Float) = 0.0
                [HideInInspector] _GlossyReflections("EnvironmentReflections", Float) = 0.0

                

                //добавляем сюда свои пропертисы
                _DissolveMap("Dissolve Map", 2D) = "white" {}
                _DissolveFactor("Dissolve Factor", Range(0, 1)) = 0.0
                _DissolveWidth("Dissolve Width", Range(0, 1)) = 0.0
                [HDR]_DissolveColor("Color", Color) = (1,1,0)
            }

            SubShader
            {
                // Lightweight Pipeline tag is required. If Lightweight render pipeline is not set in the graphics settings
                // this Subshader will fail. One can add a subshader below or fallback to Standard built-in to make this
                // material work with both Lightweight Render Pipeline and Builtin Unity Pipeline
                Tags{"RenderType" = "Opaque" "RenderPipeline" = "LightweightPipeline" "IgnoreProjector" = "True"}
                LOD 300

                // ------------------------------------------------------------------
                //  Forward pass. Shades all light in a single pass. GI + emission + Fog
                Pass
                {
                // Lightmode matches the ShaderPassName set in LightweightRenderPipeline.cs. SRPDefaultUnlit and passes with
                // no LightMode tag are also rendered by Lightweight Render Pipeline
                Name "ForwardLit"
                Tags{"LightMode" = "LightweightForward"}

                Blend[_SrcBlend][_DstBlend]
                ZWrite[_ZWrite]
                Cull[_Cull]

                HLSLPROGRAM
                // Required to compile gles 2.0 with standard SRP library
                // All shaders must be compiled with HLSLcc and currently only gles is not using HLSLcc by default
                #pragma prefer_hlslcc gles
                #pragma exclude_renderers d3d11_9x
                #pragma target 2.0

                // -------------------------------------
                // Material Keywords
                #pragma shader_feature _NORMALMAP
                #pragma shader_feature _ALPHATEST_ON
                #pragma shader_feature _ALPHAPREMULTIPLY_ON
                #pragma shader_feature _EMISSION
                #pragma shader_feature _METALLICSPECGLOSSMAP
                #pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
                #pragma shader_feature _OCCLUSIONMAP

                #pragma shader_feature _SPECULARHIGHLIGHTS_OFF
                #pragma shader_feature _ENVIRONMENTREFLECTIONS_OFF
                #pragma shader_feature _SPECULAR_SETUP
                #pragma shader_feature _RECEIVE_SHADOWS_OFF

                // -------------------------------------
                // Lightweight Pipeline keywords
                #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
                #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
                #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
                #pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
                #pragma multi_compile _ _SHADOWS_SOFT
                #pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE

                // -------------------------------------
                // Unity defined keywords
                #pragma multi_compile _ DIRLIGHTMAP_COMBINED
                #pragma multi_compile _ LIGHTMAP_ON
                #pragma multi_compile_fog

                //--------------------------------------
                // GPU Instancing
                #pragma multi_compile_instancing

                #pragma vertex LitPassVertex
                #pragma fragment LitPassFragment

                #include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/Lighting.hlsl"
                #include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/Core.hlsl"
                #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
                #include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/SurfaceInput.hlsl"

                #ifdef _SPECULAR_SETUP
                    #define SAMPLE_METALLICSPECULAR(uv) SAMPLE_TEXTURE2D(_SpecGlossMap, sampler_SpecGlossMap, uv)
                #else
                    #define SAMPLE_METALLICSPECULAR(uv) SAMPLE_TEXTURE2D(_MetallicGlossMap, sampler_MetallicGlossMap, uv)
                #endif



                struct Attributes
                {
                    float4 positionOS   : POSITION;
                    float3 normalOS     : NORMAL;
                    float4 tangentOS    : TANGENT;
                    float2 texcoord     : TEXCOORD0;
                    float2 lightmapUV   : TEXCOORD1;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                };

                struct Varyings
                {
                    float2 uv                       : TEXCOORD0;
                    DECLARE_LIGHTMAP_OR_SH(lightmapUV, vertexSH, 1);

                #ifdef _ADDITIONAL_LIGHTS
                    float3 positionWS               : TEXCOORD2;
                #endif

                #ifdef _NORMALMAP
                    half4 normalWS                  : TEXCOORD3;    // xyz: normal, w: viewDir.x
                    half4 tangentWS                 : TEXCOORD4;    // xyz: tangent, w: viewDir.y
                    half4 bitangentWS                : TEXCOORD5;    // xyz: bitangent, w: viewDir.z
                #else
                    half3 normalWS                  : TEXCOORD3;
                    half3 viewDirWS                 : TEXCOORD4;
                #endif

                    half4 fogFactorAndVertexLight   : TEXCOORD6; // x: fogFactor, yzw: vertex light

                #ifdef _MAIN_LIGHT_SHADOWS
                    float4 shadowCoord              : TEXCOORD7;
                #endif

                    float4 positionCS               : SV_POSITION;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                    UNITY_VERTEX_OUTPUT_STEREO


                    float3 uvDissolve			    : TEXCOORD8; 
                };


                CBUFFER_START(UnityPerMaterial)
                    float4 _BaseMap_ST;
                    half4 _BaseColor;
                    half4 _SpecColor;
                    half4 _EmissionColor;
                    half _Cutoff;
                    half _Smoothness;
                    half _Metallic;
                    half _BumpScale;
                    half _OcclusionStrength;
                    float4 _DissolveMap_ST; //Тайлинг и оффсет текстуры _DissolveMap
                    float _DissolveFactor;
                    float _DissolveWidth;
                    float4 _DissolveColor;
                CBUFFER_END

                TEXTURE2D(_OcclusionMap);       SAMPLER(sampler_OcclusionMap);
                TEXTURE2D(_MetallicGlossMap);   SAMPLER(sampler_MetallicGlossMap);
                TEXTURE2D(_SpecGlossMap);       SAMPLER(sampler_SpecGlossMap);
                TEXTURE2D(_DissolveMap);        SAMPLER(sampler_DissolveMap);


                half4 SampleMetallicSpecGloss(float2 uv, half albedoAlpha)
                {
                    half4 specGloss;

                    #ifdef _METALLICSPECGLOSSMAP
                    specGloss = SAMPLE_METALLICSPECULAR(uv);
                    #ifdef _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
                    specGloss.a = albedoAlpha * _Smoothness;
                    #else
                    specGloss.a *= _Smoothness;
                    #endif
                    #else // _METALLICSPECGLOSSMAP
                    #if _SPECULAR_SETUP
                    specGloss.rgb = _SpecColor.rgb;
                    #else
                    specGloss.rgb = _Metallic.rrr;
                    #endif

                    #ifdef _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
                    specGloss.a = albedoAlpha * _Smoothness;
                    #else
                    specGloss.a = _Smoothness;
                    #endif
                    #endif

                    return specGloss;
                }

                half SampleOcclusion(float2 uv)
                {
                    #ifdef _OCCLUSIONMAP
                    // TODO: Controls things like these by exposing SHADER_QUALITY levels (low, medium, high)
                    #if defined(SHADER_API_GLES)
                    return SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, uv).g;
                    #else
                    half occ = SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, uv).g;
                    return LerpWhiteTo(occ, _OcclusionStrength);
                    #endif
                    #else
                    return 1.0;
                    #endif
                }

                inline void InitializeStandardLitSurfaceData(float2 uv, out SurfaceData outSurfaceData)
                {
                    half4 albedoAlpha = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uv);
                   // half4 albedoAlpha = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap));
                    outSurfaceData.alpha = Alpha(albedoAlpha.a, _BaseColor, _Cutoff);

                    half4 specGloss = SampleMetallicSpecGloss(uv, albedoAlpha.a);
                    outSurfaceData.albedo = albedoAlpha.rgb * _BaseColor.rgb;

                    #if _SPECULAR_SETUP
                    outSurfaceData.metallic = 1.0h;
                    outSurfaceData.specular = specGloss.rgb;
                    #else
                    outSurfaceData.metallic = specGloss.r;
                    outSurfaceData.specular = half3(0.0h, 0.0h, 0.0h);
                    #endif

                    outSurfaceData.smoothness = specGloss.a;
                    outSurfaceData.normalTS = SampleNormal(uv, TEXTURE2D_ARGS(_BumpMap, sampler_BumpMap), _BumpScale);
                    outSurfaceData.occlusion = SampleOcclusion(uv);
                    outSurfaceData.emission = SampleEmission(uv, _EmissionColor.rgb, TEXTURE2D_ARGS(_EmissionMap, sampler_EmissionMap));
                   // outSurfaceData.emission = _EmissionColor.rgb * SAMPLE_TEXTURE2D(_EmissionMap, sampler_EmissionMap, uv);
                }

                void InitializeInputData(Varyings input, half3 normalTS, out InputData inputData)
                {
                    inputData = (InputData)0;

                #ifdef _ADDITIONAL_LIGHTS
                    inputData.positionWS = input.positionWS;
                #endif

                #ifdef _NORMALMAP
                    half3 viewDirWS = half3(input.normalWS.w, input.tangentWS.w, input.bitangentWS.w);
                    inputData.normalWS = TransformTangentToWorld(normalTS,
                        half3x3(input.tangentWS.xyz, input.bitangentWS.xyz, input.normalWS.xyz));
                #else
                    half3 viewDirWS = input.viewDirWS;
                    inputData.normalWS = input.normalWS;
                #endif

                    inputData.normalWS = NormalizeNormalPerPixel(inputData.normalWS);
                    viewDirWS = SafeNormalize(viewDirWS);

                    inputData.viewDirectionWS = viewDirWS;
                #if defined(_MAIN_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF)
                    inputData.shadowCoord = input.shadowCoord;
                #else
                    inputData.shadowCoord = float4(0, 0, 0, 0);
                #endif
                    inputData.fogCoord = input.fogFactorAndVertexLight.x;
                    inputData.vertexLighting = input.fogFactorAndVertexLight.yzw;
                    inputData.bakedGI = SAMPLE_GI(input.lightmapUV, input.vertexSH, inputData.normalWS);
                }

                ///////////////////////////////////////////////////////////////////////////////
                //                  Vertex and Fragment functions                            //
                ///////////////////////////////////////////////////////////////////////////////

                //Standard (Physically Based) shader
                Varyings LitPassVertex(Attributes input)
                {
                    Varyings output = (Varyings)0;


                    UNITY_SETUP_INSTANCE_ID(input);
                    UNITY_TRANSFER_INSTANCE_ID(input, output);
                    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);


                    input.positionOS.xyz += normalize(input.positionOS.xyz) * sin(input.positionOS.x) * sin(_Time.x * 100);

                    VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
                    VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS);
                    half3 viewDirWS = GetCameraPositionWS() - vertexInput.positionWS;
                    half3 vertexLight = VertexLighting(vertexInput.positionWS, normalInput.normalWS);
                    half fogFactor = ComputeFogFactor(vertexInput.positionCS.z);

                    output.uv = TRANSFORM_TEX(input.texcoord, _BaseMap);

                #ifdef _NORMALMAP
                    output.normalWS = half4(normalInput.normalWS, viewDirWS.x);
                    output.tangentWS = half4(normalInput.tangentWS, viewDirWS.y);
                    output.bitangentWS = half4(normalInput.bitangentWS, viewDirWS.z);
                #else
                    output.normalWS = NormalizeNormalPerVertex(normalInput.normalWS);
                    output.viewDirWS = viewDirWS;
                #endif

                    OUTPUT_LIGHTMAP_UV(input.lightmapUV, unity_LightmapST, output.lightmapUV);
                    OUTPUT_SH(output.normalWS.xyz, output.vertexSH);

                    output.fogFactorAndVertexLight = half4(fogFactor, vertexLight);

                #ifdef _ADDITIONAL_LIGHTS
                    output.positionWS = vertexInput.positionWS;
                #endif

                #if defined(_MAIN_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF)
                    output.shadowCoord = GetShadowCoord(vertexInput);
                #endif

                    output.positionCS = vertexInput.positionCS;

                    return output;
                }

                // Used in Standard (Physically Based) shader
                half4 LitPassFragment(Varyings input) : SV_Target
                {
                    input.uv += _Time.x * 10;
                    UNITY_SETUP_INSTANCE_ID(input);
                    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

                    SurfaceData surfaceData;
                    InitializeStandardLitSurfaceData(input.uv, surfaceData);

                    InputData inputData;
                    InitializeInputData(input, surfaceData.normalTS, inputData);
                    
                    float4 mask = SAMPLE_TEXTURE2D(_DissolveMap, sampler_DissolveMap, input.uv);

                    if (mask.r > _DissolveFactor)
                        discard;

                    bool outline = mask.r > _DissolveFactor - _DissolveWidth;
                    surfaceData.emission *= outline * _DissolveColor;
                    half4 color = LightweightFragmentPBR(inputData, surfaceData.albedo, surfaceData.metallic, surfaceData.specular, surfaceData.smoothness, surfaceData.occlusion, surfaceData.emission, surfaceData.alpha);
                    color *= lerp(1, _DissolveColor, outline);

                    color.rgb = MixFog(color.rgb, inputData.fogCoord);
                    
                    return color;
                }
                ENDHLSL
            }

            //тени
            Pass
            {
                Name "ShadowCaster"
                Tags{"LightMode" = "ShadowCaster"}

                ZWrite On
                ZTest LEqual
                Cull[_Cull]

                HLSLPROGRAM
                // Required to compile gles 2.0 with standard srp library
                #pragma prefer_hlslcc gles
                #pragma exclude_renderers d3d11_9x
                #pragma target 2.0

                // -------------------------------------
                // Material Keywords
                #pragma shader_feature _ALPHATEST_ON

                //--------------------------------------
                // GPU Instancing
                #pragma multi_compile_instancing
                #pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

                #pragma vertex ShadowPassVertex
                #pragma fragment ShadowPassFragment

                #include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitInput.hlsl"             
                #include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/Core.hlsl"
                #include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/Shadows.hlsl"

                float3 _LightDirection;

                struct Attributes
                {
                    float4 positionOS   : POSITION;
                    float3 normalOS     : NORMAL;
                    float2 texcoord     : TEXCOORD0;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                };

                struct Varyings
                {
                    float2 uv           : TEXCOORD0;
                    float4 positionCS   : SV_POSITION;
                };

                CBUFFER_START(UnityPerMaterial)
                    float4 _DissolveMap_ST; //Тайлинг и оффсет текстуры _DissolveMap
                    float _DissolveFactor;
                CBUFFER_END

                TEXTURE2D(_DissolveMap);        SAMPLER(sampler_DissolveMap);

                float4 GetShadowPositionHClip(Attributes input)
                {
                    float3 positionWS = TransformObjectToWorld(input.positionOS.xyz);
                    float3 normalWS = TransformObjectToWorldNormal(input.normalOS);

                    float4 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, _LightDirection));

                #if UNITY_REVERSED_Z
                    positionCS.z = min(positionCS.z, positionCS.w * UNITY_NEAR_CLIP_VALUE);
                #else
                    positionCS.z = max(positionCS.z, positionCS.w * UNITY_NEAR_CLIP_VALUE);
                #endif

                    return positionCS;
                }

                Varyings ShadowPassVertex(Attributes input)
                {
                    Varyings output;
                    input.positionOS.xyz += normalize(input.positionOS.xyz) * sin(input.positionOS.x) * sin(_Time.x * 100);
                    UNITY_SETUP_INSTANCE_ID(input);
                    output.uv = TRANSFORM_TEX(input.texcoord, _BaseMap);
                    output.positionCS = GetShadowPositionHClip(input);
                    return output;
                }

                half4 ShadowPassFragment(Varyings input) : SV_TARGET
                {
                     float4 mask = SAMPLE_TEXTURE2D(_DissolveMap, sampler_DissolveMap, input.uv);

                     if (mask.r > _DissolveFactor)
                         discard;

                    Alpha(SampleAlbedoAlpha(input.uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap)).a, _BaseColor, _Cutoff);
                    return 0;
                }

                ENDHLSL
            }

            Pass
            {
                Name "DepthOnly"
                Tags{"LightMode" = "DepthOnly"}

                ZWrite On
                ColorMask 0
                Cull[_Cull]

                HLSLPROGRAM
                // Required to compile gles 2.0 with standard srp library
                #pragma prefer_hlslcc gles
                #pragma exclude_renderers d3d11_9x
                #pragma target 2.0

                #pragma vertex DepthOnlyVertex
                #pragma fragment DepthOnlyFragment

                // -------------------------------------
                // Material Keywords
                #pragma shader_feature _ALPHATEST_ON
                #pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

                //--------------------------------------
                // GPU Instancing
                #pragma multi_compile_instancing

                #include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitInput.hlsl"
                #include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/Core.hlsl"

                #ifndef LIGHTWEIGHT_DEPTH_ONLY_PASS_INCLUDED
                #define LIGHTWEIGHT_DEPTH_ONLY_PASS_INCLUDED

                struct Attributes
                {
                    float4 position     : POSITION;
                    float2 texcoord     : TEXCOORD0;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                };

                struct Varyings
                {
                    float2 uv           : TEXCOORD0;
                    float4 positionCS   : SV_POSITION;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                    UNITY_VERTEX_OUTPUT_STEREO
                };

                Varyings DepthOnlyVertex(Attributes input)
                {
                    Varyings output = (Varyings)0;
                    UNITY_SETUP_INSTANCE_ID(input);
                    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

                    input.position.xyz += normalize(input.position.xyz) * sin(input.position.x) * sin(_Time.x * 100);

                    output.uv = TRANSFORM_TEX(input.texcoord, _BaseMap);
                    output.positionCS = TransformObjectToHClip(input.position.xyz);
                    return output;
                }

                half4 DepthOnlyFragment(Varyings input) : SV_TARGET
                {
                    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

                    Alpha(SampleAlbedoAlpha(input.uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap)).a, _BaseColor, _Cutoff);
                    return 0;
                }
                #endif

                ENDHLSL
            }

            //лайтмапы
            Pass
            {
                Name "Meta"
                Tags{"LightMode" = "Meta"}

                Cull Off

                HLSLPROGRAM
                // Required to compile gles 2.0 with standard srp library
                #pragma prefer_hlslcc gles
                #pragma exclude_renderers d3d11_9x

                #pragma vertex LightweightVertexMeta
                #pragma fragment LightweightFragmentMeta

                #pragma shader_feature _SPECULAR_SETUP
                #pragma shader_feature _EMISSION
                #pragma shader_feature _METALLICSPECGLOSSMAP
                #pragma shader_feature _ALPHATEST_ON
                #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

                #pragma shader_feature _SPECGLOSSMAP

                #include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitInput.hlsl"
                #include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/MetaInput.hlsl"

                #ifndef LIGHTWEIGHT_LIT_META_PASS_INCLUDED
                #define LIGHTWEIGHT_LIT_META_PASS_INCLUDED



                Varyings LightweightVertexMeta(Attributes input)
                {
                    Varyings output;
                    output.positionCS = MetaVertexPosition(input.positionOS, input.uv1, input.uv2,
                        unity_LightmapST, unity_DynamicLightmapST);
                    output.uv = TRANSFORM_TEX(input.uv0, _BaseMap);
                    return output;
                }

                half4 LightweightFragmentMeta(Varyings input) : SV_Target
                {
                    SurfaceData surfaceData;
                    InitializeStandardLitSurfaceData(input.uv, surfaceData);

                    BRDFData brdfData;
                    InitializeBRDFData(surfaceData.albedo, surfaceData.metallic, surfaceData.specular, surfaceData.smoothness, surfaceData.alpha, brdfData);

                    MetaInput metaInput;
                    metaInput.Albedo = brdfData.diffuse + brdfData.specular * brdfData.roughness * 0.5;
                    metaInput.SpecularColor = surfaceData.specular;
                    metaInput.Emission = surfaceData.emission;

                    return MetaFragment(metaInput);
                }
                #endif
                ENDHLSL
            }

            Pass
            {
                Name "Lightweight2D"
                Tags{ "LightMode" = "Lightweight2D" }

                Blend[_SrcBlend][_DstBlend]
                ZWrite[_ZWrite]
                Cull[_Cull]

                HLSLPROGRAM
                // Required to compile gles 2.0 with standard srp library
                #pragma prefer_hlslcc gles
                #pragma exclude_renderers d3d11_9x

                #pragma vertex vert
                #pragma fragment frag
                #pragma shader_feature _ALPHATEST_ON
                #pragma shader_feature _ALPHAPREMULTIPLY_ON

                #include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitInput.hlsl"
                #include "Packages/com.unity.render-pipelines.lightweight/Shaders/Utils/Lightweight2D.hlsl"
                ENDHLSL
            }


            }
                FallBack "Hidden/InternalErrorShader"
                CustomEditor "UnityEditor.Rendering.LWRP.ShaderGUI.LitShaderEditor"
}


Прежде всего, нужно разобрать что к чему в получившемся шейдере. Во-первых, нас интересует строчка:

CustomEditor "UnityEditor.Rendering.LWRP.ShaderGUI.LitShaderEditor"

Которая, как мы помним, в оригинале была следующей:

CustomEditor "UnityEditor.Rendering.LWRP.ShaderGUI.LitShader"

Итак, что же это и зачем это? Ответ на этот вопрос достаточно прост — если присмотрется, в шейдере огромное количество дефайнов разных сортов, которые, как ни странно, нужно активировать и деактивировать, а это, на минуточку, нужно делать из кода, именно поэтому нам потребуется кастомный инспектор. Более того, наш кастомный инспектор должен давать нам возможность редактировать не только встроенные проперти, но и те, которые нам могут понадобиться в своих шейдерах. У исходного шейдера уже имеется кастомный инспектор, поэтому уверенно топаем его искать по следующему пути:
%localappdata%\Unity\cache\packages\packages.unity.com\com.unity.render-pipelines.lightweight@6.9.2\Editor\ShaderGUI\Shaders\
Собственно, нас интересует файл LitShader.cs, который унаследован от BaseShaderGUI:

internal class LitShader : BaseShaderGUI
{
...
}

Основной замес происходит как раз таки в BaseShaderGUI.cs, который можно найти в папке на уровень выше:

public abstract class BaseShaderGUI : ShaderGUI
{
...
}

Берём эти скрипты и кидаем в проект в папку Editor (если такой нет — создать, иначе во время билда проекта закономерно появятся ошибки, так как пространство имён UnityEditor не входит в сборку билда). Разумеется, на нашу голову валится тысяча и одна ошибка, которые связаны с интернел-типом SavedBool, который представляет из себя сериализуемую в окне редактора переменную типа bool. Сделано это для сохранения состояния свёрнутости разделов материала. Собственно, для исправления проводим нехитрую манипуляцию.

Меняем:

SavedBool m_SurfaceOptionsFoldout;
SavedBool m_SurfaceInputsFoldout;
SavedBool m_AdvancedFoldout;

На:

AnimatedValues.AnimBool m_SurfaceOptionsFoldout;
AnimatedValues.AnimBool m_SurfaceInputsFoldout;
AnimatedValues.AnimBool m_AdvancedFoldout;

И добавим ещё одну переменную для дополнительных кастомных пропертисов:

AnimatedValues.AnimBool m_OtherFoldout;

Также нужно добавить название и описание раздела с нашими кастомными параметрами, соблюдая сложившиеся традиции внутри скрипта:

protected class Styles
{
    // Catergories      
    ...
    public static readonly GUIContent OtherOptions = 
    new GUIContent("Your own options", "You own custom options");
}

Ну а теперь провернём небольшой фокус. Как можно заметить, у всех стандартных пропертисов я выставил атрибут HideInInspector, который своим названием напрямую намекает на то, что данный проперти будет скрыт в инспекторе. Однако, это релевантно только для стандратного инспектора материалов, а у нас какой? Правильно, кастомный! А это значит, что все наши встроенные пропертисы отрисуются по-любому. Вот и скроем их:


...
[MainColor][HideInInspector] _BaseColor("Color", Color) = (0.5,0.5,0.5,1)
[MainTexture][HideInInspector] _BaseMap("Albedo", 2D) = "white" {}
[HideInInspector]_Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
[HideInInspector]_Smoothness("Smoothness", Range(0.0, 1.0)) = 0.5
[HideInInspector]_GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0
[HideInInspector]_SmoothnessTextureChannel("Smoothness texture channel", Float) = 0
...

А внутри кода кастомного редактора просто вызовем отрисовку стандартного инспектора:

public override void OnGUI(...)
{
   ...
   m_OtherFoldout.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_OtherFoldout.value, Styles.OtherOptions);
   if (m_OtherFoldout.value)
   {
         base.OnGUI(materialEditorIn, properties);
         EditorGUILayout.Space();
   }
   EditorGUILayout.EndFoldoutHeaderGroup();            
   ...
}

Результат налицо:

2020-01-19-22-58-23

А вот и код обоих скриптов:

BaseShaderEditor.cs
using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEditor.Rendering;

namespace UnityEditor
{
    public abstract class BaseShaderEditor : ShaderGUI
    {
        #region EnumsAndClasses

        public enum SurfaceType
        {
            Opaque,
            Transparent
        }

        public enum BlendMode
        {
            Alpha,   // Old school alpha-blending mode, fresnel does not affect amount of transparency
            Premultiply, // Physically plausible transparency mode, implemented as alpha pre-multiply
            Additive,
            Multiply
        }

        public enum SmoothnessSource
        {
            BaseAlpha,
            SpecularAlpha
        }

        public enum RenderFace
        {
            Front = 2,
            Back = 1,
            Both = 0
        }

        protected class Styles
        {
            // Catergories
            public static readonly GUIContent SurfaceOptions =
                new GUIContent("Surface Options", "Controls how LWRP renders the Material on a screen.");

            public static readonly GUIContent SurfaceInputs = new GUIContent("Surface Inputs",
                "These settings describe the look and feel of the surface itself.");

            public static readonly GUIContent AdvancedLabel = new GUIContent("Advanced",
                "These settings affect behind-the-scenes rendering and underlying calculations.");

            public static readonly GUIContent surfaceType = new GUIContent("Surface Type",
                "Select a surface type for your texture. Choose between Opaque or Transparent.");

            public static readonly GUIContent blendingMode = new GUIContent("Blending Mode",
                "Controls how the color of the Transparent surface blends with the Material color in the background.");

            public static readonly GUIContent cullingText = new GUIContent("Render Face",
                "Specifies which faces to cull from your geometry. Front culls front faces. Back culls backfaces. None means that both sides are rendered.");

            public static readonly GUIContent alphaClipText = new GUIContent("Alpha Clipping",
                "Makes your Material act like a Cutout shader. Use this to create a transparent effect with hard edges between opaque and transparent areas.");

            public static readonly GUIContent alphaClipThresholdText = new GUIContent("Threshold",
                "Sets where the Alpha Clipping starts. The higher the value is, the brighter the  effect is when clipping starts.");

            public static readonly GUIContent receiveShadowText = new GUIContent("Receive Shadows",
                "When enabled, other GameObjects can cast shadows onto this GameObject.");

            public static readonly GUIContent baseMap = new GUIContent("Base Map",
                "Specifies the base Material and/or Color of the surface. If you’ve selected Transparent or Alpha Clipping under Surface Options, your Material uses the Texture’s alpha channel or color.");

            public static readonly GUIContent emissionMap = new GUIContent("Emission Map",
                "Sets a Texture map to use for emission. You can also select a color with the color picker. Colors are multiplied over the Texture.");

            public static readonly GUIContent normalMapText =
                new GUIContent("Normal Map", "Assigns a tangent-space normal map.");

            public static readonly GUIContent bumpScaleNotSupported =
                new GUIContent("Bump scale is not supported on mobile platforms");

            public static readonly GUIContent fixNormalNow = new GUIContent("Fix now",
                "Converts the assigned texture to be a normal map format.");

            public static readonly GUIContent queueSlider = new GUIContent("Priority",
                "Determines the chronological rendering order for a Material. High values are rendered first.");

            public static readonly GUIContent OtherOptions =
                new GUIContent("Your own options", "You own custom options");
        }

        #endregion

        #region Variables

        protected MaterialEditor materialEditor { get; set; }

        protected MaterialProperty surfaceTypeProp { get; set; }

        protected MaterialProperty blendModeProp { get; set; }

        protected MaterialProperty cullingProp { get; set; }

        protected MaterialProperty alphaClipProp { get; set; }

        protected MaterialProperty alphaCutoffProp { get; set; }

        protected MaterialProperty receiveShadowsProp { get; set; }

        // Common Surface Input properties

        protected MaterialProperty baseMapProp { get; set; }

        protected MaterialProperty baseColorProp { get; set; }

        protected MaterialProperty emissionMapProp { get; set; }

        protected MaterialProperty emissionColorProp { get; set; }

        protected MaterialProperty queueOffsetProp { get; set; }

        public bool m_FirstTimeApply = true;

        private const string k_KeyPrefix = "LightweightRP:Material:UI_State:";

        private string m_HeaderStateKey = null;

        // Header foldout states

        AnimatedValues.AnimBool m_SurfaceOptionsFoldout;

        AnimatedValues.AnimBool m_SurfaceInputsFoldout;

        AnimatedValues.AnimBool m_AdvancedFoldout;

        AnimatedValues.AnimBool m_OtherFoldout;

        #endregion

        private const int queueOffsetRange = 50;
        ////////////////////////////////////
        // General Functions              //
        ////////////////////////////////////
        #region GeneralFunctions

        public abstract void MaterialChanged(Material material);

        public virtual void FindProperties(MaterialProperty[] properties)
        {
            surfaceTypeProp = FindProperty("_Surface", properties);
            blendModeProp = FindProperty("_Blend", properties);
            cullingProp = FindProperty("_Cull", properties);
            alphaClipProp = FindProperty("_AlphaClip", properties);
            alphaCutoffProp = FindProperty("_Cutoff", properties);
            receiveShadowsProp = FindProperty("_ReceiveShadows", properties, false);
            baseMapProp = FindProperty("_BaseMap", properties, false);
            baseColorProp = FindProperty("_BaseColor", properties, false);
            emissionMapProp = FindProperty("_EmissionMap", properties, false);
            emissionColorProp = FindProperty("_EmissionColor", properties, false);
            queueOffsetProp = FindProperty("_QueueOffset", properties, false);
        }

        public override void OnGUI(MaterialEditor materialEditorIn, MaterialProperty[] properties)
        {
            if (materialEditorIn == null)
                throw new ArgumentNullException("materialEditorIn");

            FindProperties(properties); // MaterialProperties can be animated so we do not cache them but fetch them every event to ensure animated values are updated correctly
            materialEditor = materialEditorIn;
            Material material = materialEditor.target as Material;

            // Make sure that needed setup (ie keywords/renderqueue) are set up if we're switching some existing
            // material to a lightweight shader.
            if (m_FirstTimeApply)
            {
                OnOpenGUI(material, materialEditorIn);
                m_FirstTimeApply = false;
            }

            ShaderPropertiesGUI(material);

            m_OtherFoldout.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_OtherFoldout.value, Styles.OtherOptions);
            if (m_OtherFoldout.value)
            {
                base.OnGUI(materialEditorIn, properties);
                EditorGUILayout.Space();
            }
            EditorGUILayout.EndFoldoutHeaderGroup();
            
            foreach (var obj in materialEditor.targets)
                MaterialChanged((Material)obj);
        }


        public virtual void OnOpenGUI(Material material, MaterialEditor materialEditor)
        {
            // Foldout states
            m_HeaderStateKey = k_KeyPrefix + material.shader.name; // Create key string for editor prefs
            m_SurfaceOptionsFoldout = new AnimatedValues.AnimBool(true);
            m_SurfaceInputsFoldout = new AnimatedValues.AnimBool(true);
            m_AdvancedFoldout = new AnimatedValues.AnimBool(true);
            m_OtherFoldout = new AnimatedValues.AnimBool(true);

            foreach (var obj in materialEditor.targets)
                MaterialChanged((Material)obj);
        }

        public void ShaderPropertiesGUI(Material material)
        {
            if (material == null)
                throw new ArgumentNullException("material");

            EditorGUI.BeginChangeCheck();

            m_SurfaceOptionsFoldout.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SurfaceOptionsFoldout.value, Styles.SurfaceOptions);
            if (m_SurfaceOptionsFoldout.value)
            {
                DrawSurfaceOptions(material);
                EditorGUILayout.Space();
            }
            EditorGUILayout.EndFoldoutHeaderGroup();

            m_SurfaceInputsFoldout.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SurfaceInputsFoldout.value, Styles.SurfaceInputs);
            if (m_SurfaceInputsFoldout.value)
            {
                DrawSurfaceInputs(material);
                EditorGUILayout.Space();
            }
            EditorGUILayout.EndFoldoutHeaderGroup();

            m_AdvancedFoldout.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_AdvancedFoldout.value, Styles.AdvancedLabel);
            if (m_AdvancedFoldout.value)
            {
                DrawAdvancedOptions(material);
                EditorGUILayout.Space();
            }
            EditorGUILayout.EndFoldoutHeaderGroup();

            DrawAdditionalFoldouts(material);

            if (EditorGUI.EndChangeCheck())
            {
                foreach (var obj in materialEditor.targets)
                    MaterialChanged((Material)obj);
            }
        }

        #endregion
        ////////////////////////////////////
        // Drawing Functions              //
        ////////////////////////////////////
        #region DrawingFunctions

        public virtual void DrawSurfaceOptions(Material material)
        {
            DoPopup(Styles.surfaceType, surfaceTypeProp, Enum.GetNames(typeof(SurfaceType)));
            if ((SurfaceType)material.GetFloat("_Surface") == SurfaceType.Transparent)
                DoPopup(Styles.blendingMode, blendModeProp, Enum.GetNames(typeof(BlendMode)));

            EditorGUI.BeginChangeCheck();
            EditorGUI.showMixedValue = cullingProp.hasMixedValue;
            var culling = (RenderFace)cullingProp.floatValue;
            culling = (RenderFace)EditorGUILayout.EnumPopup(Styles.cullingText, culling);
            if (EditorGUI.EndChangeCheck())
            {
                materialEditor.RegisterPropertyChangeUndo(Styles.cullingText.text);
                cullingProp.floatValue = (float)culling;
                material.doubleSidedGI = (RenderFace)cullingProp.floatValue != RenderFace.Front;
            }

            EditorGUI.showMixedValue = false;

            EditorGUI.BeginChangeCheck();
            EditorGUI.showMixedValue = alphaClipProp.hasMixedValue;
            var alphaClipEnabled = EditorGUILayout.Toggle(Styles.alphaClipText, alphaClipProp.floatValue == 1);
            if (EditorGUI.EndChangeCheck())
                alphaClipProp.floatValue = alphaClipEnabled ? 1 : 0;
            EditorGUI.showMixedValue = false;

            if (alphaClipProp.floatValue == 1)
                materialEditor.ShaderProperty(alphaCutoffProp, Styles.alphaClipThresholdText, 1);

            if (receiveShadowsProp != null)
            {
                EditorGUI.BeginChangeCheck();
                EditorGUI.showMixedValue = receiveShadowsProp.hasMixedValue;
                var receiveShadows =
                    EditorGUILayout.Toggle(Styles.receiveShadowText, receiveShadowsProp.floatValue == 1.0f);
                if (EditorGUI.EndChangeCheck())
                    receiveShadowsProp.floatValue = receiveShadows ? 1.0f : 0.0f;
                EditorGUI.showMixedValue = false;
            }
        }

        public virtual void DrawSurfaceInputs(Material material)
        {
            DrawBaseProperties(material);
        }

        public virtual void DrawAdvancedOptions(Material material)
        {
            materialEditor.EnableInstancingField();

            if (queueOffsetProp != null)
            {
                EditorGUI.BeginChangeCheck();
                EditorGUI.showMixedValue = queueOffsetProp.hasMixedValue;
                var queue = EditorGUILayout.IntSlider(Styles.queueSlider, (int)queueOffsetProp.floatValue, -queueOffsetRange, queueOffsetRange);
                if (EditorGUI.EndChangeCheck())
                    queueOffsetProp.floatValue = queue;
                EditorGUI.showMixedValue = false;
            }
        }

        public virtual void DrawAdditionalFoldouts(Material material) { }

        public virtual void DrawBaseProperties(Material material)
        {
            if (baseMapProp != null && baseColorProp != null) // Draw the baseMap, most shader will have at least a baseMap
            {
                materialEditor.TexturePropertySingleLine(Styles.baseMap, baseMapProp, baseColorProp);
                // TODO Temporary fix for lightmapping, to be replaced with attribute tag.
                if (material.HasProperty("_MainTex"))
                {
                    material.SetTexture("_MainTex", baseMapProp.textureValue);
                    var baseMapTiling = baseMapProp.textureScaleAndOffset;
                    material.SetTextureScale("_MainTex", new Vector2(baseMapTiling.x, baseMapTiling.y));
                    material.SetTextureOffset("_MainTex", new Vector2(baseMapTiling.z, baseMapTiling.w));
                }
            }
        }

        protected virtual void DrawEmissionProperties(Material material, bool keyword)
        {
            var emissive = true;
            var hadEmissionTexture = emissionMapProp.textureValue != null;

            if (!keyword)
            {
                materialEditor.TexturePropertyWithHDRColor(Styles.emissionMap, emissionMapProp, emissionColorProp,
                    false);
            }
            else
            {
                // Emission for GI?
                emissive = materialEditor.EmissionEnabledProperty();

                EditorGUI.BeginDisabledGroup(!emissive);
                {
                    // Texture and HDR color controls
                    materialEditor.TexturePropertyWithHDRColor(Styles.emissionMap, emissionMapProp,
                        emissionColorProp,
                        false);
                }
                EditorGUI.EndDisabledGroup();
            }

            // If texture was assigned and color was black set color to white
            var brightness = emissionColorProp.colorValue.maxColorComponent;
            if (emissionMapProp.textureValue != null && !hadEmissionTexture && brightness <= 0f)
                emissionColorProp.colorValue = Color.white;

            // LW does not support RealtimeEmissive. We set it to bake emissive and handle the emissive is black right.
            if (emissive)
            {
                material.globalIlluminationFlags = MaterialGlobalIlluminationFlags.BakedEmissive;
                if (brightness <= 0f)
                    material.globalIlluminationFlags |= MaterialGlobalIlluminationFlags.EmissiveIsBlack;
            }
        }

        public static void DrawNormalArea(MaterialEditor materialEditor, MaterialProperty bumpMap, MaterialProperty bumpMapScale = null)
        {
            if (bumpMapScale != null)
            {
                materialEditor.TexturePropertySingleLine(Styles.normalMapText, bumpMap,
                    bumpMap.textureValue != null ? bumpMapScale : null);
                if (bumpMapScale.floatValue != 1 &&
                    UnityEditorInternal.InternalEditorUtility.IsMobilePlatform(
                        EditorUserBuildSettings.activeBuildTarget))
                    if (materialEditor.HelpBoxWithButton(Styles.bumpScaleNotSupported, Styles.fixNormalNow))
                        bumpMapScale.floatValue = 1;
            }
            else
            {
                materialEditor.TexturePropertySingleLine(Styles.normalMapText, bumpMap);
            }
        }

        protected static void DrawTileOffset(MaterialEditor materialEditor, MaterialProperty textureProp)
        {
            materialEditor.TextureScaleOffsetProperty(textureProp);
        }

        #endregion
        ////////////////////////////////////
        // Material Data Functions        //
        ////////////////////////////////////
        #region MaterialDataFunctions

        public static void SetMaterialKeywords(Material material, Action<Material> shadingModelFunc = null, Action<Material> shaderFunc = null)
        {
            // Clear all keywords for fresh start
            material.shaderKeywords = null;
            // Setup blending - consistent across all LWRP shaders
            SetupMaterialBlendMode(material);
            // Receive Shadows
            if (material.HasProperty("_ReceiveShadows"))
                CoreUtils.SetKeyword(material, "_RECEIVE_SHADOWS_OFF", material.GetFloat("_ReceiveShadows") == 0.0f);
            // Emission
            if (material.HasProperty("_EmissionColor"))
                MaterialEditor.FixupEmissiveFlag(material);
            bool shouldEmissionBeEnabled =
                (material.globalIlluminationFlags & MaterialGlobalIlluminationFlags.EmissiveIsBlack) == 0;
            if (material.HasProperty("_EmissionEnabled") && !shouldEmissionBeEnabled)
                shouldEmissionBeEnabled = material.GetFloat("_EmissionEnabled") >= 0.5f;
            CoreUtils.SetKeyword(material, "_EMISSION", shouldEmissionBeEnabled);
            // Normal Map
            if (material.HasProperty("_BumpMap"))
                CoreUtils.SetKeyword(material, "_NORMALMAP", material.GetTexture("_BumpMap"));
            // Shader specific keyword functions
            shadingModelFunc?.Invoke(material);
            shaderFunc?.Invoke(material);
        }

        public static void SetupMaterialBlendMode(Material material)
        {
            if (material == null)
                throw new ArgumentNullException("material");

            bool alphaClip = material.GetFloat("_AlphaClip") == 1;
            if (alphaClip)
            {
                material.EnableKeyword("_ALPHATEST_ON");
            }
            else
            {
                material.DisableKeyword("_ALPHATEST_ON");
            }

            var queueOffset = 0; // queueOffsetRange;
            if (material.HasProperty("_QueueOffset"))
                queueOffset = queueOffsetRange - (int)material.GetFloat("_QueueOffset");

            SurfaceType surfaceType = (SurfaceType)material.GetFloat("_Surface");
            if (surfaceType == SurfaceType.Opaque)
            {
                if (alphaClip)
                {
                    material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest;
                    material.SetOverrideTag("RenderType", "TransparentCutout");
                }
                else
                {
                    material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Geometry;
                    material.SetOverrideTag("RenderType", "Opaque");
                }
                material.renderQueue += queueOffset;
                material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
                material.SetInt("_ZWrite", 1);
                material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                material.SetShaderPassEnabled("ShadowCaster", true);
            }
            else
            {
                BlendMode blendMode = (BlendMode)material.GetFloat("_Blend");
                var queue = (int)UnityEngine.Rendering.RenderQueue.Transparent;

                // Specific Transparent Mode Settings
                switch (blendMode)
                {
                    case BlendMode.Alpha:
                        material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
                        material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
                        material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                        break;
                    case BlendMode.Premultiply:
                        material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                        material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
                        material.EnableKeyword("_ALPHAPREMULTIPLY_ON");
                        break;
                    case BlendMode.Additive:
                        material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                        material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.One);
                        material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                        break;
                    case BlendMode.Multiply:
                        material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.DstColor);
                        material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
                        material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                        material.EnableKeyword("_ALPHAMODULATE_ON");
                        break;
                }
                // General Transparent Material Settings
                material.SetOverrideTag("RenderType", "Transparent");
                material.SetInt("_ZWrite", 0);
                material.renderQueue = queue + queueOffset;
                material.SetShaderPassEnabled("ShadowCaster", false);
            }
        }

        #endregion
        ////////////////////////////////////
        // Helper Functions               //
        ////////////////////////////////////
        #region HelperFunctions

        public static void TwoFloatSingleLine(GUIContent title, MaterialProperty prop1, GUIContent prop1Label,
            MaterialProperty prop2, GUIContent prop2Label, MaterialEditor materialEditor, float labelWidth = 30f)
        {
            EditorGUI.BeginChangeCheck();
            EditorGUI.showMixedValue = prop1.hasMixedValue || prop2.hasMixedValue;
            Rect rect = EditorGUILayout.GetControlRect();
            EditorGUI.PrefixLabel(rect, title);
            var indent = EditorGUI.indentLevel;
            var preLabelWidth = EditorGUIUtility.labelWidth;
            EditorGUI.indentLevel = 0;
            EditorGUIUtility.labelWidth = labelWidth;
            Rect propRect1 = new Rect(rect.x + preLabelWidth, rect.y,
                (rect.width - preLabelWidth) * 0.5f, EditorGUIUtility.singleLineHeight);
            var prop1val = EditorGUI.FloatField(propRect1, prop1Label, prop1.floatValue);

            Rect propRect2 = new Rect(propRect1.x + propRect1.width, rect.y,
                propRect1.width, EditorGUIUtility.singleLineHeight);
            var prop2val = EditorGUI.FloatField(propRect2, prop2Label, prop2.floatValue);

            EditorGUI.indentLevel = indent;
            EditorGUIUtility.labelWidth = preLabelWidth;

            if (EditorGUI.EndChangeCheck())
            {
                materialEditor.RegisterPropertyChangeUndo(title.text);
                prop1.floatValue = prop1val;
                prop2.floatValue = prop2val;
            }

            EditorGUI.showMixedValue = false;
        }

        public void DoPopup(GUIContent label, MaterialProperty property, string[] options)
        {
            DoPopup(label, property, options, materialEditor);
        }

        public static void DoPopup(GUIContent label, MaterialProperty property, string[] options, MaterialEditor materialEditor)
        {
            if (property == null)
                throw new ArgumentNullException("property");

            EditorGUI.showMixedValue = property.hasMixedValue;

            var mode = property.floatValue;
            EditorGUI.BeginChangeCheck();
            mode = EditorGUILayout.Popup(label, (int)mode, options);
            if (EditorGUI.EndChangeCheck())
            {
                materialEditor.RegisterPropertyChangeUndo(label.text);
                property.floatValue = mode;
            }

            EditorGUI.showMixedValue = false;
        }

        // Helper to show texture and color properties
        public static Rect TextureColorProps(MaterialEditor materialEditor, GUIContent label, MaterialProperty textureProp, MaterialProperty colorProp, bool hdr = false)
        {
            Rect rect = EditorGUILayout.GetControlRect();
            EditorGUI.showMixedValue = textureProp.hasMixedValue;
            materialEditor.TexturePropertyMiniThumbnail(rect, textureProp, label.text, label.tooltip);
            EditorGUI.showMixedValue = false;

            if (colorProp != null)
            {
                EditorGUI.BeginChangeCheck();
                EditorGUI.showMixedValue = colorProp.hasMixedValue;
                int indentLevel = EditorGUI.indentLevel;
                EditorGUI.indentLevel = 0;
                Rect rectAfterLabel = new Rect(rect.x + EditorGUIUtility.labelWidth, rect.y,
                    EditorGUIUtility.fieldWidth, EditorGUIUtility.singleLineHeight);
                var col = EditorGUI.ColorField(rectAfterLabel, GUIContent.none, colorProp.colorValue, true,
                    false, hdr);
                EditorGUI.indentLevel = indentLevel;
                if (EditorGUI.EndChangeCheck())
                {
                    materialEditor.RegisterPropertyChangeUndo(colorProp.displayName);
                    colorProp.colorValue = col;
                }
                EditorGUI.showMixedValue = false;
            }

            return rect;
        }

        // Copied from shaderGUI as it is a protected function in an abstract class, unavailable to others

        public new static MaterialProperty FindProperty(string propertyName, MaterialProperty[] properties)
        {
            return FindProperty(propertyName, properties, true);
        }

        // Copied from shaderGUI as it is a protected function in an abstract class, unavailable to others

        public new static MaterialProperty FindProperty(string propertyName, MaterialProperty[] properties, bool propertyIsMandatory)
        {
            for (int index = 0; index < properties.Length; ++index)
            {
                if (properties[index] != null && properties[index].name == propertyName)
                    return properties[index];
            }
            if (propertyIsMandatory)
                throw new ArgumentException("Could not find MaterialProperty: '" + propertyName + "', Num properties: " + (object)properties.Length);
            return null;
        }

        #endregion
    }
}


LitShaderEditor.cs
using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEditor.Rendering.LWRP;

namespace UnityEditor.Rendering.LWRP.ShaderGUI
{
    internal class LitShaderEditor : BaseShaderEditor
    {
        // Properties
        private LitGUI.LitProperties litProperties;

        // collect properties from the material properties
        public override void FindProperties(MaterialProperty[] properties)
        {
            base.FindProperties(properties);
            litProperties = new LitGUI.LitProperties(properties);
        }

        // material changed check
        public override void MaterialChanged(Material material)
        {
            if (material == null)
                throw new ArgumentNullException("material");

            SetMaterialKeywords(material, LitGUI.SetMaterialKeywords);
        }

        // material main surface options
        public override void DrawSurfaceOptions(Material material)
        {
            if (material == null)
                throw new ArgumentNullException("material");

            // Use default labelWidth
            EditorGUIUtility.labelWidth = 0f;

            // Detect any changes to the material
            EditorGUI.BeginChangeCheck();
            if (litProperties.workflowMode != null)
            {
                DoPopup(LitGUI.Styles.workflowModeText, litProperties.workflowMode, Enum.GetNames(typeof(LitGUI.WorkflowMode)));
            }
            if (EditorGUI.EndChangeCheck())
            {
                foreach (var obj in blendModeProp.targets)
                    MaterialChanged((Material)obj);
            }
            base.DrawSurfaceOptions(material);
        }

        // material main surface inputs
        public override void DrawSurfaceInputs(Material material)
        {
            base.DrawSurfaceInputs(material);
            LitGUI.Inputs(litProperties, materialEditor, material);
            DrawEmissionProperties(material, true);
            DrawTileOffset(materialEditor, baseMapProp);
        }

        // material main advanced options
        public override void DrawAdvancedOptions(Material material)
        {
            if (litProperties.reflections != null && litProperties.highlights != null)
            {
                EditorGUI.BeginChangeCheck();
                {
                    materialEditor.ShaderProperty(litProperties.highlights, LitGUI.Styles.highlightsText);
                    materialEditor.ShaderProperty(litProperties.reflections, LitGUI.Styles.reflectionsText);
                    EditorGUI.BeginChangeCheck();
                }
            }

            base.DrawAdvancedOptions(material);
        }

        public override void AssignNewShaderToMaterial(Material material, Shader oldShader, Shader newShader)
        {
            if (material == null)
                throw new ArgumentNullException("material");

            // _Emission property is lost after assigning Standard shader to the material
            // thus transfer it before assigning the new shader
            if (material.HasProperty("_Emission"))
            {
                material.SetColor("_EmissionColor", material.GetColor("_Emission"));
            }

            base.AssignNewShaderToMaterial(material, oldShader, newShader);

            if (oldShader == null || !oldShader.name.Contains("Legacy Shaders/"))
            {
                SetupMaterialBlendMode(material);
                return;
            }

            SurfaceType surfaceType = SurfaceType.Opaque;
            BlendMode blendMode = BlendMode.Alpha;
            if (oldShader.name.Contains("/Transparent/Cutout/"))
            {
                surfaceType = SurfaceType.Opaque;
                material.SetFloat("_AlphaClip", 1);
            }
            else if (oldShader.name.Contains("/Transparent/"))
            {
                // NOTE: legacy shaders did not provide physically based transparency
                // therefore Fade mode
                surfaceType = SurfaceType.Transparent;
                blendMode = BlendMode.Alpha;
            }
            material.SetFloat("_Surface", (float)surfaceType);
            material.SetFloat("_Blend", (float)blendMode);

            if (oldShader.name.Equals("Standard (Specular setup)"))
            {
                material.SetFloat("_WorkflowMode", (float)LitGUI.WorkflowMode.Specular);
                Texture texture = material.GetTexture("_SpecGlossMap");
                if (texture != null)
                    material.SetTexture("_MetallicSpecGlossMap", texture);
            }
            else
            {
                material.SetFloat("_WorkflowMode", (float)LitGUI.WorkflowMode.Metallic);
                Texture texture = material.GetTexture("_MetallicGlossMap");
                if (texture != null)
                    material.SetTexture("_MetallicSpecGlossMap", texture);
            }

            MaterialChanged(material);
        }
    }
}


Теперь посмотрим внутренности шейдера.

Первое на что обращаешь внимание — это то, что в шейдере всего пять пассов. Немного остановимся на них:

  1. ForwardLit: Forward пасс, тут мы считаем свет, PBR и туман.
  2. ShadowCaster: тут идёт рассчёт теней.
  3. DepthOnly: глубина (Z-Buffer).
  4. Meta: запечённый свет (лайтмапы).
  5. Lightweight2D: спрайты и UI.

Далее, Unity имеет несколько различных шейдерных компиляторов и кросс-компиляторов. Для SRP требуется HLSLcc. На платформах, использующих графический API OpenGL ES (например, Android), HLSLcc по умолчанию не используется.

Поэтому мы принудительно подключаем его:

#pragma prefer_hlslcc gles

А вот DirectX 9 не поддерживается, поэтому принудительно отключаем его:

#pragma exclude_renderers d3d11_9x

В плане написания самого кода — ничего не изменилось за исключением того, что теперь мы пишем не на CG, а на чистом HLSL, а следовательно теперь тело шейдерной программы будет выглядеть следующем образом:

HLSLPROGRAM
...
ENDHLSL

Также нам по-прежнему доступны привычные глобальные переменные такие как _Time, _ScreenParams или _WorldSpaceCameraPos. Однако многие стандартные функции, которые были описнаны в UnityCG.cginc нам более недоступны. В большинстве случаев это касается различных матричных трансорфмаций, например, аналогом UnityObjectToClipPos(POSITION) является TransformWorldToHClip(POSITION), ну или для тумана — вместо UNITY_APPLY_FOG(fogCoord, color) теперь мы испоьзуем MixFog(color, fogCoord) .

Собственно, пройдя по всё тому же магическому пути и найдя файл Core.hlsl:
%localappdata%\Unity\cache\packages\packages.unity.com\com.unity.render-pipelines.lightweight@(версия LWRP)\ShaderLibrary\Core.hlsl

Мы можем обнаружить полный список доступных функций.

Стоит также упомянуть константные буферы (CBUFFER) и UnityPerMaterial. Константные буферы используются для хранения данных, которые редко изменяются на GPU, соотвественно их можно использовать для хранения переменных шейдера. Для этого достаточно вызывать макросы
CBUFFER_START и CBUFFER_END:

CBUFFER_START(UnityPerMaterial)
      float4 _BaseMap_ST;
      half4 _BaseColor;
      half4 _SpecColor;
      half4 _EmissionColor;
      half _Cutoff;
      half _Smoothness;
      half _Metallic;
      half _BumpScale;
      half _OcclusionStrength;
CBUFFER_END

Объявление глобальных переменных или различных параметрически-задаваемых (из кода или анимации, например) происходит по-старинке внутри тела шейдерной программы.

LWRP использует два типа константных буферов — UnityPerObject и UnityPerMaterial. Эти буфера биндятся один раз, чтобы их можно было использовать во время отрисовки. Грубо говоря, это означает, что во время отрисовки константные буферы не будут ребиндится или не будет вызываться setpass для материалов. Это выгодно, когда несколько шейдеров совместно используют один и тот же константный буфер, так как для этого LWRP может пакетировать различные материалы.

Собственно, если внимательно изучить структуру шейдера — можно обнаружить, что большая часть стандартных данных как раз и повсеместно использует константные буфера.

Более подробно, обо всех различиях, но на английском, можно почитать вот тут.

Кстати, если внимательно присмотреться к SurfaceData:
SurfaceData surfaceData;

То можно обнаружить, что это-то и есть заветный PBR Master из ShaderGraph.

Пример



Итак, теперь у нас полностью развязаны руки, а это значит что пришло время
устроить вакханалию! Добавим-ка для примера Vertex Displacement и Dissolve Effect, а все остальное пустим в пляс.

LVRP_TextShaders_2020-01-20_21-08-42.gif

Очень удобно, что все пассы у нас перед глазами и мы можем редактировать всё комплексно.

Опишем пропертисы:

Properties
{
       ...
       _DissolveMap("Dissolve Map", 2D) = "white" {}
       _DissolveFactor("Dissolve Factor", Range(0, 1)) = 0.0
       _DissolveWidth("Dissolve Width", Range(0, 1)) = 0.0
       [HDR]_DissolveColor("Color", Color) = (1,1,0)
}

Которые несомненно отобразятся в нашей собственной и горячо любимой вкладочке в инспекторе:
2020-01-20_22-55-14.png

Добавим переменные:

CBUFFER_START(UnityPerMaterial)
     ...
     float4 _DissolveMap_ST; //Тайлинг и оффсет текстуры _DissolveMap
CBUFFER_END

TEXTURE2D(_DissolveMap);        SAMPLER(sampler_DissolveMap);             
float _DissolveFactor;
float _DissolveWidth;
float4 _DissolveColor;

Сначала отправим геометрию в пьяный угар:

Varyings LitPassVertex(Attributes input)
{
    ...
    input.positionOS.xyz += normalize(input.positionOS.xyz) * sin(input.positionOS.x) * sin(_Time.x * 100);
    ...
}

Затем тень:
Varyings ShadowPassVertex(Attributes input)
{
       ...
       input.positionOS.xyz += normalize(input.positionOS.xyz) * sin(input.positionOS.x) * sin(_Time.x * 100);
       ...         
}

Глубина:

Varyings DepthOnlyVertex(Attributes input)
{
       ...
       input.position.xyz += normalize(input.position.xyz) * sin(input.position.x) * sin(_Time.x * 100);
       ...         
}

Ну а теперь время Dissolve и движения:

half4 LitPassFragment(Varyings input) : SV_Target
{
      ...
      //отправляем UV на прогулку 
      input.uv += _Time.x * 10;
      //тут сэмплируем тексутру шума для Dissolve эффекта
      float4 mask = SAMPLE_TEXTURE2D(_DissolveMap, sampler_DissolveMap, input.uv);
      if (mask.r > _DissolveFactor)
          discard;
      //обводка диссолва
      bool outline = mask.r > _DissolveFactor - _DissolveWidth;
      //выведем результат в эмишен 
      surfaceData.emission *= outline * _DissolveColor;
      //Опа, а вот тут PBR-чик считается 
      half4 color = LightweightFragmentPBR(inputData, surfaceData.albedo, surfaceData.metallic, surfaceData.specular, surfaceData.smoothness, surfaceData.occlusion, surfaceData.emission, surfaceData.alpha);
      //Ну и добавим обводку в результат
      color *= lerp(1, _DissolveColor, outline);
      ...
}

Также можно завезти Dissolve и в тень, тогда лёгким движением руки у нас появятся правильная тень, коей добиться в Shader Graph достаточно сложно, а тут — всего лишь пара строчек кода.


half4 ShadowPassFragment(Varyings input) : SV_TARGET
{
      ...
      float4 mask = SAMPLE_TEXTURE2D(_DissolveMap, sampler_DissolveMap, input.uv);
      if (mask.r > _DissolveFactor)
          discard;
      ...
}

Ну и финальный код шейдера:

SimpleDissolve

Shader "TheProxor/LWRP/Dissolve + Vertex Offset"
{
            Properties
            {
                // Specular vs Metallic workflow
                [HideInInspector] _WorkflowMode("WorkflowMode", Float) = 1.0

                [MainColor][HideInInspector] _BaseColor("Color", Color) = (0.5,0.5,0.5,1)
                [MainTexture][HideInInspector] _BaseMap("Albedo", 2D) = "white" {}

                [HideInInspector]_Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5

                [HideInInspector]_Smoothness("Smoothness", Range(0.0, 1.0)) = 0.5
                [HideInInspector]_GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0
                [HideInInspector]_SmoothnessTextureChannel("Smoothness texture channel", Float) = 0

                [Gamma][HideInInspector] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
                [HideInInspector]_MetallicGlossMap("Metallic", 2D) = "white" {}

                [HideInInspector]_SpecColor("Specular", Color) = (0.2, 0.2, 0.2)
                [HideInInspector]_SpecGlossMap("Specular", 2D) = "white" {}

                [HideInInspector][ToggleOff] _SpecularHighlights("Specular Highlights", Float) = 1.0
                [HideInInspector][ToggleOff] _EnvironmentReflections("Environment Reflections", Float) = 1.0

                [HideInInspector]_BumpScale("Scale", Float) = 1.0
                [HideInInspector]_BumpMap("Normal Map", 2D) = "bump" {}

                [HideInInspector]_OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
                [HideInInspector]_OcclusionMap("Occlusion", 2D) = "white" {}

                [HDR][HideInInspector]_EmissionColor("Color", Color) = (0,0,0)
                [HideInInspector]_EmissionMap("Emission", 2D) = "white" {}

                // Blending state
                [HideInInspector] _Surface("__surface", Float) = 0.0
                [HideInInspector] _Blend("__blend", Float) = 0.0
                [HideInInspector] _AlphaClip("__clip", Float) = 0.0
                [HideInInspector] _SrcBlend("__src", Float) = 1.0
                [HideInInspector] _DstBlend("__dst", Float) = 0.0
                [HideInInspector] _ZWrite("__zw", Float) = 1.0
                [HideInInspector] _Cull("__cull", Float) = 2.0

                [HideInInspector]_ReceiveShadows("Receive Shadows", Float) = 1.0
                // Editmode props
                [HideInInspector] _QueueOffset("Queue offset", Float) = 0.0

                // ObsoleteProperties
                [HideInInspector] _MainTex("BaseMap", 2D) = "white" {}
                [HideInInspector] _Color("Base Color", Color) = (0.5, 0.5, 0.5, 1)
                [HideInInspector] _GlossMapScale("Smoothness", Float) = 0.0
                [HideInInspector] _Glossiness("Smoothness", Float) = 0.0
                [HideInInspector] _GlossyReflections("EnvironmentReflections", Float) = 0.0

                

                //
                _DissolveMap("Dissolve Map", 2D) = "white" {}
                _DissolveFactor("Dissolve Factor", Range(0, 1)) = 0.0
                _DissolveWidth("Dissolve Width", Range(0, 1)) = 0.0
                [HDR]_DissolveColor("Color", Color) = (1,1,0)
            }

            SubShader
            {
                // Lightweight Pipeline tag is required. If Lightweight render pipeline is not set in the graphics settings
                // this Subshader will fail. One can add a subshader below or fallback to Standard built-in to make this
                // material work with both Lightweight Render Pipeline and Builtin Unity Pipeline
                Tags{"RenderType" = "Opaque" "RenderPipeline" = "LightweightPipeline" "IgnoreProjector" = "True"}
                LOD 300

                // ------------------------------------------------------------------
                //  Forward pass. Shades all light in a single pass. GI + emission + Fog
                Pass
                {
                // Lightmode matches the ShaderPassName set in LightweightRenderPipeline.cs. SRPDefaultUnlit and passes with
                // no LightMode tag are also rendered by Lightweight Render Pipeline
                Name "ForwardLit"
                Tags{"LightMode" = "LightweightForward"}

                Blend[_SrcBlend][_DstBlend]
                ZWrite[_ZWrite]
                Cull[_Cull]

                HLSLPROGRAM
                // Required to compile gles 2.0 with standard SRP library
                // All shaders must be compiled with HLSLcc and currently only gles is not using HLSLcc by default
                #pragma prefer_hlslcc gles
                #pragma exclude_renderers d3d11_9x
                #pragma target 2.0

                // -------------------------------------
                // Material Keywords
                #pragma shader_feature _NORMALMAP
                #pragma shader_feature _ALPHATEST_ON
                #pragma shader_feature _ALPHAPREMULTIPLY_ON
                #pragma shader_feature _EMISSION
                #pragma shader_feature _METALLICSPECGLOSSMAP
                #pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
                #pragma shader_feature _OCCLUSIONMAP

                #pragma shader_feature _SPECULARHIGHLIGHTS_OFF
                #pragma shader_feature _ENVIRONMENTREFLECTIONS_OFF
                #pragma shader_feature _SPECULAR_SETUP
                #pragma shader_feature _RECEIVE_SHADOWS_OFF

                // -------------------------------------
                // Lightweight Pipeline keywords
                #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
                #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
                #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
                #pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
                #pragma multi_compile _ _SHADOWS_SOFT
                #pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE

                // -------------------------------------
                // Unity defined keywords
                #pragma multi_compile _ DIRLIGHTMAP_COMBINED
                #pragma multi_compile _ LIGHTMAP_ON
                #pragma multi_compile_fog

                //--------------------------------------
                // GPU Instancing
                #pragma multi_compile_instancing

                #pragma vertex LitPassVertex
                #pragma fragment LitPassFragment

                #include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/Lighting.hlsl"
                #include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/Core.hlsl"
                #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
                #include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/SurfaceInput.hlsl"

                #ifdef _SPECULAR_SETUP
                    #define SAMPLE_METALLICSPECULAR(uv) SAMPLE_TEXTURE2D(_SpecGlossMap, sampler_SpecGlossMap, uv)
                #else
                    #define SAMPLE_METALLICSPECULAR(uv) SAMPLE_TEXTURE2D(_MetallicGlossMap, sampler_MetallicGlossMap, uv)
                #endif



                struct Attributes
                {
                    float4 positionOS   : POSITION;
                    float3 normalOS     : NORMAL;
                    float4 tangentOS    : TANGENT;
                    float2 texcoord     : TEXCOORD0;
                    float2 lightmapUV   : TEXCOORD1;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                };

                struct Varyings
                {
                    float2 uv                       : TEXCOORD0;
                    DECLARE_LIGHTMAP_OR_SH(lightmapUV, vertexSH, 1);

                #ifdef _ADDITIONAL_LIGHTS
                    float3 positionWS               : TEXCOORD2;
                #endif

                #ifdef _NORMALMAP
                    half4 normalWS                  : TEXCOORD3;    // xyz: normal, w: viewDir.x
                    half4 tangentWS                 : TEXCOORD4;    // xyz: tangent, w: viewDir.y
                    half4 bitangentWS                : TEXCOORD5;    // xyz: bitangent, w: viewDir.z
                #else
                    half3 normalWS                  : TEXCOORD3;
                    half3 viewDirWS                 : TEXCOORD4;
                #endif

                    half4 fogFactorAndVertexLight   : TEXCOORD6; // x: fogFactor, yzw: vertex light

                #ifdef _MAIN_LIGHT_SHADOWS
                    float4 shadowCoord              : TEXCOORD7;
                #endif

                    float4 positionCS               : SV_POSITION;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                    UNITY_VERTEX_OUTPUT_STEREO


                    float3 uvDissolve			    : TEXCOORD8; 
                };


                CBUFFER_START(UnityPerMaterial)
                    float4 _BaseMap_ST;
                    half4 _BaseColor;
                    half4 _SpecColor;
                    half4 _EmissionColor;
                    half _Cutoff;
                    half _Smoothness;
                    half _Metallic;
                    half _BumpScale;
                    half _OcclusionStrength;
                    float4 _DissolveMap_ST; //Тайлинг и оффсет текстуры _DissolveMap

                CBUFFER_END

                TEXTURE2D(_OcclusionMap);       SAMPLER(sampler_OcclusionMap);
                TEXTURE2D(_MetallicGlossMap);   SAMPLER(sampler_MetallicGlossMap);
                TEXTURE2D(_SpecGlossMap);       SAMPLER(sampler_SpecGlossMap);
                TEXTURE2D(_DissolveMap);        SAMPLER(sampler_DissolveMap);
                
                float _DissolveFactor;
                float _DissolveWidth;
                float4 _DissolveColor;

                half4 SampleMetallicSpecGloss(float2 uv, half albedoAlpha)
                {
                    half4 specGloss;

                    #ifdef _METALLICSPECGLOSSMAP
                    specGloss = SAMPLE_METALLICSPECULAR(uv);
                    #ifdef _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
                    specGloss.a = albedoAlpha * _Smoothness;
                    #else
                    specGloss.a *= _Smoothness;
                    #endif
                    #else // _METALLICSPECGLOSSMAP
                    #if _SPECULAR_SETUP
                    specGloss.rgb = _SpecColor.rgb;
                    #else
                    specGloss.rgb = _Metallic.rrr;
                    #endif

                    #ifdef _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
                    specGloss.a = albedoAlpha * _Smoothness;
                    #else
                    specGloss.a = _Smoothness;
                    #endif
                    #endif

                    return specGloss;
                }

                half SampleOcclusion(float2 uv)
                {
                    #ifdef _OCCLUSIONMAP
                    // TODO: Controls things like these by exposing SHADER_QUALITY levels (low, medium, high)
                    #if defined(SHADER_API_GLES)
                    return SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, uv).g;
                    #else
                    half occ = SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, uv).g;
                    return LerpWhiteTo(occ, _OcclusionStrength);
                    #endif
                    #else
                    return 1.0;
                    #endif
                }

                inline void InitializeStandardLitSurfaceData(float2 uv, out SurfaceData outSurfaceData)
                {
                    half4 albedoAlpha = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uv);
                   // half4 albedoAlpha = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap));
                    outSurfaceData.alpha = Alpha(albedoAlpha.a, _BaseColor, _Cutoff);

                    half4 specGloss = SampleMetallicSpecGloss(uv, albedoAlpha.a);
                    outSurfaceData.albedo = albedoAlpha.rgb * _BaseColor.rgb;

                    #if _SPECULAR_SETUP
                    outSurfaceData.metallic = 1.0h;
                    outSurfaceData.specular = specGloss.rgb;
                    #else
                    outSurfaceData.metallic = specGloss.r;
                    outSurfaceData.specular = half3(0.0h, 0.0h, 0.0h);
                    #endif

                    outSurfaceData.smoothness = specGloss.a;
                    outSurfaceData.normalTS = SampleNormal(uv, TEXTURE2D_ARGS(_BumpMap, sampler_BumpMap), _BumpScale);
                    outSurfaceData.occlusion = SampleOcclusion(uv);
                    outSurfaceData.emission = SampleEmission(uv, _EmissionColor.rgb, TEXTURE2D_ARGS(_EmissionMap, sampler_EmissionMap));
                   // outSurfaceData.emission = _EmissionColor.rgb * SAMPLE_TEXTURE2D(_EmissionMap, sampler_EmissionMap, uv);
                }

                void InitializeInputData(Varyings input, half3 normalTS, out InputData inputData)
                {
                    inputData = (InputData)0;

                #ifdef _ADDITIONAL_LIGHTS
                    inputData.positionWS = input.positionWS;
                #endif

                #ifdef _NORMALMAP
                    half3 viewDirWS = half3(input.normalWS.w, input.tangentWS.w, input.bitangentWS.w);
                    inputData.normalWS = TransformTangentToWorld(normalTS,
                        half3x3(input.tangentWS.xyz, input.bitangentWS.xyz, input.normalWS.xyz));
                #else
                    half3 viewDirWS = input.viewDirWS;
                    inputData.normalWS = input.normalWS;
                #endif

                    inputData.normalWS = NormalizeNormalPerPixel(inputData.normalWS);
                    viewDirWS = SafeNormalize(viewDirWS);

                    inputData.viewDirectionWS = viewDirWS;
                #if defined(_MAIN_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF)
                    inputData.shadowCoord = input.shadowCoord;
                #else
                    inputData.shadowCoord = float4(0, 0, 0, 0);
                #endif
                    inputData.fogCoord = input.fogFactorAndVertexLight.x;
                    inputData.vertexLighting = input.fogFactorAndVertexLight.yzw;
                    inputData.bakedGI = SAMPLE_GI(input.lightmapUV, input.vertexSH, inputData.normalWS);
                }

                ///////////////////////////////////////////////////////////////////////////////
                //                  Vertex and Fragment functions                            //
                ///////////////////////////////////////////////////////////////////////////////

                //Standard (Physically Based) shader
                Varyings LitPassVertex(Attributes input)
                {
                    Varyings output = (Varyings)0;


                    UNITY_SETUP_INSTANCE_ID(input);
                    UNITY_TRANSFER_INSTANCE_ID(input, output);
                    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);


                    input.positionOS.xyz += sin(input.positionOS.x) * sin(_Time.x * 100);

                    VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
                    VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS);
                    half3 viewDirWS = GetCameraPositionWS() - vertexInput.positionWS;
                    half3 vertexLight = VertexLighting(vertexInput.positionWS, normalInput.normalWS);
                    half fogFactor = ComputeFogFactor(vertexInput.positionCS.z);

                    output.uv = TRANSFORM_TEX(input.texcoord, _BaseMap);

                #ifdef _NORMALMAP
                    output.normalWS = half4(normalInput.normalWS, viewDirWS.x);
                    output.tangentWS = half4(normalInput.tangentWS, viewDirWS.y);
                    output.bitangentWS = half4(normalInput.bitangentWS, viewDirWS.z);
                #else
                    output.normalWS = NormalizeNormalPerVertex(normalInput.normalWS);
                    output.viewDirWS = viewDirWS;
                #endif

                    OUTPUT_LIGHTMAP_UV(input.lightmapUV, unity_LightmapST, output.lightmapUV);
                    OUTPUT_SH(output.normalWS.xyz, output.vertexSH);

                    output.fogFactorAndVertexLight = half4(fogFactor, vertexLight);

                #ifdef _ADDITIONAL_LIGHTS
                    output.positionWS = vertexInput.positionWS;
                #endif

                #if defined(_MAIN_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF)
                    output.shadowCoord = GetShadowCoord(vertexInput);
                #endif

                    output.positionCS = vertexInput.positionCS;

                    return output;
                }

                // Used in Standard (Physically Based) shader
                half4 LitPassFragment(Varyings input) : SV_Target
                {
                    input.uv += _Time.x * 10;
                    UNITY_SETUP_INSTANCE_ID(input);
                    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

                    SurfaceData surfaceData;
                    InitializeStandardLitSurfaceData(input.uv, surfaceData);

                    InputData inputData;
                    InitializeInputData(input, surfaceData.normalTS, inputData);
                    
                    float4 mask = SAMPLE_TEXTURE2D(_DissolveMap, sampler_DissolveMap, input.uv);

                    if (mask.r > _DissolveFactor)
                        discard;

                    bool outline = mask.r > _DissolveFactor - _DissolveWidth;
                    surfaceData.emission *= outline * _DissolveColor;
                    half4 color = LightweightFragmentPBR(inputData, surfaceData.albedo, surfaceData.metallic, surfaceData.specular, surfaceData.smoothness, surfaceData.occlusion, surfaceData.emission, surfaceData.alpha);
                    color *= lerp(1, _DissolveColor, outline);

                    color.rgb = MixFog(color.rgb, inputData.fogCoord);
                    
                    return color;
                }
                ENDHLSL
            }

            //тени
            Pass
            {
                Name "ShadowCaster"
                Tags{"LightMode" = "ShadowCaster"}

                ZWrite On
                ZTest LEqual
                Cull[_Cull]

                HLSLPROGRAM
                // Required to compile gles 2.0 with standard srp library
                #pragma prefer_hlslcc gles
                #pragma exclude_renderers d3d11_9x
                #pragma target 2.0

                // -------------------------------------
                // Material Keywords
                #pragma shader_feature _ALPHATEST_ON

                //--------------------------------------
                // GPU Instancing
                #pragma multi_compile_instancing
                #pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

                #pragma vertex ShadowPassVertex
                #pragma fragment ShadowPassFragment

                #include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitInput.hlsl"             
                #include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/Core.hlsl"
                #include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/Shadows.hlsl"

                float3 _LightDirection;

                struct Attributes
                {
                    float4 positionOS   : POSITION;
                    float3 normalOS     : NORMAL;
                    float2 texcoord     : TEXCOORD0;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                };

                struct Varyings
                {
                    float2 uv           : TEXCOORD0;
                    float4 positionCS   : SV_POSITION;
                };

                CBUFFER_START(UnityPerMaterial)
                    float4 _DissolveMap_ST; //Тайлинг и оффсет текстуры _DissolveMap
                    float _DissolveFactor;
                CBUFFER_END

                TEXTURE2D(_DissolveMap);        SAMPLER(sampler_DissolveMap);

                float4 GetShadowPositionHClip(Attributes input)
                {
                    float3 positionWS = TransformObjectToWorld(input.positionOS.xyz);
                    float3 normalWS = TransformObjectToWorldNormal(input.normalOS);

                    float4 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, _LightDirection));

                #if UNITY_REVERSED_Z
                    positionCS.z = min(positionCS.z, positionCS.w * UNITY_NEAR_CLIP_VALUE);
                #else
                    positionCS.z = max(positionCS.z, positionCS.w * UNITY_NEAR_CLIP_VALUE);
                #endif

                    return positionCS;
                }

                Varyings ShadowPassVertex(Attributes input)
                {
                    Varyings output;
                    input.positionOS.xyz += normalize(input.positionOS.xyz) * sin(input.positionOS.x) * sin(_Time.x * 100);
                    UNITY_SETUP_INSTANCE_ID(input);
                    output.uv = TRANSFORM_TEX(input.texcoord, _BaseMap);
                    output.positionCS = GetShadowPositionHClip(input);
                    return output;
                }

                half4 ShadowPassFragment(Varyings input) : SV_TARGET
                {
                     float4 mask = SAMPLE_TEXTURE2D(_DissolveMap, sampler_DissolveMap, input.uv);

                     if (mask.r > _DissolveFactor)
                         discard;

                    Alpha(SampleAlbedoAlpha(input.uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap)).a, _BaseColor, _Cutoff);
                    return 0;
                }

                ENDHLSL
            }

            Pass
            {
                Name "DepthOnly"
                Tags{"LightMode" = "DepthOnly"}

                ZWrite On
                ColorMask 0
                Cull[_Cull]

                HLSLPROGRAM
                // Required to compile gles 2.0 with standard srp library
                #pragma prefer_hlslcc gles
                #pragma exclude_renderers d3d11_9x
                #pragma target 2.0

                #pragma vertex DepthOnlyVertex
                #pragma fragment DepthOnlyFragment

                // -------------------------------------
                // Material Keywords
                #pragma shader_feature _ALPHATEST_ON
                #pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

                //--------------------------------------
                // GPU Instancing
                #pragma multi_compile_instancing

                #include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitInput.hlsl"
                #include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/Core.hlsl"

                #ifndef LIGHTWEIGHT_DEPTH_ONLY_PASS_INCLUDED
                #define LIGHTWEIGHT_DEPTH_ONLY_PASS_INCLUDED

                struct Attributes
                {
                    float4 position     : POSITION;
                    float2 texcoord     : TEXCOORD0;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                };

                struct Varyings
                {
                    float2 uv           : TEXCOORD0;
                    float4 positionCS   : SV_POSITION;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                    UNITY_VERTEX_OUTPUT_STEREO
                };

                Varyings DepthOnlyVertex(Attributes input)
                {
                    Varyings output = (Varyings)0;
                    UNITY_SETUP_INSTANCE_ID(input);
                    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

                    input.position.xyz += normalize(input.position.xyz) * sin(input.position.x) * sin(_Time.x * 100);

                    output.uv = TRANSFORM_TEX(input.texcoord, _BaseMap);
                    output.positionCS = TransformObjectToHClip(input.position.xyz);
                    return output;
                }

                half4 DepthOnlyFragment(Varyings input) : SV_TARGET
                {
                    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

                    Alpha(SampleAlbedoAlpha(input.uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap)).a, _BaseColor, _Cutoff);
                    return 0;
                }
                #endif

                ENDHLSL
            }

            //лайтмапы
            Pass
            {
                Name "Meta"
                Tags{"LightMode" = "Meta"}

                Cull Off

                HLSLPROGRAM
                // Required to compile gles 2.0 with standard srp library
                #pragma prefer_hlslcc gles
                #pragma exclude_renderers d3d11_9x

                #pragma vertex LightweightVertexMeta
                #pragma fragment LightweightFragmentMeta

                #pragma shader_feature _SPECULAR_SETUP
                #pragma shader_feature _EMISSION
                #pragma shader_feature _METALLICSPECGLOSSMAP
                #pragma shader_feature _ALPHATEST_ON
                #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

                #pragma shader_feature _SPECGLOSSMAP

                #include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitInput.hlsl"
                #include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/MetaInput.hlsl"

                #ifndef LIGHTWEIGHT_LIT_META_PASS_INCLUDED
                #define LIGHTWEIGHT_LIT_META_PASS_INCLUDED



                Varyings LightweightVertexMeta(Attributes input)
                {
                    Varyings output;
                    output.positionCS = MetaVertexPosition(input.positionOS, input.uv1, input.uv2,
                        unity_LightmapST, unity_DynamicLightmapST);
                    output.uv = TRANSFORM_TEX(input.uv0, _BaseMap);
                    return output;
                }

                half4 LightweightFragmentMeta(Varyings input) : SV_Target
                {
                    SurfaceData surfaceData;
                    InitializeStandardLitSurfaceData(input.uv, surfaceData);

                    BRDFData brdfData;
                    InitializeBRDFData(surfaceData.albedo, surfaceData.metallic, surfaceData.specular, surfaceData.smoothness, surfaceData.alpha, brdfData);

                    MetaInput metaInput;
                    metaInput.Albedo = brdfData.diffuse + brdfData.specular * brdfData.roughness * 0.5;
                    metaInput.SpecularColor = surfaceData.specular;
                    metaInput.Emission = surfaceData.emission;

                    return MetaFragment(metaInput);
                }
                #endif
                ENDHLSL
            }

            Pass
            {
                Name "Lightweight2D"
                Tags{ "LightMode" = "Lightweight2D" }

                Blend[_SrcBlend][_DstBlend]
                ZWrite[_ZWrite]
                Cull[_Cull]

                HLSLPROGRAM
                // Required to compile gles 2.0 with standard srp library
                #pragma prefer_hlslcc gles
                #pragma exclude_renderers d3d11_9x

                #pragma vertex vert
                #pragma fragment frag
                #pragma shader_feature _ALPHATEST_ON
                #pragma shader_feature _ALPHAPREMULTIPLY_ON

                #include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitInput.hlsl"
                #include "Packages/com.unity.render-pipelines.lightweight/Shaders/Utils/Lightweight2D.hlsl"
                ENDHLSL
            }


            }
                FallBack "Hidden/InternalErrorShader"
                CustomEditor "UnityEditor.Rendering.LWRP.ShaderGUI.LitShaderEditor"
}

Заключение


Что же, вот и пришло время подвести итоги. Как стало понятно, в LWRP можно и даже нужно писать шейдеры кодом, ибо это сильно развязывает руки, помогая без всяких костылей писать крутые штуки, к примеру, свою систему освещения. Конечно, это не идёт ни в какое сравнение с удобным и привычном Standard Surface Shader-ом, однако может быть когда-нибудь у меня дойдут руки написать такой же удобный аналог для LWRP и HDRP, но об этом как-нибудь в другой раз.

Полезные ссылки:

Репозиторий со всеми материалами статьи
Нашёл это во время подготовки материала, неофициальная документация
Документация по LWRP
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 1

    0
    Странно что так мало оценок, очень важная и полезная статья! Сейчас культура работы с LWRP только зачинается и никаких best practice еще не устоялось! Респект!
    На сколько сложно теперь подвязать к этому шейдеру Master Node и расширять с помощью Shader Graph?

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое