Using the GPU to create smooth transitions between biomes

Hello,

I’ve been trying to implement this for some time, but I can’t seem to wrap my head around a good solution.

I’m creating a procedurally generated terrain, where each “point” in the map has its own height, heat and moisture levels (all calculated using Noise). I’ve got the world generation wrapped up and it’s working as intended, the part of the game where I use the info on those values is in place, everything logicwise is working fine.

The problem here lies in the GPU part now. See, I’ve computed these infos because I want my world to have biomes, like desert, grassland, etc.
I tried calculating those along with the heat and the moisture through the CPU, but the result was way too “blocky” for it to be of any use… Here’s what I tried:
-first, the algorithm “samples” the data
-then, the script compare those datas to a lookup table and returns the corresponding biome
-finally, that data is stored in a texture (in the alpha channel), to be used as an index for a texture array

This gave jagged borders when using point filtering, and “weird” results using bilinear or trilinear (you could see ALL the textures while transitioning through biomes, as if all biomes were represented)

So I thought, since there’s no need for the game to store the data regarding the biome, I could use heat and moisture to calculate the biomes through the video card. Problem is, I don’t know how to do it. Right now I’m passing a 32x32 (those are the dimensions for my terrain chunks) texture with the data for height, heat and moisture stored in the rgb channels to the shader, but that’s all I know how to do.
I think i have to combine bilinear interpolation between textures using heat and moisture (to achieve smooth transitioning) and i have to use a lookup table like before, probably there’s something more, but really, I’m lost.

Can anyone help me?

After some considerations, I think the best way to solve this problem would be using a vertex/fragment shader, but other than that, I’m still quite lost.

here’s what I wrote so far:

Shader "Custom/Biome Shader" {

    Properties {
        //_MainTex("Terrain Texture Array", 2DArray) = "white" {}
    }

        SubShader{

            Pass {
                CGPROGRAM

                #pragma vertex vert
                #pragma fragment calcBiomeInFrag

                #pragma target 3.5

                #include "UnityCG.cginc"

                const int terrainType = 3;

                //UNITY_DECLARE_TEX2DARRAY(_MainTex);

                struct vData
                {
                    float4 vertex : POSITION;
                    float4 color : COLOR;
                };
                struct fData
                {
                    float4 pos : SV_POSITION;
                    float4 color : TEXCOORD0;
                };

                fData vert(vData i)
                {
                    fData o;
                    o.pos = mul(UNITY_MATRIX_MVP, i.vertex);
                    o.color = i.color.rgba;
                    return o;
                }

                fixed4 calcBiomeInFrag(fData i) : SV_TARGET
                {
                    //r contains the height value, used solely for testing
                    return i.color.r>0.5?(1.0,1.0,1.0,1.0):(0.0,0.0,0.0,0.0);
                }

            ENDCG
        }
    }
}

This gives me smooth edges. I tried to use different values, like (0.0,0.0,0.4,0.0), but they don’t work as intended. I guess i’ll have to look around and see how SV_TARGET and fixed4 variables work.

The next step would be to implement a lookup table and simply have the shader look into it to see which texture to use.

The above code actually works fine with textures too (with some tweaking using triplanar functions to avoid artifacts on the landscape). Now for the multi-biome part I had an idea that I’d like to share with you:

I have two float values (heat and moisture), both going from 0 to 1. I need a lookup table that, given the values of heat and moist, returns me an index to use with the texture array.
What if I create a texture in which I store the indices and look for them using heat and moisture as if they were values of u and v?
Say, as an example, that the values of heat and moist are (0.85,0.12). Sampling the lookup texture I realize that those values are associated with the index 1 (which is a desert texture), so I sample that texture using the uv coordinates of the fragment I’m checking.

What are your opinions on this? Would this be a good implementation or would it be too hard on the GPU? (final sampling count would be around 4 sample per pixel, with no other effect implemented)