ct0593

I want to edit the default shader of spine (Spine-SkeletonGraphic.shader) to do the effect like the picture show.
微信截图_20220704154004.png

Is anyone can help me to edit the shader to show this effect :'(
You do not have the required permissions to view the files attached to this post.
ct0593
  • Posts: 6

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:
Help plz! How do I make my character translucent in Unity?
User avatar
Harald

Harri
  • Posts: 4104

ct0593

Harald wrote: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
hello~Harald, If I can use a constant black color to fadeout, how can I write this code, I am a shader beginner :grinteeth:
ct0593
  • Posts: 6

Harald

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]
User avatar
Harald

Harri
  • Posts: 4104

Nate

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.
User avatar
Nate

Nate
  • Posts: 11943

ct0593

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

QQ录屏20220705104419 (1).gif


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);
}
QQ录屏20220705110555_1.gif

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?
You do not have the required permissions to view the files attached to this post.
ct0593
  • Posts: 6

Harald

Glad to see that you've got it working almost perfectly already!
ct0593 wrote: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]
User avatar
Harald

Harri
  • Posts: 4104

ct0593

hello~Harald, 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
QQ录屏20220705104419 (1).gif


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?
You do not have the required permissions to view the files attached to this post.
ct0593
  • Posts: 6

Harald

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);
User avatar
Harald

Harri
  • Posts: 4104

ct0593

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.
微信图片_20220706100931.jpg

do you know what maybe caused this problem?
You do not have the required permissions to view the files attached to this post.
ct0593
  • Posts: 6

Harald

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
User avatar
Harald

Harri
  • Posts: 4104

ct0593

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
ct0593
  • Posts: 6

Harald

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

Harri
  • Posts: 4104


Return to Unity