Mélange de textures pour terrain avec shader Unity

  1. Mélange de quatre textures de base

Pour un mélange simple de quatre couches de texture (R, G, B, A) piloté par un masque RGBA, deux versions de shader sont présentées : une utilisant le pipeline Surface Shader et une autre avec Vertex/Fragment. La logique est identique : on échantillonne chaque texture indépendamment, puis on applique des interpolations linéaires (lerp) successives en fonction des canaux du masque.

Version Surface Shader (refactorisée)

Shader "Custom/TerrainBlend_Surface" {
    Properties {
        _TexR ("Couche R", 2D) = "" {}
        _TexG ("Couche G", 2D) = "" {}
        _TexB ("Couche B", 2D) = "" {}
        _TexA ("Couche A", 2D) = "" {}
        _Mask ("Masque (RGBA)", 2D) = "" {}
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
        CGPROGRAM
        #pragma surface surf Lambert
        #pragma target 3.0

        sampler2D _TexR, _TexG, _TexB, _TexA;
        sampler2D _Mask;

        struct Input {
            float2 uv_TexR;
            float2 uv_TexG;
            float2 uv_TexB;
            float2 uv_TexA;
            float2 uv_Mask;
        };

        void surf (Input IN, inout SurfaceOutput o) {
            float4 r = tex2D(_TexR, IN.uv_TexR);
            float4 g = tex2D(_TexG, IN.uv_TexG);
            float4 b = tex2D(_TexB, IN.uv_TexB);
            float4 a = tex2D(_TexA, IN.uv_TexA);
            float4 mask = tex2D(_Mask, IN.uv_Mask);

            float4 finalColor;
            // Mélange progressif : R->G, puis B, puis A
            finalColor = lerp(r, g, mask.g);
            finalColor = lerp(finalColor, b, mask.b);
            finalColor = lerp(finalColor, a, mask.a);
            finalColor = saturate(finalColor);

            o.Albedo = finalColor.rgb;
            o.Alpha = finalColor.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

Version Vertex/Fragment (refactorisée)

Shader "Custom/TerrainBlend_VF" {
    Properties {
        _Tex0 ("Couche 0 (R)", 2D) = "white" {}
        _Tex1 ("Couche 1 (G)", 2D) = "white" {}
        _Tex2 ("Couche 2 (B)", 2D) = "white" {}
        _Tex3 ("Couche 3 (A)", 2D) = "white" {}
        _Control ("Contrôle (RGBA)", 2D) = "red" {}
        _Intensity ("Intensité", Range(1,2)) = 1
    }
    SubShader {
        Tags { "Queue"="Geometry+110" }
        Pass {
            Tags { "LightMode"="ForwardBase" }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 2.0
            #include "UnityCG.cginc"

            sampler2D _Control;
            sampler2D _Tex0, _Tex1, _Tex2, _Tex3;
            half _Intensity;

            struct v2f {
                float4 pos : SV_POSITION;
                half4 uv01 : TEXCOORD0; // xy = _Tex0, zw = _Tex1
                half4 uv23 : TEXCOORD1; // xy = _Tex2, zw = _Tex3
                half2 uvCtrl : TEXCOORD2;
            };

            struct appdata {
                float4 vertex : POSITION;
                float4 texcoord : TEXCOORD0;
            };

            v2f vert (appdata v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uvCtrl = v.texcoord.xy;
                o.uv01.xy = TRANSFORM_TEX(v.texcoord, _Tex0);
                o.uv01.zw = TRANSFORM_TEX(v.texcoord, _Tex1);
                o.uv23.xy = TRANSFORM_TEX(v.texcoord, _Tex2);
                o.uv23.zw = TRANSFORM_TEX(v.texcoord, _Tex3);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target {
                fixed4 ctrl = tex2D(_Control, i.uvCtrl);
                fixed4 c0 = tex2D(_Tex0, i.uv01.xy);
                fixed4 c1 = tex2D(_Tex1, i.uv01.zw);
                fixed4 c2 = tex2D(_Tex2, i.uv23.xy);
                fixed4 c3 = tex2D(_Tex3, i.uv23.zw);

                fixed4 result = lerp(c0, c1, ctrl.g);
                result = lerp(result, c2, ctrl.b);
                result = lerp(result, c3, ctrl.a);
                return result * _Intensity;
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

  1. Mélange de quatre textures avec normales

On ajoute les cartes normales correspondant à chaque couche. Le procédé est identique : on mélange les couleurs et les normales avec le même masque. Deux versions sont fournies, avec ou sans calcul d’éclairage dans le fragment shader.

Version Surface Shader avec normlaes (refactorisée)

Shader "Custom/TerrainNormal_Surface" {
    Properties {
        _TexR ("Tex R", 2D) = "" {}
        _TexG ("Tex G", 2D) = "" {}
        _TexB ("Tex B", 2D) = "" {}
        _TexA ("Tex A", 2D) = "" {}
        _NormalR ("Normal R", 2D) = "bump" {}
        _NormalG ("Normal G", 2D) = "bump" {}
        _NormalB ("Normal B", 2D) = "bump" {}
        _NormalA ("Normal A", 2D) = "bump" {}
        _BumpScaleR ("Force R", Range(-1,1)) = 1
        _BumpScaleG ("Force G", Range(-1,1)) = 1
        _BumpScaleB ("Force B", Range(-1,1)) = 1
        _BumpScaleA ("Force A", Range(-1,1)) = 1
        _Intensity ("Intensité", Range(1,2)) = 1
        _Mask ("Masque (RGBA)", 2D) = "" {}
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
        CGPROGRAM
        #pragma surface surf Lambert
        #pragma target 4.0

        sampler2D _TexR, _TexG, _TexB, _TexA;
        sampler2D _NormalR, _NormalG, _NormalB, _NormalA;
        half _BumpScaleR, _BumpScaleG, _BumpScaleB, _BumpScaleA;
        half _Intensity;
        sampler2D _Mask;

        struct Input {
            float2 uv_TexR, uv_TexG, uv_TexB, uv_TexA;
            float2 uv_NormalR, uv_NormalG, uv_NormalB, uv_NormalA;
            float2 uv_Mask;
        };

        void surf (Input IN, inout SurfaceOutput o) {
            float4 r = tex2D(_TexR, IN.uv_TexR);
            float4 g = tex2D(_TexG, IN.uv_TexG);
            float4 b = tex2D(_TexB, IN.uv_TexB);
            float4 a = tex2D(_TexA, IN.uv_TexA);
            float4 mask = tex2D(_Mask, IN.uv_Mask);

            float4 color = lerp(r, g, mask.g);
            color = lerp(color, b, mask.b);
            color = lerp(color, a, mask.a);
            color = saturate(color);

            float3 nr = UnpackNormal(tex2D(_NormalR, IN.uv_NormalR)); nr.xy *= _BumpScaleR;
            float3 ng = UnpackNormal(tex2D(_NormalG, IN.uv_NormalG)); ng.xy *= _BumpScaleG;
            float3 nb = UnpackNormal(tex2D(_NormalB, IN.uv_NormalB)); nb.xy *= _BumpScaleB;
            float3 na = UnpackNormal(tex2D(_NormalA, IN.uv_NormalA)); na.xy *= _BumpScaleA;

            float3 n = lerp(nr, ng, mask.g);
            n = lerp(n, nb, mask.b);
            n = lerp(n, na, mask.a);

            o.Albedo = color * _Intensity;
            o.Alpha = color.a;
            o.Normal = normalize(n);
        }
        ENDCG
    }
    FallBack "Diffuse"
}

Version Vertex/Fragment avec éclairage inclus (refactorisée)

Shader "Custom/TerrainNormal_VF" {
    Properties {
        _Tex0 ("Tex 0 (R)", 2D) = "white" {}
        _Tex1 ("Tex 1 (G)", 2D) = "white" {}
        _Tex2 ("Tex 2 (B)", 2D) = "white" {}
        _Tex3 ("Tex 3 (A)", 2D) = "white" {}
        _Normal0 ("Normal 0", 2D) = "bump" {}
        _Normal1 ("Normal 1", 2D) = "bump" {}
        _Normal2 ("Normal 2", 2D) = "bump" {}
        _Normal3 ("Normal 3", 2D) = "bump" {}
        _Bump0 ("Force 0", Range(-1,1)) = 1
        _Bump1 ("Force 1", Range(-1,1)) = 1
        _Bump2 ("Force 2", Range(-1,1)) = 1
        _Bump3 ("Force 3", Range(-1,1)) = 1
        _Control ("Contrôle (RGBA)", 2D) = "red" {}
        _Intensity ("Intensité", Range(1,2)) = 1
    }
    SubShader {
        Tags { "Queue"="Geometry+110" }
        Pass {
            Tags { "LightMode"="ForwardBase" }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 2.0
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            sampler2D _Control;
            sampler2D _Tex0, _Tex1, _Tex2, _Tex3;
            sampler2D _Normal0, _Normal1, _Normal2, _Normal3;
            half _Bump0, _Bump1, _Bump2, _Bump3;
            half _Intensity;

            struct v2f {
                float4 pos : SV_POSITION;
                half4 uvTex01 : TEXCOORD0; // xy=Tex0, zw=Tex1
                half4 uvTex23 : TEXCOORD1; // xy=Tex2, zw=Tex3
                half4 uvNrm01 : TEXCOORD2; // xy=Normal0, zw=Normal1
                half4 uvNrm23 : TEXCOORD3; // xy=Normal2, zw=Normal3
                half2 uvCtrl : TEXCOORD4;
                half3 lightDir : TEXCOORD5;
            };

            struct appdata {
                float4 vertex : POSITION;
                float4 texcoord : TEXCOORD0;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
            };

            v2f vert (appdata v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uvCtrl = v.texcoord.xy;
                o.uvTex01.xy = TRANSFORM_TEX(v.texcoord, _Tex0);
                o.uvTex01.zw = TRANSFORM_TEX(v.texcoord, _Tex1);
                o.uvTex23.xy = TRANSFORM_TEX(v.texcoord, _Tex2);
                o.uvTex23.zw = TRANSFORM_TEX(v.texcoord, _Tex3);
                o.uvNrm01.xy = TRANSFORM_TEX(v.texcoord, _Normal0);
                o.uvNrm01.zw = TRANSFORM_TEX(v.texcoord, _Normal1);
                o.uvNrm23.xy = TRANSFORM_TEX(v.texcoord, _Normal2);
                o.uvNrm23.zw = TRANSFORM_TEX(v.texcoord, _Normal3);

                TANGENT_SPACE_ROTATION;
                o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));
                return o;
            }

            fixed4 frag (v2f i) : SV_Target {
                fixed3 lightDir = normalize(i.lightDir);
                fixed4 ctrl = tex2D(_Control, i.uvCtrl);

                fixed4 c0 = tex2D(_Tex0, i.uvTex01.xy);
                fixed4 c1 = tex2D(_Tex1, i.uvTex01.zw);
                fixed4 c2 = tex2D(_Tex2, i.uvTex23.xy);
                fixed4 c3 = tex2D(_Tex3, i.uvTex23.zw);

                fixed3 n0 = UnpackNormal(tex2D(_Normal0, i.uvNrm01.xy)); n0.xy *= _Bump0;
                fixed3 n1 = UnpackNormal(tex2D(_Normal1, i.uvNrm01.zw)); n1.xy *= _Bump1;
                fixed3 n2 = UnpackNormal(tex2D(_Normal2, i.uvNrm23.xy)); n2.xy *= _Bump2;
                fixed3 n3 = UnpackNormal(tex2D(_Normal3, i.uvNrm23.zw)); n3.xy *= _Bump3;

                fixed3 n = lerp(n0, n1, ctrl.g);
                n = lerp(n, n2, ctrl.b);
                n = lerp(n, n3, ctrl.a);
                n = normalize(n);

                fixed4 color = lerp(c0, c1, ctrl.g);
                color = lerp(color, c2, ctrl.b);
                color = lerp(color, c3, ctrl.a);

                fixed diff = max(0, dot(n, lightDir));
                return color * diff * _Intensity;
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

Étiquettes: Unity ShaderLab HLSL SurfaceShader VertexFragmentShader

Publié le 13 juillet à 00h20