# 2D and 3D Perlin Noise Terrain Generation

I'm using 2d perlin noise to generate a heightmap and then adding 3d perlin noise to the heightmap to create overhangs and ridges. But it's not turning out like I would like it to. There are too many hills that don't look good and lots of different places where it's flat. This is the perlin noise function I'm using for both the 2d and 3d (in LUA):

function noise(x, y, z)
local X = math.floor(x % 255)
local Y = math.floor(y % 255)
local Z = math.floor(z % 255)
x = x - math.floor(x)
y = y - math.floor(y)
z = z - math.floor(z)

A   = p[X  ]+Y
AA  = p[A]+Z
AB  = p[A+1]+Z
B   = p[X+1]+Y
BA  = p[B]+Z
BB  = p[B+1]+Z

return lerp(w, lerp(v, lerp(u, grad(p[AA  ], x  , y  , z   ),
grad(p[BA  ], x-1, y  , z   )),
lerp(u, grad(p[AB  ], x  , y-1, z   ),
grad(p[BB  ], x-1, y-1, z   ))),
lerp(v, lerp(u, grad(p[AA+1], x  , y  , z-1 ),
grad(p[BA+1], x-1, y  , z-1 )),
lerp(u, grad(p[AB+1], x  , y-1, z-1 ),
)
end

return t * t * t * (t * (t * 6 - 15) + 10)
end

function lerp(t,a,b)
return a + t * (b - a)
end

local h = hash % 16
local u
local v

if (h<8) then u = x else u = y end
if (h<4) then v = y elseif (h==12 or h==14) then v=x else v=z end
local r

if ((h%2) == 0) then r=u else r=-u end
if ((h%4) == 0) then r=r+v else r=r-v end
return r
end

p = {}
local permutation = {151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
}

for i=0,255 do
p[i] = permutation[i+1]
p[256+i] = permutation[i+1]
end


You can also find the implementation, written by Perlin himself, here: http://mrl.nyu.edu/~perlin/noise/

And this is how my loop looks like:

local smoothness = math.random(20, 30)
for x = 1, mapScale do
wait()
for z = 1, mapScale do
local height = noise(x/smoothness, z/smoothness, 0) --Find out the height
for y = 1, (height*mapHeight)+10 do
local density = noise(x/smoothness, y/smoothness, z/smoothness) --Find out the density
if y > waterLvl then
if density*10 > 0 then
t:setCell(x, y, z, 1, 0, 0) --Sets cell at position x, y, z
end
else
t:setCell(x, y, z, 1, 0, 0)
end
end
end
end


This is how it turns out to be:

How would I change the frequency, number of octaves, and persistence with this function since it doesn't take any parameters for them?

• Greetings, user, and welcome to the GameDev stack. You say your code is not producing output you like. Can you show us the output you're getting, and indicate why/where it's not matching up with your expectations? – fnord Jul 5 '13 at 22:22
• Perlin noise is just a guaranteed smooth, random distribution of data from 0..1. 3D perlin just makes it smooth in a cube. What do you expect to see? – Patrick Hughes Jul 5 '13 at 22:36
• I added a picture so you can see how it looks like. – Spynaz Jul 5 '13 at 23:28
• You could also check out libnoise, featured in a related answer. – congusbongus Jul 6 '13 at 0:24
• At this point you need to read the documentation to the two noise libraries mentioned, and then if there's a term you don't understand it then head to google. For example, libnoise has a really nice glossary libnoise.sourceforge.net/glossary to get you started. – Patrick Hughes Jul 6 '13 at 19:32

You are ONLY generating Perlin noise. This is actually the same as using only one octave, at any frequency. You can base yourself to implent FBM (Fractional Brownian Motion), It is actually what all engines use to make Perlin noise more fractal looking.

Bringing it down to code, you can use it like this:

//pseudo code
function fBm(float x, float y, float z, int octaves = 8, float lacunarity = 2.0, float gain = 0.5)
{
float amplitude = 1.0;
float frequency = 1.0;
float sum = 0.0;
for(int i = 0; i < octaves; ++i)
{
sum += amplitude * noise(x * frequency, y * frequency, z * frequency);
amplitude *= gain;
frequency *= lacunarity;
}
return sum;
}


You can change this line

sum += amplitude * noise(x * frequency, y * frequency, z * frequency);


with some other noise functions to generate some cool effects, for example:

sum += amplitude * (1-abs(noise(x * frequency, y * frequency, z * frequency)));


and you'll have a cool eroded terrain, with lots of straight mountains. Having a nice terrain is a game of playing with parameters, don't expect an already made "recipe", try to make your own!

• Thanks a lot! That seems to make my terrain look a bit better. – Spynaz Jul 7 '13 at 16:44
• One tip is to try to render the generated heightmap on an texture, then saving it on disk. Should help a lot to understand how your noise is looking. – Gustavo Maciel Jul 7 '13 at 16:48
• @Spynaz If the answer helped to solve your problem, please mark it as accepted. – Gustavo Maciel Jul 9 '13 at 6:59

I have an idea that, since you can use different octaves, you can make harmonics using multiple resolutions of perlin noise.

8, 4, 2, 1 as octaves would superposition itself nicely, I think.

The only problem with this is that it might not be 100% "random" in that you use the same seed for each octave.

The key here is the word "superposition"

• Well, all these methods are called pseudo-random because of that, but that's the usual way to do it. All number generators in a computer are seed-dependent. If you want the simulation to become really hard of being replicated with the same seed, a simple solution is to set the seed to the current second, or millisecond, or frame number, since the game started. – MAnd Nov 26 '15 at 20:43
• @ToXeye Yep! If you look at my answer, this is exaclty how it goes! The default lacunarity is 2.0, which would yield octaves as 1, 2, 4 and 8, 16, 32, 64 and 128. You could change the lacunarity to rand numbers each iteration to have a more random look. – Gustavo Maciel Jan 8 '16 at 1:48
• Or if you use prime numbers, you can then apply the chicada principle: sitepoint.com/… – Quickredfox Mar 25 '17 at 3:21