• Unity
  • help~plz~How to fade the edge of vertical.

I want to edit the default shader of spine (Spine-SkeletonGraphic.shader) to do the effect like the picture show.

Is anyone can help me to edit the shader to show this effect 😢

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

Can you use a constant black color as a fadeout target color? If so, then it can be done modifying the shader code.

If however you need transparency fadeout (to an alpha value of 0), then you will likely run into the problem of overlapping attachments, as described in this forum thread:
Help plz! How do I make my character translucent in Unity?

Harald написал

Can you use a constant black color as a fadeout target color? If so, then it can be done modifying the shader code.

If however you need transparency fadeout (to an alpha value of 0), then you will likely run into the problem of overlapping attachments, as described in this forum thread:
http://esotericsoftware.com/forum/Help-plz-How-do-I-make-my-character-translucent-in-Unity-11829

helloHarald, If I can use a constant black color to fadeout, how can I write this code, I am a shader beginner :grinteeth:

Basically you need to create your own copy of the Spine-SkeletonGraphic.shader file with a different name and the line Shader "Spine/SkeletonGraphic" changed to whater you would like to call it.

Then copy the content of the include file into the location of #include "CGIncludes/Spine-SkeletonGraphic-NormalPass.cginc" to make editing a bit easier.

Then in the function

fixed4 frag (VertexOutput IN) : SV_Target

you would add a statement before return color; which applies a certain amount of your gradient color. As a simple test-code try to replace the color completely with a black color if the Y position is below a certain threshold value. It's also easier if right from the start you add a parameter for it which is exposed in the Material properties in the Inspector. Adding such parameters is mostly self-explanatory when you read through the short shader file (you declare the Inspector parameters on top and declare the float variables a bit further down the line).

Now the question is if you want to apply the gradient in world-space, you could use IN.worldPosition.y. If you want it to be applied in local object space, then you need to pass the IN.vertex parameter from the vertex shader to the fragment shader before it has been transformed (similar to how OUT.texcoord = IN.texcoord; is passing the texcoords), you first declare a float4 localPos; attribute in the VertexOutput struct and then assign it in the vertex shader. [Edit: slightly corrected the text above]

If your background is always black like the picture, you could fake this by sliding an image that has a transparent to black gradient over your skeleton.

Thanks,Harald & Nate. but actually, I wanna the effect is alpha fade, except the problem of overlay parts,so I had inserted some code into the frag function like this

#ifdef _EDGE_SHADE 
   color.a *= smoothstep(_Slider, _Slider + 20, IN.vertex.y - 50);
   color.rgb *= color.a;
#endif

It works, but something weird, when I drag the spine, the fade edge changed too, just like the gif I recorded under

in order to solve this problem, I edited the code like this

#ifdef _EDGE_SHADE 
   color.a *= smoothstep(_Slider, _Slider + 20,  IN.vertex.y - _BottomY);
   color.rgb *= color.a;
#endif

and use a C# scripte to transfer the BottomY into the shader

private void Update()
  {
   Vector3 wPos = m_spineCamera.WorldToScreenPoint(gameObject.GetPosition());
    m_spineMat.SetFloat("_BottomY", wPos.y);
  }


It can solve the problem upstairs , but I think it maybe there is a more simple method to slove the problem without transfer the var in every frame time?

Glad to see that you've got it working almost perfectly already!

ct0593 написал

and use a C# scripte to transfer the BottomY into the shader

There is no need for that, you can just use the object-space vertex coords instead of the world-space coords after transformation. I've described this in the posting above, but I guess it was more confusing than helpful before you knew which problem it adresses. Hope it makes more sense now that you've encountered the problem:

Now the question is if you want to apply the gradient in world-space, you could use IN.worldPosition.y (which you did and received the gradient at a constant location in world-space). If you want it to be applied in local object space, then you need to pass the IN.vertex parameter from the vertex shader to the fragment shader (similar to how OUT.texcoord = IN.texcoord; is passing the texcoords), you first add a float4 localPos; attribute in the fragment shader's input struct VertexOutput and then assign it in the vertex shader like OUT.localPos = IN.vertex.

[Note: I slightly modified the text above compared to the original posting]

helloHarald, below is the code I edited from the default shader Spine-SkeletonGraphic.shader
the commented '############' is my additional code.

// This is a premultiply-alpha adaptation of the built-in Unity shader "UI/Default" in Unity 5.6.2 to allow Unity UI stencil masking.

Shader "Spine/SkeletonGraphic"
{
   Properties
   {
      [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
      [Toggle(_STRAIGHT_ALPHA_INPUT)] _StraightAlphaInput("Straight Alpha Texture", Int) = 0
      [Toggle(_CANVAS_GROUP_COMPATIBLE)] _CanvasGroupCompatible("CanvasGroup Compatible", Int) = 0
      _Color ("Tint", Color) = (1,1,1,1)

  [HideInInspector][Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp ("Stencil Comparison", Float) = 8
  [HideInInspector] _Stencil ("Stencil ID", Float) = 0
  [HideInInspector][Enum(UnityEngine.Rendering.StencilOp)] _StencilOp ("Stencil Operation", Float) = 0
  [HideInInspector] _StencilWriteMask ("Stencil Write Mask", Float) = 255
  [HideInInspector] _StencilReadMask ("Stencil Read Mask", Float) = 255

  [HideInInspector] _ColorMask ("Color Mask", Float) = 15

  [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0

  // 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"
         "CanUseSpriteAtlas"="True"
      }

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

  Cull Off
  Lighting Off
  ZWrite Off
  ZTest [unity_GUIZTestMode]
  Fog { Mode Off }
  Blend One OneMinusSrcAlpha
  ColorMask [_ColorMask]

  Pass
  {
     Name "Normal"

  CGPROGRAM
     #pragma shader_feature _ _STRAIGHT_ALPHA_INPUT
     #pragma shader_feature _ _CANVAS_GROUP_COMPATIBLE
     #pragma vertex vert
     #pragma fragment frag
     #pragma target 2.0

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

     #pragma multi_compile __ UNITY_UI_ALPHACLIP

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

     struct VertexOutput {
        float4 vertex   : SV_POSITION;
        fixed4 color    : COLOR;
        half2 texcoord  : TEXCOORD0;
        float4 worldPosition : TEXCOORD1;
        float4 localPos : TEXCOORD2; //##################1##################
        UNITY_VERTEX_OUTPUT_STEREO
     };

     fixed4 _Color;
     fixed4 _TextureSampleAdd;
     float4 _ClipRect;

     VertexOutput vert (VertexInput IN) {
        VertexOutput OUT;

        UNITY_SETUP_INSTANCE_ID(IN);
        UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);

        OUT.worldPosition = IN.vertex;
        OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
        OUT.texcoord = IN.texcoord;
        OUT.localPos = IN.vertex;//##################2##################

        #ifdef UNITY_HALF_TEXEL_OFFSET
        OUT.vertex.xy += (_ScreenParams.zw-1.0) * float2(-1,1);
        #endif

        OUT.color = IN.color * float4(_Color.rgb * _Color.a, _Color.a); // Combine a PMA version of _Color with vertexColor.
        return OUT;
     }

     sampler2D _MainTex;

     fixed4 frag (VertexOutput IN) : SV_Target
     {
        half4 texColor = tex2D(_MainTex, IN.texcoord);

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

        half4 color = (texColor + _TextureSampleAdd) * IN.color;
        #ifdef _CANVAS_GROUP_COMPATIBLE
        // CanvasGroup alpha sets vertex color alpha, but does not premultiply it to rgb components.
        color.rgb *= IN.color.a;
        #endif

        color *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);

        //##################3##################
        color.a *= smoothstep(10, 30, IN.localPos.y - 50);
        color.rgb *= color.a;
        
        #ifdef UNITY_UI_ALPHACLIP
        clip (color.a - 0.001);
        #endif

        return color;
     }
  ENDCG
  }
   }
   CustomEditor "SpineShaderWithOutlineGUI"
}

It works still like the pic like this

I have a question, the code "OUT.worldPosition = IN.vertex;" in the default shader, just like the same code "OUT.localPos = IN.vertex;" that you called me to add.

I feel like that this kind of default shader is the key of the problem, I have to use the SkeletonGraphic shader, because my spine would show in the ugui canvans.

I think the object-space vertex coords in SkeletonGraphic shader maybe out of work, is it right?

Oops, sorry, I just noticed that any UI shader receiving a mesh from a CanvasRenderer already receives pre-transformed vertex positions as input. I assume (but didn't test) that the input vertex positions are in canvas space (so "almost" world space) and that the matrices are setup accordingly so that a call to UnityObjectToClipPos() transforms from canvas space (over world space) to clip space, instead of like normal MeshRenderer shaders transform from local object space (over world space) to clip space.

So I'm afraid you will indeed need to pass the position to the shader, as you already described earlier with this code:

Vector3 wPos = m_spineCamera.WorldToScreenPoint(gameObject.GetPosition());
    m_spineMat.SetFloat("_BottomY", wPos.y);

Thanks Harald, I have optimized my code like this

// This is a premultiply-alpha adaptation of the built-in Unity shader "UI/Default" in Unity 5.6.2 to allow Unity UI stencil masking.

Shader "Spine/SkeletonGraphic"
{
   Properties
   {
      [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
      [Toggle(_STRAIGHT_ALPHA_INPUT)] _StraightAlphaInput("Straight Alpha Texture", Int) = 0
      [Toggle(_CANVAS_GROUP_COMPATIBLE)] _CanvasGroupCompatible("CanvasGroup Compatible", Int) = 0
      [Toggle(_EDGE_SHADE)] _EDGE_SHADE("Edge Shade", Int) = 0 //############
      _Color ("Tint", Color) = (1,1,1,1)
      
_Slider("Slider", Range(0,1500)) = 10 //############ _Soft("SoftRadus", Range(0,1)) = 0.05 //############ [HideInInspector][Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp ("Stencil Comparison", Float) = 8 [HideInInspector] _Stencil ("Stencil ID", Float) = 0 [HideInInspector][Enum(UnityEngine.Rendering.StencilOp)] _StencilOp ("Stencil Operation", Float) = 0 [HideInInspector] _StencilWriteMask ("Stencil Write Mask", Float) = 255 [HideInInspector] _StencilReadMask ("Stencil Read Mask", Float) = 255 [HideInInspector] _ColorMask ("Color Mask", Float) = 15 [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0 // 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" "CanUseSpriteAtlas"="True" } Stencil { Ref [_Stencil] Comp [_StencilComp] Pass [_StencilOp] ReadMask [_StencilReadMask] WriteMask [_StencilWriteMask] } Cull Off Lighting Off ZWrite Off ZTest [unity_GUIZTestMode] Fog { Mode Off } Blend One OneMinusSrcAlpha ColorMask [_ColorMask] Pass { Name "Normal" CGPROGRAM #pragma shader_feature _ _STRAIGHT_ALPHA_INPUT #pragma shader_feature _ _CANVAS_GROUP_COMPATIBLE #pragma shader_feature _ _EDGE_SHADE #pragma vertex vert #pragma fragment frag #pragma target 2.0 #include "UnityCG.cginc" #include "UnityUI.cginc" #pragma multi_compile __ UNITY_UI_ALPHACLIP struct VertexInput { float4 vertex : POSITION; float4 color : COLOR; float2 texcoord : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct VertexOutput { float4 vertex : SV_POSITION; fixed4 color : COLOR; half2 texcoord : TEXCOORD0; float4 worldPosition : TEXCOORD1; UNITY_VERTEX_OUTPUT_STEREO }; uniform float _BottomY; //############ fixed4 _Color; fixed4 _TextureSampleAdd; float4 _ClipRect; VertexOutput vert (VertexInput IN) { VertexOutput OUT; UNITY_SETUP_INSTANCE_ID(IN); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); OUT.worldPosition = IN.vertex; OUT.vertex = UnityObjectToClipPos(OUT.worldPosition); OUT.texcoord = IN.texcoord; #ifdef UNITY_HALF_TEXEL_OFFSET OUT.vertex.xy += (_ScreenParams.zw-1.0) * float2(-1,1); #endif OUT.color = IN.color * float4(_Color.rgb * _Color.a, _Color.a); // Combine a PMA version of _Color with vertexColor. return OUT; } sampler2D _MainTex; float _Slider; //############ float _Soft; //############ fixed4 frag (VertexOutput IN) : SV_Target { half4 texColor = tex2D(_MainTex, IN.texcoord); #if defined(_STRAIGHT_ALPHA_INPUT) && !defined(_EDGE_SHADE) //############ texColor.rgb *= texColor.a; #endif half4 color = (texColor + _TextureSampleAdd) * IN.color; #ifdef _CANVAS_GROUP_COMPATIBLE // CanvasGroup alpha sets vertex color alpha, but does not premultiply it to rgb components. color.rgb *= IN.color.a; #endif color *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect); #ifdef _EDGE_SHADE //############ color.a *= smoothstep(_Slider, _Slider + _ScreenParams.y * _Soft, IN.vertex.y - _BottomY); //############ color.rgb *= color.a; //############ #endif //############ #ifdef UNITY_UI_ALPHACLIP clip (color.a - 0.001); #endif return color; } ENDCG } } CustomEditor "SpineShaderWithOutlineGUI" }

The commented '############' is my additional code
I also transfer the BottomY into the shader from C# every frame
BUT! new problem appeared, It can works perfectly in PC and android device, but in IOS device, it shows like this pic.
It seems that the IN.vertex.y is upsidedown in IOS device.

do you know what maybe caused this problem?

It seems like you need to apply a potential -1 multiplier (or use 1-y respectively depending on which space you currently are) on the Y axis to cover all targets / graphics APIs.

You may want to check out _ProjectionParams.x (e.g. clipPos.y *= _ProjectionParams.x;) which seems to provide the multiplied for a flipped Y axis:
https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html
https://docs.unity3d.com/Manual/SL-PlatformDifferences.html

Finally, I use the code to fix it, Thanks a lot Harald~

#ifdef _EDGE_SHADE
    float transY = _ProjectionParams.x > 0 ? _ScreenParams.y - IN.vertex.y : IN.vertex.y;
    color.a *= smoothstep(_Slider, _Slider + _ScreenParams.y * _Soft, transY - _BottomY);
    color.rgb *= color.a;
#endif

Glad to hear you've figured it out, thanks for getting back to us. 8)