• Unity
  • Edit shader to work with another asset

  • Изменено
Related Discussions
...

I'm currently trying to implement a lighting asset that does occlusion and dynamic lighting, but I can't get it to occlude my skeletons as I need to apply a specific shader to the skeleton to render if within the lighting field of view, or occlude if not.
I have 0 shader skills, and I did my best to move the occlusion pass into the skeleton tint shader, but it does not achieve the desired effect. Instead, if occluded they render normally, but if within the light, all frames are being rendered, regardless of the skin applied. It's very odd, but probably makes complete sense to someone who understands shaders.

I'm hoping someone here can help combine the shaders? I believe that's all that's needed... I hope. I've attached both shaders.

Thanks!

Lighting Occlusion Shader

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "2D Dynamic Lights/Masks/Occluded"
{
    Properties
    {
        [PerRendererData] _MainTex ( "Sprite Texture", 2D ) = "white" {}
        _Color ( "Shadowed Tint", Color ) = ( 1, 1, 1, 1 )
       // [MaterialToggle] PixelSnap ( "Pixel snap", Float ) = 0
        _OccludedColor ( "Lit Tint", Color ) = ( 0, 0, 0, 0.5 )
    }


CGINCLUDE

struct appdata_t  
{ float4 vertex : POSITION; float4 color : COLOR; float2 texcoord : TEXCOORD0; }; struct v2f
{ float4 vertex : SV_POSITION; fixed4 color : COLOR; half2 texcoord : TEXCOORD0; }; fixed4 _Color;
sampler2D _MainTex; v2f vert( appdata_t IN )
{ v2f OUT; OUT.vertex = UnityObjectToClipPos( IN.vertex ); OUT.texcoord = IN.texcoord; OUT.color = IN.color * _Color; #ifdef PIXELSNAP_ON //OUT.vertex = UnityPixelSnap( OUT.vertex ); #endif return OUT; } ENDCG SubShader { Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "PreviewType" = "Plane" "CanUseSpriteAtlas" = "True" } Cull Off Lighting Off ZWrite Off Blend One OneMinusSrcAlpha Pass { Stencil { Ref 4 Comp NotEqual } CGPROGRAM #pragma vertex vert #pragma fragment frag // #pragma multi_compile _ PIXELSNAP_ON #include "UnityCG.cginc" fixed4 frag( v2f IN ) : SV_Target { fixed4 c = tex2D( _MainTex, IN.texcoord ) * IN.color; c.rgb *= c.a; return c; } ENDCG } Pass { Stencil { Ref 4 Comp Equal } CGPROGRAM #pragma vertex vert #pragma fragment frag //#pragma multi_compile _ PIXELSNAP_ON #include "UnityCG.cginc" fixed4 _OccludedColor; fixed4 frag( v2f IN ) : SV_Target { fixed4 c = tex2D( _MainTex, IN.texcoord ); return _OccludedColor * c * c.a; } ENDCG } } }

Spine Skeleton Tint

// Spine/Skeleton Tint
// - Two color tint
// - unlit
// - Premultiplied alpha blending (Optional straight alpha input)
// - No depth, no backface culling, no fog.

Shader "Spine/Skeleton Tint" {
   Properties {
      _Color ("Tint Color", Color) = (1,1,1,1)
      _Black ("Black Point", Color) = (0,0,0,0)
      [NoScaleOffset] _MainTex ("MainTex", 2D) = "black" {}
      [Toggle(_STRAIGHT_ALPHA_INPUT)] _StraightAlphaInput("Straight Alpha Texture", Int) = 0
      _Cutoff("Shadow alpha cutoff", Range(0,1)) = 0.1
      [HideInInspector] _StencilRef("Stencil Reference", Float) = 1.0
      [HideInInspector][Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp("Stencil Comparison", Float) = 8 // Set to Always as default

  // Outline properties are drawn via custom editor.
  [HideInInspector] _OutlineWidth("Outline Width", Range(0,8)) = 3.0
  [HideInInspector] _OutlineColor("Outline Color", Color) = (1,1,0,1)
  [HideInInspector] _OutlineReferenceTexWidth("Reference Texture Width", Int) = 1024
  [HideInInspector] _ThresholdEnd("Outline Threshold", Range(0,1)) = 0.25
  [HideInInspector] _OutlineSmoothness("Outline Smoothness", Range(0,1)) = 1.0
  [HideInInspector][MaterialToggle(_USE8NEIGHBOURHOOD_ON)] _Use8Neighbourhood("Sample 8 Neighbours", Float) = 1
  [HideInInspector] _OutlineMipLevel("Outline Mip Level", Range(0,3)) = 0
   }

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

  Fog { Mode Off }
  Cull Off
  ZWrite Off
  Blend One OneMinusSrcAlpha
  Lighting Off

  Stencil {
     Ref[_StencilRef]
     Comp[_StencilComp]
     Pass Keep
  }

  Pass {
     Name "Normal"

     CGPROGRAM
     #pragma shader_feature _ _STRAIGHT_ALPHA_INPUT
     #pragma vertex vert
     #pragma fragment frag
     #include "UnityCG.cginc"
     sampler2D _MainTex;
     float4 _Color;
     float4 _Black;

     struct VertexInput {
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
        float4 vertexColor : COLOR;
     };

     struct VertexOutput {
        float4 pos : SV_POSITION;
        float2 uv : TEXCOORD0;
        float4 vertexColor : COLOR;
     };

     VertexOutput vert (VertexInput v) {
        VertexOutput o;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.uv = v.uv;
        o.vertexColor = v.vertexColor * float4(_Color.rgb * _Color.a, _Color.a); // Combine a PMA version of _Color with vertexColor.
        return o;
     }

     float4 frag (VertexOutput i) : SV_Target {
        float4 texColor = tex2D(_MainTex, i.uv);

        #if defined(_STRAIGHT_ALPHA_INPUT)
        texColor.rgb *= texColor.a;
        #endif

        return (texColor * i.vertexColor) + float4(((1-texColor.rgb) * _Black.rgb * texColor.a[i]_Color.a[/i]i.vertexColor.a), 0);
     }
     ENDCG
  }

  Pass {
     Name "Caster"
     Tags { "LightMode"="ShadowCaster" }
     Offset 1, 1
     ZWrite On
     ZTest LEqual

     Fog { Mode Off }
     Cull Off
     Lighting Off

     CGPROGRAM
     #pragma vertex vert
     #pragma fragment frag
     #pragma multi_compile_shadowcaster
     #pragma fragmentoption ARB_precision_hint_fastest
     #include "UnityCG.cginc"
     sampler2D _MainTex;
     fixed _Cutoff;

     struct VertexOutput {
        V2F_SHADOW_CASTER;
        float4 uvAndAlpha : TEXCOORD1;
     };

     VertexOutput vert (appdata_base v, float4 vertexColor : COLOR) {
        VertexOutput o;
        o.uvAndAlpha = v.texcoord;
        o.uvAndAlpha.a = vertexColor.a;
        TRANSFER_SHADOW_CASTER(o)
        return o;
     }

     float4 frag (VertexOutput i) : SV_Target {
        fixed4 texcol = tex2D(_MainTex, i.uvAndAlpha.xy);
        clip(texcol.a * i.uvAndAlpha.a - _Cutoff);
        SHADOW_CASTER_FRAGMENT(i)
     }
     ENDCG
  }
   }
   CustomEditor "SpineShaderWithOutlineGUI"
}
nngafook написал

I have 0 shader skills, and I did my best to move the occlusion pass into the skeleton tint shader, but it does not achieve the desired effect. Instead, if occluded they render normally, but if within the light, all frames are being rendered, regardless of the skin applied.

Could you please post your current non-working version of your combined shader? Then we can help you out by pointing out any obvious mistakes.

Sure thing. Here it is. As I said, I just combined the two and made it compile. Best I could do with my lack of knowledge 😃

Shader "Custom/OccludedSkeleton" {
    Properties {
        _Cutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1
        [NoScaleOffset] _MainTex ("Main Texture", 2D) = "black" {}
        [Toggle(_STRAIGHT_ALPHA_INPUT)] _StraightAlphaInput("Straight Alpha Texture", Int) = 0
        [HideInInspector] _StencilRef("Stencil Reference", Float) = 1.0
        [HideInInspector][Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp("Stencil Comparison", Float) = 8 // Set to Always as default

    // Outline properties are drawn via custom editor.
    [HideInInspector] _OutlineWidth("Outline Width", Range(0,8)) = 3.0
    [HideInInspector] _OutlineColor("Outline Color", Color) = (1,1,0,1)
    [HideInInspector] _OutlineReferenceTexWidth("Reference Texture Width", Int) = 1024
    [HideInInspector] _ThresholdEnd("Outline Threshold", Range(0,1)) = 0.25
    [HideInInspector] _OutlineSmoothness("Outline Smoothness", Range(0,1)) = 1.0
    [HideInInspector][MaterialToggle(_USE8NEIGHBOURHOOD_ON)] _Use8Neighbourhood("Sample 8 Neighbours", Float) = 1
    [HideInInspector] _OutlineMipLevel("Outline Mip Level", Range(0,3)) = 0

    _Color ( "Shadowed Tint", Color ) = ( 1, 1, 1, 1 )
    _OccludedColor ( "Lit Tint", Color ) = ( 0, 0, 0, 0.5 )
}

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

    struct v2f  
    {
        float4 vertex   : SV_POSITION;
        fixed4 color    : COLOR;
        half2 texcoord  : TEXCOORD0;
    };


    fixed4 _Color; 

    v2f vert( appdata_t IN )  
    {
        v2f OUT;
        OUT.vertex = UnityObjectToClipPos( IN.vertex );
        OUT.texcoord = IN.texcoord;
        OUT.color = IN.color * _Color;
        #ifdef PIXELSNAP_ON
        //OUT.vertex = UnityPixelSnap( OUT.vertex );
        #endif

        return OUT;
    }
ENDCG

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

    Fog { Mode Off }
    Cull Off
    ZWrite Off
    Blend One OneMinusSrcAlpha
    Lighting Off

    Stencil {
        Ref[_StencilRef]
        Comp[_StencilComp]
        Pass Keep
    }

    Pass
    {
        Stencil
        {
            Ref 4
            Comp NotEqual
        }


        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
        // #pragma multi_compile _ PIXELSNAP_ON
            #include "UnityCG.cginc"
            sampler2D _MainTex;

            fixed4 frag( v2f IN ) : SV_Target
            {
                fixed4 c = tex2D( _MainTex, IN.texcoord ) * IN.color;
                c.rgb *= c.a;
                return c;
            }
        ENDCG
    }

    Pass
    {
        Stencil
        {
            Ref 4
            Comp Equal
        }

        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            //#pragma multi_compile _ PIXELSNAP_ON
            #include "UnityCG.cginc"
            sampler2D _MainTex;

            fixed4 _OccludedColor;


            fixed4 frag( v2f IN ) : SV_Target
            {
                fixed4 c = tex2D( _MainTex, IN.texcoord );
                return _OccludedColor * c * c.a;
            }
        ENDCG
    }

    Pass {
        Name "Normal"

        CGPROGRAM
        #pragma shader_feature _ _STRAIGHT_ALPHA_INPUT
        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"
        sampler2D _MainTex;

        struct VertexInput {
            float4 vertex : POSITION;
            float2 uv : TEXCOORD0;
            float4 vertexColor : COLOR;
        };

        struct VertexOutput {
            float4 pos : SV_POSITION;
            float2 uv : TEXCOORD0;
            float4 vertexColor : COLOR;
        };

        VertexOutput vert (VertexInput v) {
            VertexOutput o;
            o.pos = UnityObjectToClipPos(v.vertex);
            o.uv = v.uv;
            o.vertexColor = v.vertexColor;
            return o;
        }

        float4 frag (VertexOutput i) : SV_Target {
            float4 texColor = tex2D(_MainTex, i.uv);

            #if defined(_STRAIGHT_ALPHA_INPUT)
            texColor.rgb *= texColor.a;
            #endif

            return (texColor * i.vertexColor);
        }
        ENDCG
    }

    Pass {
        Name "Caster"
        Tags { "LightMode"="ShadowCaster" }
        Offset 1, 1
        ZWrite On
        ZTest LEqual

        Fog { Mode Off }
        Cull Off
        Lighting Off

        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #pragma multi_compile_shadowcaster
        #pragma fragmentoption ARB_precision_hint_fastest
        #include "UnityCG.cginc"
        sampler2D _MainTex;
        fixed _Cutoff;

        struct VertexOutput {
            V2F_SHADOW_CASTER;
            float4 uvAndAlpha : TEXCOORD1;
        };

        VertexOutput vert (appdata_base v, float4 vertexColor : COLOR) {
            VertexOutput o;
            o.uvAndAlpha = v.texcoord;
            o.uvAndAlpha.a = vertexColor.a;
            TRANSFER_SHADOW_CASTER(o)
            return o;
        }

        float4 frag (VertexOutput i) : SV_Target {
            fixed4 texcol = tex2D(_MainTex, i.uvAndAlpha.xy);
            clip(texcol.a * i.uvAndAlpha.a - _Cutoff);
            SHADOW_CASTER_FRAGMENT(i)
        }
        ENDCG
    }
}
}

Harald написал
nngafook написал

I have 0 shader skills, and I did my best to move the occlusion pass into the skeleton tint shader, but it does not achieve the desired effect. Instead, if occluded they render normally, but if within the light, all frames are being rendered, regardless of the skin applied.

Could you please post your current non-working version of your combined shader? Then we can help you out by pointing out any obvious mistakes.

Any tips Harald?

You need to understand what the Lighting Occlusion Shader shader does to combine it with the Spine shader, not only concatenate both.

The Lighting Occlusion Shader relies on the Stencil buffer being setup beforehand with lit and occluded pixels, encoded by the stencil value 4 when occluded. The shader basically just needs to do the following:

if (stencil == occludedStencilValue) // occludedStencilValue == 4
   renderOccluded
else
  renderNormal

Unfortunately you cannot query the stencil value in a shader function, so you need two render passes with the corresponding stencil tests stencil == 4 and stencil != 4.
If you look at the shader code, it renders in two passes and uses a different stencil test to draw the first normal pass in the condition stencil not equal to 4:

 Pass
{
    Stencil
    {
        Ref 4
        Comp NotEqual
    }
    // code to draw the geometry normally follows below

and a second pass for drawing the same mesh in the occluded color:

Pass
{
    Stencil
    {
        Ref 4
        Comp Equal
    }
    // code to draw the geometry with occlusion color overlaid follows below

So what you need to do is:
1) Remove the existing Spine stencil code, as your asset works differently:

Stencil {
            Ref[_StencilRef]
            Comp[_StencilComp]
            Pass Keep
        }

2) You just need a duplicated "Normal" pass of the Spine shader:

Pass {
         Name "Normal"

one with stencil test for "lit" and one for "occluded", as described above:

Pass
{
    Name "Normal"
    Stencil
    {
        Ref 4
        Comp NotEqual
    }
    // add Spine shader code to draw the geometry normally here, copy from Spine shader
}

Pass
{
    Name "Normal Occluded"
    Stencil
    {
        Ref 4
        Comp Equal
    }
    // add Spine shader code to draw the geometry with occlusion color overlaid here
}
Harald написал

You need to understand what the Lighting Occlusion Shader shader does to combine it with the Spine shader, not only concatenate both.

The Lighting Occlusion Shader relies on the Stencil buffer being setup beforehand with lit and occluded pixels, encoded by the stencil value 4 when occluded. The shader basically just needs to do the following:

if (stencil == occludedStencilValue) // occludedStencilValue == 4
   renderOccluded
else
  renderNormal

Unfortunately you cannot query the stencil value in a shader function, so you need two render passes with the corresponding stencil tests stencil == 4 and stencil != 4.
If you look at the shader code, it renders in two passes and uses a different stencil test to draw the first normal pass in the condition stencil not equal to 4:

 Pass
{
    Stencil
    {
        Ref 4
        Comp NotEqual
    }
    // code to draw the geometry normally follows below

and a second pass for drawing the same mesh in the occluded color:

Pass
{
    Stencil
    {
        Ref 4
        Comp Equal
    }
    // code to draw the geometry with occlusion color overlaid follows below

So what you need to do is:
1) Remove the existing Spine stencil code, as your asset works differently:

Stencil {
            Ref[_StencilRef]
            Comp[_StencilComp]
            Pass Keep
        }

2) You just need a duplicated "Normal" pass of the Spine shader:

Pass {
         Name "Normal"

one with stencil test for "lit" and one for "occluded", as described above:

Pass
{
    Name "Normal"
    Stencil
    {
        Ref 4
        Comp NotEqual
    }
    // add Spine shader code to draw the geometry normally here, copy from Spine shader
}

Pass
{
    Name "Normal Occluded"
    Stencil
    {
        Ref 4
        Comp Equal
    }
    // add Spine shader code to draw the geometry with occlusion color overlaid here
}

Harald... you are a god. EXCELLENT explanation and guidance. I floundered around a bit and eventually got it working. One thing I missed was adding a second _Color as it was being applied when rendering normally AND occluded.

Thanks so much again. If I knew you in person, and there wasn't a major epidemic going on, I'd buy you a beer. 🙂

Cheers!

Thanks very much for your kind words, very glad that it helped! 8) :beer: