HLSL Shader doesnt work on mobile

Hello, I tried to rewrite a shader from cg to hlsl. I succeeded in the editor, but the shader does not work on an Android device.
Does anyone know very well and sees the error?

Shader "Test/Terrain"
{
    Properties
    {
        _SplatMap("Splatmap (RGB)", 2D) = "black" {}
        _GroundTexture("Ground Texture", 2D) = "white" {}
        _TextureA("Texture A", 2D) = "white" {}
        _TextureB("Texture B", 2D) = "white" {}
        _TextureC("Texture C", 2D) = "white" {}
        _TextureD("Texture D", 2D) = "white" {}
        _TextureScale("TextureScale", Range(0.01,10)) = 0.25
        _PrioGround("Prio Ground", Range(0.01, 2.0)) = 1
        _PrioA("Prio A", Range(0.01, 2.0)) = 1
        _PrioB("Prio B", Range(0.01, 2.0)) = 1
        _PrioC("Prio C", Range(0.01, 2.0)) = 1
        _PrioD("Prio D", Range(0.01, 2.0)) = 1
        _Depth("Depth", Range(0.01,1.0)) = 0.2
    }
        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
        CBUFFER_START(UnityPerMaterial)

        sampler2D _SplatMap;
        sampler2D _GroundTexture;
        sampler2D _TextureA;
        sampler2D _TextureB;
        sampler2D _TextureC;
        sampler2D _TextureD;

        half _TextureScale;

        half _PrioGround;
        half _PrioA;
        half _PrioB;
        half _PrioC;
        half _PrioD;

        half _Depth;
        CBUFFER_END
        ENDHLSL

        SubShader
        {
            // Set Queue to AlphaTest+2 to render the terrain after all other solid geometry.
            // We do this because the terrain shader is expensive and this way we ensure most pixels
            // are already discarded before the fragment shader is executed:
            Tags{"RenderPipeline" = "UniversalRenderPipeline" "RenderType" = "Opaque" "Queue" = "AlphaTest+2"}
            Pass
            {
                Tags{ "LightMode" = "UniversalForward" }

                HLSLPROGRAM
                #pragma target 2.0
                #pragma vertex vert
                #pragma fragment frag
                #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
                #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
                #pragma skip_variants DIRLIGHTMAP_COMBINED LIGHTPROBE_SH POINT SPOT SHADOWS_DEPTH SHADOWS_CUBE VERTEXLIGHT_ON

                struct a2v
                {
                    float4 vertex : POSITION;
                    half3 normal : NORMAL;
                    half4 color : COLOR;
                    float3 uv : TEXCOORD0;
                };

                struct v2f
                {
                    float4 pos : SV_POSITION;
                    float2 uvSplat : TEXCOORD0;
                    float2 uvMaterial : TEXCOORD1;
                    half4 materialPrios : TEXCOORD2;
                    // put shadows data into TEXCOORD3
                    float4 shadowCoord : TEXCOORD3;
                    half4 color : COLOR0;
                    half3 diff : COLOR1;
                    half3 ambient : COLOR2;
                };

                v2f vert(a2v v)
                {
                    v2f OUT;
                    OUT.pos = TransformObjectToHClip(v.vertex.xyz);
                    OUT.uvSplat = v.uv.xy;
                    // uvs of the rendered materials are based on world position
                    OUT.uvMaterial = mul(unity_ObjectToWorld, v.vertex).xz * _TextureScale;
                    OUT.materialPrios = half4(_PrioA, _PrioB, _PrioC, _PrioD);
                    OUT.color = v.color;

                    // calculate light
                    half3 worldNormal = TransformObjectToWorldNormal(v.normal);
                    half nl = max(0, dot(worldNormal, GetMainLight().direction));
                    OUT.diff = nl * GetMainLight().color;
                    OUT.ambient = SampleSH(worldNormal);

                    // Transfer shadow coordinates:
                    OUT.shadowCoord = TransformWorldToShadowCoord(OUT.pos.xyz);

                    return OUT;
                }

                half4 frag(v2f IN) : SV_Target
                {
                    half4 groundColor = tex2D(_GroundTexture, IN.uvMaterial);
                    half4 materialAColor = tex2D(_TextureA, IN.uvMaterial);
                    half4 materialBColor = tex2D(_TextureB, IN.uvMaterial);
                    half4 materialCColor = tex2D(_TextureC, IN.uvMaterial);
                    half4 materialDColor = tex2D(_TextureD, IN.uvMaterial);

                    // store heights for all materials on this pixel
                    half groundHeight = groundColor.a;
                    half4  materialHeights = half4(materialAColor.a, materialBColor.a, materialCColor.a, materialDColor.a);
                    // avoid black artefacts by division by zero
                    materialHeights = max(0.0001, materialHeights);

                    // get material amounts from splatmap
                    half4  materialAmounts = tex2D(_SplatMap, IN.uvSplat).argb;
                    // the ground amount takes up all unused space
                    half groundAmount = 1.0 - min(1.0, materialAmounts.r + materialAmounts.g + materialAmounts.b + materialAmounts.a);

                    // calculate material strenghts
                    half alphaGround = groundAmount * _PrioGround * groundHeight;
                    half4  alphaMaterials = materialAmounts * IN.materialPrios * materialHeights;

                    // find strongest point of all materials
                    half max_01234 = max(alphaGround, alphaMaterials.r);
                    max_01234 = max(max_01234, alphaMaterials.g);
                    max_01234 = max(max_01234, alphaMaterials.b);
                    max_01234 = max(max_01234, alphaMaterials.a);

                    //lower threshold
                    max_01234 = max(max_01234 - _Depth, 0);

                    // mask all materials above threshold
                    half b0 = max(alphaGround - max_01234, 0);
                    half b1 = max(alphaMaterials.r - max_01234, 0);
                    half b2 = max(alphaMaterials.g - max_01234, 0);
                    half b3 = max(alphaMaterials.b - max_01234, 0);
                    half b4 = max(alphaMaterials.a - max_01234, 0);

                    // combine all materials and normalize
                    half alphaSum = b0 + b1 + b2 + b3 + b4;
                    half4 col = (
                        groundColor * b0 +
                        materialAColor * b1 +
                        materialBColor * b2 +
                        materialCColor * b3 +
                        materialDColor * b4
                    ) / alphaSum;

                    //include vertex colors
                    col *= IN.color;

                    // compute shadow attenuation (1.0 = fully lit, 0.0 = fully shadowed)
                    half shadow = MainLightRealtimeShadow(IN.shadowCoord);
                    // darken light's illumination with shadow, keep ambient intact
                    half3 lighting = IN.diff * shadow + IN.ambient;

                    col.rgb *= IN.diff * MainLightRealtimeShadow(IN.shadowCoord) + IN.ambient;

                    return col;
                }
                ENDHLSL
            }

            // shadow casting support
            UsePass "Legacy Shaders/VertexLit/SHADOWCASTER"
        }
}

Which part doesn’t work? You can debug it step by step by inserting something like return materialHeights.xxx and check exactly which part stops working in the device. Or use a graphics debugger.

You should actually not be using tex2d directly, but rather SAMPLE_TEXTURE2D. Since the latter translates to whichever sampling function the graphics API has available. Otherwise it may work in DirectX (editor), but fail on OpenGLES/Vulkan (device).

UsePass likely has to be removed, as you’ve be mixing URP and Built-In RP shader code. This in any case breaks the SRP batcher.

here it is written that you should use tex2d so that it works on all platforms or do i understand something wrong?

https://docs.unity3d.com/Manual/SL-ShaderPrograms.html

now it works :slight_smile:

Thank you :slight_smile:

Ha, great, easy fix then! :slight_smile:

The Unity manual actually only covers shader documentation for the Built-In RP. When writing for URP/HDRP much of it is rendered useless. A lot of things are the same, but just have a different name in every RP. The texturing sampling macro being a good example.