mirror of
https://github.com/seigler/generative
synced 2025-07-26 22:56:10 +00:00
Add a blur shader, a bunch of other stuff. It looks like art!
This commit is contained in:
parent
ee252f0d6f
commit
2e601c4bb1
3 changed files with 218 additions and 36 deletions
18
app/assets/shaders/blur-two-pass/base.vert
Normal file
18
app/assets/shaders/blur-two-pass/base.vert
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// our vertex data
|
||||||
|
attribute vec3 aPosition;
|
||||||
|
attribute vec2 aTexCoord;
|
||||||
|
|
||||||
|
// lets get texcoords just for fun!
|
||||||
|
varying vec2 vTexCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// copy the texcoords
|
||||||
|
vTexCoord = aTexCoord;
|
||||||
|
|
||||||
|
// copy the position data into a vec4, using 1.0 as the w component
|
||||||
|
vec4 positionVec4 = vec4(aPosition, 1.0);
|
||||||
|
positionVec4.xy = positionVec4.xy * 2.0 - 1.0;
|
||||||
|
|
||||||
|
// send the vertex information on to the fragment shader
|
||||||
|
gl_Position = positionVec4;
|
||||||
|
}
|
80
app/assets/shaders/blur-two-pass/blur.frag
Normal file
80
app/assets/shaders/blur-two-pass/blur.frag
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
// texcoords from the vertex shader
|
||||||
|
varying vec2 vTexCoord;
|
||||||
|
|
||||||
|
// our texture coming from p5
|
||||||
|
uniform sampler2D tex0;
|
||||||
|
|
||||||
|
// the size of a texel or 1.0 / width , 1.0 / height
|
||||||
|
uniform vec2 texelSize;
|
||||||
|
|
||||||
|
// which way to blur, vec2(1.0, 0.0) is horizontal, vec2(0.0, 1.0) is vertical
|
||||||
|
uniform vec2 direction;
|
||||||
|
|
||||||
|
// gaussian blur filter modified from Filip S. at intel
|
||||||
|
// https://software.intel.com/en-us/blogs/2014/07/15/an-investigation-of-fast-real-time-gpu-based-image-blur-algorithms
|
||||||
|
// this function takes three parameters, the texture we want to blur, the uvs, and the texelSize
|
||||||
|
vec3 gaussianBlur( sampler2D t, vec2 texUV, vec2 stepSize ){
|
||||||
|
// a variable for our output
|
||||||
|
vec3 colOut = vec3( 0.0 );
|
||||||
|
|
||||||
|
// stepCount is 9 because we have 9 items in our array , const means that 9 will never change and is required loops in glsl
|
||||||
|
const int stepCount = 9;
|
||||||
|
|
||||||
|
// these weights were pulled from the link above
|
||||||
|
float gWeights[stepCount];
|
||||||
|
gWeights[0] = 0.10855;
|
||||||
|
gWeights[1] = 0.13135;
|
||||||
|
gWeights[2] = 0.10406;
|
||||||
|
gWeights[3] = 0.07216;
|
||||||
|
gWeights[4] = 0.04380;
|
||||||
|
gWeights[5] = 0.02328;
|
||||||
|
gWeights[6] = 0.01083;
|
||||||
|
gWeights[7] = 0.00441;
|
||||||
|
gWeights[8] = 0.00157;
|
||||||
|
|
||||||
|
// these offsets were also pulled from the link above
|
||||||
|
float gOffsets[stepCount];
|
||||||
|
gOffsets[0] = 0.66293;
|
||||||
|
gOffsets[1] = 2.47904;
|
||||||
|
gOffsets[2] = 4.46232;
|
||||||
|
gOffsets[3] = 6.44568;
|
||||||
|
gOffsets[4] = 8.42917;
|
||||||
|
gOffsets[5] = 10.41281;
|
||||||
|
gOffsets[6] = 12.39664;
|
||||||
|
gOffsets[7] = 14.38070;
|
||||||
|
gOffsets[8] = 16.36501;
|
||||||
|
|
||||||
|
// lets loop nine times
|
||||||
|
for( int i = 0; i < stepCount; i++ ){
|
||||||
|
|
||||||
|
// multiply the texel size by the by the offset value
|
||||||
|
vec2 texCoordOffset = gOffsets[i] * stepSize;
|
||||||
|
|
||||||
|
// sample to the left and to the right of the texture and add them together
|
||||||
|
vec3 col = texture2D( t, texUV + texCoordOffset ).xyz + texture2D( t, texUV - texCoordOffset ).xyz;
|
||||||
|
|
||||||
|
// multiply col by the gaussian weight value from the array
|
||||||
|
col *= gWeights[i];
|
||||||
|
|
||||||
|
// add it all up
|
||||||
|
colOut += col;
|
||||||
|
}
|
||||||
|
|
||||||
|
// our final value is returned as col out
|
||||||
|
return colOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
vec2 uv = vTexCoord;
|
||||||
|
// the texture is loaded upside down and backwards by default so lets flip it
|
||||||
|
uv = 1.0 - uv;
|
||||||
|
|
||||||
|
// use our blur function
|
||||||
|
vec3 blur = gaussianBlur(tex0, uv, texelSize * direction);
|
||||||
|
|
||||||
|
gl_FragColor = vec4(blur, 1.0);
|
||||||
|
}
|
156
app/sketch.js
156
app/sketch.js
|
@ -1,53 +1,137 @@
|
||||||
new p5(sketch => {
|
new p5(sketch => {
|
||||||
let width = document.documentElement.scrollWidth;
|
// reused dimensions and a seed
|
||||||
let height = document.documentElement.scrollHeight;
|
let seed, width, height, maxD, goalInstances;
|
||||||
let maxD = Math.min(width, height) / 2;
|
|
||||||
|
|
||||||
let buffer = sketch.createGraphics(maxD, maxD);
|
// offscreen layers
|
||||||
|
let buffer, pass1, pass2;
|
||||||
|
|
||||||
function generate() {
|
// shaders
|
||||||
sketch.blendMode(sketch.REPLACE);
|
let blurH, blurV;
|
||||||
sketch.background('#000');
|
|
||||||
sketch.blendMode(sketch.ADD);
|
|
||||||
for (let i = 0; i < 60; i++) {
|
|
||||||
buffer.background('#000');
|
|
||||||
let d = maxD * sketch.random(0.2, 1);
|
|
||||||
let c = sketch.color(sketch.random(100), 100, 100, 80)
|
|
||||||
buffer.fill(c);
|
|
||||||
buffer.circle(maxD / 2, maxD / 2, d);
|
|
||||||
buffer.fill('#000');
|
|
||||||
buffer.circle(sketch.random(maxD), sketch.random(maxD), sketch.random(0.2, 0.8) * d);
|
|
||||||
while (sketch.random() > 0.1) {
|
|
||||||
let a1 = sketch.random(2 * Math.PI);
|
|
||||||
let a2 = sketch.random(2 * Math.PI);
|
|
||||||
buffer.stroke(0);
|
|
||||||
buffer.strokeWeight(sketch.random(1, maxD * 0.1));
|
|
||||||
buffer.line(maxD * (Math.sin(a1) + 0.5), maxD * (Math.cos(a1) + 0.5),
|
|
||||||
maxD * (Math.sin(a2) + 0.5), maxD * (Math.cos(a2) + 0.5));
|
|
||||||
}
|
|
||||||
let w = sketch.random(-d, width + d);
|
|
||||||
let h = sketch.random(-d, height + d);
|
|
||||||
sketch.image(buffer, w, h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sketch.preload = () => {
|
sketch.preload = () => {
|
||||||
/* load images, music, etc */
|
// shaders, we will use the same vertex shader and frag shaders for both passes
|
||||||
}
|
blurH = sketch.loadShader('shaders/blur-two-pass/base.vert', 'shaders/blur-two-pass/blur.frag');
|
||||||
|
blurV = sketch.loadShader('shaders/blur-two-pass/base.vert', 'shaders/blur-two-pass/blur.frag');
|
||||||
sketch.keyPressed = () => {
|
|
||||||
generate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sketch.setup = () => {
|
sketch.setup = () => {
|
||||||
sketch.createCanvas(width, height);
|
goalInstances = 100;
|
||||||
|
|
||||||
|
seed = window.location.hash.substr(1);
|
||||||
|
sketch.noStroke();
|
||||||
sketch.colorMode(sketch.HSB, 100);
|
sketch.colorMode(sketch.HSB, 100);
|
||||||
|
|
||||||
|
width = document.documentElement.scrollWidth;
|
||||||
|
height = document.documentElement.scrollHeight;
|
||||||
|
maxD = (width + height) * 1.75 / Math.sqrt(goalInstances);
|
||||||
|
|
||||||
|
sketch.createCanvas(width, height);
|
||||||
|
|
||||||
|
buffer = sketch.createGraphics(maxD, maxD);
|
||||||
|
pass1 = sketch.createGraphics(maxD, maxD, sketch.WEBGL);
|
||||||
|
pass2 = sketch.createGraphics(maxD, maxD, sketch.WEBGL);
|
||||||
|
|
||||||
buffer.noStroke();
|
buffer.noStroke();
|
||||||
buffer.blendMode(sketch.BLEND);
|
pass1.noStroke();
|
||||||
|
pass2.noStroke();
|
||||||
|
|
||||||
generate();
|
generate();
|
||||||
}
|
}
|
||||||
|
|
||||||
sketch.draw = () => {
|
sketch.draw = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sketch.keyPressed = () => {
|
||||||
|
if (sketch.key == ' ') {
|
||||||
|
seed = null;
|
||||||
|
generate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generate() {
|
||||||
|
if (seed) {
|
||||||
|
sketch.randomSeed(seed);
|
||||||
|
} else {
|
||||||
|
let seed = Math.floor(sketch.random(1000000000000));
|
||||||
|
window.location.hash = seed;
|
||||||
|
sketch.randomSeed(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
sketch.noiseSeed(sketch.random());
|
||||||
|
sketch.blendMode(sketch.BLEND);
|
||||||
|
sketch.background('#000');
|
||||||
|
sketch.blendMode(sketch.ADD);
|
||||||
|
let resolution = 2;
|
||||||
|
|
||||||
|
let sqpxEach = width * height / goalInstances; // square pixels per circle, helps with gridding
|
||||||
|
let unit = Math.sqrt(sqpxEach);
|
||||||
|
let rows = Math.max(1, Math.round(height / unit)) + 1;
|
||||||
|
let cols = Math.max(1, Math.round(width / unit)) + 1;
|
||||||
|
let noiseOffset = sketch.random(0, 1000);
|
||||||
|
for (let i = 0; i < rows * cols; i++) {
|
||||||
|
// calculate row and col from i
|
||||||
|
let col = i % cols;
|
||||||
|
let row = Math.floor(i / cols);
|
||||||
|
|
||||||
|
buffer.noStroke();
|
||||||
|
buffer.background('#000');
|
||||||
|
|
||||||
|
// perlin noise "intensity"
|
||||||
|
let intensity = sketch.noise(
|
||||||
|
noiseOffset + row / rows * resolution,
|
||||||
|
noiseOffset + col / cols * resolution
|
||||||
|
);
|
||||||
|
let d = maxD * intensity; // diameter
|
||||||
|
let c = sketch.color(100 * sketch.random(), 100, intensity * 90 + 10, intensity * 70 + 10); // color
|
||||||
|
buffer.fill(c);
|
||||||
|
buffer.circle(maxD / 2, maxD / 2, d); // always at the center of the buffer
|
||||||
|
|
||||||
|
// giant, lo-fi blur
|
||||||
|
pass1.shader(blurH);
|
||||||
|
blurH.setUniform('tex0', buffer);
|
||||||
|
blurH.setUniform('texelSize', [8.0/maxD, 8.0/maxD]);
|
||||||
|
blurH.setUniform('direction', [1.0, 0.0]);
|
||||||
|
pass1.rect(0,0,maxD, maxD);
|
||||||
|
pass2.shader(blurV);
|
||||||
|
blurV.setUniform('tex0', pass1);
|
||||||
|
blurV.setUniform('texelSize', [8.0/maxD, 8.0/maxD]);
|
||||||
|
blurV.setUniform('direction', [0.0, 1.0]);
|
||||||
|
pass2.rect(0,0,maxD, maxD);
|
||||||
|
|
||||||
|
// regular blur to hide artifacts
|
||||||
|
pass1.shader(blurH);
|
||||||
|
blurH.setUniform('tex0', pass2);
|
||||||
|
blurH.setUniform('texelSize', [1.0/maxD, 1.0/maxD]);
|
||||||
|
blurH.setUniform('direction', [1.0, 0.0]);
|
||||||
|
pass1.rect(0,0,maxD, maxD);
|
||||||
|
pass2.shader(blurV);
|
||||||
|
blurV.setUniform('tex0', pass1);
|
||||||
|
blurV.setUniform('texelSize', [1.0/maxD, 1.0/maxD]);
|
||||||
|
blurV.setUniform('direction', [0.0, 1.0]);
|
||||||
|
pass2.rect(0,0,maxD, maxD);
|
||||||
|
|
||||||
|
buffer.image(pass2, 0, 0, maxD, maxD);
|
||||||
|
|
||||||
|
buffer.fill('#000');
|
||||||
|
let cutoutAngle = sketch.random(2 * Math.PI);
|
||||||
|
buffer.circle(d * Math.cos(cutoutAngle), d * Math.sin(cutoutAngle), sketch.random(0.3, 0.5) * d);
|
||||||
|
do {
|
||||||
|
let a1 = sketch.random(2 * Math.PI);
|
||||||
|
let a2 = sketch.random(2 * Math.PI);
|
||||||
|
buffer.stroke(0);
|
||||||
|
buffer.strokeWeight(1 + d * Math.pow(sketch.random(0.7368), 3));// as much as 0.4*d
|
||||||
|
buffer.line(
|
||||||
|
maxD * (Math.sin(a1) + 0.5), maxD * (Math.cos(a1) + 0.5),
|
||||||
|
maxD * (Math.sin(a2) + 0.5), maxD * (Math.cos(a2) + 0.5)
|
||||||
|
);
|
||||||
|
} while (sketch.random() < 0.5 + 0.45 * intensity);
|
||||||
|
|
||||||
|
let displacementAngle = sketch.random(0, Math.PI * 2);
|
||||||
|
let displacementAmount = sketch.random(unit);
|
||||||
|
let w = width / (cols - 1) * col + displacementAmount * Math.cos(displacementAngle);
|
||||||
|
let h = height / (rows - 1) * row + displacementAmount * Math.sin(displacementAngle);
|
||||||
|
|
||||||
|
sketch.image(buffer, w - maxD / 2, h - maxD / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue