/procedural-terrain-generation

2023 SP NYCU Numerical Software Development - Final Project

Primary LanguageC++

Procedural Terrain Generation

Procedurally generate random terrain with Perlin Noise.

Basic Information

GitHub repository : https://github.com/B10732009/procedural-terrain-generation

Procedural Generation is a method of creating data algorithmically as opposed to manually, typically through a combination of human-generated assets and algorithms coupled with computer-generated randomness and processing power.

In video games, Procedural Terrain Generation is very useful where you want to generate natural terrain (caves, hills, rivers, etc.) that has a smooth feel, but is still random.

Problem to Solve

Perlin Noise is a very popular algorithm for procedural generation developed by Ken Perlin in 1983. It can be used for any kind of wave-like, undulating material, texture, or terrain.

Compared with just some random values, Perlin Noise can generate values very smoothly and continuously, which looks more realistic in terrain generation.

The graph below shows the difference between normal random values and perlin noise.

compare

In this project, I am going to implement a set of API (for C++ and Python) of the Perlin Noise algorithm in 1D, 2D, and 3D (if time is enough), and use these API to render some terrain with Ursina (a game engine in Python).

Prospective Users

Anyone who wants to simulate random terrain or uses Perlin Noise for application.

System Architecture

  • Use C++ to implement the algorithm.
  • Use Pybind11 to wrap C++ functions for Python.
  • Use Python to render the terrain with these APIs (with Ursina).

full_system_architecture

API Description

API functions :

  • Create class object
  • Create class object with detail options (scale, octaves, lacunarity, persistance)
  • Get noise value at specific position
  • Get noise value list
  • Get single parameter value (seed, xsz, ysz, zsz, scale, octaves, lacunarity, persistance)

The API will have both C++ and Python version.

For Python users :

import noise

# create class object
n = noise.Noise1D(seed, x_size)
n = noise.Noise2D(seed, x_size, y_size)
n = noise.Noise3D(seed, x_size, y_size, z_size)

# create class object with optional parameters
n = Noise1D(seed, x_size, scale, octaves, lacunarity, persistance)
n = Noise2D(seed, x_size, y_size, scale, octaves, lacunarity, persistance)
n = Noise2D(seed, x_size, y_size, z_size, scale, octaves, lacunarity, persistance)

# get noise at specific position
n[i]        # 1D
n[i, j]     # 2D
n[i, j, k]  # 3D

# get other attributes
n.seed
n.xsz
n.ysz           # (only for 2D & 3D)
n.zsz           # (only for 3D)
n.scale
n.octaves
n.lacunarity
n.persistance
n.data          # get noise list

For C++ users :

#include "noise1d.hpp"
#include "noise2d.hpp"
#include "noise3d.hpp"

// create class object
Noise1D n(std::size_t _seed, std::size_t _xsz);
Noise2D n(std::size_t _seed, std::size_t _xsz, std::size_t _ysz);
Noise3D n(std::size_t _seed, std::size_t _xsz, std::size_t _ysz, std::size_t _zsz);

// create class object with optional parameters
Noise1D n(std::size_t _seed, std::size_t _xsz, 
        std::size_t _scale, std::size_t _octaves, double _lacunarity, double _persistance);
Noise2D n(std::size_t _seed, std::size_t _xsz, std::size_t _ysz, 
        std::size_t _scale, std::size_t _octaves, double _lacunarity, double _persistance);
Noise3D n(std::size_t _seed, std::size_t _xsz, std::size_t _ysz, std::size_t _zsz, 
        std::size_t _scale, std::size_t _octaves, double _lacunarity, double _persistance);

// get noise at specific position
n(std::size_t idx1);                                        // 1D
n(std::size_t idx1, std::size_t idx2);                      // 2D
n(std::size_t idx1, std::size_t idx2, std::size_t idx3);    // 3D

// get other attributes
std::size_t n.seed();
std::size_t n.xsz();
std::size_t n.ysz();          // (only for 2D & 3D)
std::size_t n.zsz();          // (only for 3D)
std::size_t n.scale();
std::size_t n.octaves();
double n.lacunarity();
double n.persistance();
std::vector<double> n.data(); // get noise list

Build System

This system is built by makefile.

Makefile targets :

Target Description
(all) compile a noise.so in noise/ folder.
test use pytest to test python API.
graph1 display 1D noise testing graph.
graph2 display 2D noise testing graph.
graph3 display 3D noise testing graph and generate a noise3d.gif file in test/ folder.
render take parameters in render.conf, generate heightmap.png and colormap.png, and render terrain by Ursina.
clean remove all generated files (*.so, __pycache__/, etc).
  • Before rendering terrain, remember to put the parameters in render.conf.

Engineering Infrastructure

  • Automatic build system : GNU Make
  • Version control : git
  • Testing framework : pytest
  • Documentation : README.md in the github repository

Gallery

1D Noise

2D Noise

3D Noise

Rendered Terrain

References