Search Unity

How to use a 2DArray with Biome SplatMap

Discussion in 'Shaders' started by Micky_V, Jun 3, 2019.

  1. Micky_V

    Micky_V

    Joined:
    Jan 9, 2015
    Posts:
    7
    Hi there,
    I used this tutorial as a reference guide for building Plane based terrains using a Biome splat map with simple RGB colours.
    https://gamedevacademy.org/complete-guide-to-procedural-level-generation-in-unity-part-2/

    I then found a Shader which would allow me to take the biome splat map and use the RGBA values to apply textures at the vertex locations with a nice side-effect being the blend effect where the 'terrain' textures blend based on the generated biome texture (see attached images from different game runs).

    However this approach only allows for 4 terrain textures based on the RGBA values from the biome texture, what I would like is to change to the Shader to support 2DArray's so I can use 16 textures and not be restricted to RGBA colours.

    The Shader below works nicely with 4 textures, and the C# snippet works fine as well, however the TextureArray is not being used in the shader code below at the moment

    Also, the resultant material doesn't receive shadows! Not sure how to fix that either...

    Code (CSharp):
    1. Shader "Custom/Biome Tilemap"
    2. {
    3.     Properties
    4.     {
    5.         // Biome splat map for texturing
    6.         _MainTex ("Biome Splat Map", 2D) = "white" {}
    7.  
    8.         // Want to use this 2DArray that will have 16 textures...
    9.         _TerrainTextures ("Terrain Textures", 2DArray) = "white" {}
    10.  
    11.         // As opposed to 4 specific textures
    12.         [NoScaleOffset] _Texture1 ("Texture 1", 2D) = "white" {}
    13.         [NoScaleOffset] _Texture2 ("Texture 2", 2D) = "white" {}
    14.         [NoScaleOffset] _Texture3 ("Texture 3", 2D) = "white" {}
    15.         [NoScaleOffset] _Texture4 ("Texture 4", 2D) = "white" {}
    16.     }
    17.  
    18.     SubShader
    19.     {
    20.         Tags{ "RenderType"="Opaque" "Queue"="Geometry"}
    21.         LOD 150
    22.  
    23.         Pass
    24.         {
    25.             CGPROGRAM
    26.  
    27.             // Doesn't receive shadows, creating a surface program and method "surf" (excluded)
    28.             // results in the following error messages:
    29.             //
    30.             // "Shader error in 'TileMaps': CGPROGRAM cannot contain #pragma surface
    31.             // as well as other programs at line 25".
    32.             //
    33.             // "Shader error in 'TileMaps': Parse error: syntax error, unexpected TOK_PASS,
    34.             // expecting TOK_SETTEXTURE or '}' at line 26".
    35.             //#pragma surface surf Standard fullforwardshadows
    36.  
    37.             #pragma vertex vert
    38.             #pragma fragment frag
    39.  
    40.             // to use texture arrays we need to target DX10/OpenGLES3 which
    41.             // is shader model 3.5 minimum
    42.             #pragma target 3.5
    43.  
    44.             #include "UnityCG.cginc"
    45.            
    46.             sampler2D _MainTex;
    47.             float4 _MainTex_ST;
    48.  
    49.             // How to use 2DArray instead?!
    50.             sampler2D _Texture1, _Texture2, _Texture3, _Texture4;
    51.             UNITY_DECLARE_TEX2DARRAY(_TerrainTextures);
    52.  
    53.             struct VertexData
    54.             {
    55.                 float4 position : POSITION;
    56.                 float2 uv : TEXCOORD0;
    57.             };
    58.  
    59.             struct Interpolators
    60.             {
    61.                 float4 position : SV_POSITION;
    62.                 float2 uv : TEXCOORD0;
    63.                 float2 uvSplat : TEXCOORD1;
    64.             };
    65.  
    66.             Interpolators vert (VertexData v)
    67.             {
    68.                 Interpolators i;
    69.                 i.position = UnityObjectToClipPos(v.position);
    70.                 i.uv = TRANSFORM_TEX(v.uv, _MainTex);
    71.                 i.uvSplat = v.uv;
    72.                 return i;
    73.             }
    74.  
    75.             float4 frag (Interpolators i) : SV_TARGET
    76.             {
    77.                 // Apply using 2DArray based on colours from Biome texture (_MainTex),
    78.                 // I want to be able to use 16 textures and apply based on biome colours
    79.                 float4 splat = tex2D(_MainTex, i.uvSplat);
    80.                 return
    81.                     tex2D(_Texture1, i.uv) * splat.r +
    82.                     tex2D(_Texture2, i.uv) * splat.g +
    83.                     tex2D(_Texture3, i.uv) * splat.b +
    84.                     tex2D(_Texture4, i.uv) * (1 - splat.r - splat.g - splat.b);
    85.             }
    86.  
    87.             ENDCG
    88.         }
    89.     }
    90. }
    The C# code that configures the shader and textures:

    Code (CSharp):
    1. // Load textures, create Texture2D[] etc.
    2.  
    3.                 // Set the textures to a material
    4.                 tileRenderer.material.SetTexture("_MainTex", biomeProfileTexture);
    5.                 tileRenderer.material.SetTexture("_TerrainTextures", texture2DArray);
    6.                 tileRenderer.material.SetTexture("_Texture1", grassTexture8);
    7.                 tileRenderer.material.SetTexture("_Texture2", grassTexture5);
    8.                 tileRenderer.material.SetTexture("_Texture3", waterTexture1);
    9.                 tileRenderer.material.SetTexture("_Texture4", grassTexture11);
     

    Attached Files:

  2. tmcthee

    tmcthee

    Joined:
    Mar 8, 2013
    Posts:
    119
    What do you mean you don't want to be restricted to RGBA?
    And why don't you want to use 4 seperate textures?
     
  3. Micky_V

    Micky_V

    Joined:
    Jan 9, 2015
    Posts:
    7
    Probably worded that wrong! To be clear, my splat map produces 16 colours, for each of those 16 colours I want to assign a texture at that UV coordinate, I figured that using a texture 2DArray would be the cleanest way to achieve that, but I am unsure how to do it.

    With my Shader code above, I am only using 4 textures and assigning them based on the R, G , B, A values from the splat map UV's. For example, _Texture1 is assigned for "Red" UV cords:
    Code (CSharp):
    1. tex2D(_Texture1, i.uv) * splat.r
    Essentially, I want to be able to combine 16 textures from a 2DArray based on different shades of Green for example. With my shader, _Texture2 is assigned for green "splat.g". How can I assign for light green, green and dark green? Then apply the same concept for red, purple etc.
     
  4. tmcthee

    tmcthee

    Joined:
    Mar 8, 2013
    Posts:
    119
    what does your 16 colour splat map look like?
     
  5. Micky_V

    Micky_V

    Joined:
    Jan 9, 2015
    Posts:
    7
    Hi tmcthee,
    See attached for an example of part of my splat map.

    I have complete control over the colours that are generated. The algorithm that I have used to generate the splat map can be found here: https://gamedevacademy.org/complete-guide-to-procedural-level-generation-in-unity-part-2/

    However, I was thinking that I would use shades of Green for Grass textures, Blue for Water, Red for Rock textures and Yellow for other.

    The area that I am really stuck on is how to combine them all (16 textures) into a single texture in the frag method, as stated, my example works with 4 textures and uses R, G, B and A for texture assignment at UV coords. I need it for a 2DArray or something similar that will support 16 colours to textures using say, different shades of green:

    Code (CSharp):
    1.             float4 frag (vertOutput i) : SV_TARGET
    2.             {
    3.                 // Get splat map
    4.                 float4 splat = tex2D(_MainTex, i.uvSplat);
    5.  
    6.                 // How to combine 16 textures from 2DArray based on RGBA variations
    7.                 // and return as a single texture? (This does 4 textures)
    8.                 return
    9.                     tex2D(_Texture1, i.uv) * splat.r +
    10.                     tex2D(_Texture2, i.uv) * splat.g +
    11.                     tex2D(_Texture3, i.uv) * splat.b +
    12.                     tex2D(_Texture4, i.uv) * (1 - splat.r - splat.g - splat.b);
    13.             }
     

    Attached Files: