Landscape Generation in Unity3d

I think everyone noticed that now all sorts of Minecraft -style survival adventure games have begun to appear . I decided to make one. The beginning was easy - Unity3d has great functionality for the consciousness of simple games (and not only). Character, game objects, in general, make the basis quickly. But what is minecraft without a randomly generated world? This was the first difficult task. And I think not only for me. After reviewing all of Google and spending a lot of time on this useless thing, I decided to write this article in order to reduce the suffering of others.

Next you will find a description of the algorithms (and code) for creating more or less realistic landscapes. I’ll clarify that all the examples are in C #.

Action plan

For starters, it would be nice to figure out what is meant by landscape generation:
  1. Height Map Generation. This is the most important part; a terrain (or mesh) is built on a height map. It can also be used to color terrain depending on height
    and to arrange game objects.
  2. Building a landscape. There are two ways to accomplish this point, depending on whether you want complexity and whether you use unity3d, or you don't care about performance, but it’s important for you to be beautiful. In the first case, I advise you to use the terrain editor built into unity3d.
    A simple code for this:

    	Terrain terrain = FindObjectOfType (); // Находи наш terrain
    	float[,] heights = new float[resolution,resolution]; // Создаём массив вершин
    	// ...
    	// Делаем с heights всё, что хотим
    	// ...
    	terrain.terrainData.size = new Vector3(width,height,length); // Устанавливаем размер нашей карты
    	terrain.terrainData.heightmapResolution = resolution; // Задаём разрешение (кол-во высот)
    	terrain.terrainData.SetHeights(0, 0, heights); // И, наконец, применяем нашу карту высот (heights)

    The second way is to create a mesh. This method gives greater scope for actions on the landscape, but it is also more complicated: you have to create a mesh, then break it into triangles and work on shaders for painting. This article will help you figure out the 2 way .
  3. Texture mapping The final stage in the generation of the landscape. Here again, a height map from the first point is useful to us. For blending and blending textures we will use a simple shader.

    Shader "Custom/TerrainShader" {
        Properties {
            _HTex ("heightMap texture", 2D) = "white" {}
            _GTex ("grass texture", 2D) = "white" {}
            _RTex ("rock texture", 2D) = "white" {}
        SubShader {
            Tags { "RenderType"="Opaque" }
            LOD 2048
            #pragma surface surf Lambert
            sampler2D _GrassTex;
            sampler2D _RockTex;
            sampler2D _HeightTex;
            struct Input {
                float2 uv_GTex;
                float2 uv_RTex;
                float2 uv_HTex;
            void surf (Input IN, inout SurfaceOutput o) {
                float4 grass = tex2D(_GTex, IN.uv_GTex);
                float4 rock = tex2D(_RTex, IN.uv_RTex);
                float4 height = tex2D(_HTex, IN.uv_HTex);
                o.Albedo = lerp(grass, rock, height);
        FallBack "Diffuse"

    Here we got 3 textures at the entrance: a height map, grass and stone textures. Next, we mix the stone and grass textures along the height map using the lerp () function. And at the exit, we submit our height map but painted in the desired texture.

So, having understood the general plan of action, it is necessary to get down to business.

Common mistakes

From the very beginning, I thought that everything would be very simple and for the random generation of the landscape you can do with the usual function Random (). But this is the most wrong way. Its result is not a beautiful map at all, but a comb in the approximation.

Noise perlin

There are many ways to create a height map, but almost all of them are similar in one - the use of noise. The very first algorithm that I came across was a method using Perlin noise
Perlin noise (Perlin noise, also sometimes Classic Perlin noise) is a mathematical algorithm for generating a procedural texture using a pseudo-random method. Used in computer graphics to increase the realism or graphic complexity of the surface of geometric objects. It can also be used to generate effects of smoke, fog, etc.

I think many were scared by the prefix pseudo, but it is easy to get rid of it. The following is a way to implement noise in Unity3d:

  using UnityEngine;
  using System.Collections;
    public class PerlinNoisePlane : MonoBehaviour {
        public float power = 3.0f;
        public float scale = 1.0f;
        private Vector2 startPoint = new Vector2(0f, 0f);
        void Start () {
            MakeNoise ();
        void MakeNoise() {
            MeshFilter mf = GetComponent(); // Ищем mesh
            Vector3[] vertices = mf.mesh.vertices; // Получаем его вершины
            for (int i = 0; i < vertices.Length; i++) {    
            float x = startPoint.x + vertices[i].x  * scale; // X координата вершины
            float z = startPoint.y + vertices[i].z  * scale; // Z координата вершины
                vertices[i].y = (Mathf.PerlinNoise (x, z) - 0.5f) * power;  // Задаём высоту для точки с вышеуказанными координатами
            mf.mesh.vertices = vertices; // Присваиваем вершины
            mf.mesh.RecalculateBounds(); // Обновляем вершины
            mf.mesh.RecalculateNormals(); // Обновляем нормали

I would not say that this method gives stunningly realistic results, but it is pretty good for creating deserts or plains.

Diamond-square algorithm

After many hours of wandering around the internet, I came across this algorithm, and it met all my expectations. It gives excellent results. There is a very simple formula for calculating vertices.
Imagine a plane, its 4 vertices and a point in the center. Its height will be equal to the sum of the heights of 4 vertices, divided by their number and a certain random number with a coefficient. Here is the code for unity3d (free copy-pasteur):

using UnityEngine;
using System.Collections;
public class TerrainGenerator : MonoBehaviour {
	public float R; // Коэффициент скалистости
	public int GRAIN=8; // Коэффициент зернистости
	public bool FLAT = false; // Делать ли равнины
	public Material material; 
	private int width=2048;
	private int height=2048;
	private float WH;
	private Color32[] cols;
	private Texture2D texture;
	void Start () 
		int resolution = width;
		WH = (float)width+height;
		// Задаём карту высот
		Terrain terrain = FindObjectOfType ();
		float[,] heights = new float[resolution,resolution]; 
		// Создаём карту высот
		texture = new Texture2D(width, height);
		cols = new Color32[width*height];
		drawPlasma(width, height);
		// Используем шейдер (смотри пункт 3 во 2 части)
		material.SetTexture ("_HeightTex", texture);
		// Задаём высоту вершинам по карте высот
		for (int i=0; i 1.0f)
					middle = 1.0f;
			divide(x, y, newWidth, newHeight, c1, edge1, middle, edge4);
			divide(x + newWidth, y, newWidth, newHeight, edge1, c2, edge2, middle);
			divide(x + newWidth, y + newHeight, newWidth, newHeight, middle, edge2, c3, edge3);
			divide(x, y + newHeight, newWidth, newHeight, edge4, middle, edge3, c4);

Related Materials

Also popular now: