World Bending Shader Effect Messes Up With Procedurally Generated Terrain Textures

Hello everyone, I recently finished Sebastian Lague’s procedural terrain generation playlist. To be honest, because I’m kind of a beginner of this whole procedural thing, I couln’t understand most of the code. I played it ariund a little and decided to add a world bending effect with a surface shader code I found on the internet for a planet-like look. I don’t actually know how shaders work but I managed to merge the shader code I found with the custom shader Sebastian created in the series and it worked but there is a problem.

The code works but because of the bending effect, the height values are much below their orginal values without the effect. As a result, the textures are not working as intended. For example, the far sides of the map is just all water and the textures shift from water to grass as the player moves.

It’s my understanding that the shader first curves the world and then adds the textures. Is there a way to reverse this?

For context, this is without the world bending effect:

[198338-upload-2022-8-3-21-35-25r.png*_|198338]

And this is with the world bending effect:

[198339-upload-2022-8-3-21-37-29r.png*_|198339]

This is the world bending shader code I found:

Shader "playmint/curved/curved"
{
    Properties
    {
        _Color("Main Colour", Color) = (1,1,1,1)
        _MainTex("Base (RGB)", 2D) = "white" {}
    }
 
        SubShader
    {
        Tags { "RenderType" = "Opaque" }
        LOD 200
 
        CGPROGRAM
        #pragma surface surf Lambert vertex:vert addshadow
 
        // Global Shader values
        uniform float2 _BendAmount;
        uniform float3 _BendOrigin;
        uniform float _BendFalloff;
 
        sampler2D _MainTex;
        fixed4 _Color;
 
        struct Input
        {
              float2 uv_MainTex;
        };
 
        float4 Curve(float4 v)
        {
            //HACK: Considerably reduce amount of Bend
            _BendAmount *= .0001;
 
            float4 world = mul(unity_ObjectToWorld, v);
 
            float dist = length(world.xz - _BendOrigin.xz);
 
            dist = max(0, dist - _BendFalloff);
 
            // Distance squared
            dist = dist * dist;
 
            world.xy += dist * _BendAmount;
            return mul(unity_WorldToObject, world);
        }
 
      void vert(inout appdata_full v)
      {
            v.vertex = Curve(v.vertex);
      }
 
      void surf(Input IN, inout SurfaceOutput o)
      {
            fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Alpha = c.a;
      }
 
      ENDCG
    }
        Fallback "Mobile/Diffuse"
}

And this is the shader code from attempted merge:

Shader "Custom/Terrain"
{
 
    Properties
    {
        testTexture("Texture", 2D) = "white"{}
        testScale("Scale", Float) = 1
 
    }
 
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200
     
        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows vertex:vert addshadow // vert add
 
        // Use shader model 3.0 target, to get nicer looking lighting
        #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;
 
        // Global Shader values
        uniform float2 _BendAmount;
        uniform float3 _BendOrigin;
        uniform float _BendFalloff;
 
        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;
        }
 
        float4 Curve(float4 v)
        {
            //HACK: Considerably reduce amount of Bend
            _BendAmount *= .0001;
 
            float4 world = mul(unity_ObjectToWorld, v);
 
            float dist = length(world.xz - _BendOrigin.xz);
 
            dist = max(0, dist - _BendFalloff);
 
            // Distance squared
            dist = dist * dist;
 
            world.xy += dist * _BendAmount;
            return mul(unity_WorldToObject, world);
        }
 
        void vert(inout appdata_full v)
        {
            v.vertex = Curve(v.vertex);
        }
 
        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<em>/2 - epsilon, baseBlends_/2, heightPercent - baseStartHeights*);*_</em>

float3 baseColour = baseColours * baseColourStrength*;*
float3 textureColour = triplanar(IN.worldPos, baseTextureScales_, blendAxes, i) * (1-baseColourStrength*);*_

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

ENDCG
}
FallBack “Diffuse”
}
As I said, Sebastian’s code felt a bit hard for me to understand and I have near zero experience with shaders. I would like to ask if there is a way to fix this problem.
*
*

Thanks to helpful people in Official Unity Discord, I fixed it! And the good part is, the answer was not complicated as some people said. They explained me what I should do step by step without even looking at the code.

I added my own variable to struct Input and used mul(unity_ObjectToWorld, v.vertex).xyz; before I modify the vertex. And in the surf function, I used my own custom variable instead.

I suggest anyone who is trying to assign different textures according to the heights of the different regions of a mesh/terrain and trying to use vertex bending shaders at the same time to have a look at this.

Here is the modified code (marked the new additions as “//add”):

Shader "Custom/CustomCurvedTerrain"
{
    Properties
    {
        testTexture("Texture", 2D) = "white"{}
        testScale("Scale", Float) = 1
 
    }
 
        SubShader
        {
            Tags { "RenderType" = "Opaque" }
            LOD 200
 
            CGPROGRAM
            // Physically based Standard lighting model, and enable shadows on all light types
            #pragma surface surf Standard fullforwardshadows vertex:vert addshadow // vert add
 
            // Use shader model 3.0 target, to get nicer looking lighting
            #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;
 
            // Global Shader values
            uniform float2 _BendAmount;
            uniform float3 _BendOrigin;
            uniform float _BendFalloff;
 
            sampler2D testTexture;
            float testScale;
 
            UNITY_DECLARE_TEX2DARRAY(baseTextures);
 
            struct Input
            {
                float3 worldPos;
                float3 worldNormal;
 
                float3 customWorldPos; //add
            };
 
            float4 Curve(float4 v)
            {
                //HACK: Considerably reduce amount of Bend
                _BendAmount *= .0001;
 
                float4 world = mul(unity_ObjectToWorld, v);
 
                float dist = length(world.xz - _BendOrigin.xz);
 
                dist = max(0, dist - _BendFalloff);
 
                // Distance squared
                dist = dist * dist;
 
                world.xy += dist * _BendAmount;
                return mul(unity_WorldToObject, world);
            }
 
            void vert(inout appdata_full v, out Input o)
            {
                //add
                UNITY_INITIALIZE_OUTPUT(Input, o);
                o.customWorldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                //add
 
                v.vertex = Curve(v.vertex);
            }
 
            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.customWorldPos.y); //add
                float3 blendAxes = abs(IN.worldNormal);
                blendAxes /= blendAxes.x + blendAxes.y + blendAxes.z;
 
                for (int i = 0; i < layerCount; i++)
                {
                    float drawStrength = inverseLerp(-baseBlends <em>/ 2 - epsilon, baseBlends _/ 2, heightPercent - baseStartHeights*);*_</em>

float3 baseColour = baseColours * baseColourStrength*;*
float3 textureColour = triplanar(IN.customWorldPos, baseTextureScales_, blendAxes, i) * (1 - baseColourStrength*); //add*_

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

ENDCG
}
FallBack “Diffuse”
}
People in discord are really helpful for beginners and are always online. They will always try to help you and find you a solution rather than saying “go learn it yourself”.