Многослойный текстурный шейдер в Unity - PullRequest
1 голос
/ 13 марта 2020

Я действительно новичок в шейдерах. Я искал на разных форумах похожую проблему, но ни один из них не помог мне с тем, с чем я борюсь.

По сути, мне нужно сделать шейдер, который объединит 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;

Чтобы избежать этой проблемы, я думал о том, существуют ли операторы, которые будут проверять, существует ли текстура на каждом слое, но я слышал, что должен избегайте этого в любое время, если это возможно. Есть ли лучший способ справиться с этим?

РЕДАКТИРОВАТЬ 2 Я не знаю, если ответ, который наконец-то мне помог, все еще виден из-за моего последнего ответа на удаление поста, но вот решение от: trojanfoe

Вместо того, чтобы создавать ответ для расширения вопроса, отредактируйте исходный вопрос. Не используйте операторы if; скорее, вы можете захотеть использовать функции шейдера и использовать #if HAVE_TEXTURE_2 et c. Таким образом, Unity создаст несколько версий вашего шейдера, каждая из которых адаптирована к текстурам, которые включены. Также вам не нужны .rgba алкогольные напитки, так как это по умолчанию.

РЕДАКТИРОВАТЬ 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 слоев, это бы сработало.

Могу ли я перехватить более 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;

Это лучший способ сделать это? Или я могу как-то проще / более оптимизировать все эти текстуры?

1 Ответ

2 голосов
/ 13 марта 2020

Обычно вы умножаете значения цвета, чтобы объединить их, поэтому в фрагментном шейдере вы бы сделали что-то вроде:

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() с нормализованным входным значением для каждой текстуры.

РЕДАКТИРОВАТЬ: Вы расширили свой вопрос, отметив, что это не работает, когда текстуры не назначены на входы шейдеров. Мой ответ на это будет использовать шейдерные функции , где вы можете добавить переключатели для каждого необязательного ввода текстуры и затем использовать #if HAVE_TEXTURE_2 ... #endif. Таким образом, Unity будет генерировать разные шейдеры в зависимости от того, какие текстуры включены, и это будет более эффективно, чем использование if (have_texture_2) et c.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...