diff --git a/main.lua b/main.lua index 312d0ae..874f979 100644 --- a/main.lua +++ b/main.lua @@ -11,12 +11,13 @@ local function rng() local r = love.math.newRandomGenerator(1) return function() return r:random() end end -local n1,g1 = Noise.make1d(8,rng(),decay) -local n2,g2 = Noise.make2d(9,rng(),decay) -local n3,g3 = Noise.make3d(11,rng(),decay) -local s1,s1g = Noise.make1dShader(9,rng(),decay) -local s2,s2g = Noise.make2dShader(13,rng(),decay) -local s3,s3g = Noise.make3dShader(17,rng(),decay) +local makeAmps = Noise.makeAmplitudes +local n1,g1 = Noise.make1d(makeAmps(8, decay), rng()) +local n2,g2 = Noise.make2d(makeAmps(9, decay), rng()) +local n3,g3 = Noise.make3d(makeAmps(11, decay), rng()) +local s1,s1g = Noise.make1dShader(makeAmps(9, decay), rng()) +local s2,s2g = Noise.make2dShader(makeAmps(13, decay), rng()) +local s3,s3g = Noise.make3dShader(makeAmps(17, decay), rng()) local cw,ch = W/2,H/3 local canv = lg.newCanvas(cw,ch) diff --git a/noise.lua b/noise.lua index f2eaf3d..e889221 100644 --- a/noise.lua +++ b/noise.lua @@ -10,37 +10,44 @@ local function cdf_prime(x, dx) return (0.31831 * exp(-2/PI * x*x) * abs(x)*dx)/sqrt(1-exp(-2/PI*x*x)) end -local function _defaultArgs(resolution,random,decayFn) - if not resolution then resolution = 4 end +local function _defaultArgs(amplitudes,random) + if amplitudes == nil then amplitudes = 5 end + if type(amplitudes) == 'number' then + amplitudes = noise.makeAmplitudes(amplitudes, function(x) return .1^x end) + end if not random then random = math.random end - if not decayFn then decayFn = function(x) return .1^x end end - return resolution,random,decayFn + return amplitudes, random end -local function _amplitudesAndOffsets(decayFn,numAmplitudes, numOffsets, random) +local function calculateSigma(amplitudes) local sigma = 0 - local amplitudes = {} - for i=1,numAmplitudes do - local a = decayFn((i-1+random())/numAmplitudes) - amplitudes[i] = a - assert(a > 0.0001) - sigma = sigma + a^2 + for _,a in ipairs(amplitudes) do + sigma = sigma + a*a end - sigma = math.sqrt(sigma/2) + return math.sqrt(sigma/2) +end + +local function makeOffsets(count, random) local offsets = {} - for i=1,numOffsets do + for i=1,count do offsets[i] = random()*TAU end - return amplitudes,sigma,offsets + return offsets end -local function sign(x) - if x == 0 then return 0 - elseif x < 0 then return -1 else return 1 end +noise.makeAmplitudes = function(count, fn) + local amplitudes = {} + for i=1,count do + amplitudes[i] = fn((i-.5)/count) + end + return amplitudes end -noise.make1d = function(resolution,random,decayFn) - resolution,random,decayFn = _defaultArgs(resolution,random,decayFn) - local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,resolution,random) + +noise.make1d = function(amplitudes,random) + amplitudes, random = _defaultArgs(amplitudes, random) + local resolution = #amplitudes + local sigma = calculateSigma(amplitudes) + local offsets = makeOffsets(#amplitudes, random) local function noise(x) local n = 0 for i,a in ipairs(amplitudes) do @@ -63,9 +70,11 @@ noise.make1d = function(resolution,random,decayFn) return noise, gradient end -noise.make2d = function(resolution,random,decayFn) - resolution,random,decayFn = _defaultArgs(resolution,random,decayFn) - local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,2*resolution,random) +noise.make2d = function(amplitudes, random) + amplitudes, random = _defaultArgs(amplitudes, random) + local resolution = #amplitudes + local sigma = calculateSigma(amplitudes) + local offsets = makeOffsets(2*#amplitudes, random) sigma = sigma/sqrt(2) local function noise(x,y) local n = 0 @@ -100,9 +109,11 @@ noise.make2d = function(resolution,random,decayFn) return noise, gradient end -noise.make3d = function(resolution,random,decayFn) - resolution,random,decayFn = _defaultArgs(resolution,random,decayFn) - local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,3*resolution,random) +noise.make3d = function(amplitudes, random) + amplitudes, random = _defaultArgs(amplitudes, random) + local resolution = #amplitudes + local sigma = calculateSigma(amplitudes) + local offsets = makeOffsets(3*#amplitudes, random) sigma = sigma/sqrt(3) local function noise(x,y,z) -- Find the biggest fibonacci number F_n such that F_n < resolution @@ -228,15 +239,14 @@ vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) } ]] -noise.make1dShader = function(resolution,random,decayFn) - resolution,random,decayFn = _defaultArgs(resolution,random,decayFn) - if type(resolution) ~= 'number' or (resolution % 1 > 0) or (resolution <= 0) then - error("Resolution must be a positive integer.") - end +noise.make1dShader = function(amplitudes, random) + amplitudes, random = _defaultArgs(amplitudes, random) + local resolution = #amplitudes + local sigma = calculateSigma(amplitudes) + local offsets = makeOffsets(#amplitudes, random) local shaderCode = "#define RESOLUTION "..tostring(resolution).."\n"..shader1d local noiseShader = lg.newShader(shaderCode) local gradShader = lg.newShader("#define GRADIENT\n"..shaderCode) - local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,resolution,random) do -- Dumb hack to work around a bug in Love 0.10.2 not sending the last value table.insert(amplitudes, 1.) table.insert(offsets, 1.) @@ -299,15 +309,14 @@ vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) } ]] -noise.make2dShader = function(resolution,random,decayFn) - resolution,random,decayFn = _defaultArgs(resolution,random,decayFn) - if type(resolution) ~= 'number' or (resolution % 1 > 0) or (resolution <= 0) then - error("Resolution must be a positive integer.") - end +noise.make2dShader = function(amplitudes, random) + amplitudes, random = _defaultArgs(amplitudes, random) + local resolution = #amplitudes + local sigma = calculateSigma(amplitudes) + local offsets = makeOffsets(2*#amplitudes, random) local shaderCode = "#define RESOLUTION "..tostring(resolution).."\n"..shader2d local noiseShader = lg.newShader(shaderCode) local gradShader = lg.newShader("#define GRADIENT\n"..shaderCode) - local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,2*resolution,random) sigma = sigma/sqrt(2) local offsets2 = {} for i=1,#offsets-1,2 do @@ -405,15 +414,14 @@ vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) } ]] -noise.make3dShader = function(resolution,random,decayFn) - resolution,random,decayFn = _defaultArgs(resolution,random,decayFn) - if type(resolution) ~= 'number' or (resolution % 1 > 0) or (resolution <= 0) then - error("Resolution must be a positive integer.") - end +noise.make3dShader = function(amplitudes, random) + amplitudes, random = _defaultArgs(amplitudes, random) + local resolution = #amplitudes + local sigma = calculateSigma(amplitudes) + local offsets = makeOffsets(3*#amplitudes, random) local shaderCode = "#define RESOLUTION "..tostring(resolution).."\n"..shader3d local noiseShader = lg.newShader(shaderCode) local gradShader = lg.newShader("#define GRADIENT\n"..shaderCode) - local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,3*resolution,random) sigma = sigma/sqrt(3) local offsets2 = {} for i=1,#offsets-1,3 do