Unity 中的多层纹理着色器

Multi layer texture shader in Unity

我对着色器真的很陌生。我在不同的论坛上搜索了类似的问题,但 none 的论坛帮助我解决了我正在努力解决的问题。

基本上,我必须制作一个将 6 个纹理合并为一个的着色器。我有6层。每一层都是一个纹理,所有这些层都必须显示在彼此之上。每层纹理大小相同。

这是我现在的代码:

Shader "TileShaders/Chunk"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _SecondLayer ("Second Layer", 2D) = "white" {}
        _ThirdLayer ("Third Layer", 2D) = "white" {}
        _FourthLayer ("Fourth Layer", 2D) = "white" {}
        _FifthLayer ("Fifth Layer", 2D) = "white" {}
        _SixthLayer ("Sixth Layer", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
        [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
    }

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

        Cull Off
        Lighting Off
        ZWrite Off
        Blend One OneMinusSrcAlpha

        Pass
        {
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile _ PIXELSNAP_ON
            #include "UnityCG.cginc"

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

            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                float2 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;
            }

            sampler2D _MainTex;
            sampler2D _AlphaTex;
            float _AlphaSplitEnabled;

            fixed4 SampleSpriteTexture (float2 uv)
            {
                fixed4 color = tex2D (_MainTex, uv);

                #if UNITY_TEXTURE_ALPHASPLIT_ALLOWED
                    if (_AlphaSplitEnabled)
                        color.a = tex2D (_AlphaTex, uv).r;
                #endif //UNITY_TEXTURE_ALPHASPLIT_ALLOWED

                return color;
            }

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

它是 Sprites/Default 统一着色器,我只是添加了这几个变量,因为我知道我需要这些数据才能将其显示为一个纹理:

_SecondLayer ("Second Layer", 2D) = "white" {}
_ThirdLayer ("Third Layer", 2D) = "white" {}
_FourthLayer ("Fourth Layer", 2D) = "white" {}
_FifthLayer ("Fifth Layer", 2D) = "white" {}
_SixthLayer ("Sixth Layer", 2D) = "white" {}

我就是这么做的。我真的不知道如何开始。这是我第一次使用着色器

编辑

感谢您的回复! lerp() 帮了我很多,没有它,所有层都同时可见,看起来真的很奇怪。

现在一切看起来都很棒。但是有一个小问题。只要所有图层都包含纹理,它看起来就很棒。但如果只有一层分配了纹理而其余层没有分配,则整个结果纹理为白色。

代码现在是这样的:

        fixed4 c = tex2D(_MainTex, IN.texcoord);
        fixed4 c2 = tex2D(_SecondLayer, IN.texcoord);

        fixed4 returnTexture = c;

        returnTexture.rgba = lerp(c, c2, c2.a).rgba;

        return returnTexture;

为了避免这个问题,我正在考虑 if 语句来检查每层上的纹理是否存在,但我听说如果可能的话我应该随时避免它。有更好的处理方式吗?

编辑 2 由于我对 post 删除的最新回答,我不知道最终帮助我的答案是否仍然是 visiblue,但这里是来自:trojanfoe

的解决方案

请编辑您的原始问题,而不是创建一个答案来扩展您的问题。不要使用 if 语句;相反,您可能想使用着色器功能并使用 #if HAVE_TEXTURE_2 等。这种方式统一将生成多个版本的着色器,每个版本都针对转向 "on" 的纹理进行定制。此外,您不需要 .rgba swizzles,因为这是默认设置。

编辑 3

所以我尝试将所有这些图层显示在彼此之上,但我在 lerp() 方面遇到了一些问题。

        fixed4 c1 = tex2D(_MainTex, IN.texcoord);
        fixed4 c2 = tex2D(_SecondLayer, IN.texcoord);
        fixed4 c3 = tex2D(_ThirdLayer, IN.texcoord);
        fixed4 c4 = tex2D(_FourthLayer, IN.texcoord);
        fixed4 c5 = tex2D(_FifthLayer, IN.texcoord);
        fixed4 c6 = tex2D(_SixthLayer, IN.texcoord);

        fixed4 returnTexture1 = c1;
        fixed4 returnTexture2 = c2;
        fixed4 returnTexture3 = c3;
        fixed4 returnTexture4 = c4;
        fixed4 returnTexture5 = c5;
        fixed4 returnTexture6 = c6;

        returnTexture1 = lerp(c1, c2, c2.a);
        returnTexture2 = lerp(c3, c4, c4.a);
        returnTexture3 = lerp(c5, c6, c6.a);

        fixed4 returnAlmostFinalTexture = lerp(returnTexture1, returnTexture2, returnTexture2.a);
        fixed4 returnFinalTexture = lerp(returnTexture2, returnTexture3, returnTexture3.a);

        return returnFinalTexture;

'returnAlmostFinalTexture' 正常工作,但 'returnFinalTexture' 中断。 现在我有 4 个工作层,但我无法让所有 6 个层都工作。但我很确定如果我总共有 8 层它就可以完成工作。

我可以更简单地 lerp 2 个以上的纹理吗?

编辑 4

好吧,我让它工作了,这就是它现在的样子:

        fixed4 c1 = tex2D(_MainTex, IN.texcoord);
        fixed4 c2 = tex2D(_SecondLayer, IN.texcoord);
        fixed4 c3 = tex2D(_ThirdLayer, IN.texcoord);
        fixed4 c4 = tex2D(_FourthLayer, IN.texcoord);
        fixed4 c5 = tex2D(_FifthLayer, IN.texcoord);
        fixed4 c6 = tex2D(_SixthLayer, IN.texcoord);

        fixed4 returnTexture1 = lerp(c1, c2, c2.a);
        fixed4 returnTexture2 = lerp(c3, c4, c4.a);
        fixed4 returnTexture3 = lerp(c5, c6, c6.a);

        fixed4 returnAlmostFinalTexture = lerp(returnTexture1, returnTexture2, returnTexture2.a);
        fixed4 returnFinalTexture = lerp(returnAlmostFinalTexture, returnTexture3, returnTexture3.a);

        return returnFinalTexture;

这是我能做到的最好方法吗?或者我可以以某种方式 lerp 所有这些纹理 easier/more 优化方式?

您通常会乘以颜色值以合并它们,因此在片段着色器中您会执行如下操作:

fixed4 c1 = tex2D(_MainTex, IN.texcoord);
fixed4 c2 = tex2D(_SecondLayer, IN.texcoord);
fixed4 c3 = tex2D(_ThirdLayer, IN.texcoord);
fixed4 c4 = tex2D(_FourthLayer, IN.texcoord);
fixed4 c5 = tex2D(_FifthLayer, IN.texcoord);
fixed4 c6 = tex2D(_SixthLayer, IN.texcoord);

return c1 * c2 * c3 * c4 * c5 * c6 * _Color;

如果您想配置每个图层对最终颜色的影响程度,您可以使用 lerp() 每个纹理的标准化输入值。

编辑:您已经扩展了您的问题,提到当纹理未分配给着色器输入时这不起作用。我对此的回应是使用 shader features,您可以在其中为每个可选纹理输入添加切换,然后使用 #if HAVE_TEXTURE_2 ... #endif。这样,Unity 将根据转换的纹理生成不同的着色器 "on",这将比使用 if (have_texture_2) 等更有效