Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Resolved Issues with Array Size Mismatch and SetVectorArray in URP Custom Shader

Discussion in 'Shaders' started by JustTiredOfEverything, Sep 7, 2023.

  1. JustTiredOfEverything

    JustTiredOfEverything

    Joined:
    Aug 4, 2022
    Posts:
    83
    I'm new to creating custom shaders in the URP. My project involves creating procedural terrain using noise algorithms for elevation. I've also implemented a biome system using Voronoi diagrams.



    While I've successfully visualized the biomes using Unity's Gizmo class, my next goal is to texture the Voronoi regions with corresponding biome textures. I have attempted to accomplish this by creating a texture atlas in Unity and sampling from it in the shader. However, I'm running into issues with the SetVectorArray function and array handling in the shader.

    Code (CSharp):
    1. public void SetMaterialProperties (Material mat) {
    2.         biomeTex.Clear ();
    3.         for (int i = 0; i < biomes.Count; i++) {
    4.             biomeTex.Add (biomes[i].texture);
    5.         }
    6.         mat.SetTexture ("_BiomeTextureAtlas", CreateTextureAtlas (biomeTex));
    7.  
    8.         // Send Voronoi points to shader
    9.         Vector4[] voronoiPointsArray = new Vector4[256]; // Ensure a size of 256
    10.         for (int i = 0; i < biomePoints.Count && i < 256; i++) {
    11.             voronoiPointsArray[i] = new Vector4 (biomePoints[i].x, biomePoints[i].y, biomePoints[i].z, 0);
    12.         }
    13.         mat.SetVectorArray ("_VoronoiPoints", voronoiPointsArray);
    14.     }
    Error Message:
    The console outputs the following message:

    Code (CSharp):
    1. Property (_VoronoiPoints) exceeds previous array size (256 vs 1). Cap to previous size. Restart Unity to recreate the arrays.
    2. UnityEngine.Material:SetVectorArray (string,UnityEngine.Vector4[])
    3. MeshGen:SetMaterialProperties (UnityEngine.Material) (at Assets/Scripts/MeshGen.cs:69)
    4. MeshGen:CreateMesh (MeshGen,int) (at Assets/Scripts/MeshGen.cs:179)
    5. MeshGenEditor:OnInspectorGUI () (at Assets/Scripts/Editor/MeshGenEditor.cs:22)
    6. UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)

    It seems like there's a mismatch between the array lengths in the shader and the one I'm passing in. I don't understand how this can be.

    What does the message mean, particularly about the array size (256 vs 1)?
    Is SetVectorArray the correct function for what I'm trying to achieve?
    How can I successfully pass an array of biome textures to the shader and sample from it?

    I appreciate any help or insights.

    Code:

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. public class MeshGen : MonoBehaviour {
    5.     public bool autoUpdate;
    6.     public Vector2Int meshSize = Vector2Int.one;
    7.     public Vector2Int voronoiWindowSize = Vector2Int.one;
    8.     public int subdivisions = 1;
    9.     public List<NoiseLayer> noiseLayers;
    10.     public Material meshMaterial;
    11.     public bool generateOnStart;
    12.  
    13.     public Material waterMaterial;
    14.     public float seaLevel = 0;
    15.  
    16.     public Texture baseTexture;
    17.     public Texture seaFloorTexture;
    18.     [Range (0, 1)] public float transitionSmoothness = 0.5f;
    19.  
    20.     public Mesh mesh;
    21.     public Vector3[] vertices;
    22.     public int[] triangles;
    23.     public Vector2[] uvs;
    24.     public MeshFilter meshFilter;
    25.     public MeshRenderer meshRenderer;
    26.     [HideInInspector] public GameObject waterPlane;
    27.  
    28.     public float voronoiRegionSize = 5.0f;
    29.     public List<Vector3> voronoiPoints = new List<Vector3> ();
    30.     [HideInInspector] public List<int> voronoiPointBiomeTypes = new List<int> ();
    31.  
    32.     [System.Serializable]
    33.     public class NoiseLayer {
    34.         public float frequency = 1;
    35.         public float amplitude = 1;
    36.         public float offsetX = 0;
    37.         public float offsetY = 0;
    38.         public bool active = true;
    39.     }
    40.  
    41.     [System.Serializable]
    42.     public class Biome {
    43.         public string name;
    44.         public Texture2D texture;
    45.         public Color debugColor;
    46.     }
    47.     public List<Biome> biomes;
    48.     public int seed;
    49.     List<Texture2D> biomeTex = new List<Texture2D> ();
    50.  
    51.     void Start () {
    52.         if (generateOnStart) {
    53.             CreateMesh (this, seed);
    54.         }
    55.     }
    56.  
    57.     public void SetMaterialProperties (Material mat) {
    58.         biomeTex.Clear ();
    59.         for (int i = 0; i < biomes.Count; i++) {
    60.             biomeTex.Add (biomes[i].texture);
    61.         }
    62.         mat.SetTexture ("_BiomeTextureAtlas", CreateTextureAtlas (biomeTex));
    63.  
    64.         // Send Voronoi points to shader
    65.         Vector4[] voronoiPointsArray = new Vector4[256]; // Ensure a size of 256
    66.         for (int i = 0; i < voronoiPoints.Count && i < 256; i++) {
    67.             voronoiPointsArray[i] = new Vector4 (voronoiPoints[i].x, voronoiPoints[i].y, voronoiPoints[i].z, 0);
    68.         }
    69.         mat.SetVectorArray ("_VoronoiPoints", voronoiPointsArray);
    70.     }
    71.  
    72.  
    73.     void OnDrawGizmos () {
    74.         // Draw bounding box
    75.         Gizmos.color = Color.white;
    76.         Gizmos.DrawWireCube (Vector3.zero, new Vector3 (voronoiWindowSize.x, 0, voronoiWindowSize.y));
    77.  
    78.         // Draw Voronoi points
    79.         for (int i = 0; i < voronoiPoints.Count; i++) {
    80.             // Change color based on biome type
    81.             int biomeType = voronoiPointBiomeTypes[i];
    82.             if (biomeType >= 0 && biomeType < biomes.Count) {
    83.                 Gizmos.color = biomes[biomeType].debugColor;
    84.             }
    85.  
    86.             Gizmos.DrawSphere (voronoiPoints[i], 0.5f);
    87.         }
    88.  
    89.         // Draw Voronoi regions with biome debug colors
    90.         if (vertices != null) {
    91.             foreach (Vector3 vertex in vertices) {
    92.                 int closestPointIndex = FindClosestVoronoiPoint (voronoiPoints, vertex);
    93.                 int biomeType = voronoiPointBiomeTypes[closestPointIndex];
    94.                 if (biomeType >= 0 && biomeType < biomes.Count) {
    95.                     Gizmos.color = biomes[biomeType].debugColor;
    96.                 }
    97.                 Gizmos.DrawCube (vertex, Vector3.one * 0.1f);
    98.             }
    99.         }
    100.     }
    101.  
    102.     public static void GenerateVoronoiPoints (MeshGen meshGen, int seed) {
    103.         meshGen.voronoiPoints.Clear ();
    104.         meshGen.voronoiPointBiomeTypes.Clear ();
    105.  
    106.         System.Random pseudoRandom = new System.Random (seed);
    107.         int numberOfBiomes = meshGen.biomes.Count;
    108.  
    109.         Vector2Int size = meshGen.voronoiWindowSize;
    110.         float voronoiRegionSize = meshGen.voronoiRegionSize;
    111.  
    112.         int pointsX = Mathf.FloorToInt (size.x / 1.0f);
    113.         int pointsY = Mathf.FloorToInt (size.y / 1.0f);
    114.  
    115.         // Calculate the offsets to center the Voronoi points
    116.         float offsetX = (-0.5f * voronoiRegionSize * pointsX);
    117.         float offsetZ = (-0.5f * voronoiRegionSize * pointsY);
    118.  
    119.         for (int i = 0; i < pointsX; i++) {
    120.             for (int j = 0; j < pointsY; j++) {
    121.              
    122.                 float randomX = (float) pseudoRandom.NextDouble ();
    123.                 float randomZ = (float) pseudoRandom.NextDouble ();
    124.  
    125.              
    126.                 float x = (i + randomX) * voronoiRegionSize + offsetX;
    127.                 float z = (j + randomZ) * voronoiRegionSize + offsetZ;
    128.  
    129.                 meshGen.voronoiPoints.Add (new Vector3 (x, 0, z));
    130.  
    131.                 int biomeType = pseudoRandom.Next (0, numberOfBiomes);
    132.                 meshGen.voronoiPointBiomeTypes.Add (biomeType); // Store biome type for this point
    133.             }
    134.         }
    135.     }
    136.  
    137.     public static void CreateOrUpdateWater (MeshGen meshGen) {
    138.         Vector2Int size = meshGen.meshSize;
    139.         // Look for existing water plane under this GameObject
    140.         Transform existingWaterPlane = meshGen.transform.Find ("WaterPlane");
    141.  
    142.         if (existingWaterPlane != null) {
    143.             meshGen.waterPlane = existingWaterPlane.gameObject;
    144.         } else {
    145.             meshGen.waterPlane = GameObject.CreatePrimitive (PrimitiveType.Plane);
    146.             meshGen.waterPlane.name = "WaterPlane";
    147.             meshGen.waterPlane.transform.parent = meshGen.transform;
    148.         }
    149.  
    150.         meshGen.waterPlane.transform.position = new Vector3 (0f, meshGen.seaLevel, 0f);
    151.         meshGen.waterPlane.transform.localScale = new Vector3 (size.x / 10.0f, 1, size.y / 10.0f);
    152.  
    153.         MeshRenderer waterRenderer = meshGen.waterPlane.GetComponent<MeshRenderer> ();
    154.         if (meshGen.waterMaterial != null) {
    155.             waterRenderer.material = meshGen.waterMaterial;
    156.         }
    157.     }
    158.  
    159.     public static void CreateMesh (MeshGen meshGenInstance, int seed) {
    160.         GenerateVoronoiPoints (meshGenInstance, seed);
    161.         CreateOrUpdateWater (meshGenInstance);
    162.         if (meshGenInstance.meshFilter == null) {
    163.             meshGenInstance.meshFilter = meshGenInstance.gameObject.GetComponent<MeshFilter> ();
    164.             if (meshGenInstance.meshFilter == null) {
    165.                 meshGenInstance.meshFilter = meshGenInstance.gameObject.AddComponent<MeshFilter> ();
    166.             }
    167.         }
    168.         if (meshGenInstance.meshRenderer == null) {
    169.             meshGenInstance.meshRenderer = meshGenInstance.gameObject.GetComponent<MeshRenderer> ();
    170.             if (meshGenInstance.meshRenderer == null) {
    171.                 meshGenInstance.meshRenderer = meshGenInstance.gameObject.AddComponent<MeshRenderer> ();
    172.             }
    173.         }
    174.  
    175.         if (meshGenInstance.meshMaterial != null) {
    176.             if (Application.isPlaying) {
    177.                 meshGenInstance.SetMaterialProperties (meshGenInstance.meshRenderer.material);
    178.             } else {
    179.                 meshGenInstance.SetMaterialProperties (meshGenInstance.meshRenderer.sharedMaterial);
    180.             }
    181.         }
    182.  
    183.         meshGenInstance.mesh = new Mesh ();
    184.         meshGenInstance.meshFilter.mesh = meshGenInstance.mesh;
    185.  
    186.         int xSize = meshGenInstance.meshSize.x * (int) Mathf.Pow (2, meshGenInstance.subdivisions);
    187.         int zSize = meshGenInstance.meshSize.y * (int) Mathf.Pow (2, meshGenInstance.subdivisions);
    188.  
    189.         meshGenInstance.vertices = new Vector3[(xSize + 1) * (zSize + 1)];
    190.         meshGenInstance.uvs = new Vector2[meshGenInstance.vertices.Length];
    191.  
    192.         // Calculate offsets to ensure the mesh is centered at (0, 0)
    193.         float offsetX = xSize / 2.0f;
    194.         float offsetZ = zSize / 2.0f;
    195.  
    196.         for (int i = 0, z = 0; z <= zSize; z++) {
    197.             for (int x = 0; x <= xSize; x++) {
    198.                 float scaledX = (x - offsetX) / (float) Mathf.Pow (2, meshGenInstance.subdivisions);
    199.                 float scaledZ = (z - offsetZ) / (float) Mathf.Pow (2, meshGenInstance.subdivisions);
    200.                 float y = 0;
    201.  
    202.                 foreach (MeshGen.NoiseLayer layer in meshGenInstance.noiseLayers) {
    203.                     if (layer.active) {
    204.                         y += Mathf.PerlinNoise (scaledX * layer.frequency + layer.offsetX, scaledZ * layer.frequency + layer.offsetY) * layer.amplitude;
    205.                     }
    206.                 }
    207.  
    208.                 meshGenInstance.vertices[i] = new Vector3 (scaledX, y, scaledZ);
    209.                 meshGenInstance.uvs[i] = new Vector2 ((float) x / xSize, (float) z / zSize);
    210.  
    211.                 i++;
    212.             }
    213.         }
    214.  
    215.         meshGenInstance.triangles = new int[xSize * zSize * 6];
    216.  
    217.         for (int ti = 0, vi = 0, y = 0; y < zSize; y++, vi++) {
    218.             for (int x = 0; x < xSize; x++, ti += 6, vi++) {
    219.                 meshGenInstance.triangles[ti] = vi;
    220.                 meshGenInstance.triangles[ti + 3] = meshGenInstance.triangles[ti + 2] = vi + 1;
    221.                 meshGenInstance.triangles[ti + 4] = meshGenInstance.triangles[ti + 1] = vi + xSize + 1;
    222.                 meshGenInstance.triangles[ti + 5] = vi + xSize + 2;
    223.             }
    224.         }
    225.  
    226.         meshGenInstance.mesh.vertices = meshGenInstance.vertices;
    227.         meshGenInstance.mesh.uv = meshGenInstance.uvs;
    228.         meshGenInstance.mesh.triangles = meshGenInstance.triangles;
    229.  
    230.         meshGenInstance.mesh.RecalculateNormals ();
    231.     }
    232.  
    233.  
    234.     public static int FindClosestVoronoiPoint (List<Vector3> voronoiPoints, Vector3 target) {
    235.         float minDistanceSqr = float.MaxValue;
    236.         int closestIndex = -1;
    237.  
    238.         for (int i = 0; i < voronoiPoints.Count; i++) {
    239.             float distanceSqr = (voronoiPoints[i] - target).sqrMagnitude;
    240.             if (distanceSqr < minDistanceSqr) {
    241.                 minDistanceSqr = distanceSqr;
    242.                 closestIndex = i;
    243.             }
    244.         }
    245.  
    246.         return closestIndex;
    247.     }
    248.  
    249.     public static Texture2D CreateTextureAtlas (List<Texture2D> textures, int maxAtlasSize = 16) {
    250.         if (textures.Count == 0) {
    251.             return null;
    252.         }
    253.  
    254.         int textureWidth = textures[0].width;
    255.         int textureHeight = textures[0].height;
    256.  
    257.         int atlasWidth = textureWidth * maxAtlasSize;
    258.         int atlasHeight = textureHeight;
    259.  
    260.         Texture2D atlas = new Texture2D (atlasWidth, atlasHeight);
    261.  
    262.        
    263.         for (int i = 0; i < textures.Count; i++) {
    264.             Color[] pixelData = textures[i].GetPixels ();
    265.             atlas.SetPixels (i * textureWidth, 0, textureWidth, textureHeight, pixelData);
    266.         }
    267.  
    268.        
    269.         Color[] blankPixels = new Color[textureWidth * textureHeight];
    270.         for (int i = textures.Count; i < maxAtlasSize; i++) {
    271.             atlas.SetPixels (i * textureWidth, 0, textureWidth, textureHeight, blankPixels);
    272.         }
    273.  
    274.         atlas.Apply ();
    275.  
    276.         return atlas;
    277.     }
    278. }
    Shader code:


    Code (CSharp):
    1. Shader "Custom/VoronoiBiomeShader"
    2. {
    3.     Properties
    4.     {
    5.         _BiomeTextureAtlas ("Biome Texture Atlas", 2D) = "white" {}
    6.         _VoronoiPoints ("Voronoi Points", Vector) = (0,0,0,0)
    7.     }
    8.  
    9.     SubShader
    10.     {
    11.         Tags { "RenderType"="Opaque" "Queue"="Geometry" }
    12.  
    13.         Pass
    14.         {
    15.             Name "ForwardBase"
    16.             CGPROGRAM
    17.             #pragma target 3.0
    18.             #pragma vertex vert
    19.             #pragma fragment frag
    20.  
    21.             #include "UnityCG.cginc"
    22.  
    23.             struct appdata_t
    24.             {
    25.                 float4 vertex : POSITION;
    26.                 float2 uv : TEXCOORD0;
    27.             };
    28.  
    29.             struct v2f
    30.             {
    31.                 float2 uv : TEXCOORD0;
    32.                 float3 worldPos : TEXCOORD1;
    33.                 float4 vertex : SV_POSITION;
    34.             };
    35.  
    36.             sampler2D _BiomeTextureAtlas;
    37.             float4 _VoronoiPoints[256]; // Set of voronoi points. Max 256
    38.  
    39.             v2f vert(appdata_t v)
    40.             {
    41.                 v2f o;
    42.                 o.vertex = UnityObjectToClipPos(v.vertex);
    43.                 o.uv = v.uv;
    44.                 o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
    45.                 return o;
    46.             }
    47.  
    48.             float4 frag(v2f i) : SV_Target
    49.             {
    50.                 //FindClosestVoronoiPoint
    51.                 float closestDist = 99999999.0;
    52.                 int closestPointIndex = -1;
    53.  
    54.                 for (int j = 0; j < 256; j++)
    55.                 {
    56.                     float dist = distance(i.worldPos, _VoronoiPoints[j].xyz);
    57.                     if (dist < closestDist)
    58.                     {
    59.                         closestDist = dist;
    60.                         closestPointIndex = j;
    61.                     }
    62.                 }
    63.  
    64.                 float atlasWidth = 1.0 / 16.0;
    65.                 float2 biomeTexUV = float2(atlasWidth * closestPointIndex + i.uv.x * atlasWidth, i.uv.y);
    66.              
    67.                 float4 biomeColor = tex2D(_BiomeTextureAtlas, biomeTexUV);
    68.                 return biomeColor;
    69.             }
    70.             ENDCG
    71.         }
    72.     }
    73. }
    74.  
     
    Last edited: Sep 7, 2023
  2. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    3,046
    You're declaring your points as a single
    float4
    in the Properties section of the shader. Simply remove this line.
     
    JustTiredOfEverything likes this.
  3. JustTiredOfEverything

    JustTiredOfEverything

    Joined:
    Aug 4, 2022
    Posts:
    83
    Thank you. I have very little written shaders experience and honestly, I had no idea what I was doing half the time. I've been hammering at this issue for like three to four days straight. Removing that line indeed fixed the array issue. It was honestly that simple -_-

    Error is gone, and the shader worked with some small extra tweaks.

    Here's how it looks:



    shader code:

    Code (CSharp):
    1. Shader "Custom/VoronoiBiomeShader"
    2. {
    3.     Properties
    4.     {
    5.         _BiomeTextureAtlas ("Biome Texture Atlas", 2D) = "white" {}
    6.    
    7.     }
    8.  
    9.     SubShader
    10.     {
    11.         Tags { "RenderType"="Opaque" "Queue"="Geometry" }
    12.  
    13.         Pass
    14.         {
    15.             Name "ForwardBase"
    16.             CGPROGRAM
    17.             #pragma target 3.0
    18.             #pragma vertex vert
    19.             #pragma fragment frag
    20.  
    21.             #include "UnityCG.cginc"
    22.  
    23.             struct appdata_t
    24.             {
    25.                 float4 vertex : POSITION;
    26.                 float2 uv : TEXCOORD0;
    27.             };
    28.  
    29.             struct v2f
    30.             {
    31.                 float2 uv : TEXCOORD0;
    32.                 float3 worldPos : TEXCOORD1;
    33.                 float4 vertex : SV_POSITION;
    34.             };
    35.  
    36.             sampler2D _BiomeTextureAtlas;
    37.             float4 _VoronoiPoints[256]; // Set of voronoi points. Max 256
    38.             float4 _BiomeValues[256]; // Set of voronoi points. Max 256
    39.  
    40.             v2f vert(appdata_t v)
    41.             {
    42.                 v2f o;
    43.                 o.vertex = UnityObjectToClipPos(v.vertex);
    44.                 o.uv = v.uv;
    45.                 o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
    46.                 return o;
    47.             }
    48.  
    49.             float4 frag(v2f i) : SV_Target
    50.             {
    51.                 //FindClosestVoronoiPoint
    52.                 float closestDist = 99999999.0;
    53.                 int closestPointIndex = -1;
    54.  
    55.                 for (int j = 0; j < 256; j++)
    56.                 {
    57.                     float dist = distance(i.worldPos, _VoronoiPoints[j].xyz);
    58.                     if (dist < closestDist)
    59.                     {
    60.                         closestDist = dist;
    61.                         closestPointIndex = j;
    62.                     }
    63.                 }
    64.                 int index = (int) _BiomeValues[closestPointIndex].x;
    65.                 float atlasWidth = 1.0 / 16.0;
    66.                 float2 biomeTexUV = float2(atlasWidth * index + i.uv.x * atlasWidth, i.uv.y);
    67.              
    68.                 float4 biomeColor = tex2D(_BiomeTextureAtlas, biomeTexUV);
    69.                 return biomeColor;
    70.             }
    71.             ENDCG
    72.         }
    73.     }
    74. }
    75.  

    c# code:

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. public class MeshGen : MonoBehaviour {
    5.     public bool autoUpdate;
    6.     public Vector2Int meshSize = Vector2Int.one;
    7.     public Vector2Int voronoiWindowSize = Vector2Int.one;
    8.     public int subdivisions = 1;
    9.     public List<NoiseLayer> noiseLayers;
    10.     public Material meshMaterial;
    11.     public bool generateOnStart;
    12.  
    13.     public Material waterMaterial;
    14.     public float seaLevel = 0;
    15.  
    16.     public Texture baseTexture;
    17.     public Texture seaFloorTexture;
    18.     [Range (0, 1)] public float transitionSmoothness = 0.5f;
    19.  
    20.     public Mesh mesh;
    21.     public Vector3[] vertices;
    22.     public int[] triangles;
    23.     public Vector2[] uvs;
    24.     public MeshFilter meshFilter;
    25.     public MeshRenderer meshRenderer;
    26.     [HideInInspector] public GameObject waterPlane;
    27.  
    28.     public float voronoiRegionSize = 5.0f;
    29.     public List<Vector3> voronoiPoints = new List<Vector3> ();
    30.     [HideInInspector] public List<int> voronoiPointBiomeTypes = new List<int> ();
    31.  
    32.     [System.Serializable]
    33.     public class NoiseLayer {
    34.         public float frequency = 1;
    35.         public float amplitude = 1;
    36.         public float offsetX = 0; // Added offset for X
    37.         public float offsetY = 0; // Added offset for Y
    38.         public bool active = true;
    39.     }
    40.  
    41.     [System.Serializable]
    42.     public class Biome {
    43.         public string name;
    44.         public Texture2D texture;
    45.         public Color debugColor;
    46.     }
    47.     public List<Biome> biomes;
    48.     public int seed;
    49.     List<Texture2D> biomeTex = new List<Texture2D> ();
    50.  
    51.     void Start () {
    52.         if (generateOnStart) {
    53.             CreateMesh (this, seed);
    54.         }
    55.     }
    56.  
    57.     public void SetMaterialProperties (Material mat) {
    58.         biomeTex.Clear ();
    59.         for (int i = 0; i < biomes.Count; i++) {
    60.             biomeTex.Add (biomes[i].texture);
    61.         }
    62.         mat.SetTexture ("_BiomeTextureAtlas", CreateTextureAtlas (biomeTex));
    63.  
    64.         // Send Voronoi points to shader
    65.         Vector4[] voronoiPointsArray = new Vector4[256]; // Ensure a size of 256
    66.         for (int i = 0; i < voronoiPoints.Count && i < 256; i++) {
    67.             voronoiPointsArray[i] = new Vector4 (voronoiPoints[i].x, voronoiPoints[i].y, voronoiPoints[i].z, 0);
    68.         }
    69.         mat.SetVectorArray ("_VoronoiPoints", voronoiPointsArray);
    70.  
    71.          // Send point biomes to shader
    72.         Vector4[] biomeValuesArray = new Vector4[256]; // Ensure a size of 256
    73.         for (int i = 0; i < voronoiPoints.Count && i < 256; i++) {
    74.             biomeValuesArray[i] = new Vector4 (voronoiPointBiomeTypes[i], 0, 0, 0);
    75.         }
    76.         mat.SetVectorArray ("_BiomeValues", biomeValuesArray);
    77.     }
    78.  
    79.  
    80.     void OnDrawGizmos () {
    81.         // Draw bounding box
    82.         Gizmos.color = Color.white;
    83.         Gizmos.DrawWireCube (Vector3.zero, new Vector3 (voronoiWindowSize.x, 0, voronoiWindowSize.y));
    84.  
    85.         // Draw Voronoi points
    86.         for (int i = 0; i < voronoiPoints.Count; i++) {
    87.             // Change color based on biome type
    88.             int biomeType = voronoiPointBiomeTypes[i];
    89.             if (biomeType >= 0 && biomeType < biomes.Count) {
    90.                 Gizmos.color = biomes[biomeType].debugColor;
    91.             }
    92.  
    93.             Gizmos.DrawSphere (voronoiPoints[i], 0.5f);
    94.         }
    95.  
    96.         // Draw Voronoi regions with biome debug colors
    97.         if (vertices != null) {
    98.             foreach (Vector3 vertex in vertices) {
    99.                 int closestPointIndex = FindClosestVoronoiPoint (voronoiPoints, vertex);
    100.                 int biomeType = voronoiPointBiomeTypes[closestPointIndex];
    101.                 if (biomeType >= 0 && biomeType < biomes.Count) {
    102.                     Gizmos.color = biomes[biomeType].debugColor;
    103.                 }
    104.                 Gizmos.DrawCube (vertex, Vector3.one * 0.1f);
    105.             }
    106.         }
    107.     }
    108.  
    109.     public static void GenerateVoronoiPoints (MeshGen meshGen, int seed) {
    110.         meshGen.voronoiPoints.Clear ();
    111.         meshGen.voronoiPointBiomeTypes.Clear ();
    112.  
    113.         System.Random pseudoRandom = new System.Random (seed);
    114.         int numberOfBiomes = meshGen.biomes.Count;
    115.  
    116.         Vector2Int size = meshGen.voronoiWindowSize;
    117.         float voronoiRegionSize = meshGen.voronoiRegionSize;
    118.  
    119.         int pointsX = Mathf.FloorToInt (size.x / 1.0f);
    120.         int pointsY = Mathf.FloorToInt (size.y / 1.0f);
    121.  
    122.         // Calculate the offsets to center the Voronoi points
    123.         float offsetX = (-0.5f * voronoiRegionSize * pointsX);
    124.         float offsetZ = (-0.5f * voronoiRegionSize * pointsY);
    125.  
    126.         for (int i = 0; i < pointsX; i++) {
    127.             for (int j = 0; j < pointsY; j++) {
    128.              
    129.                 float randomX = (float) pseudoRandom.NextDouble ();
    130.                 float randomZ = (float) pseudoRandom.NextDouble ();
    131.  
    132.              
    133.                 float x = (i + randomX) * voronoiRegionSize + offsetX;
    134.                 float z = (j + randomZ) * voronoiRegionSize + offsetZ;
    135.  
    136.                 meshGen.voronoiPoints.Add (new Vector3 (x, 0, z));
    137.  
    138.                 int biomeType = pseudoRandom.Next (0, numberOfBiomes);
    139.                 meshGen.voronoiPointBiomeTypes.Add (biomeType); // Store biome type for this point
    140.             }
    141.         }
    142.     }
    143.  
    144.     public static void CreateOrUpdateWater (MeshGen meshGen) {
    145.         Vector2Int size = meshGen.meshSize;
    146.         // Look for existing water plane under this GameObject
    147.         Transform existingWaterPlane = meshGen.transform.Find ("WaterPlane");
    148.  
    149.         if (existingWaterPlane != null) {
    150.             meshGen.waterPlane = existingWaterPlane.gameObject;
    151.         } else {
    152.             meshGen.waterPlane = GameObject.CreatePrimitive (PrimitiveType.Plane);
    153.             meshGen.waterPlane.name = "WaterPlane";
    154.             meshGen.waterPlane.transform.parent = meshGen.transform;
    155.         }
    156.  
    157.         meshGen.waterPlane.transform.position = new Vector3 (0f, meshGen.seaLevel, 0f);
    158.         meshGen.waterPlane.transform.localScale = new Vector3 (size.x / 10.0f, 1, size.y / 10.0f);
    159.  
    160.         MeshRenderer waterRenderer = meshGen.waterPlane.GetComponent<MeshRenderer> ();
    161.         if (meshGen.waterMaterial != null) {
    162.             waterRenderer.material = meshGen.waterMaterial;
    163.         }
    164.     }
    165.  
    166.     public static void CreateMesh (MeshGen meshGenInstance, int seed) {
    167.         GenerateVoronoiPoints (meshGenInstance, seed);
    168.         CreateOrUpdateWater (meshGenInstance);
    169.         if (meshGenInstance.meshFilter == null) {
    170.             meshGenInstance.meshFilter = meshGenInstance.gameObject.GetComponent<MeshFilter> ();
    171.             if (meshGenInstance.meshFilter == null) {
    172.                 meshGenInstance.meshFilter = meshGenInstance.gameObject.AddComponent<MeshFilter> ();
    173.             }
    174.         }
    175.         if (meshGenInstance.meshRenderer == null) {
    176.             meshGenInstance.meshRenderer = meshGenInstance.gameObject.GetComponent<MeshRenderer> ();
    177.             if (meshGenInstance.meshRenderer == null) {
    178.                 meshGenInstance.meshRenderer = meshGenInstance.gameObject.AddComponent<MeshRenderer> ();
    179.             }
    180.         }
    181.  
    182.         if (meshGenInstance.meshMaterial != null) {
    183.             if (Application.isPlaying) {
    184.                 meshGenInstance.SetMaterialProperties (meshGenInstance.meshRenderer.material);
    185.             } else {
    186.                 meshGenInstance.SetMaterialProperties (meshGenInstance.meshRenderer.sharedMaterial);
    187.             }
    188.         }
    189.  
    190.         meshGenInstance.mesh = new Mesh ();
    191.         meshGenInstance.meshFilter.mesh = meshGenInstance.mesh;
    192.  
    193.         int xSize = meshGenInstance.meshSize.x * (int) Mathf.Pow (2, meshGenInstance.subdivisions);
    194.         int zSize = meshGenInstance.meshSize.y * (int) Mathf.Pow (2, meshGenInstance.subdivisions);
    195.  
    196.         meshGenInstance.vertices = new Vector3[(xSize + 1) * (zSize + 1)];
    197.         meshGenInstance.uvs = new Vector2[meshGenInstance.vertices.Length];
    198.  
    199.         // Calculate offsets to ensure the mesh is centered at (0, 0)
    200.         float offsetX = xSize / 2.0f;
    201.         float offsetZ = zSize / 2.0f;
    202.  
    203.         for (int i = 0, z = 0; z <= zSize; z++) {
    204.             for (int x = 0; x <= xSize; x++) {
    205.                 float scaledX = (x - offsetX) / (float) Mathf.Pow (2, meshGenInstance.subdivisions);
    206.                 float scaledZ = (z - offsetZ) / (float) Mathf.Pow (2, meshGenInstance.subdivisions);
    207.                 float y = 0;
    208.  
    209.                 foreach (MeshGen.NoiseLayer layer in meshGenInstance.noiseLayers) {
    210.                     if (layer.active) {
    211.                         y += Mathf.PerlinNoise (scaledX * layer.frequency + layer.offsetX, scaledZ * layer.frequency + layer.offsetY) * layer.amplitude;
    212.                     }
    213.                 }
    214.  
    215.                 meshGenInstance.vertices[i] = new Vector3 (scaledX, y, scaledZ);
    216.                 meshGenInstance.uvs[i] = new Vector2 ((float) x / xSize, (float) z / zSize);
    217.  
    218.                 i++;
    219.             }
    220.         }
    221.  
    222.         meshGenInstance.triangles = new int[xSize * zSize * 6];
    223.  
    224.         for (int ti = 0, vi = 0, y = 0; y < zSize; y++, vi++) {
    225.             for (int x = 0; x < xSize; x++, ti += 6, vi++) {
    226.                 meshGenInstance.triangles[ti] = vi;
    227.                 meshGenInstance.triangles[ti + 3] = meshGenInstance.triangles[ti + 2] = vi + 1;
    228.                 meshGenInstance.triangles[ti + 4] = meshGenInstance.triangles[ti + 1] = vi + xSize + 1;
    229.                 meshGenInstance.triangles[ti + 5] = vi + xSize + 2;
    230.             }
    231.         }
    232.  
    233.         meshGenInstance.mesh.vertices = meshGenInstance.vertices;
    234.         meshGenInstance.mesh.uv = meshGenInstance.uvs;
    235.         meshGenInstance.mesh.triangles = meshGenInstance.triangles;
    236.  
    237.         meshGenInstance.mesh.RecalculateNormals ();
    238.     }
    239.  
    240.  
    241.     public static int FindClosestVoronoiPoint (List<Vector3> voronoiPoints, Vector3 target) {
    242.         float minDistanceSqr = float.MaxValue;
    243.         int closestIndex = -1;
    244.  
    245.         for (int i = 0; i < voronoiPoints.Count; i++) {
    246.             float distanceSqr = (voronoiPoints[i] - target).sqrMagnitude;
    247.             if (distanceSqr < minDistanceSqr) {
    248.                 minDistanceSqr = distanceSqr;
    249.                 closestIndex = i;
    250.             }
    251.         }
    252.  
    253.         return closestIndex;
    254.     }
    255.  
    256.     public static Texture2D CreateTextureAtlas (List<Texture2D> textures, int maxAtlasSize = 16) {
    257.         if (textures.Count == 0) {
    258.             return null;
    259.         }
    260.  
    261.         int textureWidth = textures[0].width;
    262.         int textureHeight = textures[0].height;
    263.  
    264.         int atlasWidth = textureWidth * maxAtlasSize;
    265.         int atlasHeight = textureHeight;
    266.  
    267.         Texture2D atlas = new Texture2D (atlasWidth, atlasHeight);
    268.  
    269.         // Filling in provided textures
    270.         for (int i = 0; i < textures.Count; i++) {
    271.             Color[] pixelData = textures[i].GetPixels ();
    272.             atlas.SetPixels (i * textureWidth, 0, textureWidth, textureHeight, pixelData);
    273.         }
    274.  
    275.         // Filling in blank textures for missing slots
    276.         Color[] blankPixels = new Color[textureWidth * textureHeight];
    277.         for (int i = textures.Count; i < maxAtlasSize; i++) {
    278.             atlas.SetPixels (i * textureWidth, 0, textureWidth, textureHeight, blankPixels);
    279.         }
    280.  
    281.         atlas.Apply ();
    282.  
    283.         return atlas;
    284.     }
    285. }

    Thanks again for your help!
     
    aleksandrk likes this.