diff --git a/main.lua b/main.lua index 023b151..7fac3bb 100644 --- a/main.lua +++ b/main.lua @@ -26,20 +26,6 @@ function love.load() yOffset = 0 t0 = love.timer.getTime() love.mouse.setCursor(love.mouse.getSystemCursor("hand")) - - local data1,data2,data3 = {},{},{} - for i=1,100000 do - local x = math.random()*100000 - local y = math.random()*100000 - local z = math.random()*100000 - table.insert(data1, n1(x)) - table.insert(data2, n2(x,y)) - table.insert(data3, n3(x,y,z)) - end - histogram1 = Histogram:fromList(data1,{pos={x=0,y=0},size={x=W/2,y=H/3},xmin=0,xmax=1,numBuckets=30}) - histogram2 = Histogram:fromList(data2,{pos={x=0,y=H/3},size={x=W/2,y=H/3},xmin=0,xmax=1,numBuckets=30}) - histogram3 = Histogram:fromList(data3,{pos={x=0,y=2*H/3},size={x=W/2,y=H/3},xmin=0,xmax=1,numBuckets=30}) - end function love.mousepressed() @@ -104,15 +90,15 @@ function love.draw() lg.setShader(s1) s1:send("range_min", xOffset) - s1:send("range_max", xOffset+cw/SCALE) + s1:send("range_max", xOffset+2*cw/SCALE) lg.draw(canv,W-cw,0) lg.setShader(s2) s2:send("range_min", {xOffset,yOffset}) - s2:send("range_max", {xOffset+cw/SCALE,yOffset+ch/SCALE}) + s2:send("range_max", {xOffset+2*cw/SCALE,yOffset+2*ch/SCALE}) lg.draw(canv,W-cw,ch) lg.setShader(s3) s3:send("range_min", {xOffset,yOffset}) - s3:send("range_max", {xOffset+cw/SCALE,yOffset+ch/SCALE}) + s3:send("range_max", {xOffset+2*cw/SCALE,yOffset+2*ch/SCALE}) s3:send('z', t) lg.draw(canv,W-cw,2*ch) lg.setShader(nil) @@ -142,6 +128,20 @@ end function love.keypressed(key) if key == 'escape' then love.event.quit() end if key == 'r' then love.load() end + if key == 'h' and not histogram1 then + local data1,data2,data3 = {},{},{} + for i=1,100000 do + local x = math.random()*100000 + local y = math.random()*100000 + local z = math.random()*100000 + table.insert(data1, n1(x)) + table.insert(data2, n2(x,y)) + table.insert(data3, n3(x,y,z)) + end + histogram1 = Histogram:fromList(data1,{pos={x=0,y=0},size={x=W/4,y=H/3},xmin=0,xmax=1,numBuckets=30}) + histogram2 = Histogram:fromList(data2,{pos={x=0,y=H/3},size={x=W/4,y=H/3},xmin=0,xmax=1,numBuckets=30}) + histogram3 = Histogram:fromList(data3,{pos={x=0,y=2*H/3},size={x=W/4,y=H/3},xmin=0,xmax=1,numBuckets=30}) + end end function love.mousemoved(x,y,dx,dy) diff --git a/noise.lua b/noise.lua index 8a8ec23..59ddc31 100644 --- a/noise.lua +++ b/noise.lua @@ -102,8 +102,33 @@ noise.make3d = function(resolution,random,decayFn) end end +local shader1d = [[ +#define TAU 6.283185307179586476925286766559005768394338798750211641949 +#define MAX_RESOLUTION 64 +extern int resolution; +extern float sigma, range_min, range_max; +extern float amplitudes[MAX_RESOLUTION]; +extern float offsets[MAX_RESOLUTION]; + +vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) +{ + float x = mix(range_min,range_max,texture_coords.x); + float noise = 0.; + for (int i=0; i < resolution; i++) { + float a = amplitudes[i]; + noise += a*cos(x/a + offsets[i]); + } + noise = noise/sigma; + noise = .5 + .5*sign(noise)*sqrt(1.-exp(-4./TAU * noise*noise)); + return vec4(noise,noise,noise,1.); +} +]] + noise.make1dShader = function(resolution,random,decayFn) - local shader = lg.newShader("noise1d.glsl") + if resolution > 64 then + error("Resolution cannot exceed 64") + end + local shader = lg.newShader(shader1d) resolution,random,decayFn = _defaultArgs(resolution,random,decayFn) local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,resolution,random) shader:send("sigma",sigma) @@ -115,8 +140,38 @@ noise.make1dShader = function(resolution,random,decayFn) return shader end +local shader2d = [[ +#define TAU 6.283185307179586476925286766559005768394338798750211641949 +#define PHI 1.618033988749894848204586834365638117720309179805762862135 +#define MAX_RESOLUTION 64 +extern int resolution; +extern float sigma; +extern float amplitudes[MAX_RESOLUTION]; +extern vec2 offsets[MAX_RESOLUTION]; +extern vec2 range_min, range_max; + +vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) +{ + vec2 pos = mix(range_min,range_max,texture_coords); + float noise = 0.; + for (int i=0; i < resolution; i++) { + float angle = mod(float(i)*PHI, 1.)*TAU; + float u = pos.x*cos(angle) - pos.y*sin(angle); + float v = pos.x*cos(angle+TAU/4.) - pos.y*sin(angle+TAU/4.); + float a = amplitudes[i]; + noise += a*mix(cos(u/a + offsets[i].x), cos(v/a + offsets[i].y), .5); + } + noise = noise/sigma; + noise = .5 + .5*sign(noise)*sqrt(1.-exp(-4./TAU * noise*noise)); + return vec4(noise,noise,noise,1.); +} +]] + noise.make2dShader = function(resolution,random,decayFn) - local shader = lg.newShader("noise2d.glsl") + if resolution > 64 then + error("Resolution cannot exceed 64") + end + local shader = lg.newShader(shader2d) resolution,random,decayFn = _defaultArgs(resolution,random,decayFn) local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,2*resolution,random) sigma = sigma/sqrt(2) @@ -133,8 +188,66 @@ noise.make2dShader = function(resolution,random,decayFn) return shader end +local shader3d = [[ +#define TAU 6.283185307179586476925286766559005768394338798750211641949 +#define PHI 1.618033988749894848204586834365638117720309179805762862135 +#define LOG_PHI 0.481211825059603447497758913424368423135184334385660519660 +#define SQRT5 2.236067977499789696409173668731276235440618359611525724270 +#define MAX_RESOLUTION 64 +extern int resolution; +extern float sigma, z; +extern float amplitudes[MAX_RESOLUTION]; +extern vec3 offsets[MAX_RESOLUTION]; +extern vec2 range_min, range_max; + +// https://www.graphics.rwth-aachen.de/media/papers/jgt.pdf +vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) +{ + vec3 pos = vec3(mix(range_min,range_max,texture_coords), z); + // Find the biggest fibonacci number F_n such that F_n < RESOLUTION + int 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 inc = int(.5 + dec/PHI); // F_(n-1) + + float noise = 0.; + for (int i=0, j=0; i= dec) { + j -= dec; + } else { + j += inc; + if (j >= resolution) + j -= dec; + } + // Convert golden ratio sequence into polar coordinate unit vector + float phi = mod(float(i)*PHI,1.)*TAU; + float theta = acos(mix(-1.,1.,mod(float(j)*PHI,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 + vec3 n1 = vec3(sin(phi)*cos(theta), sin(phi)*sin(theta), cos(phi)); + vec3 n2 = vec3(sin(phi+TAU/4.)*cos(theta), sin(phi+TAU/4.)*sin(theta), cos(phi+TAU/4.)); + vec3 n3 = cross(n1,n2); + // Convert pos from x/y/z coordinates to n1/n2/n3 coordinates + float u = dot(n1, pos); + float v = dot(n2, pos); + float w = dot(n3, pos); + // Pull the amplitude from the shuffled array index ("j"), not "i", + // otherwise neighboring unit vectors will have similar amplitudes! + float a = amplitudes[j]; + //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 += a*(cos(u/a + offsets[i].x) + cos(v/a + offsets[i].y) + cos(w/a + offsets[i].z))/3.; + } + noise = noise/sigma; + noise = .5 + .5*sign(noise)*sqrt(1.-exp(-4./TAU * noise*noise)); + return vec4(noise,noise,noise,1.); +} +]] + noise.make3dShader = function(resolution,random,decayFn) - local shader = lg.newShader("noise3d.glsl") + if resolution > 64 then + error("Resolution cannot exceed 64") + end + local shader = lg.newShader(shader3d) resolution,random,decayFn = _defaultArgs(resolution,random,decayFn) local amplitudes,sigma,offsets = _amplitudesAndOffsets(decayFn,resolution,3*resolution,random) sigma = sigma/sqrt(3) diff --git a/noise1d.glsl b/noise1d.glsl index 1c1d40c..84af06b 100644 --- a/noise1d.glsl +++ b/noise1d.glsl @@ -24,5 +24,5 @@ vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) float x = mix(range_min,range_max,texture_coords.x); float n = noise1d(x); //return vec4(n,n,n,1.); - return texture_coords.y > n ? vec4(1.,1.,1.,1.) : vec4(0.,0.,0.,1.); + return texture_coords.y > (1.-n) ? vec4(1.,1.,1.,1.) : vec4(0.,0.,0.,1.); }