Convert a Custom Shader to URP in Unity6

I am trying to convert my custom terrain shader to work with the Universal Render Pipeline (URP) in Unity6 (6000.0.34f1) with Universal RP 17.0.3. The shader works perfectly in the Built-in Render Pipeline, but after switching to URP, the material and terrain turn pink, indicating compatibility issues.

I attempted to follow the Upgrade custom shaders for URP compatibility but it did not resolve the problem.

Shader "Custom/Terrain" {
    Properties {
        testTexture("Texture", 2D) = "white"{}
        testScale("Scale", Float) = 1

    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
        
        CGPROGRAM

        #pragma surface surf Standard fullforwardshadows

        #pragma target 3.0

        const static int maxLayerCount = 8;
        const static float epsilon = 1E-4;

        int layerCount;
        float3 baseColours[maxLayerCount];
        float baseStartHeights[maxLayerCount];
        float baseBlends[maxLayerCount];
        float baseColourStrength[maxLayerCount];
        float baseTextureScales[maxLayerCount];

        float minHeight;
        float maxHeight;

        sampler2D testTexture;
        float testScale;

        UNITY_DECLARE_TEX2DARRAY(baseTextures);

        struct Input {
            float3 worldPos;
            float3 worldNormal;
        };

        float inverseLerp(float a, float b, float value) {
            return saturate((value-a)/(b-a));
        }

        float3 triplanar(float3 worldPos, float scale, float3 blendAxes, int textureIndex) {
            float3 scaledWorldPos = worldPos / scale;
            float3 xProjection = UNITY_SAMPLE_TEX2DARRAY(baseTextures, float3(scaledWorldPos.y, scaledWorldPos.z, textureIndex)) * blendAxes.x;
            float3 yProjection = UNITY_SAMPLE_TEX2DARRAY(baseTextures, float3(scaledWorldPos.x, scaledWorldPos.z, textureIndex)) * blendAxes.y;
            float3 zProjection = UNITY_SAMPLE_TEX2DARRAY(baseTextures, float3(scaledWorldPos.x, scaledWorldPos.y, textureIndex)) * blendAxes.z;
            return xProjection + yProjection + zProjection;
        }

        void surf (Input IN, inout SurfaceOutputStandard o) {
            float heightPercent = inverseLerp(minHeight,maxHeight, IN.worldPos.y);
            float3 blendAxes = abs(IN.worldNormal);
            blendAxes /= blendAxes.x + blendAxes.y + blendAxes.z;

            for (int i = 0; i < layerCount; i ++) {
                float drawStrength = inverseLerp(-baseBlends[i]/2 - epsilon, baseBlends[i]/2, heightPercent - baseStartHeights[i]);

                float3 baseColour = baseColours[i] * baseColourStrength[i];
                float3 textureColour = triplanar(IN.worldPos, baseTextureScales[i], blendAxes, i) * (1-baseColourStrength[i]);

                o.Albedo = o.Albedo * (1-drawStrength) + (baseColour+textureColour) * drawStrength;
            }

        
        }


        ENDCG
    }
    FallBack "Diffuse"
}

Unity Engine URP Shaders Terrain Built-in-Render-Pipeline Graphics World-Building-Terrain Universal-Render-Pipeline Graphics-Shaders Shader-Graph Unity Questions & Answers Android

I didn’t read the article that you linked, but your shader is using Unity’s surface shader template. That’s a special template that is built-in only, so there’s no way to convert them to URP right now. The easiest thing to do would probably be to recreate your shader from scratch using Shader Graph.

I’m sure if you search you will be able to find examples of triplanar texturing Shader Graphs.

1 Like

I made an utility to automatically convert Surface Shaders to URP/HDRP shaders : Surface Shader Converter | Utilities Tools | Unity Asset Store
If you have any issues with it, send me an email and I’ll fix it.

1 Like