Quantcast
Channel: CodeSection,代码区,Python开发技术文章_教程 - CodeSec
Viewing all articles
Browse latest Browse all 9596

2D Terrain generation using midpoint displacement:

$
0
0

Today I will present how to implement in python a simple yet effective algorithm for proceduraly generating 2D landscapes. It is called Midpoint Displacement(or Diamond-square algorithm, which seems less intuitive to me) and, with some tweaking it can also be used for creating rivers, lighting strikes or (fake) graphs. The final output may look like the following image.


2D Terrain generation using midpoint displacement:

Example terrain generated with the presented algorithm.

Algorithm overview

The main idea of the algorithm is as follows: Begin witha straight line segment, compute its midpoint and displace it by a bounded random value. This displacement can be done either by:

Displacing the midpoint in the direction perpendicular to the line segment. Displacing only the y coordinate value of the midpoint.
2D Terrain generation using midpoint displacement:

Different methods for displacing the midpoint.

This first iteration will result intwo straight line segments obtained from the displacement of the midpoint of the original segment. The same process of computing and displacing the midpoint can be then applied to each of this new two segments and it will result in four straight line segments. Then we can repeat the process for each of this four segments to obtain eight and so on. The process can be repeated iteratively or recursively as many times as desiredor until the segments cannot be reduced more (for graphical applications this limit would be two pixel’s width segments). The followingimage may help to clarifywhat I just said.


2D Terrain generation using midpoint displacement:

From top to bottom, successive iterations of the algorithm.

And that’s it! This is the core idea of the midpoint displacement algorithm. In pseudocode it looks like:

Initialize segment While iterations < num_of_iterations and segments_length > min_length: For each segment: Compute midpoint Displace midpoint Update segments Reduce displacement bounds iterations+1

However, before implementing the algorithm we should dig deeper in some of the concepts that have arisen so far. These are mainly:

How much should we displace the midpoint? How much should the displacement boundsbe reduced after each iteration? How muchshould we displace the midpoint?

Sadly, there is no general answer for this question because it greatly depends on two aspects:

The application the algorithm is being used for The desired effect

Since in this post our scope is terrain generation I will limit the explanation to the effects that this parameter has in this area. However, the ideas that I will explain now can be extrapolated to any other application where the Midpoint Displacement algorithm may be used. As I see it, there are two key considerationsthat should be taken into account when deciding the initial displacement value.

First of all, we should consider which is the desired type of terrain. Hopefully it makes sense to say that the bigger the mountainswe want to generatethe bigger the initial displacement value should be and viceversa. With a bit of trial and error it is easy to get an idea of the average profiles generated by different values and how do they look. The point here is that bigger mountains need bigger initial displacement values.

Secondly, the overall dimensions (width and height) of the generated terrain. The initial displacement shouldbe regarded as a value which depends onthe generated terrain dimensions. What I want to say is that an initial displacement of 5may be huge when dealing with a 5×5 image but will hardly be noticed in a 1500×1500 image.


2D Terrain generation using midpoint displacement:

The same initial displacement value may be to big for a certain image size but may suit well another image size.

How much should the bounds be reduced after each iteration?

Well, the answer again depends on which is the desired output. It should be intuitive that the smaller the displacement reduction the more jagged the obtained profile will be and viceversa. The two extremes are no displacement reduction at all and setting the displacement to 0 after the first iteration. This two cases can be observed in the figure below.


2D Terrain generation using midpoint displacement:

On the left: no displacement reduction. On the right: Displacement reduction to 0 after first iteration.

Somewhere in between is the displacement reduction that will yield the desired output. There are plenty of ways to reduce the displacement bounds each iteration (linearly, exponentially, logarithmically, etc.) and I encourage you to try different ones and see how the results vary.

What I did was definea standard displacement reduction of 1/2, which means that the displacement is reduced by half each new iteration, and a displacement decay power i such that the displacement reduction is

displacement_reduction = 1/(2^i)

and

displacement_bounds(k+1) = displacement_bounds(k)*displacement_reduction

were k is the current iteration and k+1 the next iteration. We can then talk about the obtained terrain profiles in terms of this decay power i. Belowyou can see how the algorithm performs for different decay powers.


2D Terrain generation using midpoint displacement:

Obtained profiles for differentvalues of the displacement decay powers.

Bear in mind that the two factors we just saw, the bounds reduction and initial displacement are related one to the other and that they do not affect the outputindependently. Smaller initial displacements may look good with smaller decay powers and viceversa. Here we have talked about some guidelines that may help when deciding which values to use but there will besome trial and error until the right parametres for the desired output are found. Finally, the number of iterations is another factor that also affects the output in relation with the initial displacement and the bounds reduction.

Python implementation

Finally it is time to, with all the ideas explained above, code our 2D terrain generator. For this particular implementation I have decided to:

Displace only the y coordinate of the midpoints (Second of the two displacement methods explained at the begining). Use symmetric bounds with respect to zero for the displacement (if b is the upper bound then b will be the lower bound.) Choose thedisplacement valueto be either the upper bound orthe lower bound, but neverallow values in between. Reduce the bounds after each iteration by multiplying the current bounds by 1/(2^ i )

We will have threefunctions: one that will generate the terrain, one that will drawthe generated terrain and one that will handle the above processes.

Before implementing the functions we should first import the modules that we will use:

import os # path resolving and image saving import random # midpoint displacement from PIL import Image, ImageDraw # image creation and drawing import bisect # working with the sorted list of points Terrain generation For the terrain generation we need a function that, given a straight line segment returns the profile o

Viewing all articles
Browse latest Browse all 9596

Trending Articles