Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Using the GPU to create smooth transitions between biomes

Discussion in 'General Graphics' started by Jacho_Mendt, Mar 13, 2017.

  1. Jacho_Mendt

    Jacho_Mendt

    Joined:
    Jun 20, 2015
    Posts:
    13
    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?
     
    Last edited: Mar 13, 2017
  2. Jacho_Mendt

    Jacho_Mendt

    Joined:
    Jun 20, 2015
    Posts:
    13
    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:

    Code (CSharp):
    1. Shader "Custom/Biome Shader" {
    2.  
    3.     Properties {
    4.         //_MainTex("Terrain Texture Array", 2DArray) = "white" {}
    5.     }
    6.  
    7.         SubShader{
    8.  
    9.             Pass {
    10.                 CGPROGRAM
    11.  
    12.                 #pragma vertex vert
    13.                 #pragma fragment calcBiomeInFrag
    14.  
    15.                 #pragma target 3.5
    16.  
    17.                 #include "UnityCG.cginc"
    18.  
    19.                 const int terrainType = 3;
    20.  
    21.                 //UNITY_DECLARE_TEX2DARRAY(_MainTex);
    22.  
    23.                 struct vData
    24.                 {
    25.                     float4 vertex : POSITION;
    26.                     float4 color : COLOR;
    27.                 };
    28.                 struct fData
    29.                 {
    30.                     float4 pos : SV_POSITION;
    31.                     float4 color : TEXCOORD0;
    32.                 };
    33.  
    34.                 fData vert(vData i)
    35.                 {
    36.                     fData o;
    37.                     o.pos = mul(UNITY_MATRIX_MVP, i.vertex);
    38.                     o.color = i.color.rgba;
    39.                     return o;
    40.                 }
    41.  
    42.                 fixed4 calcBiomeInFrag(fData i) : SV_TARGET
    43.                 {
    44.                     //r contains the height value, used solely for testing
    45.                     return i.color.r>0.5?(1.0,1.0,1.0,1.0):(0.0,0.0,0.0,0.0);
    46.                 }
    47.  
    48.             ENDCG
    49.         }
    50.     }
    51. }
    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.
     
  3. Jacho_Mendt

    Jacho_Mendt

    Joined:
    Jun 20, 2015
    Posts:
    13
    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)