Splat mapping and shaders with more than 4 textures HDRP

Unity C# Terrain Generation Shaders Issue: Multiple Biomes and Splat Maps

I’m generating a Unity terrain with 6 biomes, each with a unique texture. Since Unity splat maps support only 4 textures per map, I’m using multiple splat maps to manage all 6 biomes. However, after generation, only one texture appears on the terrain instead of the distinct textures for all biomes.

Possible Issues:

Incorrect texture assignment.
Errors in splat map generation (e.g., weights ).
Shader setup issues.
Other logical or coding errors.

Code:

where i set the splat maps!

 if (terrainDataThreadInfoQueue.Count > 0)
        {
            for (int i = 0; i < terrainDataThreadInfoQueue.Count; i++)
            {
                MapThreadInfo<DataStructure.TerrainData> threadInfo = terrainDataThreadInfoQueue.Dequeue();

                Texture2D[] splatMap = null;
                //Texture2D splatMap = new Texture2D(chunkSize, chunkSize);
                if (terrainTextureBasedOnVoronoiPoints)
                {

                    splatMap = SplatMapGenerator.GenerateSplatMaps(this, threadInfo.parameter.biomeMap);
                   // splatMap = SplatMapGenerator.GenerateSplatMapOutsideMainThread(this, threadInfo.parameter.biomeMap, splatMap);

                }
              
                threadInfo.parameter.splatMap = splatMap;
                threadInfo.callback(threadInfo.parameter);
            }
        }

splat map function


    public static Texture2D[] GenerateSplatMaps(TerrainGenerator terrainGenerator, Biome[,] biomeMap)
    {
        int chunkSize = terrainGenerator.ChunkSize;
        int numBiomes = terrainGenerator.BiomeDefinitions.Length;
        int numSplatMaps = Mathf.CeilToInt(numBiomes / 4f);

        // Initialize the splat maps
        Texture2D[] splatMaps = new Texture2D[numSplatMaps];
        for (int i = 0; i < numSplatMaps; i++)
        {
            splatMaps[i] = new Texture2D(chunkSize, chunkSize, TextureFormat.RGBA32, false);
        }

        // Iterate over each point in the chunk
        for (int y = 0; y < chunkSize; y++)
        {
            for (int x = 0; x < chunkSize; x++)
            {
                Biome biome = biomeMap[x, y];
                int biomeIndex = Array.FindIndex(terrainGenerator.BiomeDefinitions, def => def.BiomePrefab.name == biome.name);

                if (biomeIndex >= 0)
                {
                    int splatMapIndex = biomeIndex / 4; // Determine which splat map to use
                    int channelIndex = biomeIndex % 4; // Determine which channel to modify

                    // Get the current color for all splat maps
                    Color[] colors = new Color[numSplatMaps];
                    for (int i = 0; i < numSplatMaps; i++)
                        colors[i] = splatMaps[i].GetPixel(x, y);

                    // Update the correct channel in the correct splat map
                    colors[splatMapIndex][channelIndex] = 1;

                    // Set the updated colors back to the splat maps
                    for (int i = 0; i < numSplatMaps; i++)
                        splatMaps[i].SetPixel(x, y, colors[i]);
                }
            }
        }

        // Apply changes to all splat maps
        foreach (var splatMap in splatMaps)
        {
            splatMap.Apply();
        }

        return splatMaps;
    }

where i assign the texture


    public void AssignTexture(Texture2D[] splatMaps, TerrainGenerator terrainGenerator, MeshRenderer meshRenderer)
    {
        Shader shader = Shader.Find("Custom/TerrainSplatMapShaderHDRP");
        if (shader == null)
        {
            Debug.LogError("Failed to find shader: Custom/TerrainSplatMapShaderHDRP");
            return;
        }

        Material mat = new Material(shader);

        int textureWidth = 1024;
        int textureHeight = 1024;
        TextureFormat format = TextureFormat.RGBA32;

        // Create texture array for biomes
        Texture2DArray textureArray = new Texture2DArray(textureWidth, textureHeight, terrainGenerator.BiomeDefinitions.Length, format, true);
        for (int i = 0; i < terrainGenerator.BiomeDefinitions.Length; i++)
        {
            Texture2D standardizedTexture = StandardizeTexture(terrainGenerator.BiomeDefinitions[i].BiomePrefab.texture, textureWidth, textureHeight, format);
            Graphics.CopyTexture(standardizedTexture, 0, 0, textureArray, i, 0);
        }
        textureArray.Apply();

        // Create texture array for splat maps
        Texture2DArray splatMapArray = new Texture2DArray(splatMaps[0].width, splatMaps[0].height, splatMaps.Length, format, false);
        for (int i = 0; i < splatMaps.Length; i++)
        {
            Graphics.CopyTexture(splatMaps[i], 0, 0, splatMapArray, i, 0);
        }
        splatMapArray.Apply();

        // Assign textures to material
        mat.SetTexture("_TextureArray", textureArray);
        mat.SetTexture("_SplatMaps", splatMapArray);
        mat.SetInt("_TextureArrayLength", terrainGenerator.BiomeDefinitions.Length);
        mat.SetInt("_SplatMapCount", splatMaps.Length);

        meshRenderer.sharedMaterial = mat;
    }

    Texture2D StandardizeTexture(Texture2D sourceTexture, int width, int height, TextureFormat format)
    {
        // Create a temporary RenderTexture
        RenderTexture renderTexture = RenderTexture.GetTemporary(width, height, 0);
        RenderTexture.active = renderTexture;

        // Blit the source texture onto the RenderTexture
        Graphics.Blit(sourceTexture, renderTexture);

        // Create a new Texture2D and read the pixels from the RenderTexture
        Texture2D standardizedTexture = new Texture2D(width, height, format, true);
        standardizedTexture.ReadPixels(new Rect(0, 0, width, height), 0, 0);
        standardizedTexture.Apply();

        // Release the RenderTexture
        RenderTexture.ReleaseTemporary(renderTexture);
        RenderTexture.active = null;

        return standardizedTexture;
    }

my shaders:



Shader "Custom/TerrainSplatMapShaderHDRP"
{
    Properties
    {
        _TextureArray("Texture Array", 2DArray) = "" {}
        _SplatMaps("Splat Maps", 2DArray) = "" {}
    }
    HLSLINCLUDE

    #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Material.hlsl"

    TEXTURE2D_ARRAY(_TextureArray);
    SAMPLER(sampler_TextureArray);

    TEXTURE2D_ARRAY(_SplatMaps);
    SAMPLER(sampler_SplatMaps);

    CBUFFER_START(UnityPerMaterial)
        int _TextureArrayLength;
        int _SplatMapCount;
    CBUFFER_END

    struct Attributes
    {
        float3 positionOS : POSITION;
        float2 uv : TEXCOORD0;
    };

    struct Varyings
    {
        float4 positionCS : SV_POSITION;
        float2 uv : TEXCOORD0;
    };

    Varyings Vert(Attributes input)
    {
        Varyings output;
        output.positionCS = TransformObjectToHClip(input.positionOS);
        output.uv = input.uv;
        return output;
    }

    float4 Frag(Varyings input) : SV_Target
    {
        float4 color = float4(0, 0, 0, 0);

        for (int i = 0; i < _SplatMapCount; i++)
        {
            // Sample the splat map array with proper arguments
            float4 splatControl = SAMPLE_TEXTURE2D_ARRAY(_SplatMaps, sampler_SplatMaps, input.uv, i);

            for (int j = 0; j < 4; j++)
            {
                int textureIndex = i * 4 + j;
                if (textureIndex < _TextureArrayLength)
                {
                    // Sample the texture array with proper arguments
                    color += splatControl[j] * SAMPLE_TEXTURE2D_ARRAY(_TextureArray, sampler_TextureArray, input.uv, textureIndex);
                }
            }
        }

        return color;
    }

    ENDHLSL

    SubShader
    {
        Tags { "RenderPipeline"="HDRenderPipeline" }
        Pass
        {
            Name "Forward"
            Tags { "LightMode"="Forward" }
            HLSLPROGRAM
            #pragma vertex Vert
            #pragma fragment Frag
            ENDHLSL
        }
    }

    Fallback "HDRenderPipeline/Unlit"
}