• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by Razor1994 · Jul 01, 2019 at 06:30 PM · terrainoptimizationproceduralvoxel

[Optimization] How can i optimize this voxel terrain more?

Hey there :)

so over the last few weeks i tried to optimize my terrain generation because it is still a bit too slow.

What i´ve done so far:


  1. Create the visible faces using quads.

  2. Combine the quads to a block

  3. Create chunks of blocks

  4. Combine all blocks in a chunk


i´ve tried adding some time in the coroutines to minimize the fps hit but this does slow down the generation too much and doesn´t seem to be the correct way of doing this.


I´d be grateful if someone has the time to take a look at the scripts :)

Thanks in advance.


Here are the main scripts


  1. Generator

  2. Chunk

  3. CreateQuads


I am sorry if this is a bit too hard to read in this editor here.

Here is the main generator

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 using LibNoise.Generator;
 using Realtime.Messaging.Internal;
 using TerrainGenerator;
 using System.Threading;
 
 public class Generator : MonoBehaviour
 {
 
     public static Generator generator;
     public bool generateChunks = true;
     public bool removeChunks = true;
 
     public int removeDistanceInChunks;
     public int renderDistance;
     public int HeightmapResolution;
     public int chunkHeight;
     public Material cubeMaterial;
     private Perlin perlinNoiseGenerator;
 
     public Transform player;
     public static ConcurrentDictionary<string, Chunk> chunks;
 
     public double _frequency = 1.0;
     public double _lacunarity = 2.0;
     public int _octaveCount = 6;
     public double _persistence = 0.5;
     public int _seed;
 
     public CoroutineQueue queue;
     public static uint maxCoroutines = 1500;
 
     public CoroutineQueue buildFinishQueue;
     public static uint maxBuildFinishCoroutines = 1500;
 
     public Vector3 lastbuildPos;
     private Vector3 savedPlayerPos;
 
     void Start()
     {
         generator = this;
         Loom.Initialize();
         player.gameObject.SetActive(false);
         chunks = new ConcurrentDictionary<string, Chunk>();
         player.position = new Vector3(0, 2, 0);
         perlinNoiseGenerator = new Perlin();
         queue = new CoroutineQueue(maxCoroutines, StartCoroutine);
         buildFinishQueue = new CoroutineQueue(maxBuildFinishCoroutines, StartCoroutine);
         perlinNoiseGenerator.Frequency = _frequency;
         perlinNoiseGenerator.Lacunarity = _lacunarity;
         perlinNoiseGenerator.OctaveCount = _octaveCount;
         perlinNoiseGenerator.Persistence = _persistence;
         perlinNoiseGenerator.Seed = _seed;
         Generate();
     }
 
 
     void Update()
     {
         savedPlayerPos.x = player.position.x;
         savedPlayerPos.z = player.position.z;
 
         UpdateTerrain(savedPlayerPos, renderDistance);
     }
 
     public void Generate()
     {
         var newChunks = GetChunkPositionsInRadius(GetChunkPosition(new Vector3(0, 0, 0)), renderDistance);
         foreach (var position in newChunks)
         {
             GenerateChunk(position.X, position.Z);
         }
 
         player.gameObject.SetActive(true);
 
     }
 
     public void UpdateTerrain(Vector3 worldPosition, int radius)
     {
         Vector3 movement = lastbuildPos - worldPosition;
         if (movement.magnitude > HeightmapResolution)
         {
             queue.Run(BuildWorld(worldPosition, radius));
         }
     }
 
     IEnumerator BuildWorld(Vector3 worldPosition, int radius)
     {
         lastbuildPos = worldPosition;
         var newChunks = GetChunkPositionsInRadius(GetChunkPosition(worldPosition), radius);
 
         StartCoroutine(GenerateChunkTimed(newChunks));
         yield return new WaitForSeconds(0.2f);
         if (removeChunks)
             queue.Run(RemoveChunksOutOfRangeEnum(removeDistanceInChunks));
     }
 
     IEnumerator GenerateChunkTimed(List<Vector2i> newChunks)
     {
         foreach (var position in newChunks)
         {
             if (generateChunks)
             {
                 if (!CheckObsoleteChunk(position.X, position.Z))
                 {
                     GenerateChunk(position.X, position.Z, true);
                     yield return new WaitForSeconds(0.2f);
                 }
             }
 
 
         }
     }
 
     IEnumerator RemoveChunksOutOfRangeEnum(int distanceInChunks)
     {
         foreach (var chunk in chunks)
         {
             float dist = Vector3.Distance(new Vector3(chunk.Value.Position.X, 0, chunk.Value.Position.Z), player.position);
 
             if (dist < 0)
                 dist = dist * -1;
 
             if (dist >= distanceInChunks * HeightmapResolution)
             {
                 RemoveChunk(chunk.Value);
                 yield return null;
             }
         }
     }
 
     public Vector2i GetChunkPosition(Vector3 worldPosition)
     {
         var x = (int)Mathf.Floor(worldPosition.x / HeightmapResolution);
         var z = (int)Mathf.Floor(worldPosition.z / HeightmapResolution);
 
         return new Vector2i(x, z);
     }
 
     private List<Vector2i> GetChunkPositionsInRadius(Vector2i chunkPosition, int radius)
     {
         var result = new List<Vector2i>();
 
         for (var zCircle = -radius; zCircle <= radius; zCircle++)
         {
             for (var xCircle = -radius; xCircle <= radius; xCircle++)
             {
                 if (xCircle * xCircle + zCircle * zCircle < radius * radius)
                     result.Add(new Vector2i((chunkPosition.X + xCircle) * HeightmapResolution, (chunkPosition.Z + zCircle) * HeightmapResolution));
             }
         }
 
         return result;
     }
 
     public bool CheckObsoleteChunk(int x, int z)
     {
         if (chunks.ContainsKey(x + ", " + z))
             return true;
 
         return false;
     }
 
     public void GenerateChunk(int x, int z, bool newGeneration = false)
     {
         if (!CheckObsoleteChunk(x, z))
             chunks.TryAdd(x + ", " + z, new Chunk(HeightmapResolution, chunkHeight, x, z, cubeMaterial, perlinNoiseGenerator, newGeneration));
     }
 
     public void RemoveChunk(Chunk ch)
     {
         Chunk o;
         Destroy(ch.spawnObject.gameObject);
 
         chunks.TryRemove(ch.Position.X + ", " + ch.Position.Z, out o);
         ch = null;
     }
 
 }
 


Chunk:

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 using TerrainGenerator;
 using System;
 using LibNoise.Generator;
 using System.Threading;
 
 public class Chunk
 {
 
     public Transform spawnObject;
 
     public Material cubeMaterial;
 
     public Vector2i Position;
 
     public float[,] Heightmap;
 
     private List<Vector3> blockPos = new List<Vector3>();
 
     public GameObject[] blocks;
 
     public int HeightmapResolution;
 
     private Perlin PerlinNoiseGenerator;
 
     public int chunkHeight;
 
     private static Generator generator = null;
 
     private Thread generateHeightMapThread;
 
     private bool newGeneration = false;
 
     public Chunk(int HeightmapResolution, int cHeight, int x, int z, Material mat, Perlin p, bool newGeneration)
     {
         if (generator == null)
             generator = Generator.generator;
         spawnObject = new GameObject("Chunk").transform;
 
         this.newGeneration = newGeneration;
 
         spawnObject.position = new Vector3(x, 0, z);
         chunkHeight = cHeight;
         blocks = new GameObject[(HeightmapResolution * HeightmapResolution) * chunkHeight];
 
         cubeMaterial = mat;
         Position = new Vector2i(x, z);
         this.HeightmapResolution = HeightmapResolution;
         PerlinNoiseGenerator = p;
 
         //generateHeightMapThread =
         //     new Thread(
         //       unused => GenerateHeightmapThread()
         //     );
         //generateHeightMapThread.Start();
 
         GenerateHeightmapThread();
 
     }
 
     private static float FastFloor(double f)
     {
         return (f >= 0.0f ? (int)f : (int)f - 1);
     }
 
     public void GenerateHeightmapThread()
     {
         var heightmap = new float[HeightmapResolution, HeightmapResolution];
 
         for (var zRes = 0; zRes < HeightmapResolution; zRes++)
         {
             for (var xRes = 0; xRes < HeightmapResolution; xRes++)
             {
                 var xCoordinate = Position.X + xRes;
                 var zCoordinate = Position.Z + zRes;
 
                 //  float height = (float)Math.Floor(PerlinNoiseGenerator.GetValue(xCoordinate, 0, zCoordinate) * 10);
                 float height = FastFloor(PerlinNoiseGenerator.GetValue(xCoordinate, 0, zCoordinate) * 10);
 
                 if (height < 0)
                     height *= -1;
 
                 heightmap[zRes, xRes] = height;
             }
         }
 
         Heightmap = heightmap;
 
         // Loom.QueueOnMainThread(() =>
         //   {
         if (newGeneration)
             generator.queue.Run(BuildChunkE());
         else
             BuildChunk();
         //   });
     }
 
 
 
     public void BuildChunk()
     {
         // generateHeightMapThread.Abort();
 
         int count = 0;
 
         for (int z = 0; z < HeightmapResolution; z++)
         {
             for (int x = 0; x < HeightmapResolution; x++)
             {
                 GameObject newBlock;
 
                 for (int i = 0; i < chunkHeight; i++)
                 {
                     newBlock = new GameObject("cube" + z + " " + x);
                     newBlock.AddComponent<CreateQuads>();
                     newBlock.GetComponent<CreateQuads>().owner = newBlock;
                     newBlock.GetComponent<CreateQuads>().cubeMaterial = cubeMaterial;
 
                     float y = Heightmap[z, x] - i;
 
                     if (y == 0)
                         newBlock.GetComponent<CreateQuads>().bType = CreateQuads.BlockType.GRASS;
                     else if (y == 1)
                         newBlock.GetComponent<CreateQuads>().bType = CreateQuads.BlockType.DIRT;
                     else
                         newBlock.GetComponent<CreateQuads>().bType = CreateQuads.BlockType.STONE;
 
                     count++;
                     newBlock.transform.parent = spawnObject.transform;
 
                     newBlock.transform.position = new Vector3(Position.X + x, y, Position.Z + z);
 
                     blocks[count - 1] = newBlock;
                     //blockPos.Add(new Vector3(newBlock.transform.position.x, newBlock.transform.position.y, newBlock.transform.position.z));
                     blockPos.Add(newBlock.transform.position);
                 }
 
             }
         }
 
         foreach (GameObject block in blocks)
         {
             bool front, back, top, bottom, left, right;
             front = back = top = bottom = left = right = false;
 
             Vector3 position = block.transform.position;
             if (!blockPos.Contains(new Vector3((int)position.x + 1, (int)position.y, (int)position.z)))
             {
                 //DRAW
                 right = true;
             }
             if (!blockPos.Contains(new Vector3((int)position.x - 1, (int)position.y, (int)position.z)))
             {
                 //DRAW
                 left = true;
             }
             if (!blockPos.Contains(new Vector3((int)position.x, (int)position.y + 1, (int)position.z)))
             {
                 //DRAW
                 top = true;
             }
             //if (!blockPos.Contains(new Vector3((int)position.x, (int)position.y - 1, (int)position.z)))
             //{
             //    //DRAW
             //    bottom = true;
             //}
             if (!blockPos.Contains(new Vector3((int)position.x, (int)position.y, (int)position.z + 1)))
             {
                 //DRAW
                 front = true;
             }
             if (!blockPos.Contains(new Vector3((int)position.x, (int)position.y, (int)position.z - 1)))
             {
                 //DRAW
                 back = true;
             }
 
             block.GetComponent<CreateQuads>().CreateCube(front, back, top, bottom, left, right);
         }
 
         spawnObject.gameObject.AddComponent<Combine>().cubeMaterial = cubeMaterial;
         spawnObject.GetComponent<Combine>().NewCombine();
     }
 
     IEnumerator BuildChunkE()
     {
         int count = 0;
 
         for (int z = 0; z < HeightmapResolution; z++)
         {
             for (int x = 0; x < HeightmapResolution; x++)
             {
 
                 GameObject newBlock;
 
                 for (int i = 0; i < chunkHeight; i++)
                 {
                     newBlock = new GameObject("cube" + z + " " + x);
                     newBlock.AddComponent<CreateQuads>();
                     newBlock.GetComponent<CreateQuads>().owner = newBlock;
                     newBlock.GetComponent<CreateQuads>().cubeMaterial = cubeMaterial;
 
                     float y = Heightmap[z, x] - i;
 
                     if (y == 0)
                         newBlock.GetComponent<CreateQuads>().bType = CreateQuads.BlockType.GRASS;
                     else if (y == 1)
                         newBlock.GetComponent<CreateQuads>().bType = CreateQuads.BlockType.DIRT;
                     else
                         newBlock.GetComponent<CreateQuads>().bType = CreateQuads.BlockType.STONE;
 
                     count++;
                     newBlock.transform.parent = spawnObject.transform;
 
                     newBlock.transform.position = new Vector3(Position.X + x, y, Position.Z + z);
 
                     blocks[count - 1] = newBlock;
                     //blockPos.Add(new Vector3(newBlock.transform.position.x, newBlock.transform.position.y, newBlock.transform.position.z));
                     blockPos.Add(newBlock.transform.position);
                 }
 
             }
         }
         yield return null;
         foreach (GameObject block in blocks)
         {
             bool front, back, top, bottom, left, right;
             front = back = top = bottom = left = right = false;
 
             Vector3 position = block.transform.position;
             if (!blockPos.Contains(new Vector3((int)position.x + 1, (int)position.y, (int)position.z)))
             {
                 //DRAW
                 right = true;
             }
             if (!blockPos.Contains(new Vector3((int)position.x - 1, (int)position.y, (int)position.z)))
             {
                 //DRAW
                 left = true;
             }
             if (!blockPos.Contains(new Vector3((int)position.x, (int)position.y + 1, (int)position.z)))
             {
                 //DRAW
                 top = true;
             }
             //if (!blockPos.Contains(new Vector3((int)position.x, (int)position.y - 1, (int)position.z)))
             //{
             //    //DRAW
             //    bottom = true;
             //}
             if (!blockPos.Contains(new Vector3((int)position.x, (int)position.y, (int)position.z + 1)))
             {
                 //DRAW
                 front = true;
             }
             if (!blockPos.Contains(new Vector3((int)position.x, (int)position.y, (int)position.z - 1)))
             {
                 //DRAW
                 back = true;
             }
 
             block.GetComponent<CreateQuads>().CreateCube(front, back, top, bottom, left, right);
         }
 
         generator.queue.Run(finishBuild());
     }
 
 
     public IEnumerator finishBuild()
     {
         yield return new WaitForSeconds(0.1f);
         spawnObject.gameObject.AddComponent<Combine>().cubeMaterial = cubeMaterial;
         yield return null;
         spawnObject.GetComponent<Combine>().NewCombine();
     }
 }
 


And the last script CreateQuads

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class CreateQuads : MonoBehaviour
 {
     public GameObject owner;
 
     enum Cubeside { BOTTOM, TOP, LEFT, RIGHT, FRONT, BACK };
     public enum BlockType { GRASS, DIRT, STONE };
 
     public Material cubeMaterial;
     public BlockType bType;
 
     Vector2[,] blockUVs = { 
         /*GRASS TOP*/        {new Vector2( 0.125f, 0.375f ), new Vector2( 0.1875f, 0.375f),
                                 new Vector2( 0.125f, 0.4375f ),new Vector2( 0.1875f, 0.4375f )},
         /*GRASS SIDE*/        {new Vector2( 0.1875f, 0.9375f ), new Vector2( 0.25f, 0.9375f),
                                 new Vector2( 0.1875f, 1.0f ),new Vector2( 0.25f, 1.0f )},
         /*DIRT*/            {new Vector2( 0.125f, 0.9375f ), new Vector2( 0.1875f, 0.9375f),
                                 new Vector2( 0.125f, 1.0f ),new Vector2( 0.1875f, 1.0f )},
         /*STONE*/            {new Vector2( 0, 0.875f ), new Vector2( 0.0625f, 0.875f),
                                 new Vector2( 0, 0.9375f ),new Vector2( 0.0625f, 0.9375f )}
                         };
 
     void CreateQuad(Cubeside side)
     {
         Mesh mesh = new Mesh();
         mesh.name = "ScriptedMesh" + side.ToString();
 
         Vector3[] vertices = new Vector3[4];
         Vector3[] normals = new Vector3[4];
         Vector2[] uvs = new Vector2[4];
         int[] triangles = new int[6];
 
         //all possible UVs
         Vector2 uv00;
         Vector2 uv10;
         Vector2 uv01;
         Vector2 uv11;
 
         if (bType == BlockType.GRASS && side == Cubeside.TOP)
         {
             uv00 = blockUVs[0, 0];
             uv10 = blockUVs[0, 1];
             uv01 = blockUVs[0, 2];
             uv11 = blockUVs[0, 3];
         }
         else if (bType == BlockType.GRASS && side == Cubeside.BOTTOM)
         {
             uv00 = blockUVs[(int)(BlockType.DIRT + 1), 0];
             uv10 = blockUVs[(int)(BlockType.DIRT + 1), 1];
             uv01 = blockUVs[(int)(BlockType.DIRT + 1), 2];
             uv11 = blockUVs[(int)(BlockType.DIRT + 1), 3];
         }
         else
         {
             uv00 = blockUVs[(int)(bType + 1), 0];
             uv10 = blockUVs[(int)(bType + 1), 1];
             uv01 = blockUVs[(int)(bType + 1), 2];
             uv11 = blockUVs[(int)(bType + 1), 3];
         }
 
         //all possible vertices 
         Vector3 p0 = new Vector3(-0.5f, -0.5f, 0.5f);
         Vector3 p1 = new Vector3(0.5f, -0.5f, 0.5f);
         Vector3 p2 = new Vector3(0.5f, -0.5f, -0.5f);
         Vector3 p3 = new Vector3(-0.5f, -0.5f, -0.5f);
         Vector3 p4 = new Vector3(-0.5f, 0.5f, 0.5f);
         Vector3 p5 = new Vector3(0.5f, 0.5f, 0.5f);
         Vector3 p6 = new Vector3(0.5f, 0.5f, -0.5f);
         Vector3 p7 = new Vector3(-0.5f, 0.5f, -0.5f);
 
         switch (side)
         {
             case Cubeside.BOTTOM:
                 vertices = new Vector3[] { p0, p1, p2, p3 };
                 normals = new Vector3[] {Vector3.down, Vector3.down,
                                             Vector3.down, Vector3.down};
                 uvs = new Vector2[] { uv11, uv01, uv00, uv10 };
                 triangles = new int[] { 3, 1, 0, 3, 2, 1 };
                 break;
             case Cubeside.TOP:
                 vertices = new Vector3[] { p7, p6, p5, p4 };
                 normals = new Vector3[] {Vector3.up, Vector3.up,
                                             Vector3.up, Vector3.up};
                 uvs = new Vector2[] { uv11, uv01, uv00, uv10 };
                 triangles = new int[] { 3, 1, 0, 3, 2, 1 };
                 break;
             case Cubeside.LEFT:
                 vertices = new Vector3[] { p7, p4, p0, p3 };
                 normals = new Vector3[] {Vector3.left, Vector3.left,
                                             Vector3.left, Vector3.left};
                 uvs = new Vector2[] { uv11, uv01, uv00, uv10 };
                 triangles = new int[] { 3, 1, 0, 3, 2, 1 };
                 break;
             case Cubeside.RIGHT:
                 vertices = new Vector3[] { p5, p6, p2, p1 };
                 normals = new Vector3[] {Vector3.right, Vector3.right,
                                             Vector3.right, Vector3.right};
                 uvs = new Vector2[] { uv11, uv01, uv00, uv10 };
                 triangles = new int[] { 3, 1, 0, 3, 2, 1 };
                 break;
             case Cubeside.FRONT:
                 vertices = new Vector3[] { p4, p5, p1, p0 };
                 normals = new Vector3[] {Vector3.forward, Vector3.forward,
                                             Vector3.forward, Vector3.forward};
                 uvs = new Vector2[] { uv11, uv01, uv00, uv10 };
                 triangles = new int[] { 3, 1, 0, 3, 2, 1 };
                 break;
             case Cubeside.BACK:
                 vertices = new Vector3[] { p6, p7, p3, p2 };
                 normals = new Vector3[] {Vector3.back, Vector3.back,
                                             Vector3.back, Vector3.back};
                 uvs = new Vector2[] { uv11, uv01, uv00, uv10 };
                 triangles = new int[] { 3, 1, 0, 3, 2, 1 };
                 break;
         }
 
         mesh.vertices = vertices;
         mesh.normals = normals;
         mesh.uv = uvs;
         mesh.triangles = triangles;
 
         mesh.RecalculateBounds();
 
         GameObject quad = new GameObject("Quad");
         quad.transform.parent = this.gameObject.transform;
         MeshFilter meshFilter = (MeshFilter)quad.AddComponent(typeof(MeshFilter));
         meshFilter.mesh = mesh;
         MeshRenderer renderer = quad.AddComponent(typeof(MeshRenderer)) as MeshRenderer;
         renderer.material = cubeMaterial;
     }
 
     void CombineQuads()
     {
         MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
         CombineInstance[] combine = new CombineInstance[meshFilters.Length];
         int i = 0;
         while (i < meshFilters.Length)
         {
             combine[i].mesh = meshFilters[i].sharedMesh;
             combine[i].transform = meshFilters[i].transform.localToWorldMatrix;
             i++;
         }
 
         MeshFilter mf = (MeshFilter)this.gameObject.AddComponent(typeof(MeshFilter));
         mf.mesh = new Mesh();
 
         mf.mesh.CombineMeshes(combine);
 
         MeshRenderer renderer = this.gameObject.AddComponent(typeof(MeshRenderer)) as MeshRenderer;
         renderer.material = cubeMaterial;
 
         foreach (Transform quad in this.transform)
         {
             Destroy(quad.gameObject);
         }
 
     }
 
     public void CreateCube(bool front, bool back, bool top, bool bottom, bool left, bool right)
     {
         if (front)
             CreateQuad(Cubeside.FRONT);
         if (back)
             CreateQuad(Cubeside.BACK);
         if (top)
             CreateQuad(Cubeside.TOP);
         if (bottom)
             CreateQuad(Cubeside.BOTTOM);
         if (left)
             CreateQuad(Cubeside.LEFT);
         if (right)
             CreateQuad(Cubeside.RIGHT);
         CombineQuads();
     }
 
     public IEnumerator CreateCubeE(bool front, bool back, bool top, bool bottom, bool left, bool right)
     {
         if (front)
             CreateQuad(Cubeside.FRONT);
         if (back)
             CreateQuad(Cubeside.BACK);
         if (top)
             CreateQuad(Cubeside.TOP);
         if (bottom)
             CreateQuad(Cubeside.BOTTOM);
         if (left)
             CreateQuad(Cubeside.LEFT);
         if (right)
             CreateQuad(Cubeside.RIGHT);
         yield return null;
         CombineQuads();
     }
 }
 



Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

3 Replies

· Add your reply
  • Sort: 
avatar image
1
Best Answer

Answer by Optrix · Jul 02, 2019 at 04:12 AM

You'd get a lot more performance if you just built a single Mesh rather than building hundreds of smaller Quad meshes and sticking them together with Combine. You're creating and destroying a LOT of objects that really don't need to be done that way. You've gone for simplicity of code, but it's destroying your performance.


Basically, you should add vertices, triangles and UV coordinates to lists until you either run out of voxels for a chunk, or hit around 64,000 vertices (Unity doesn't support meshes of greater than 65535 vertices). Once that happens, then create your GameObject and Mesh.


You can also add a colour to your vertices - these are useful for faking Ambient Occlusion (for example, the vertices that meet where a horizontal edge meet a vertical one should be a little bit darker).


It's never a trivial amount of work - voxels take time to build - but you should be able to shave off a lot of excess processing, object destruction and memory allocation.

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Razor1994 · Jul 02, 2019 at 05:53 PM 0
Share

Okay that really makes sense, i just did a few tests and just generating one mesh is much much faster. Though this is going to be a bit harder to implement i guess. Sometimes you can´t see the things that are in plain sight :D

Thank you :)

avatar image
0

Answer by jamesheaton · Oct 07, 2020 at 09:44 AM

Did you ever manage to resolve this? I am also having the same problem and my code is similar - I believe we both got it from the same Udemy course :) ,Did you ever manage to resolve this?

Comment
Add comment · Show 6 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Razor1994 · Oct 07, 2020 at 09:52 AM 1
Share

Hey, i decided to write it again and after that i saw how bad the old code was. I am now able to generate the chunks extremly quick and have no lag when walking around and generating new chunks. The course really only taught some basics but has a few flaws that really kill performance. What you need to do is exactly what @Optrix described and it should point you into the correct direction.

avatar image jamesheaton Razor1994 · Oct 07, 2020 at 09:56 AM 0
Share

Would you be able to share this code? I looked into this briefly but I don't really know where to start. I suppose the main problem is converting the vertices on each of the quads into a world position but there is quite a lot more to be done, just wondering if you could give me a little more help.

avatar image Razor1994 jamesheaton · Oct 07, 2020 at 08:08 PM 0
Share

right now i do not have the code on me, i will reply to it once i do :)

Show more comments
avatar image
0

Answer by KidsWithSpraycans · May 20 at 12:46 PM

Although this post is specific to voxel terrain, anyone looking into making a voxel game should try to optimize their 3D voxel models, since they can be quite inefficient.


I've created the Ultimate Voxel Optimizer as a way of helping this problem. Using classic meshing techniques with voxel models doesn't result in the most optimal models. UVO, however, uses a custom meshing algorithm to ensure that you get the best reduction out of all model optimizer programs.

Simply load in your .vox models from MagicaVoxel, select your export method and format, and click export. Some models can receive a 50% reduction in polygon count, with more features coming soon! The program is currently $3.99, and available on Windows, Mac, and Linux!


You can get more info at https://nateonus.itch.io/ultimate-voxel-optimizer

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

147 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Optimization problems 1 Answer

Texture mapping procedural mesh with texture atlas? (Minecraft style terrain gen) 1 Answer

Huge world without huge lag? 1 Answer

Color changes at distance in procedural terrain 0 Answers

100000+ Instantiated Objects Optimization 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges