Infinite terrain generation in Unity 3D – Part 1

With the constant improvement of hardware performance game terrains got much larger and more detailed – featuring detailed shape, a lot of grass and other objects (trees, water etc.). During the last years size of terrains grew up to hundreds square miles – especially in RPG games.

In this tutorial I’ll show you how to generate a 3D terrain that could take a long, long hours to wander. We’ll be using Unity3D engine and C# language for writing code. Some basic programming knowledge would be required – although entire source code is free to download (see below), in this article I’ll explain only most important parts and code examples.
Let’s start…

One of the most popular way of visualizing 3D terrains is to use some form of heightmap. It is a set of elevation data stored as an image whose dimensions match width and height of the terrain. The darker the color is the lower the ground is elevated. Below you can see how such data transforms into visible mesh:

heightmap  Heightmap_rendered

Because we want to make terrain that is virtually infinite (or at least veeeery large) we cannot use this technique directly – memory footprint required to store all the data would be huge and heightmap size would grow to thousands of pixels.
Instead of making one large terrain we’ll divide it into smaller fragments called “chunks”. Each chunk will have its own mesh and several neighboring chunks will blend seamlessly into one larger terrain. You can treat them as a square tiles with 2D coordinates (X/Z). The key is to create several chunks of terrain around the player (up to the view distance) and add new ones as player moves. Old chunks that are too far from camera are then removed to free memory. See image below:

text3824

We can describe single chunk geometry as:

  • Length – which is the chunk border size in Unity units
  • Height – which is the maximum available height of terrain in the chunk (also in Unity distance units)
  • Heightmap & alphamap resolution – which define how accurate chunk mesh and textures will be – the higher the value the more complex mesh we’ll get. According to the Unity documentation (here) it should be a power of two plus one (129, 257 etc.).

Let’s put it in code – here is out TerrainChunkSettings class:

public class TerrainChunkSettings
{
    public int HeightmapResolution { get; private set; }
    public int AlphamapResolution { get; private set; }

    public int Length { get; private set; }
    public int Height { get; private set; }
}

Now here is our terrain chunk class (for now we will skip the methods):

public class TerrainChunk
{
    public int X { get; private set; }

    public int Z { get; private set; }

    private Terrain Terrain { get; set; }

    private TerrainChunkSettings Settings { get; set; }

    private NoiseProvider NoiseProvider { get; set; }
}

Each chunk is defined by its X/Z position (see image above), settings and Unity Terrain object – this the actual gameobject that holds mesh and all stuff needed to render the terrain on the scene. The last field – NoiseProvider – will be discussed below.

So – how do we create a heightmap texture with all the fancy mountains, hills and so on?
There are a lot of approaches to do that – you can find tons of information about it on the Procedural Generation Wiki. We’ll fill our heightmap with coherent noise by using a LibNoise library. A lot of details about coherent noise and how to use it can be found here and here – I highly recommend reading both of those.
Without getting much into detail let’s just say that for any position in 3D space (x, y, z) we can get a particular noise value that – when put on texture – form a very nice images resembling a real terrain shape.
For our purpose we’ll skip Y component because we’re creating terrain on the flat surface. As LibNoise returns noise values from -1 to 1 we need to scale it to 0..1 (that scale is much more convenient).

I’ve created INoiseProvider interface that forces returning a value for given X/Z coordinates in Unity world space (that is an important information). NoiseProvider class gives us such a value from Perlin noise (I hope you’ve read two links above) – that’s just for a start.

public class NoiseProvider : INoiseProvider
{
    private Perlin PerlinNoiseGenerator;

    public NoiseProvider()
    {
        PerlinNoiseGenerator = new Perlin();
    }

    public float GetValue(float x, float z)
    {
        return (float)(PerlinNoiseGenerator.GetValue(x, 0, z) / 2f) + 0.5f;
    }
}

OK – we’ve got our simple noise generator. Now some more technical stuff about Unity terrain.
Normally you could create single terrain from GameObject/3D Object/Terrain menu. But if we want to create terrain from code we need a TerrainData object which contains most of information required to generate terrain mesh. Here we set our heightmap values, resolutions and map size. After that we create Terrain gameobject using Unity CreateTerrainGameObject() method, place our newly generated object in proper place by setting its transform position and Flush() all the data – rest is done automatically by Unity.
Method below does all those things:

public void CreateTerrain()
{
    var terrainData = new TerrainData();
    terrainData.heightmapResolution = Settings.HeightmapResolution;
    terrainData.alphamapResolution = Settings.AlphamapResolution;

    var heightmap = GetHeightmap();
    terrainData.SetHeights(0, 0, heightmap);
    terrainData.size = new Vector3(Settings.Length, Settings.Height, Settings.Length);

    var newTerrainGameObject = Terrain.CreateTerrainGameObject(terrainData);
    newTerrainGameObject.transform.position = new Vector3(X * Settings.Length, 0, Z * Settings.Length);
    Terrain = newTerrainGameObject.GetComponent<Terrain>();
    Terrain.Flush();
}

As you can see there is a GetHeightmap() method that fills our elevation values by using noise mentioned earlier:

private float[,] GetHeightmap()
{
    var heightmap = new float[Settings.HeightmapResolution, Settings.HeightmapResolution];

    for (var zRes = 0; zRes < Settings.HeightmapResolution; zRes++)
    {
        for (var xRes = 0; xRes < Settings.HeightmapResolution; xRes++)
        {
            var xCoordinate = X + (float)xRes / (Settings.HeightmapResolution - 1);
            var zCoordinate = Z + (float)zRes / (Settings.HeightmapResolution - 1);

            heightmap[zRes, xRes] = NoiseProvider.GetValue(xCoordinate, zCoordinate);
        }
    }

    return heightmap;
}

How does it work?
To fill an entire elevation array – which both dimensions equal terrain resolution – we need to iterate it and get noise value for every position (X/Z). Final coordinates for our NoiseProvider are generated from chunk position plus iteration step divided by resolution minus one. This way we scale our X/Z direction to 0..1 (for first chunk), 1..2 (for second chunk), 2..3 (third chunk) etc. It’ll keep our generation compatible with previously created NoiseProvider.

OK – we’ve got a core of our application and now it is time for some testing.
Let’s create a single chunk with a 129 resolution, 100 meters in size and 20 meters tall:

void Test()
{
    var settings = new TerrainChunkSettings(129, 129, 100, 20);
    var noiseProvider = new NoiseProvider();
    var terrain = new TerrainChunk(settings, noiseProvider, 0, 0);
    terrain.CreateTerrain();
}

After calling this method we’ve got our first terrain!

first_terrain

It doesn’t look impressive (yet!) and has no textures applied – but you can see some hills there – that’s a good start.
Now we need to get it better – let’s create some more chunks to make our terrain larger:

void Test()
{
    Settings = new TerrainChunkSettings(129, 129, 100, 20);
    NoiseProvider = new NoiseProvider();
    for (var i = 0; i < 4; i ++)
        for (var j = 0; j < 4; j++)
            new TerrainChunk(Settings, NoiseProvider, i, j).CreateTerrain();
}

second_terrain

Our terrain is growing – that’s exactly what we want to achieve. We’ve got 16 chunks of terrain, each independent and with own mesh. We could add more of them – thus enlarging our map – but let’s stop for a second…

As you might notice, creating larger terrain can take some time. On my PC creating 16 chunks took about ~1500 milliseconds during which entire application was frozen – it was very visible and is not allowed in a normal game.
Most of this delay was used to calculate a lot of noise values for every part of terrain. Such performance issues are common in single-threaded applications. To fix it we need to put our heightmap generating functions in separate thread that will be independent from main thread. To make it even better – we’ll create a number of threads to simultaneously create few chunks at the same time. It will prevent us from blocking main application thread and should speed up chunk generation time.

This improvement has created a lot of changes in our code – here are most important of them:

  • TerrainChunkGenerator class was added – it will manage adding and removing chunks, keeping them up to date and will serve as a primary interface between our terrain and rest of application. If something in terrain requires modification – it should by done by using appropriate method from this class.
  • Added ChunkCache class – it is used to hold information about all requested and created chunks. It also tracks chunk status.
  • Chunks are explicitly identified by their X/Z position – and it is our only way to distinct one chunk from another. I’ve created Vector2i class to hold information about chunk position.

I’ve also added ability to remove chunks. When chunk needs to be removed it is added to the queue. Each frame chunk cache checks this queue and tries to delete all requested chunks. Chunk cannot be deleted if it is under generation – in this case its removal is delayed until generation is finished. It may not be the most efficient way, but it’s fast to implement and easy to control. Everything is implemented in TryToDeleteQueuedChunks method in ChunkCache class.

Now let’s write method to generate a sufficient amount of chunks around the player. As input we’ll need player position and distance (radius) how far to generate new chunks. As a result we expect a list of coordinates of all chunks that need to be created around the player. Let’s take a look at this code:

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, chunkPosition.Z + zCircle));
        }
    }

    return result;
}

This method takes initial chunk position and radius (in chunk units) as an input and gives as all coordinates that match circle equation. Here’s an example – input is in (0, 0) position and chunk radius is 7 (player position is in the middle):

radiusNow it’s time for the magic trick – to give player an illusion of infinite terrain we must constantly watch his position and add new chunks of terrain as he moves towards some direction. Old chunks that are out of sight are then removed. This way player may move for a very long distances and still see miles of terrain in front of him (or her).
We are monitoring player movement in our newly created class – GameController. It is responsible for high level application control – managing player, communication with terrain generator and UI interaction. After we detect that player has moved far enough to regenerate terrain (in other words – he moved from chunk to chunk) we must calculate new chunks in radius of sight, create them and delete some far ones.
Here’s a code for this:

public void UpdateTerrain(Vector3 worldPosition, int radius)
{
    var chunkPosition = GetChunkPosition(worldPosition);
    var newPositions = GetChunkPositionsInRadius(chunkPosition, radius);

    var loadedChunks = Cache.GetGeneratedChunks();
    var chunksToRemove = loadedChunks.Except(newPositions).ToList();

    var positionsToGenerate = newPositions.Except(chunksToRemove).ToList();
    foreach (var position in positionsToGenerate)
        GenerateChunk(position.X, position.Z);

    foreach (var position in chunksToRemove)
        RemoveChunk(position.X, position.Z);
}

First we are calculating chunk that player is in. Then new circle of chunks around the player is computed and we must determine which chunks to remove and which to add (simple subtraction/intersection operations). At the end we are generating and removing respective parts of terrain.
This is repeated each time player moves from chunk to chunk.
To test all we’ve created above I’ve added a standard Unity FPS controller and created player management code in GameController class (along with a simple UI to start terrain generation). Now we can freely walk around and travel hundreds of miles of infinite terrain!

Unfortunately it still looks a bit… dull.
Last thing we need to do is to add some textures so our terrain would look like a real thing.
Adding them is very simple – first we need to define a number of textures (called SplatPrototypes) that we’d like to use on our terrain and then we need to specify the amount of each texture for every point on terrain (the number of depends on AlphamapResolution). All this information is entered to TerrainData class that we already know. We’ll store textures in out TerrainChunkSettings class.
In this tutorial I’ve taken two textures – one for flat terrain and one for steep surfaces. The influence of each texture is based on terrain steepness (which we can obtain from TerrainData class after applying elevation data).
Here’s a code snippet:

private void ApplyTextures(TerrainData terrainData)
{
    var flatSplat = new SplatPrototype();
    var steepSplat = new SplatPrototype();

    flatSplat.texture = Settings.FlatTexture;
    steepSplat.texture = Settings.SteepTexture;

    terrainData.splatPrototypes = new SplatPrototype[]
    {
        flatSplat,
        steepSplat
    };

    terrainData.RefreshPrototypes();

    var splatMap = new float[terrainData.alphamapResolution, terrainData.alphamapResolution, 2];

    for (var zRes = 0; zRes < terrainData.alphamapHeight; zRes++)
    {
        for (var xRes = 0; xRes < terrainData.alphamapWidth; xRes++)
        {
            var normalizedX = (float)xRes / (terrainData.alphamapWidth - 1);
            var normalizedZ = (float)zRes / (terrainData.alphamapHeight - 1);

            var steepness = terrainData.GetSteepness(normalizedX, normalizedZ);
            var steepnessNormalized = Mathf.Clamp(steepness / 1.5f, 0, 1f);

            splatMap[zRes, xRes, 0] = 1f - steepnessNormalized;
            splatMap[zRes, xRes, 1] = steepnessNormalized;
        }
    }

    terrainData.SetAlphamaps(0, 0, splatMap);
}

Let’s build it again – now it looks way better (CC0 textures taken from here):

final_effect

Now we’ve got a fully functional terrain that we can walk on and that can be very large in size. Although it is not really infinite – we’ve got a very convincing illusion of it.
That would be all for the basic tutorial. There are a lot of things that could be improved – mostly performance and visual improvements, but we’ll handle them in the another part.
Have fun!

Source project can be downloaded here.

Comments 39

  • […] Infinite terrain generation in Unity 3D (part 1). […]

  • The tutorial was explained great, but in the downloaded project there is a scripting error in NoiseProvider.cs (16,49) “Type ‘Perlin’ does not contain a definition for ‘GetValue'”

    • Strange – do you have LibNoise files in the project (they are already attached in rar archive)? Eventually is “using LibNoise.Generator” added in the NoiseProvider class?

      • Hey,

        Awesome tutorial. As Kujo mentioned, I am getting the same issue. I was able to download your project, open it up and get it running. However, when I imported it into my own project, I got the same error.

        After digging around, I found that there was another file in my project called Perlin.cs . The error Kujo and I got was from the fact that the compiler was trying to call the GetValue method on another Perlin object rather than the one that is included in the LibNoise source.

        I removed that file and bingo bango, it worked!

        Hope this helps someone,
        @maxfelker

  • That’s very nice tutorial, thank you. :) When do you plan to release next part?

  • […] via Infinite terrain generation in Unity 3D – Part 1 | Code Phi. […]

  • […] Unity, this was done using Rick King’s (kulesz) tutorial and source code which can be found here. I do like the idea of using procedural terrain but I think I will create small contained levels by […]

  • Nice tutorial. I followed it all the way till I managed to reproduce it myself. However, original source code still have some parts missing (e.g. in TerrainChunk class only “Position” is assigned and values “X” and “Z” are not – this gives zeros when running CreateTerrain() method there and terrain is spawned always on (0,0)).

    Another thing is that it has very large number of separate files, which is very hard to follow if one or another file is set incorrectly. I managed to copy everything (including LibNoice classes) into a single file, which can be copy-pasted, dragged onto game objects and “versioned” much easier than such large project. Rewritten script is here:
    https://dl.dropboxusercontent.com/u/248943005/InfiniteTerrain/TerrainGenerator1.cs

    In order to use it, you only need to have camera in the scene and drag the script on any GameObject.

    I did this as I realised that currently procedural terrain generation is tricky question and what was trickenning me was that I was unable to simply reproduce any kind of procedural terrain generation myself. I also keep usually number of files as low as possible, because when building larger games, amount of files becomes unimaginably large (try to imagine adding forests, sounds, weather, day-night cycles, multiplayer, UI, game mechanics, etc.).

    Anyways, it’s quite good tutorial.

    • In you code X/Z properties are not needed – they are redundant with the Position property which includes them.

      As for the large number of separate files I cannot agree – the golden rule is to keep one class in a single file, it helps to keep it clean and easier to maintain. Especially for large projects (which my tutorial is not).

      • There are some other strange things there. e.g. how did it come solution for
        steepnessNormalized = Mathf.Clamp(steepness / 1.5f, 0, 1f);
        ? I used
        steepnessNormalized = steepness / (90f*Settings.Height/Settings.Length);
        which gives approximately half/half of area for steep and flat textures.
        P.S. Isn’t there possible to use some Perlin noise function, which would take and use for calculations only 2 arguments (I guess 3rd argument for y axis only drains performance).
        P.P.S. Wouldn’t it be better to use coroutines and WaitForSeconds, which allows more friendly solution for FPS rates when new chunks starts to be loaded?

        • As for steepness – I’ll explain it in another part. Generally speaking steepness from Unity is relative to terrain size and resolution, so several approaches may be fine for this (just like the one you mentioned).
          2D Perlin functions could be available – LibNoise is attached as a source code so you may take a look at it. I may be able to do it in the upcoming days.
          WaitForSeconds and coroutines could be added in several places, especially when new chunk is created. As for heightmap generation separate threads are still better and faster solution.

          • Well, I would probably do these things, which runs on runtime in Coroutines, as the most important thing during the game play is if the game doesn’t get laggy. Player usually don’t care very much if tile is made faster or slower – the point is that the tile creation wouldn’t lag the whole game.

            I would probably do threading stuff more for editor approach, as there are no such thing as FPS rates. It’s also the best thing for simulations, when you simulate heightmaps (especially with more features, like erosions) and save them into files, which are just loaded during the game play.

            I was recently thinking to apply xxHash random numbers (http://blogs.unity3d.com/2015/01/07/a-primer-on-repeatable-random-numbers/) which should give better randomisation – it has also function with two coordinates input. But at the moment I can’t find out properly how LibNoise is dealing with octaves, which defines sizes of mountains.

  • […] Infinite terrain generator: tutorial de creación de exactamente lo que dice en el texto del enlace. […]

  • I really like this project but it seems to be broken in unity 4.
    after moving the player unity crashes. i changed t player to a cube and moved this cube manually. unity crashes again.

    any ideas?

  • Hey, I am extremely new to Unity and am currently working on a project where I need to create a single piece of flat terrain a specific width and length using c#. I know it is probably obvious in your tutorial how to do this but I have tried many times to no prevail. I was wondering if it was easy if you could write up the code for me so I could learn it of you. Thank you for your tutorials so far!

    • using UnityEngine;
      using System.Collections;

      public class terraintest : MonoBehaviour
      {

      // Use this for initialization
      void Start()
      {
      GameObject terrain = new GameObject();
      TerrainData _terraindata = new TerrainData();
      terrain = Terrain.CreateTerrainGameObject(_terraindata);
      Vector3 position = new Vector3(2000, 100, 2000);
      GameObject; ingameTerrainGameObject = Instantiate(terrain, position, Quaternion.identity);

      }

      // Update is called once per frame
      void Update()
      {

      }
      }

      This is what I have written, but for some reason it is not working, any advice?

      • In terrainGeneration script. look for this line at 22, and where I put a 0, this is the height, so 0 mean flat anf then, I chose a size of 500 for the width and lenght of my terrain :
        Settings = new TerrainChunkSettings(0, 0, 500, 40, FlatTexture, SteepTexture, TerrainMaterial);

        • SORRY, I put you in ERROR. yess it will make you a flat terrain, put keep 129 as value and change the 40 for 0 : (129,129, 500, 0)…!!! Hope it’s not to late for your project. :P

  • I’m assuming this tutorial is written for experienced Unity developers? I’m barely a beginner unity developer. I’m a java developer who’s boss is tasking me with creating games unity.

    I’ve downloaded the source and can see all the scripts. But when I load the MainScene.unity I see in the Assets folder, it only has a directional light and main camera. When I attempt to “play” the scene, nothing happens, no terrain created, just a shot from the camera with nothing in it. This doesn’t appear to be a working example of tutorial, and due to my lack of experience, I have no clue where to add the Test method for creating a terrain nor how to have it run when I start the game.

    Do you have any suggestions on other reading I can do so I can eventually attempt to implement your code?

    • Looks like the MainScene did not load for whatever reason.
      Try opening the project (File->Open project…, choose folder with downloaded project in it) and then find MainScene in lower part of Unity editor, in Project tab, in Assets folder. Right click it and choose Open.
      That worked for me.

    • just double click (to open) the scene ‘.unity file’ and it will load everything.

    • If you are only seeing the main camera and a directional light, then you have not loaded the scene correctly.

      Click File->Open Scene -> MainScene

  • So I have everything working with the project but one thing I noticed was that the terrain resolution is pretty low at the moment so I found where you are setting the resolution but when I increase it there are large gaps between the chunks, is there some other setting i’m missing or what?

  • How would I go about making the terrain less bumpy and more smooth?

  • Greetings.
    You have no shadow flickering in your project when you use real time directional light. This flickering is common for huge terrains in Unity.
    At all forums people say the flickering can be reduced by increasing the camera near plane parameter. But you project has no flickering even at minimum near plane value of 0.01.
    How did you do this?
    Thank you.

  • pretty sweet!

    a simple suggestion: make the terrain all fall under a single gamobject and give the coords as name.
    makes it a bit clearer :)

    quick and dirty that would be like:
    newTerrainGameObject.transform.SetParent(GameObject.Find(“chunks”).transform);
    newTerrainGameObject.name = “(” + (Position.X * Settings.Length).ToString() + ” , ” + (Position.Z * Settings.Length).ToString() + “)”;

    With a empty gameobject named ‘chunks’ ofcourse.
    (for a non dirty fix; add a chunks object to settings and use that instead of .Find.

  • I use a similar method to this for a planet sized world map for a VR-MMO.

    I have 360* by 180* with kilometersPerDegree of 100km, and kilometersPerChunk of 36km2

    This gives me 500,000 km2 in game. And uses about 144GB for the world map. And yes I know to create it takes a LONG time.

    Main problem I have is that the world map is 36:18 ratio but in reality a planet is spherical. So a player can travel 36000km east/west at north pole. hahaha

    Only solution I can think of is to just design wise try and prevent a player from reaching a polar location in game.

    Or have the world a 6-sided cube. and when externally viewing the planet render with a 6-sided cube texture. Map co-ordinates would have to be based on the 6 sides.

  • Hi there, hope you are well. So how is part2 coming up? Any news, plans, updates?

  • Thank you for this great article, your template saved me quite a bit of time!

  • Great tutorial but how do place a prefab in the chunk as it generates(e.g a naturally generated tree)?

  • Hey there, I’m trying this out for the first time and I wanted to combine it with VR. I’m using the free VRToolkit on the Unity Asset store and the teleport scripts that it comes with.

    When i run the terrain generation and drop my VR prefab in, It works initially, I land on the terrain and everything, but once I try to teleport, I clip through the terrain and end up on 0 y-axis. Why would this be? The teleport script reacts to mesh colliders so theoretically I should be able to just teleport to the next spot and not clip through down to 0… Any ideas?

  • […] Procedural Generation Tutorial – http://code-phi.com/infinite-terrain-generation-in-unity-3d/ […]

  • This is a greaat tutorial, thanks – I’m really looking forward to part two.

    In the meantime, though – and as a newbie to Unity – where would the starting point be for adding things like grass to your method?

  • Interesting tutorial!
    I have not figured out how to add more textures in addition to two in this code.
    I’m pretty noob .. :p

  • Very interesting, thanks for the tuto!
    Would it be possible to add a plane per terrain chunck to get waterlevel?
    Where in the code should it be added?

    Thanks a lot!

  • Hello, I wanted to put some structures and stuffs. May I ask how am I able to place them when terrains were generated?

Leave a Reply

Your email address will not be published. Required fields are marked *