• Unity
  • Animated Mask/Unity (HELP)

Related Discussions
...

Hey, I need help here about Masks and Unity.

I have 2 different animations:

  • First one I have some trees moving with the wind.
  • Second animation is like a textures moving up.

My idea was to use the first animation as a mask of itself and to put the second animation only playing inside of the trees.

I was following this guide here: http://en.esotericsoftware.com/blog/Unity-SpriteMask-and-RectMask2D-support
The problem is this only work for static masks.

Is there a way to use an animation from spine as Mask itself?

I'm using SkeletonAnimation as component.
(creating mask on spine itself is not option)

Thanks 🙂

I'm afraid there is no easy way to have an animated skeleton act as a mask for another skeleton.

Typical solutions would require to render the mask object in front of the to-be-masked object with a shader that either:
a) writes to the stencil buffer (for which you would need a custom shader, Spine shaders do not offer this option), and a second shader at the masked object that performs a stencil-test accordingly.
b) or writes to the depth buffer but not to the color buffer (an "invisible" shader that only blocks other object's pixels from being rendered).

Could you please show a few images that describe what exactly you are trying to achieve? Perhaps this could be done in an easier way that does not require special shaders at all.

Harald wrote

Could you please show a few images that describe what exactly you are trying to achieve? Perhaps this could be done in an easier way that does not require special shaders at all.

So on the left I have a static frame of the ice particles and on the right I have one of the tree tops also static.

This is what I'm trying to achieve in detail is explained in this post:
https://answers.unity.com/questions/1848598/using-an-object-as-a-mask-for-another-object-using.html

I got it "half-working" but I can't understand what's wrong now.
In the same post it is included a gif with the behavior.

Thanks for your attention Harald

Unfortunately I currently cannot access Unity Answers (most likely due to this issue on Unity's side, my license is definitely not expired).
If you can still access it and it's not too much of an effort, could you perhaps create some screenshots or copy text of the relevant parts of this Answers page and post it here? Otherwise I'm afraid I will need some more explanation of what you are trying to achieve and what your current problem is.

Harald wrote

Unfortunately I currently cannot access Unity Answers (most likely due to this issue on Unity's side, my license is definitely not expired).
If you can still access it and it's not too much of an effort, could you perhaps create some screenshots or copy text of the relevant parts of this Answers page and post it here? Otherwise I'm afraid I will need some more explanation of what you are trying to achieve and what your current problem is.

Seems like the access is up again.

But here is a copy paste.

Hi! I'm doing a project and am using Spine for animations. I export those to Unity and use the Spine-runtime to control everything in Unity.

I wanted to use one animated object as a mask for another animated object. I know next to nothing about shaders. I only use the shadergraph here and there.

Spine objects come with their own shaders and I tried to modify a bit the code to achieve what I want, and the second object is indeed masked within the first object, but the problem is that it is masked in the whole image area, even where pixels are trasparent.

Here's a gif with the behaviour right now: https://i.gyazo.com/d7bd76bfd662da1e1afb0aaf1a7bb8d6.mp4

Here are the shader codes.

Mask

Shader "Spine/SkeletonMask" {
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
 }
 
 SubShader {
     Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" }
 
     Fog { Mode Off }
     Cull Off
     ZWrite Off
     Blend SrcAlpha OneMinusSrcAlpha
     Lighting Off
 
     Stencil {
         Ref[_StencilRef]
         Comp[_StencilComp]
         Pass Replace
     }
 
     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
     }
 }
 CustomEditor "SpineShaderWithOutlineGUI"

}

Masked:

// Spine/Skeleton PMA Screen
// - single color multiply tint
// - unlit
// - Premultiplied alpha Multiply blending
// - No depth, no backface culling, no fog.
// - ShadowCaster pass

Shader "Spine/Blend Modes/Skeleton PMA Screen Masked" {
Properties {
_Color ("Tint Color", Color) = (1,1,1,1)
[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" }
     LOD 100
 
     Fog { Mode Off }
     Cull Off
     ZWrite Off
     Blend One OneMinusSrcColor
     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"
         uniform sampler2D _MainTex;
         uniform float4 _Color;
 
         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);
         }
 
         ENDCG
     }
 
     Pass {
         Name "Caster"
         Tags { "LightMode"="ShadowCaster" }
         Offset 1, 1
 
         ZWrite On
         ZTest LEqual
 
         CGPROGRAM
         #pragma vertex vert
         #pragma fragment frag
         #pragma multi_compile_shadowcaster
         #pragma fragmentoption ARB_precision_hint_fastest
         #include "UnityCG.cginc"
         struct v2f {
             V2F_SHADOW_CASTER;
             float4 uvAndAlpha : TEXCOORD1;
         };
 
         uniform float4 _MainTex_ST;
 
         v2f vert (appdata_base v, float4 vertexColor : COLOR) {
             v2f o;
             TRANSFER_SHADOW_CASTER(o)
             o.uvAndAlpha.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
             o.uvAndAlpha.z = 0;
             o.uvAndAlpha.a = vertexColor.a;
             return o;
         }
 
         uniform sampler2D _MainTex;
         uniform fixed _Cutoff;
 
         float4 frag (v2f i) : SV_Target {
             fixed4 texcol = tex2D(_MainTex, i.uvAndAlpha.xy);
             clip(texcol.a * i.uvAndAlpha.a - _Cutoff);
             SHADOW_CASTER_FRAGMENT(i)
         }
 
         ENDCG
     }
 }
 CustomEditor "SpineShaderWithOutlineGUI"

}

It's indeed up again, thanks anyway for the copy&paste.

duarteabh wrote

but the problem is that it is masked in the whole image area, even where pixels are trasparent.

If this is your only problem, then it's simply because you never discard any fragments in your mask's fragment shader, they are always writing e.g. 1 to the stencil buffer. The stencil buffer acts in a binary way, it does not allow "semi-transparent" writes. What you need is to discard the fragment when its alpha value is below a threshold:

float4 frag (VertexOutput i) : SV_Target {
    float4 texColor = tex2D(_MainTex, i.uv);
    clip((texColor.a * i.vertexColor.a) - _Cutoff); // add this line

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

return (texColor * i.vertexColor);
}

This is identical to ALPHA_CLIP(texColor, i.vertexColor) and adding #define _ALPHA_CLIP before including Sprite\CGIncludes\ShaderShared.cginc.

Hey Harald, yap that was the problem. All good now 🙂 Thanks for the help.

Glad to hear, thanks for letting us know! 🙂