4
\$\begingroup\$

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)
  local u = fade(x)
  local v = fade(y)
  local w = fade(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 ),
                                 grad(p[BB+1], x-1, y-1, z-1 )))
  )
end


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


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


function grad(hash,x,y,z)
  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: http://i43.tinypic.com/fk7acm.png

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

\$\endgroup\$
9
  • 2
    \$\begingroup\$ 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? \$\endgroup\$
    – fnord
    Jul 5, 2013 at 22:22
  • 1
    \$\begingroup\$ 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? \$\endgroup\$ Jul 5, 2013 at 22:36
  • \$\begingroup\$ I added a picture so you can see how it looks like. \$\endgroup\$
    – Spynaz
    Jul 5, 2013 at 23:28
  • 1
    \$\begingroup\$ You could also check out libnoise, featured in a related answer. \$\endgroup\$ Jul 6, 2013 at 0:24
  • 3
    \$\begingroup\$ 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. \$\endgroup\$ Jul 6, 2013 at 19:32

2 Answers 2

13
\$\begingroup\$

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!

\$\endgroup\$
3
  • \$\begingroup\$ Thanks a lot! That seems to make my terrain look a bit better. \$\endgroup\$
    – Spynaz
    Jul 7, 2013 at 16:44
  • \$\begingroup\$ 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. \$\endgroup\$ Jul 7, 2013 at 16:48
  • \$\begingroup\$ @Spynaz If the answer helped to solve your problem, please mark it as accepted. \$\endgroup\$ Jul 9, 2013 at 6:59
0
\$\begingroup\$

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"

\$\endgroup\$
3
  • \$\begingroup\$ 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. \$\endgroup\$
    – MAnd
    Nov 26, 2015 at 20:43
  • \$\begingroup\$ @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. \$\endgroup\$ Jan 8, 2016 at 1:48
  • \$\begingroup\$ Or if you use prime numbers, you can then apply the chicada principle: sitepoint.com/… \$\endgroup\$ Mar 25, 2017 at 3:21

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .