-
-
Save kymckay/25758d37f8e3872e1636d90ad41fe2ed to your computer and use it in GitHub Desktop.
--[[ | |
Implemented as described here: | |
http://flafla2.github.io/2014/08/09/perlinnoise.html | |
]]-- | |
perlin = {} | |
perlin.p = {} | |
-- Hash lookup table as defined by Ken Perlin | |
-- This is a randomly arranged array of all numbers from 0-255 inclusive | |
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 | |
} | |
-- p is used to hash unit cube coordinates to [0, 255] | |
for i=0,255 do | |
-- Convert to 0 based index table | |
perlin.p[i] = permutation[i+1] | |
-- Repeat the array to avoid buffer overflow in hash function | |
perlin.p[i+256] = permutation[i+1] | |
end | |
-- Return range: [-1, 1] | |
function perlin:noise(x, y, z) | |
y = y or 0 | |
z = z or 0 | |
-- Calculate the "unit cube" that the point asked will be located in | |
local xi = bit32.band(math.floor(x),255) | |
local yi = bit32.band(math.floor(y),255) | |
local zi = bit32.band(math.floor(z),255) | |
-- Next we calculate the location (from 0 to 1) in that cube | |
x = x - math.floor(x) | |
y = y - math.floor(y) | |
z = z - math.floor(z) | |
-- We also fade the location to smooth the result | |
local u = self.fade(x) | |
local v = self.fade(y) | |
local w = self.fade(z) | |
-- Hash all 8 unit cube coordinates surrounding input coordinate | |
local p = self.p | |
local A, AA, AB, AAA, ABA, AAB, ABB, B, BA, BB, BAA, BBA, BAB, BBB | |
A = p[xi ] + yi | |
AA = p[A ] + zi | |
AB = p[A+1 ] + zi | |
AAA = p[ AA ] | |
ABA = p[ AB ] | |
AAB = p[ AA+1 ] | |
ABB = p[ AB+1 ] | |
B = p[xi+1] + yi | |
BA = p[B ] + zi | |
BB = p[B+1 ] + zi | |
BAA = p[ BA ] | |
BBA = p[ BB ] | |
BAB = p[ BA+1 ] | |
BBB = p[ BB+1 ] | |
-- Take the weighted average between all 8 unit cube coordinates | |
return self.lerp(w, | |
self.lerp(v, | |
self.lerp(u, | |
self:grad(AAA,x,y,z), | |
self:grad(BAA,x-1,y,z) | |
), | |
self.lerp(u, | |
self:grad(ABA,x,y-1,z), | |
self:grad(BBA,x-1,y-1,z) | |
) | |
), | |
self.lerp(v, | |
self.lerp(u, | |
self:grad(AAB,x,y,z-1), self:grad(BAB,x-1,y,z-1) | |
), | |
self.lerp(u, | |
self:grad(ABB,x,y-1,z-1), self:grad(BBB,x-1,y-1,z-1) | |
) | |
) | |
) | |
end | |
-- Gradient function finds dot product between pseudorandom gradient vector | |
-- and the vector from input coordinate to a unit cube vertex | |
perlin.dot_product = { | |
[0x0]=function(x,y,z) return x + y end, | |
[0x1]=function(x,y,z) return -x + y end, | |
[0x2]=function(x,y,z) return x - y end, | |
[0x3]=function(x,y,z) return -x - y end, | |
[0x4]=function(x,y,z) return x + z end, | |
[0x5]=function(x,y,z) return -x + z end, | |
[0x6]=function(x,y,z) return x - z end, | |
[0x7]=function(x,y,z) return -x - z end, | |
[0x8]=function(x,y,z) return y + z end, | |
[0x9]=function(x,y,z) return -y + z end, | |
[0xA]=function(x,y,z) return y - z end, | |
[0xB]=function(x,y,z) return -y - z end, | |
[0xC]=function(x,y,z) return y + x end, | |
[0xD]=function(x,y,z) return -y + z end, | |
[0xE]=function(x,y,z) return y - x end, | |
[0xF]=function(x,y,z) return -y - z end | |
} | |
function perlin:grad(hash, x, y, z) | |
return self.dot_product[bit32.band(hash,0xF)](x,y,z) | |
end | |
-- Fade function is used to smooth final output | |
function perlin.fade(t) | |
return t * t * t * (t * (t * 6 - 15) + 10) | |
end | |
function perlin.lerp(t, a, b) | |
return a + t * (b - a) | |
end |
I use lua pretty exclusively inside of Filter Forge, so there are often elements of scripts like this that I am unfamiliar with. I tested it out as an internally referenced function and it generated a warning on "function perlin:noise(x, y, z)" as an attempt to index a nil value (global 'perlin'). I don't think I've seen a colon in a function name before this, so I suspect I'm a victim of my own ignorance, here. I don't mind doing a bit more research to figure this out but I'm not sure where to begin. Any help would be appreciated.
Me again. I was able to get this noise mapped to a sphere and a spherical equatorial map, both with native seamlessness. I was not able to get seamless noise on a flat plane, though. Some poking around via Google led me to some complex methods for different languages, including 3D toroidal mapping and some 4D implementations o simplex noise. I looked at the source you cited and saw that tiling was mentioned, but it was not clear if it was seamless tiling or just repeating blocks. That's essentially what I ended up with, cascading down through each octave.
I don't know if you can help with this, but it's better to ask than to assume nothing. Do you have any thoughts or advice on this? Hope you don't mind the questions.
attempt to index a nil value (global 'perlin'). I don't think I've seen a colon in a function name before this
If you're still wondering, the colon is a way to declare a function as a "method" so-to-speak. In declarations, it adds an implicit first parameter to the function called self
(equivalent to this
in languages that have that concept), and in calls it passes in the containing table as the first argument (even if the function wasn't intended to be able to do that).
local module = { }
function module:setValue(n)
self._value = n
end
function module:getValue()
return self._value
end
-- ...
module:setValue(32)
print(module:getValue()) -- Just prints 32
As for the global perlin, pretty sure the top perlin = {}
declaration is supposed to be a local variable, which gets returned at the end of the file. That's best practice anyway, but maybe the author is intending this to work in environments where require
isn't available or something.
Thanks for the explanation of [function]:[method]
usage. I've seen self.
and this.
used in some code and script examples in various languages but I've never worked with them. So, I don't fully understand the full usage. Your reply at least gives me the right search topics to look it up on my own. This lua script gives me a working example of self.
and :[method]
in use. As I come across other examples, I'll get a better sense of how to implement them in my own scripts.
I'm still trying to find out how to implement seamless perlin in lua, preferably with proper octave scaling. I was backtracking to the implementation linked in this script when I finally saw your response, I'm hoping that page will get me on the right track!
Ah, now I remember. That article mentions seamlessness but does not include the implementation. It's going to take a lot more research to find one.
@ryanzec Apologies, I hadn't seen your comment until today. For any future readers:
Feel free to use this code in any project commercial/uncommercial with/without attribution.
Sorry to bug you more about this, but your "permission" is not a license that would fly with FOSS projects. Would you mind specifying something like BSD0 (which most closely matches your permission statement), or MIT, or some Free and Open source license, so that we can use this in FOSS projects?
Having a legally-accepted license statement makes this code actually usable.
Cheers, thanks for going through the whole mess so I don't have to.
(Non-commercial project, you will be attributed)