Added gradient calculations and moved resolution to a static #define.
This commit is contained in:
parent
7d72317846
commit
48f9da8196
74
main.lua
74
main.lua
@ -11,12 +11,12 @@ local function rng()
|
|||||||
local r = love.math.newRandomGenerator(1)
|
local r = love.math.newRandomGenerator(1)
|
||||||
return function() return r:random() end
|
return function() return r:random() end
|
||||||
end
|
end
|
||||||
local n1 = Noise.make1d(8,rng(),decay)
|
local n1,g1 = Noise.make1d(8,rng(),decay)
|
||||||
local n2 = Noise.make2d(9,rng(),decay)
|
local n2,g2 = Noise.make2d(9,rng(),decay)
|
||||||
local n3 = Noise.make3d(11,rng(),decay)
|
local n3,g3 = Noise.make3d(11,rng(),decay)
|
||||||
local s1 = Noise.make1dShader(9,rng(),decay)
|
local s1,s1g = Noise.make1dShader(9,rng(),decay)
|
||||||
local s2 = Noise.make2dShader(13,rng(),decay)
|
local s2,s2g = Noise.make2dShader(13,rng(),decay)
|
||||||
local s3 = Noise.make3dShader(17,rng(),decay)
|
local s3,s3g = Noise.make3dShader(17,rng(),decay)
|
||||||
|
|
||||||
local cw,ch = W/2,H/3
|
local cw,ch = W/2,H/3
|
||||||
local canv = lg.newCanvas(cw,ch)
|
local canv = lg.newCanvas(cw,ch)
|
||||||
@ -52,17 +52,38 @@ function love.draw()
|
|||||||
local p1 = {}
|
local p1 = {}
|
||||||
for x=0,W-H/3,DRAW_RES do
|
for x=0,W-H/3,DRAW_RES do
|
||||||
table.insert(p1,x)
|
table.insert(p1,x)
|
||||||
table.insert(p1,(1-n1(x/SCALE+xOffset))*H/3)
|
table.insert(p1,n1(x/SCALE+xOffset)*H/3)
|
||||||
end
|
end
|
||||||
lg.line(p1)
|
lg.line(p1)
|
||||||
|
do -- Draw tangent line
|
||||||
|
local mx = love.mouse.getX()
|
||||||
|
local y, dydx = n1(mx/SCALE + xOffset), g1(mx/SCALE + xOffset)
|
||||||
|
y = y * H/3
|
||||||
|
dydx = dydx * H/3 / SCALE
|
||||||
|
local h = 50/math.sqrt(1+dydx^2)
|
||||||
|
lg.setColor(255,255,0)
|
||||||
|
lg.line(mx-h, y - h*dydx, mx + h, y + h*dydx)
|
||||||
|
lg.setColor(255,255,255)
|
||||||
|
end
|
||||||
|
|
||||||
local p2 = {}
|
local p2 = {}
|
||||||
local t = love.timer.getTime()-t0
|
local t = love.timer.getTime()-t0
|
||||||
for x=0,W-H/3,DRAW_RES do
|
for x=0,W-H/3,DRAW_RES do
|
||||||
table.insert(p2,x)
|
table.insert(p2,x)
|
||||||
table.insert(p2,(1+1-n2(x/SCALE+xOffset,t))*H/3)
|
table.insert(p2,(1+n2(x/SCALE+xOffset,t))*H/3)
|
||||||
end
|
end
|
||||||
lg.line(p2)
|
lg.line(p2)
|
||||||
|
do -- Draw tangent line
|
||||||
|
local mx = love.mouse.getX()
|
||||||
|
local y = n2(mx/SCALE + xOffset, t)
|
||||||
|
local dydx, dydz = g2(mx/SCALE + xOffset, t)
|
||||||
|
y = (1+y) * H/3
|
||||||
|
dydx = dydx * H/3 / SCALE
|
||||||
|
local h = 50/math.sqrt(1+dydx^2)
|
||||||
|
lg.setColor(255,255,0)
|
||||||
|
lg.line(mx-h, y - h*dydx, mx + h, y + h*dydx)
|
||||||
|
lg.setColor(255,255,255)
|
||||||
|
end
|
||||||
|
|
||||||
for y=0,H/6*1.2,2*DRAW_RES do
|
for y=0,H/6*1.2,2*DRAW_RES do
|
||||||
local p3 = {}
|
local p3 = {}
|
||||||
@ -87,19 +108,36 @@ function love.draw()
|
|||||||
lg.line(p3)
|
lg.line(p3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
do -- Draw tangent line
|
||||||
|
local mx = love.mouse.getX()
|
||||||
|
local my = love.mouse.getY()/2 - 1/3*H
|
||||||
|
if my < 1/6*H then my = 1/6*H end
|
||||||
|
mx = mx + .5*my
|
||||||
|
local y = 2/3*H+H/6*(1-n3(mx/SCALE + xOffset, my/SCALE + yOffset, t))+my
|
||||||
|
local dydx, dydz, _ = g3(mx/SCALE + xOffset, my/SCALE + yOffset, t)
|
||||||
|
dydx = dydx * H/3 / SCALE
|
||||||
|
local h = 50/math.sqrt(1+dydx^2)
|
||||||
|
lg.setColor(255,255,0)
|
||||||
|
lg.line(mx-h, y - h*dydx, mx + h, y + h*dydx)
|
||||||
|
lg.setColor(255,255,255)
|
||||||
|
end
|
||||||
|
|
||||||
lg.setShader(s1)
|
local shader
|
||||||
s1:send("range_min", xOffset)
|
shader = love.keyboard.isDown('g') and s1g or s1
|
||||||
s1:send("range_max", xOffset+2*cw/SCALE)
|
lg.setShader(shader)
|
||||||
|
shader:send("range_min", xOffset)
|
||||||
|
shader:send("range_max", xOffset+2*cw/SCALE)
|
||||||
lg.draw(canv,W-cw,0)
|
lg.draw(canv,W-cw,0)
|
||||||
lg.setShader(s2)
|
shader = love.keyboard.isDown('g') and s2g or s2
|
||||||
s2:send("range_min", {xOffset,yOffset})
|
lg.setShader(shader)
|
||||||
s2:send("range_max", {xOffset+2*cw/SCALE,yOffset+2*ch/SCALE})
|
shader:send("range_min", {xOffset,yOffset})
|
||||||
|
shader:send("range_max", {xOffset+2*cw/SCALE,yOffset+2*ch/SCALE})
|
||||||
lg.draw(canv,W-cw,ch)
|
lg.draw(canv,W-cw,ch)
|
||||||
lg.setShader(s3)
|
shader = love.keyboard.isDown('g') and s3g or s3
|
||||||
s3:send("range_min", {xOffset,yOffset})
|
lg.setShader(shader)
|
||||||
s3:send("range_max", {xOffset+2*cw/SCALE,yOffset+2*ch/SCALE})
|
shader:send("range_min", {xOffset,yOffset})
|
||||||
s3:send('z', t)
|
shader:send("range_max", {xOffset+2*cw/SCALE,yOffset+2*ch/SCALE})
|
||||||
|
shader:send('z', t)
|
||||||
lg.draw(canv,W-cw,2*ch)
|
lg.draw(canv,W-cw,2*ch)
|
||||||
lg.setShader(nil)
|
lg.setShader(nil)
|
||||||
|
|
||||||
|
333
noise.lua
333
noise.lua
@ -1,11 +1,15 @@
|
|||||||
local noise = {}
|
local noise = {}
|
||||||
local exp,sin,cos,floor,log,acos,sqrt = math.exp,math.sin,math.cos,math.floor,math.log,math.acos,math.sqrt
|
local exp,sin,cos,floor,log,acos,sqrt,abs = math.exp,math.sin,math.cos,math.floor,math.log,math.acos,math.sqrt,math.abs
|
||||||
local GR, PI, TAU, SQRT5, LOG_GR = (sqrt(5)+1)/2, math.pi, 2*math.pi, sqrt(5), log((sqrt(5)+1)/2)
|
local GR, PI, TAU, SQRT5, LOG_GR = (sqrt(5)+1)/2, math.pi, 2*math.pi, sqrt(5), log((sqrt(5)+1)/2)
|
||||||
|
|
||||||
local function cdf(x,sigma)
|
local function cdf(x)
|
||||||
return .5 + .5*(x<0 and -1 or 1)*sqrt(1.-exp(-4./TAU * x*x))
|
return .5 + .5*(x<0 and -1 or 1)*sqrt(1.-exp(-4./TAU * x*x))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
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)
|
local function _defaultArgs(resolution,random,decayFn)
|
||||||
if not resolution then resolution = 4 end
|
if not resolution then resolution = 4 end
|
||||||
if not random then random = math.random end
|
if not random then random = math.random end
|
||||||
@ -19,6 +23,7 @@ local function _amplitudesAndOffsets(decayFn,numAmplitudes, numOffsets, random)
|
|||||||
for i=1,numAmplitudes do
|
for i=1,numAmplitudes do
|
||||||
local a = decayFn((i-1+random())/numAmplitudes)
|
local a = decayFn((i-1+random())/numAmplitudes)
|
||||||
amplitudes[i] = a
|
amplitudes[i] = a
|
||||||
|
assert(a > 0.0001)
|
||||||
sigma = sigma + a^2
|
sigma = sigma + a^2
|
||||||
end
|
end
|
||||||
sigma = math.sqrt(sigma/2)
|
sigma = math.sqrt(sigma/2)
|
||||||
@ -29,45 +34,83 @@ local function _amplitudesAndOffsets(decayFn,numAmplitudes, numOffsets, random)
|
|||||||
return amplitudes,sigma,offsets
|
return amplitudes,sigma,offsets
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function sign(x)
|
||||||
|
if x == 0 then return 0
|
||||||
|
elseif x < 0 then return -1 else return 1 end
|
||||||
|
end
|
||||||
noise.make1d = function(resolution,random,decayFn)
|
noise.make1d = function(resolution,random,decayFn)
|
||||||
resolution,random,decayFn = _defaultArgs(resolution,random,decayFn)
|
resolution,random,decayFn = _defaultArgs(resolution,random,decayFn)
|
||||||
local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,resolution,random)
|
local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,resolution,random)
|
||||||
return function(x)
|
local function noise(x)
|
||||||
local noise = 0
|
local n = 0
|
||||||
for i,a in ipairs(amplitudes) do
|
for i,a in ipairs(amplitudes) do
|
||||||
noise = noise + a*cos(x/a + offsets[i])
|
n = n + a*cos(x/a + offsets[i])
|
||||||
end
|
end
|
||||||
return cdf(noise/sigma);
|
n = n/sigma
|
||||||
|
return cdf(n)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function gradient(x)
|
||||||
|
local n, dndx = 0, 0
|
||||||
|
for i,a in ipairs(amplitudes) do
|
||||||
|
n = n + a*cos(x/a + offsets[i])
|
||||||
|
dndx = dndx - sin(x/a + offsets[i])
|
||||||
|
end
|
||||||
|
dndx = dndx/sigma
|
||||||
|
n = n/sigma
|
||||||
|
return cdf_prime(n,dndx)
|
||||||
|
end
|
||||||
|
return noise, gradient
|
||||||
end
|
end
|
||||||
|
|
||||||
noise.make2d = function(resolution,random,decayFn)
|
noise.make2d = function(resolution,random,decayFn)
|
||||||
resolution,random,decayFn = _defaultArgs(resolution,random,decayFn)
|
resolution,random,decayFn = _defaultArgs(resolution,random,decayFn)
|
||||||
local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,2*resolution,random)
|
local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,2*resolution,random)
|
||||||
sigma = sigma/sqrt(2)
|
sigma = sigma/sqrt(2)
|
||||||
return function(x,y)
|
local function noise(x,y)
|
||||||
local noise = 0
|
local n = 0
|
||||||
for i,a in ipairs(amplitudes) do
|
for i,a in ipairs(amplitudes) do
|
||||||
local angle = ((i*GR) % 1)*TAU
|
local angle = ((i*GR) % 1)*TAU
|
||||||
local u = x*cos(angle) - y*sin(angle)
|
local sina, cosa = sin(angle), cos(angle)
|
||||||
local v = x*cos(angle+TAU/4) - y*sin(angle+TAU/4)
|
local u = x*cosa - y*sina
|
||||||
noise = noise + a/2*(cos(u/a + offsets[2*i]) + cos(v/a + offsets[2*i-1]))
|
local v = -x*sina - y*cosa
|
||||||
|
local k, w = offsets[2*i], offsets[2*i-1]
|
||||||
|
n = n + a*(cos(u/a + k) + cos(v/a + w))
|
||||||
end
|
end
|
||||||
return cdf(noise/sigma);
|
n = n/(2*sigma)
|
||||||
|
return cdf(n)
|
||||||
end
|
end
|
||||||
|
local function gradient(x,y)
|
||||||
|
local n, dx, dy = 0,0,0
|
||||||
|
for i,a in ipairs(amplitudes) do
|
||||||
|
local angle = ((i*GR) % 1)*TAU
|
||||||
|
local sina, cosa = sin(angle), cos(angle)
|
||||||
|
local u = x*cosa - y*sina
|
||||||
|
local v = -x*sina - y*cosa
|
||||||
|
local k, w = offsets[2*i], offsets[2*i-1]
|
||||||
|
n = n + a*(cos(u/a + k) + cos(v/a + w))
|
||||||
|
dx = dx + (sina*sin(v/a + w) - cosa*sin(u/a + k))
|
||||||
|
dy = dy + (sina*sin(u/a + k) + cosa*sin(v/a + w))
|
||||||
|
end
|
||||||
|
n = n/(2*sigma)
|
||||||
|
dx = dx/(2*sigma)
|
||||||
|
dy = dy/(2*sigma)
|
||||||
|
return cdf_prime(n,dx), cdf_prime(n,dy)
|
||||||
|
end
|
||||||
|
return noise, gradient
|
||||||
end
|
end
|
||||||
|
|
||||||
noise.make3d = function(resolution,random,decayFn)
|
noise.make3d = function(resolution,random,decayFn)
|
||||||
resolution,random,decayFn = _defaultArgs(resolution,random,decayFn)
|
resolution,random,decayFn = _defaultArgs(resolution,random,decayFn)
|
||||||
local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,3*resolution,random)
|
local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,3*resolution,random)
|
||||||
sigma = sigma/sqrt(3)
|
sigma = sigma/sqrt(3)
|
||||||
return function(x,y,z)
|
local function noise(x,y,z)
|
||||||
-- Find the biggest fibonacci number F_n such that F_n < resolution
|
-- Find the biggest fibonacci number F_n such that F_n < resolution
|
||||||
local n = floor(log((resolution-1)*SQRT5 + .5)/LOG_GR)
|
local n = floor(log((resolution-1)*SQRT5 + .5)/LOG_GR)
|
||||||
local dec = floor(.5 + (GR^n)/SQRT5) -- F_n, using closed form Fibonacci
|
local dec = floor(.5 + (GR^n)/SQRT5) -- F_n, using closed form Fibonacci
|
||||||
local inc = floor(.5 + dec/GR) -- F_(n-1)
|
local inc = floor(.5 + dec/GR) -- F_(n-1)
|
||||||
|
|
||||||
local noise,i,j = 0,0,0
|
local n,i,j = 0,0,0
|
||||||
for i=0,resolution-1 do
|
for i=0,resolution-1 do
|
||||||
if j >= dec then
|
if j >= dec then
|
||||||
j = j - dec
|
j = j - dec
|
||||||
@ -82,24 +125,71 @@ noise.make3d = function(resolution,random,decayFn)
|
|||||||
local theta = acos(-1+2*((j*GR) % 1))
|
local theta = acos(-1+2*((j*GR) % 1))
|
||||||
-- Make an orthonormal basis, where n1 is from polar phi/theta,
|
-- Make an orthonormal basis, where n1 is from polar phi/theta,
|
||||||
-- n2 is roated 90 degrees along phi, and n3 is the cross product of the two
|
-- n2 is roated 90 degrees along phi, and n3 is the cross product of the two
|
||||||
local n1 = {sin(phi)*cos(theta), sin(phi)*sin(theta), cos(phi)}
|
local n1x,n1y,n1z = sin(phi)*cos(theta), sin(phi)*sin(theta), cos(phi)
|
||||||
local n2 = {sin(phi+TAU/4.)*cos(theta), sin(phi+TAU/4.)*sin(theta), cos(phi+TAU/4.)}
|
local n2x,n2y,n2z = sin(phi+TAU/4.)*cos(theta), sin(phi+TAU/4.)*sin(theta), cos(phi+TAU/4.)
|
||||||
-- Cross product
|
-- Cross product
|
||||||
local n3 = {n1[2]*n2[3] - n1[3]*n2[2],
|
local n3x,n3y,n3z = n1y*n2z - n1z*n2y,
|
||||||
n1[3]*n2[1] - n1[1]*n2[3],
|
n1z*n2x - n1x*n2z,
|
||||||
n1[1]*n2[2] - n1[2]*n2[1]}
|
n1x*n2y - n1y*n2x
|
||||||
-- Convert pos from x/y/z coordinates to n1/n2/n3 coordinates
|
-- Convert pos from x/y/z coordinates to n1/n2/n3 coordinates
|
||||||
local u = n1[1]*x + n1[2]*y + n1[3]*z
|
local u = n1x*x + n1y*y + n1z*z
|
||||||
local v = n2[1]*x + n2[2]*y + n2[3]*z
|
local v = n2x*x + n2y*y + n2z*z
|
||||||
local w = n3[1]*x + n3[2]*y + n3[3]*z
|
local w = n3x*x + n3y*y + n3z*z
|
||||||
-- Pull the amplitude from the shuffled array index ("j"), not "i",
|
-- Pull the amplitude from the shuffled array index ("j"), not "i",
|
||||||
-- otherwise neighboring unit vectors will have similar amplitudes!
|
-- otherwise neighboring unit vectors will have similar amplitudes!
|
||||||
local a = amplitudes[j+1]
|
local a = amplitudes[j+1]
|
||||||
-- Noise is the average of cosine of distance along each axis, shifted by offsets and scaled by amplitude.
|
-- Noise is the average of cosine of distance along each axis, shifted by offsets and scaled by amplitude.
|
||||||
noise = noise + a*(cos(u/a + offsets[3*i+1]) + cos(v/a + offsets[3*i+2]) + cos(w/a + offsets[3*i+3]))/3
|
n = n + a*(cos(u/a + offsets[3*i+1]) + cos(v/a + offsets[3*i+2]) + cos(w/a + offsets[3*i+3]))
|
||||||
end
|
end
|
||||||
return cdf(noise/sigma)
|
return cdf(n/(3*sigma))
|
||||||
end
|
end
|
||||||
|
local function gradient(x,y,z)
|
||||||
|
-- Find the biggest fibonacci number F_n such that F_n < resolution
|
||||||
|
local n = floor(log((resolution-1)*SQRT5 + .5)/LOG_GR)
|
||||||
|
local dec = floor(.5 + (GR^n)/SQRT5) -- F_n, using closed form Fibonacci
|
||||||
|
local inc = floor(.5 + dec/GR) -- F_(n-1)
|
||||||
|
|
||||||
|
local n,i,j = 0,0,0
|
||||||
|
local dndx,dndy,dndz = 0,0,0
|
||||||
|
for i=0,resolution-1 do
|
||||||
|
if j >= dec then
|
||||||
|
j = j - dec
|
||||||
|
else
|
||||||
|
j = j + inc
|
||||||
|
if j >= resolution then
|
||||||
|
j = j - dec
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Convert golden ratio sequence into polar coordinate unit vector
|
||||||
|
local phi = ((i*GR) % 1)*TAU
|
||||||
|
local theta = acos(-1+2*((j*GR) % 1))
|
||||||
|
-- Make an orthonormal basis, where n1 is from polar phi/theta,
|
||||||
|
-- n2 is roated 90 degrees along phi, and n3 is the cross product of the two
|
||||||
|
local n1x,n1y,n1z = sin(phi)*cos(theta), sin(phi)*sin(theta), cos(phi)
|
||||||
|
local n2x,n2y,n2z = sin(phi+TAU/4.)*cos(theta), sin(phi+TAU/4.)*sin(theta), cos(phi+TAU/4.)
|
||||||
|
-- Cross product
|
||||||
|
local n3x,n3y,n3z = n1y*n2z - n1z*n2y,
|
||||||
|
n1z*n2x - n1x*n2z,
|
||||||
|
n1x*n2y - n1y*n2x
|
||||||
|
-- Convert pos from x/y/z coordinates to n1/n2/n3 coordinates
|
||||||
|
local u = n1x*x + n1y*y + n1z*z
|
||||||
|
local v = n2x*x + n2y*y + n2z*z
|
||||||
|
local w = n3x*x + n3y*y + n3z*z
|
||||||
|
-- Pull the amplitude from the shuffled array index ("j"), not "i",
|
||||||
|
-- otherwise neighboring unit vectors will have similar amplitudes!
|
||||||
|
local a = amplitudes[j+1]
|
||||||
|
-- Noise is the average of cosine of distance along each axis, shifted by offsets and scaled by amplitude.
|
||||||
|
n = n + a*(cos(u/a + offsets[3*i+1]) + cos(v/a + offsets[3*i+2]) + cos(w/a + offsets[3*i+3]))
|
||||||
|
|
||||||
|
local kx,ky,kz = -sin(u/a+offsets[3*i+1]),-sin(v/a+offsets[3*i+2]),-sin(w/a + offsets[3*i+3])
|
||||||
|
dndx = dndx + (n1x*kx + n2x*ky + n3x*kz)
|
||||||
|
dndy = dndy + (n1y*kx + n2y*ky + n3y*kz)
|
||||||
|
dndz = dndz + (n1z*kx + n2z*ky + n3z*kz)
|
||||||
|
end
|
||||||
|
n = n / (3*sigma)
|
||||||
|
return cdf_prime(n, dndx/(3*sigma)), cdf_prime(n, dndy/(3*sigma)), cdf_prime(n, dndz/(3*sigma))
|
||||||
|
end
|
||||||
|
return noise, gradient
|
||||||
end
|
end
|
||||||
|
|
||||||
local shader1d = [[
|
local shader1d = [[
|
||||||
@ -114,78 +204,127 @@ vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords)
|
|||||||
{
|
{
|
||||||
float x = mix(range_min,range_max,texture_coords.x);
|
float x = mix(range_min,range_max,texture_coords.x);
|
||||||
float noise = 0.;
|
float noise = 0.;
|
||||||
|
#ifdef GRADIENT
|
||||||
|
float dndx = 0.;
|
||||||
|
#endif
|
||||||
for (int i=0; i < resolution; i++) {
|
for (int i=0; i < resolution; i++) {
|
||||||
float a = amplitudes[i];
|
float a = amplitudes[i];
|
||||||
noise += a*cos(x/a + offsets[i]);
|
noise += a*cos(x/a + offsets[i]);
|
||||||
|
#ifdef GRADIENT
|
||||||
|
dndx -= sin(x/a + offsets[i]);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
noise = noise/sigma;
|
noise /= sigma;
|
||||||
|
#ifdef GRADIENT
|
||||||
|
dndx /= sigma;
|
||||||
|
dndx = (0.31831 * exp(-4./TAU * noise*noise) * abs(noise)*dndx)/sqrt(1.0-exp(-4./TAU * noise*noise));
|
||||||
|
// TODO: normalize properly
|
||||||
|
dndx = .5 + .5*dndx;
|
||||||
|
return vec4(dndx,dndx,dndx, 1.);
|
||||||
|
#else
|
||||||
noise = .5 + .5*sign(noise)*sqrt(1.-exp(-4./TAU * noise*noise));
|
noise = .5 + .5*sign(noise)*sqrt(1.-exp(-4./TAU * noise*noise));
|
||||||
return vec4(noise,noise,noise,1.);
|
return vec4(noise,noise,noise, 1.);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
]]
|
]]
|
||||||
|
|
||||||
noise.make1dShader = function(resolution,random,decayFn)
|
noise.make1dShader = function(resolution,random,decayFn)
|
||||||
if resolution > 64 then
|
|
||||||
error("Resolution cannot exceed 64")
|
|
||||||
end
|
|
||||||
local shader = lg.newShader(shader1d)
|
|
||||||
resolution,random,decayFn = _defaultArgs(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
|
||||||
|
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)
|
local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,resolution,random)
|
||||||
shader:send("sigma",sigma)
|
do -- Dumb hack to work around a bug in Love 0.10.2 not sending the last value
|
||||||
shader:send("resolution",resolution)
|
table.insert(amplitudes, 1.)
|
||||||
shader:send("offsets",unpack(offsets))
|
table.insert(offsets, 1.)
|
||||||
shader:send("amplitudes",unpack(amplitudes))
|
end
|
||||||
shader:send("range_min", 0)
|
for _,shader in ipairs{noiseShader, gradShader} do
|
||||||
shader:send("range_max", 0)
|
shader:send("sigma",sigma)
|
||||||
return shader
|
shader:send("resolution",resolution)
|
||||||
|
shader:send("offsets",unpack(offsets))
|
||||||
|
shader:send("amplitudes",unpack(amplitudes))
|
||||||
|
shader:send("range_min", 0)
|
||||||
|
shader:send("range_max", 0)
|
||||||
|
end
|
||||||
|
return noiseShader,gradShader
|
||||||
end
|
end
|
||||||
|
|
||||||
local shader2d = [[
|
local shader2d = [[
|
||||||
#define TAU 6.283185307179586476925286766559005768394338798750211641949
|
#define TAU 6.283185307179586476925286766559005768394338798750211641949
|
||||||
#define PHI 1.618033988749894848204586834365638117720309179805762862135
|
#define PHI 1.618033988749894848204586834365638117720309179805762862135
|
||||||
#define MAX_RESOLUTION 64
|
|
||||||
extern int resolution;
|
|
||||||
extern float sigma;
|
extern float sigma;
|
||||||
extern float amplitudes[MAX_RESOLUTION];
|
extern float amplitudes[RESOLUTION];
|
||||||
extern vec2 offsets[MAX_RESOLUTION];
|
extern vec2 offsets[RESOLUTION];
|
||||||
extern vec2 range_min, range_max;
|
extern vec2 range_min, range_max;
|
||||||
|
|
||||||
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords)
|
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords)
|
||||||
{
|
{
|
||||||
vec2 pos = mix(range_min,range_max,texture_coords);
|
vec2 pos = mix(range_min,range_max,texture_coords);
|
||||||
float noise = 0.;
|
float noise = 0.;
|
||||||
for (int i=0; i < resolution; i++) {
|
#ifdef GRADIENT
|
||||||
|
float dndx = 0., dndy = 0.;
|
||||||
|
#endif
|
||||||
|
for (int i=0; i < RESOLUTION; i++) {
|
||||||
float angle = mod(float(i)*PHI, 1.)*TAU;
|
float angle = mod(float(i)*PHI, 1.)*TAU;
|
||||||
float u = pos.x*cos(angle) - pos.y*sin(angle);
|
float cosa = cos(angle), sina = sin(angle);
|
||||||
float v = pos.x*cos(angle+TAU/4.) - pos.y*sin(angle+TAU/4.);
|
float u = pos.x*cosa - pos.y*sina;
|
||||||
|
float v = -pos.x*sina - pos.y*cosa;
|
||||||
float a = amplitudes[i];
|
float a = amplitudes[i];
|
||||||
noise += a*mix(cos(u/a + offsets[i].x), cos(v/a + offsets[i].y), .5);
|
float k = offsets[i].x, w = offsets[i].y;
|
||||||
|
noise += a*(cos(u/a + k) + cos(v/a + w));
|
||||||
|
#ifdef GRADIENT
|
||||||
|
dndx += -cosa*sin(u/a + k) + sina*sin(v/a + w);
|
||||||
|
dndy += sina*sin(u/a + k) + cosa*sin(v/a + w);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
noise = noise/sigma;
|
noise /= 2.*sigma;
|
||||||
|
|
||||||
|
#ifdef GRADIENT
|
||||||
|
dndx /= 2.*sigma;
|
||||||
|
dndx = (0.31831 * exp(-4./TAU * noise*noise) * abs(noise)*dndx)/sqrt(1.0-exp(-4./TAU * noise*noise));
|
||||||
|
dndx = .5 + .5*dndx;
|
||||||
|
|
||||||
|
dndy /= 2.*sigma;
|
||||||
|
dndy = (0.31831 * exp(-4./TAU * noise*noise) * abs(noise)*dndy)/sqrt(1.0-exp(-4./TAU * noise*noise));
|
||||||
|
dndy = .5 + .5*dndy;
|
||||||
|
|
||||||
|
return vec4(dndx,dndy,0.,1.);
|
||||||
|
#else
|
||||||
noise = .5 + .5*sign(noise)*sqrt(1.-exp(-4./TAU * noise*noise));
|
noise = .5 + .5*sign(noise)*sqrt(1.-exp(-4./TAU * noise*noise));
|
||||||
return vec4(noise,noise,noise,1.);
|
return vec4(noise,noise,noise,1.);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
]]
|
]]
|
||||||
|
|
||||||
noise.make2dShader = function(resolution,random,decayFn)
|
noise.make2dShader = function(resolution,random,decayFn)
|
||||||
if resolution > 64 then
|
|
||||||
error("Resolution cannot exceed 64")
|
|
||||||
end
|
|
||||||
local shader = lg.newShader(shader2d)
|
|
||||||
resolution,random,decayFn = _defaultArgs(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
|
||||||
|
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)
|
local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,2*resolution,random)
|
||||||
sigma = sigma/sqrt(2)
|
sigma = sigma/sqrt(2)
|
||||||
local offsets2 = {}
|
local offsets2 = {}
|
||||||
for i=1,#offsets-1,2 do
|
for i=1,#offsets-1,2 do
|
||||||
table.insert(offsets2, {offsets[i],offsets[i+1]})
|
table.insert(offsets2, {offsets[i],offsets[i+1]})
|
||||||
end
|
end
|
||||||
shader:send("sigma",sigma)
|
do -- Dumb hack to work around a bug in Love 0.10.2 not sending the last value
|
||||||
shader:send("resolution",resolution)
|
table.insert(amplitudes, 1.)
|
||||||
shader:send("offsets",unpack(offsets2))
|
table.insert(offsets2, {1,1})
|
||||||
shader:send("amplitudes",unpack(amplitudes))
|
end
|
||||||
shader:send("range_min", {0,0})
|
for _,shader in ipairs{noiseShader, gradShader} do
|
||||||
shader:send("range_max", {1,1})
|
shader:send("sigma",sigma)
|
||||||
return shader
|
shader:send("offsets",unpack(offsets2))
|
||||||
|
shader:send("amplitudes",unpack(amplitudes))
|
||||||
|
shader:send("range_min", {0,0})
|
||||||
|
shader:send("range_max", {1,1})
|
||||||
|
end
|
||||||
|
return noiseShader,gradShader
|
||||||
end
|
end
|
||||||
|
|
||||||
local shader3d = [[
|
local shader3d = [[
|
||||||
@ -193,11 +332,9 @@ local shader3d = [[
|
|||||||
#define PHI 1.618033988749894848204586834365638117720309179805762862135
|
#define PHI 1.618033988749894848204586834365638117720309179805762862135
|
||||||
#define LOG_PHI 0.481211825059603447497758913424368423135184334385660519660
|
#define LOG_PHI 0.481211825059603447497758913424368423135184334385660519660
|
||||||
#define SQRT5 2.236067977499789696409173668731276235440618359611525724270
|
#define SQRT5 2.236067977499789696409173668731276235440618359611525724270
|
||||||
#define MAX_RESOLUTION 64
|
|
||||||
extern int resolution;
|
|
||||||
extern float sigma, z;
|
extern float sigma, z;
|
||||||
extern float amplitudes[MAX_RESOLUTION];
|
extern float amplitudes[RESOLUTION];
|
||||||
extern vec3 offsets[MAX_RESOLUTION];
|
extern vec3 offsets[RESOLUTION];
|
||||||
extern vec2 range_min, range_max;
|
extern vec2 range_min, range_max;
|
||||||
|
|
||||||
// https://www.graphics.rwth-aachen.de/media/papers/jgt.pdf
|
// https://www.graphics.rwth-aachen.de/media/papers/jgt.pdf
|
||||||
@ -205,17 +342,20 @@ vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords)
|
|||||||
{
|
{
|
||||||
vec3 pos = vec3(mix(range_min,range_max,texture_coords), z);
|
vec3 pos = vec3(mix(range_min,range_max,texture_coords), z);
|
||||||
// Find the biggest fibonacci number F_n such that F_n < RESOLUTION
|
// Find the biggest fibonacci number F_n such that F_n < RESOLUTION
|
||||||
int n = int(log((float(resolution)-1.)*SQRT5 + .5)/LOG_PHI);
|
int fib_n = int(log((float(RESOLUTION)-1.)*SQRT5 + .5)/LOG_PHI);
|
||||||
int dec = int(.5 + pow(PHI,n)/SQRT5); // F_n, using closed form Fibonacci
|
int dec = int(.5 + pow(PHI,fib_n)/SQRT5); // F_n, using closed form Fibonacci
|
||||||
int inc = int(.5 + dec/PHI); // F_(n-1)
|
int inc = int(.5 + dec/PHI); // F_(fib_n-1)
|
||||||
|
|
||||||
float noise = 0.;
|
float n = 0.;
|
||||||
for (int i=0, j=0; i<resolution; ++i) {
|
#ifdef GRADIENT
|
||||||
|
float dndx = 0., dndy = 0., dndz = 0.;
|
||||||
|
#endif
|
||||||
|
for (int i=0, j=0; i<RESOLUTION; ++i) {
|
||||||
if (j >= dec) {
|
if (j >= dec) {
|
||||||
j -= dec;
|
j -= dec;
|
||||||
} else {
|
} else {
|
||||||
j += inc;
|
j += inc;
|
||||||
if (j >= resolution)
|
if (j >= RESOLUTION)
|
||||||
j -= dec;
|
j -= dec;
|
||||||
}
|
}
|
||||||
// Convert golden ratio sequence into polar coordinate unit vector
|
// Convert golden ratio sequence into polar coordinate unit vector
|
||||||
@ -235,33 +375,62 @@ vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords)
|
|||||||
float a = amplitudes[j];
|
float a = amplitudes[j];
|
||||||
//float a = pow(mod(float(i+1)*(PHI-1.), 1.), .3);
|
//float a = pow(mod(float(i+1)*(PHI-1.), 1.), .3);
|
||||||
// Noise is the average of cosine of distance along each axis, shifted by offsets and scaled by amplitude.
|
// Noise is the average of cosine of distance along each axis, shifted by offsets and scaled by amplitude.
|
||||||
noise += a*(cos(u/a + offsets[i].x) + cos(v/a + offsets[i].y) + cos(w/a + offsets[i].z))/3.;
|
n += a*(cos(u/a + offsets[i].x) + cos(v/a + offsets[i].y) + cos(w/a + offsets[i].z));
|
||||||
|
#ifdef GRADIENT
|
||||||
|
vec3 k = vec3(-sin(u/a+offsets[i].x),-sin(v/a+offsets[i].y),-sin(w/a + offsets[i].z));
|
||||||
|
dndx += (n1.x*k.x + n2.x*k.y + n3.x*k.z)/3.;
|
||||||
|
dndy += (n1.y*k.x + n2.y*k.y + n3.y*k.z)/3.;
|
||||||
|
dndz += (n1.z*k.x + n2.z*k.y + n3.z*k.z)/3.;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
noise = noise/sigma;
|
n /= 3.*sigma;
|
||||||
noise = .5 + .5*sign(noise)*sqrt(1.-exp(-4./TAU * noise*noise));
|
#ifdef GRADIENT
|
||||||
return vec4(noise,noise,noise,1.);
|
dndx /= sigma;
|
||||||
|
dndx = (0.31831 * exp(-4./TAU * n*n) * abs(n)*dndx)/sqrt(1.0-exp(-4./TAU * n*n));
|
||||||
|
dndx = .5 + .5*dndx;
|
||||||
|
|
||||||
|
dndy /= sigma;
|
||||||
|
dndy = (0.31831 * exp(-4./TAU * n*n) * abs(n)*dndy)/sqrt(1.0-exp(-4./TAU * n*n));
|
||||||
|
dndy = .5 + .5*dndy;
|
||||||
|
|
||||||
|
dndz /= sigma;
|
||||||
|
dndz = (0.31831 * exp(-4./TAU * n*n) * abs(n)*dndz)/sqrt(1.0-exp(-4./TAU * n*n));
|
||||||
|
dndz = .5 + .5*dndz;
|
||||||
|
|
||||||
|
return vec4(dndx,dndy,dndz, 1.);
|
||||||
|
#else
|
||||||
|
n = .5 + .5*sign(n)*sqrt(1.-exp(-4./TAU * n*n));
|
||||||
|
return vec4(n,n,n,1.);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
]]
|
]]
|
||||||
|
|
||||||
noise.make3dShader = function(resolution,random,decayFn)
|
noise.make3dShader = function(resolution,random,decayFn)
|
||||||
if resolution > 64 then
|
|
||||||
error("Resolution cannot exceed 64")
|
|
||||||
end
|
|
||||||
local shader = lg.newShader(shader3d)
|
|
||||||
resolution,random,decayFn = _defaultArgs(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
|
||||||
|
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)
|
local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,3*resolution,random)
|
||||||
sigma = sigma/sqrt(3)
|
sigma = sigma/sqrt(3)
|
||||||
local offsets2 = {}
|
local offsets2 = {}
|
||||||
for i=1,#offsets-1,3 do
|
for i=1,#offsets-1,3 do
|
||||||
table.insert(offsets2, {offsets[i],offsets[i+1],offsets[i+2]})
|
table.insert(offsets2, {offsets[i],offsets[i+1],offsets[i+2]})
|
||||||
end
|
end
|
||||||
shader:send("sigma",sigma)
|
do -- Dumb hack to work around a bug in Love 0.10.2 not sending the last value
|
||||||
shader:send("resolution",resolution)
|
table.insert(amplitudes, 1.)
|
||||||
shader:send("offsets",unpack(offsets2))
|
table.insert(offsets, {1,1,1})
|
||||||
shader:send("amplitudes",unpack(amplitudes))
|
end
|
||||||
shader:send("range_min", {0,0})
|
for _,shader in ipairs{noiseShader, gradShader} do
|
||||||
shader:send("range_max", {1,1})
|
shader:send("sigma",sigma)
|
||||||
return shader
|
shader:send("offsets",unpack(offsets2))
|
||||||
|
shader:send("amplitudes",unpack(amplitudes))
|
||||||
|
shader:send("range_min", {0,0})
|
||||||
|
shader:send("range_max", {1,1})
|
||||||
|
end
|
||||||
|
return noiseShader, gradShader
|
||||||
end
|
end
|
||||||
|
|
||||||
return noise
|
return noise
|
||||||
|
Loading…
Reference in New Issue
Block a user