简体   繁体   English

Unity3D - 用于精灵剪辑的着色器

[英]Unity3D - Shader for sprite clipping

I am trying to create a shader that can be used to clip 2D sprites in a game, I found this shader in another question我正在尝试创建一个可用于在游戏中剪辑 2D 精灵的着色器,我在另一个问题中找到了这个着色器

Shader "Sprites/ClipArea"
{
Properties
{
    _MainTex ("Base (RGB), Alpha (A)", 2D) = "white" {}
    _Length ("Length", Range(0.0, 1.0)) = 1.0
    _Width ("Width", Range(0.0, 1.0)) = 0.5
 }

SubShader
{
    LOD 200

    Tags
    {
        "Queue" = "Transparent"
        "IgnoreProjector" = "True"
        "RenderType" = "Transparent"
    }

    Pass
    {
        Cull Off
        Lighting Off
        ZWrite Off
        Offset -1, -1
        Fog { Mode Off }
        ColorMask RGB
        Blend SrcAlpha OneMinusSrcAlpha

        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag

        #include "UnityCG.cginc"

        sampler2D _MainTex;
        float4 _MainTex_ST;
        float _Length;
        float _Width;

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

        struct v2f
        {
            float4 vertex : POSITION;
            float2 texcoord : TEXCOORD0;
        };

        v2f vert (appdata_t v)
        {
            v2f o;
            o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
            o.texcoord = v.texcoord;
            return o;
        }

        half4 frag (v2f IN) : COLOR
        {
            if ((IN.texcoord.x<0) || (IN.texcoord.x>_Width) || (IN.texcoord.y<0) || (IN.texcoord.y>_Length))

            {
                half4 colorTransparent = half4(0,0,0,0) ;
                return  colorTransparent ;
            }
            return tex2D(_MainTex, IN.texcoord);
        }
        ENDCG
    }
}
}

which works perfectly on single sprites, but I am using sprite sheets, divided by Unity Sprite editor.它适用于单个精灵,但我使用的是由 Unity Sprite 编辑器划分的精灵表。

The _Width variable in the shader is covering the whole sprite sheet, not the sprite I am working on.着色器中的 _Width 变量覆盖了整个精灵表,而不是我正在处理的精灵。 I searched for a way to get the current sprite rect inside the shader but couldnt find anything.我搜索了一种在着色器中获取当前精灵矩形的方法,但找不到任何东西。

Well, after pulling my hair for three days, I managed to find a workaround.好吧,在拉我的头发三天后,我设法找到了解决方法。 After searching more about how shaders work and there role in the pipeline, I realized that the sprite rect info will probably be not available in the shader for one simple reason, the functionality of almost all shaders (except mine) does not require this info, because the job of the shader is to take a vertex, modify its position (if needed) through the vertex function and then decide its pixel colour through the fragment function, it does not care about the whole sprite, it only needs to lookup the pixel colour for a certain vertex from the texture using its texture coordinates.在搜索了有关着色器如何工作以及在管道中的作用的更多信息后,我意识到由于一个简单的原因,着色器中的精灵矩形信息可能不可用,几乎所有着色器(除了我的)的功能都不需要此信息,因为着色器的工作是取一个顶点,通过顶点函数修改它的位置(如果需要),然后通过片段函数决定它的像素颜色,它不关心整个精灵,它只需要查找像素使用纹理坐标为纹理中的某个顶点设置颜色。 I am sure this is trivial info for people working in shaders, but it took me time to realize it ( This was my first ever shader).我确信这对于在着色器中工作的人来说是微不足道的信息,但我花了一些时间才意识到这一点(这是我的第一个着色器)。 So as a workaround I had to use the shader properties to pass the MinX and MaxX of the current sprite the shader is working on in the sprite sheet, so the shader now looks like this:因此,作为一种解决方法,我必须使用着色器属性来传递着色器在精灵表中正在处理的当前精灵的 MinX 和 MaxX,因此着色器现在看起来像这样:

Shader "Sprites/ClipArea"
{
    Properties
    {
        _MainTex ("Base (RGB), Alpha (A)", 2D) = "white" {}
        _Fill ("Fill", Range(0.0, 1.0)) = 1.0
        _MinX ("MinX", Float) = 0
        _MaxX ("MaxX", Float) = 1
     }

    SubShader
    {
        LOD 200

        Tags
        {
            "Queue" = "Transparent"
            "IgnoreProjector" = "True"
            "RenderType" = "Transparent"
        }

        Pass
        {
            Cull Off 
            Lighting Off
            ZWrite Off
            Offset -1, -1
            Fog { Mode Off }
            ColorMask RGB
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _MinX;
            float _MaxX;
            float _Fill;

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

            struct v2f
            {
                float4 vertex : POSITION;
                float2 texcoord : TEXCOORD0;
            };

            v2f vert (appdata_t v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.texcoord = v.texcoord;
                return o;
            }

            half4 frag (v2f IN) : COLOR 
            {
            if ((IN.texcoord.x<_MinX)|| (IN.texcoord.x>(_MinX+_Fill*(_MaxX-_MinX))))
                {
                    half4 colorTransparent = half4(0,0,0,0) ;
                    return  colorTransparent ;
                }
                return tex2D(_MainTex, IN.texcoord);
            }
            ENDCG
        }
    }
} 

To use this shader, you need to create a material that uses it, then use that material in the SpriteRenderer, you can change the Fill amount, MinX , and MaxX from the inspector, or call the spriteRenderer.material.setFloat(property, value) from code.要使用此着色器,您需要创建一个使用它的材质,然后在 SpriteRenderer 中使用该材质,您可以从检查器中更改填充量、MinX 和 MaxX,或调用 spriteRenderer.material.setFloat(property, value ) 来自代码。

I then faced another issue with animated sprites, I had to keep updating the MinX and MaxX on every frame, but when I did it in the Update function the animation started flickering, and thats because the update was being called after the sprite is rendered, so I had to use the Main Camera OnPreRender event to update the material properties.然后我遇到了动画精灵的另一个问题,我不得不在每一帧上不断更新 MinX 和 MaxX,但是当我在 Update 函数中这样做时,动画开始闪烁,那是因为在渲染精灵之后调用了更新,所以我不得不使用 Main Camera OnPreRender 事件来更新材质属性。

Maybe there is a better way to achieve all this but this is the best I could come up with, and I hope this will benefit someone trying to achieve the same effect.也许有更好的方法来实现这一切,但这是我能想到的最好的方法,我希望这对尝试实现相同效果的人有益。

I have improve Khaled-AbuA-lkheir shader adding base sprite color:我改进了 Khaled-AbuA-lkheir 着色器,添加了基本的精灵颜色:

Shader "Sprites/ClipArea"
{
    Properties
    {
        _MainTex ("Base (RGB), Alpha (A)", 2D) = "white" {}
        _Fill ("Fill", Range(0.0, 1.0)) = 1.0
        _MinX ("MinX", Float) = 0
        _MaxX ("MaxX", Float) = 1
     }

    SubShader
    {
        LOD 200

        Tags
        {
            "Queue" = "Transparent"
            "IgnoreProjector" = "True"
            "RenderType" = "Transparent"
        }

        Pass
        {
            Cull Off 
            Lighting Off
            ZWrite Off
            Offset -1, -1
            Fog { Mode Off }
            ColorMask RGB
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _MinX;
            float _MaxX;
            float _Fill;

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

            struct v2f
            {
                float4 vertex : POSITION;
                float2 texcoord : TEXCOORD0;
                fixed4 color : COLOR;
            };

            v2f vert (appdata_t v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.texcoord = v.texcoord;
                o.color = v.color;
                return o;
            }

            half4 frag (v2f IN) : COLOR 
            {
                if ((IN.texcoord.x<_MinX)|| (IN.texcoord.x>(_MinX+_Fill*(_MaxX-_MinX))))
                {
                    half4 colorTransparent = half4(0,0,0,0) ;
                    return  colorTransparent ;
                }
                else
                {
                    return tex2D(_MainTex, IN.texcoord)*IN.color;
                }
            }
            ENDCG
        }
    }
} 

I'm necroing this because I think it can be useful: I added the possibility to set _MinX greater than _MaxX.我正在解决这个问题,因为我认为它很有用:我添加了将 _MinX 设置为大于 _MaxX 的可能性。 That way, you have a "right to left" clipping, instead of "left to right".这样,您就有了“从右到左”的剪辑,而不是“从左到右”。

Shader "Sprites/ClipArea"
{
Properties
{
    _MainTex("Base (RGB), Alpha (A)", 2D) = "white" {}
    _Fill("Fill", Range(0.0, 1.0)) = 1.0
    _MinX("MinX", Float) = 0
    _MaxX("MaxX", Float) = 1
}

    SubShader
    {
        LOD 200

        Tags
        {
            "Queue" = "Transparent"
            "IgnoreProjector" = "True"
            "RenderType" = "Transparent"
        }

        Pass
        {
            Cull Off
            Lighting Off
            ZWrite Off
            Offset -1, -1
            Fog { Mode Off }
            ColorMask RGB
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _MinX;
            float _MaxX;
            float _Fill;

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

            struct v2f
            {
                float4 vertex : POSITION;
                float2 texcoord : TEXCOORD0;
                fixed4 color : COLOR;
            };

            v2f vert(appdata_t v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.texcoord = v.texcoord;
                o.color = v.color;
                return o;
            }

            half4 frag(v2f IN) : COLOR
            {
                if (_MinX < _MaxX && ((IN.texcoord.x < _MinX) || (IN.texcoord.x > (_MinX + _Fill * (_MaxX - _MinX)))) ||
                    _MinX > _MaxX && ((IN.texcoord.x > _MinX) || (IN.texcoord.x < (_MinX + _Fill * (_MaxX - _MinX)))))
                {
                    return half4(0, 0, 0, 0);
                }
                else
                {
                    return tex2D(_MainTex, IN.texcoord) * IN.color;
                }
            }
            ENDCG
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM