- Home /
How can I generate procedural 3D geometry faster?
In my game I have 3D grids that are generated then the y positions of the vertices are displaced to perlin noise, which makes terrain which has to be loaded in chunks. Currently I have the grid loading in chunks, the player can go in every direction infinitely and the way I do this is by removing the 3 chunks behind you and instantiating 3 chunks in front of you. This would be ok, if it didn't take ten seconds to make the 3 chunks in front of you. How can I generate the chunks faster? I have experimented with not using perlin noise, which I thought was what was making it slow, but it didn't make a difference. Here's my code for generating the chunk, which are instantiated in another script. (well, its not really my code)
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class MeshTerrain : MonoBehaviour {
public int sizeOfChunk = 128;
private int size;
private int length;
public float spacing = 1;
public MeshFilter terrainMesh = null;
public int eMid = 2;
public int sMid = 5;
public int eHigh = 5;
public int sHigh = 10;
public int eLow = 1;
public int sLow = 5;
public int eMacro = 2;
public int eTemp = 10;
public int sTemp = 2;
static int tempRandomiser = Random.Range (0,10000);
public int ePrecip = 10;
public int sPrecip = 2;
static int precipRandomiser = Random.Range (0,10000);
static int randomiser = Random.Range(0,10000);
// Use this for initialization
void Start () {
size = sizeOfChunk + 3;
if (terrainMesh == null)
{
Debug.LogError("ProceduralTerrain requires its target terrainMesh to be assigned.");
}
GenerateMesh();
}
// Update is called once per frame
void Update () {
}
//this part generate terrain
float GetHeight (float x, float z){
float height;
float heightA = Noise.Noise.GetNoise((x+transform.position.x) / sMid + randomiser,0, (z+transform.position.z) / sMid + randomiser) * -eMid;
float heightB = Noise.Noise.GetNoise((x+transform.position.x) / sHigh + randomiser,0, (z+transform.position.z) / sHigh + randomiser) * -eHigh;
float heightC = Noise.Noise.GetNoise((x+transform.position.x) / sLow + randomiser,0, (z+transform.position.z) / sLow + randomiser) * -eLow;
float heightM = Noise.Noise.GetNoise((x+transform.position.x) + randomiser,0, (z+transform.position.z) + randomiser) * -eMacro +800;
//float temperature = Noise.Noise.GetNoise((x+transform.position.x)/1.2f+ tempRandomiser,0, (z+ transform.position.z)/1.2f + tempRandomiser) * eTemp;
//float precipitation = Noise.Noise.GetNoise((x+transform.position.x)/1.2f+ precipRandomiser,0, (z+ transform.position.z)/1.2f + precipRandomiser) * ePrecip;
height = heightA + heightB + heightC + heightM;
//Debug.Log (precipitation);
//if(precipitation >= 10 && temperature <= 10){
// height -= 10;
//}
return height;
}
void GenerateMesh ()
{
List<Vector3[]> verts = new List<Vector3[]>();
List<int> tris = new List<int>();
List<Vector2> uvs = new List<Vector2>();
for (int z = 0; z < size; z++)
{
verts.Add(new Vector3[size+1]);
for (int x = 0; x < size; x++)
{
Vector3 current_point = new Vector3();
current_point.x = (x * spacing);
current_point.z = z * spacing;
current_point.y = GetHeight(current_point.x-1, current_point.z);
verts[z][x] = current_point;
//uvs.Add(new Vector2(x,z));
if (x-1 <= 0 || z <= 0 || x >= size)
{
continue;
}
// Generate the triangle north of you.
tris.Add(x + z*size);
tris.Add(x + (z-1)*size);
tris.Add((x-1) + (z-1)*size);
tris.Add((x-1) + z*size);
tris.Add(x + z*size);
tris.Add((x-1) + (z-1)*size);
}
// Unfold the 2d array of verticies into a 1d array.
Vector3[] unfolded_verts = new Vector3[size*size];
int i = 0;
foreach (Vector3[] v in verts)
{
v.CopyTo(unfolded_verts, i * size);
i++;
}
// Generate the mesh object.
Mesh ret = new Mesh();
ret.vertices = unfolded_verts;
ret.triangles = tris.ToArray();
//ret.uv = uvs.ToArray();
// Assign the mesh object and update it.
ret.RecalculateBounds();
ret.RecalculateNormals();
terrainMesh.mesh = ret;
}
}
}
So, how can I make this faster? Is there something here which is slowing down the game or do I need to try a different method?
I see inefficiency here, but I'm not sure it is enough to account for the kind of slowness you describe. I'm assu$$anonymous$$g a chunk is one mesh. I just ran a quick test of some chunk code I have. It took aprox. eight seconds to generate 1000 meshes, each containing 64$$anonymous$$ of vertices.
The thing that jumps out at me is the use of generic Lists. You end up with a function call per entry, plus the resizing and copying that goes on underneath as the list has to grow.
First: check out this thread if you haven't already:
http://forum.unity3d.com/threads/after-playing-$$anonymous$$ecraft.63149/
Answer by CalxDesign · Sep 24, 2014 at 04:30 PM
Have you established what's actually being done in that 10 seconds? Could you not do this constantly so you're streaming a single line of verts/tri's rather than doing entire blocks of terrain at once. The overhead might be considerably less.
Answer by levis501 · Sep 24, 2014 at 10:15 PM
Give the built-in profiler a try to speed up your code. http://docs.unity3d.com/Manual/ProfilerWindow.html
For example, use Profiler.BeginSample and Profiler.EndSample to mark sections of your code and see how much time is being spent in each section.
void Update() {
for (int i=0; i<count; i++) {
Profiler.BeginSample("Section 1");
SomeFunction();
Profiler.EndSample();
Profiler.BeginSample("Section 2");
AnotherFunction();
Profiler.EndSample();
}
}
The profiler will tell you about the time used in the different sections. Start with the section that takes the longest time and try to optimize that first.
I realize the profiler may not be available in the non-pro version. In that case, I would alternately comment out one section at a time, seeing which version runs faster.