0
\$\begingroup\$

I am using lua with the love2d engine to make infinite 2d tiled terrain with perlin noise. This is just the first step and I want to fix the bugs early. The problem is that there is no gradient between the black and white islands. There should be a smooth transition all through the map.

two chunks of terrain

Also there is a line between the two chunks that I can't get rid of. I am completely lost and cannot figure these problems out. Here is what I am going for:

enter image description here

Here is my code.

seed = 257
local canvas = love.graphics.newCanvas()

function Interpolate(a, b, x)
    ft = x * 3.1415927
    f = (1 - math.cos(ft)) * 0.5

    return  a*(1-f) + b*f
end

function Noise(x, y)
    n = x + y * seed
    n = bit.bxor((bit.lshift(n,13)),n)
    return ( 1.0 - ( bit.band((n * (n * n * 15731 + 789221) + 
      1376312589), 0x7fffffff) / 1073741824.0))  
end

function SmoothedNoise1(x, y)
    corners = ( Noise(x-1, y-1)+Noise(x+1, y-1)+Noise(x-1, 
      y+1)+Noise(x+1, y+1) ) / 16
    sides   = ( Noise(x-1, y)  +Noise(x+1, y)  +Noise(x, y-1)  +Noise(x, 
      y+1) ) /  8
    center  =  Noise(x, y) / 4
    return corners + sides + center
end

function InterpolatedNoise_1(x, y)
    integer_X, fractional_X = math.modf(x)
    integer_Y, fractional_Y = math.modf(y)

    v1 = SmoothedNoise1(integer_X,     integer_Y)
    v2 = SmoothedNoise1(integer_X + 1, integer_Y)
    v3 = SmoothedNoise1(integer_X,     integer_Y + 1)
    v4 = SmoothedNoise1(integer_X + 1, integer_Y + 1)

    i1 = Interpolate(v1 , v2 , fractional_X)
    i2 = Interpolate(v3 , v4 , fractional_X)

    return Interpolate(i1 , i2 , fractional_Y)
end

function getExampleMap()
    mapWidth = 400
    mapHeight = 400
    mapMin = InterpolatedNoise_1(0, 0)
    mapMax = mapMin
    map = {}
    amp = 128  
    freq =32 
    octaves = 6 
    for x = 0, mapWidth - 1 do
        map[x] = {}
        for y = 0, mapHeight - 1 do
            map[x][y] = 0
            for i = 1, octaves do
                map[x][y] = map[x][y] + getNoiseValue(x, y, freq / i, 
                  amp / i)
            end
            mapMin = math.min(mapMin, map[x][y])
            mapMax = math.max(mapMax, map[x][y])
        end
    end
    -- /*scale the values between 0 and 255 for rendering in grey 
    scale.*/
    mapMultiplier = 255 / (mapMax - mapMin)
    for x = 0, mapWidth - 1 do
        for y = 0, mapHeight - 1 do
            map[x][y] = map[x][y] * mapMultiplier
        end
    end
end

function getExampleMap2()
    mapWidth = 400
    mapHeight = 400
    mapMin = InterpolatedNoise_1(0, 0)
    mapMax = mapMin
    map2 = {}
    amp = 128
    freq =32
    octaves = 6
    for x = 400, (mapWidth + 400) do
        map2[x] = {}
        for y = 0, mapHeight - 1 do
            map2[x][y] = 0
            for i = 1, octaves do
                map2[x][y] = map2[x][y] + getNoiseValue(x, y, freq / i, 
                  amp / i)

            end
            mapMin = math.min(mapMin, map2[x][y])
            mapMax = math.max(mapMax, map2[x][y])
        end
    end
    -- /*scale the values between 0 and 255 for rendering in grey 
      scale.*/
    mapMultiplier = 255 / (mapMax - mapMin)
    for x = 400, (mapWidth + 400) do
        for y = 0, mapHeight - 1 do
            map2[x][y] = map2[x][y] * mapMultiplier
        end
    end
end

function getNoiseValue(x, y, freq, amp)
    return InterpolatedNoise_1(x / freq, y / freq) * amp
end

function love.load()
    love.window.setMode( 1920, 1080, {fullscreen=false})
    getExampleMap()
    getExampleMap2()
end

function love.draw()

    for x = 0, mapWidth - 1 do
        for y = 0, mapHeight - 1 do
            love.graphics.setColor(map[x][y], map[x][y], map[x][y], 255)
            love.graphics.point(x,y)
        end
    end
    for x = 400, (mapWidth + 400) do
        for y = 0, mapHeight - 1 do
            love.graphics.setColor(map2[x][y], map2[x][y], map2[x][y], 
              255)
            love.graphics.point(x,y)
        end
    end

end

function love.update()

end

function love.keyreleased(key)
    if key == "escape" then
        love.event.quit()
    end
end
\$\endgroup\$
  • \$\begingroup\$ I quite like the result you get - however if you don't get the result you want, it's because you aren't using the right algorithm. What you are doing is creating value noise, not Perlin noise. They get easily confused because of some articles on the Internet that call Perlin noise what really is value noise. I have a fairly good implementation of true Perlin noise written in Lua for the same purpose as yours. I will post it as an answer as soon as possible. \$\endgroup\$ – user6245072 Jul 28 '16 at 7:47
  • \$\begingroup\$ The image you supplied looks like it would make for a nice ocean and mountain map. Just change the values until you get it right. \$\endgroup\$ – Krythic Nov 8 '16 at 15:15
1
\$\begingroup\$

There is a bug in the final normalisation code, fortunately it’s easy to fix. You only need to change:

map2[x][y] = map2[x][y] * mapMultiplier

into:

map2[x][y] = (map2[x][y] - mapMin) * mapMultiplier
\$\endgroup\$
0
\$\begingroup\$

As I said in the comments, what you are using is just value noise, not true Perlin noise. Here's my Lua implementation of true Perlin noise:

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

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

local function g(h, x, y, z)
 local h, u, v, r = h % 16
  if h < 8 then u = x else u = y end
 if h < 4 then v = y elseif h == (12 or 14) then v = x else v = z end
  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

local m = math.modf
local function noise(o, x, y, z)
 y, z = y or 1 / 3, z or 2 / 3
    local f, l, g, m = f, l, g, m
  local X, x = m(x % 256)
  local Y, y = m(y % 256)
    local Z, z = m(z % 256)
 local u, v, w = f(x), f(y), f(z)
 local A = o[X] + Y
 local AA = o[A] + Z
 local AB = o[A + 1] + Z
  local B = o[X + 1] + Y
 local BA = o[B] + Z
   local BB = o[B + 1] + Z
 return l(w, l(v, l(u, g(o[AA], x, y, z), 
                          g(o[BA], x - 1, y, z)), 
                    l(u, g(o[AB], x, y - 1, z), 
                        g(o[BB], x - 1, y - 1, z))),
              l(v, l(u, g(o[AA + 1], x, y, z - 1),  
                         g(o[BA + 1], x - 1, y, z - 1)),
                   l(u, g(o[AB + 1], x, y - 1, z - 1),
                         g(o[BB + 1], x - 1, y - 1, z - 1))))
end

local m = {
 __index = function(t, i)
        return type(i) == "number" and t[i % 256] or noise
 end}

return function(s)
   local o = {}
  local rand = math.random
 local _ = s and math.randomseed(tonumber(s) or os.time())
 for n = 0, 255 do
     local t = rand(0, 255)
      while o[t] do
           t = rand(0, 255)
     end
      o[t] = n
 end
 return setmetatable(o, m)
end

Paste this code in a separated file in the same directory, then in your main.lua file do:

function love.load()
    --Assuming you called the module perlin.lua
    newperlin = require("perlin")
    --newperlin is a function that generates Perlin noise objects:

    myperlin = newperlin()
    --when provided no arguments, newperlin seeds the Perlin object with the current time to create an always different result, but you can provide a seed which will create always the same result:
    myperlin = newperlin(seed)

    --then, to create noise:
    local noise = myperlin:noise(x, y, z)
    --y and z are optional; x is mandatory
end

Then see this question to understand how to actually get the result you asked for.

\$\endgroup\$
  • \$\begingroup\$ Thank you user6245072 , I have decided to stick with my original code. But I can't figure out why there is a line between my 2 chunks as seen in the picture above. \$\endgroup\$ – digitalsoldier0 Jul 29 '16 at 10:01
  • \$\begingroup\$ I think it's an error in the map generation code, as it is at the very center of the map, right where map1 ends and map2 starts, but I can't spot it. \$\endgroup\$ – user6245072 Jul 29 '16 at 12:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.