Skip to content

Instantly share code, notes, and snippets.

@Kaixhin
Last active January 2, 2017 17:01
Show Gist options
  • Save Kaixhin/022ffbfbcf8d542ca1a5f562b28b740a to your computer and use it in GitHub Desktop.
Save Kaixhin/022ffbfbcf8d542ca1a5f562b28b740a to your computer and use it in GitHub Desktop.
Using Perlin Noise to Generate 2D Terrain and Water
--[[
-- Using Perlin Noise to Generate 2D Terrain and Water
-- http://gpfault.net/posts/perlin-noise.txt.html
--]]
local image = require 'image'
-- Fade function
local fade = function(t)
-- Provides continuous higher order derivatives for smoothness (this specifically is in the class of sigmoid functions)
return math.pow(t, 3) * (t * (t * 6 - 15) + 10)
end
-- Lookup table of permuted integers
local P = torch.cat(torch.randperm(256), torch.randperm(256))
-- Uniformly distributed gradient values
local grads = torch.Tensor(512):uniform(-1, 1)
-- 1D gradient function
local grad = function(p)
-- Have to use p + 1 as Lua is 1-indexed
return grads[P[p + 1]]
end
-- 1D noise function
local noise = function(p)
local p0 = math.floor(p)
local p1 = p0 + 1
local t = p - p0
local fadeT = fade(t) -- Used for linear interpolation
local g0 = grad(p0)
local g1 = grad(p1)
-- Specifies desired growth in the neighbourhood and fits a smooth curve given the growth requirements
return (1 - fadeT) * g0 * t + fadeT * g1 * (p - p1)
end
-- Create 1D Perlin noise
local frequencies = {1/300, 1/150, 1/75, 1/37.5}
local amplitudes = {1, 0.5, 0.25, 0.125}
local dim = 300
local img = torch.Tensor(dim, dim)
for x = 1, dim do
for y = 1, dim do
local n = noise(x * frequencies[1]) * amplitudes[1] + noise(x * frequencies[2]) * amplitudes[2] + noise(x * frequencies[3]) * amplitudes[3] + noise(x * frequencies[4]) * amplitudes[4]
local y2 = 2 * (y / dim) - 1 -- Map y into [-1, 1] range
local colour = n > y2 and 1 or 0
img[y][x] = colour
end
end
image.display(img)
-- 2D gradient function
local grad2 = function(p)
-- Just use random values
--local g = torch.Tensor(2):uniform(-1, 1)
local g = torch.Tensor{grads[P[p[1] + 1]], grads[P[p[1] + 1] + p[2]]}
g:div(g:norm()) -- Normalise to unit vector
return g
end
-- 2D noise function
local noise2 = function(p)
-- Calculate lattice points
local p0 = torch.floor(p)
local p1 = p0 + torch.Tensor{1, 0}
local p2 = p0 + torch.Tensor{0, 1}
local p3 = p0 + torch.Tensor{1, 1}
-- Look up gradients at lattice points
local g0 = grad2(p0)
local g1 = grad2(p1)
local g2 = grad2(p2)
local g3 = grad2(p3)
local t0 = p[1] - p0[1]
local fadeT0 = fade(t0) -- Used for horizontal interpolation
local t1 = p[2] - p0[2]
local fadeT1 = fade(t1) -- Used for vertical interpolation
-- Calculate dot products and interpolate
local p0p1 = (1 - fadeT0) * torch.dot(g0, p - p0) + fadeT0 * torch.dot(g1, p - p1) -- Between upper two lattice points
local p2p3 = (1 - fadeT0) * torch.dot(g2, p - p2) + fadeT0 * torch.dot(g3, p - p3) -- Between lower two lattice points
-- Calculate final result
return (1 - fadeT1) * p0p1 + fadeT1 * p2p3
end
-- Create 2D Perlin noise
for x = 1, dim do
for y = 1, dim do
local xy = torch.Tensor{x, y}
local n = noise2(xy * frequencies[1]) * amplitudes[1] + noise2(xy * frequencies[2]) * amplitudes[2] + noise2(xy * frequencies[3]) * amplitudes[3] + noise2(xy * frequencies[4]) * amplitudes[4]
img[y][x] = n
end
end
image.display(img)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment