Skip to content

Instantly share code, notes, and snippets.

@mattdesl
Created June 14, 2018 20:14
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mattdesl/3715117f831d759ef25c6d1dfb4f0213 to your computer and use it in GitHub Desktop.
Save mattdesl/3715117f831d759ef25c6d1dfb4f0213 to your computer and use it in GitHub Desktop.
dither-blob.js
const sketcher = require('canvas-sketch-tool'); // not yet public
// Import geometry & utilities
const createRegl = require('regl');
const createPrimitive = require('primitive-icosphere');
const createCamera = require('perspective-camera');
const glslify = require('glslify');
const hexRgb = require('hex-rgb');
// Utility to convert hex string to [ r, g, b] floats
const hexToRGB = hex => {
const rgba = hexRgb(hex, { format: 'array' });
return rgba.slice(0, 3).map(n => n / 255);
};
const settings = {
// Output size in pixels
dimensions: [ 512, 512 ],
// Setup render loop
animation: true, // start requestAnimationFrame loop
duration: 6, // time in seconds of loop
fps: 24, // frame rate for export
// Set up a WebGL context instead of regular 2D context
context: 'webgl',
// We can pass down some properties to the WebGL context...
attributes: {
antialias: true // turn on MSAA
}
};
const sketch = ({ gl, canvasWidth, canvasHeight }) => {
// Background & foreground colors
const color = '#f2bac4';
const foregroundRGB = hexToRGB(color);
const backgroundRGBA = [ ...foregroundRGB, 1.0 ];
// Setup REGL with our canvas context
const regl = createRegl({ gl });
// Create a simple sphere mesh
const sphere = createPrimitive(1, { subdivisions: 5 });
// Create a perspective camera
const camera = createCamera({
fov: 45 * Math.PI / 180
});
// Place our camera
camera.translate([ 0, 0, 6 ]);
camera.lookAt([ 0, 0, 0 ]);
camera.update();
// Build a draw command for the mesh
const drawMesh = regl({
// Fragment & Vertex shaders
frag: glslify(`
precision highp float;
uniform vec3 color;
uniform float time;
uniform vec2 resolution;
varying vec3 vNormal;
varying vec3 vPosition;
varying vec2 screenUV;
#pragma glslify: dither = require('glsl-dither/8x8');
void main () {
// Spin light around a bit
float angle = sin(time * 0.25) * 2.0 + 3.14 * 0.5;
vec3 lightPosition = vec3(cos(angle), sin(time * 1.0), sin(angle));
vec3 L = normalize(lightPosition);
vec3 N = normalize(vNormal);
// Get diffuse contribution
float diffuse = max(0.0, dot(N, L));
diffuse = smoothstep(0.0, 1.25, diffuse);
diffuse = pow(diffuse, 0.25);
diffuse *= max(0.0, 1.0 - distance(vPosition, lightPosition) / 2.0) * 1.0;
diffuse += pow(vNormal.z, 0.95) * 0.05;
diffuse = clamp(diffuse, 0.0, 1.0);
float ditherSize = 256.0;
diffuse = dither(gl_FragCoord.xy / resolution.xy * ditherSize, diffuse);
gl_FragColor = vec4(color * diffuse, 1.0);
}
`),
vert: glslify(`
precision highp float;
attribute vec3 position;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
uniform float time;
varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 screenUV;
#pragma glslify: noise = require('glsl-noise/classic/4d');
void main () {
// Get initial normal
vNormal = normalize(position);
// Contribute noise
vec3 pos = position;
pos += vNormal * pow(noise(vec4(position.xyz * 1.0, time * 0.25)), 0.75) * 1.0;
pos += vNormal * pow(noise(vec4(position.xyz * 0.75, time * 0.25)), 0.75) * 0.5;
pos += vNormal * pow(noise(vec4(position.xyz * 40.0, time * 0.5)), 0.1) * 0.3;
pos *= 0.75;
// Update normal
vNormal = normalize(pos);
vPosition = pos;
// Final position
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos.xyz, 1.0);
screenUV = gl_Position.xy;
}
`),
uniforms: {
projectionMatrix: regl.prop('projectionMatrix'),
modelViewMatrix: regl.prop('modelViewMatrix'),
color: foregroundRGB,
resolution: [ canvasWidth, canvasHeight ],
time: regl.prop('time')
},
attributes: {
position: regl.buffer(sphere.positions),
normal: regl.buffer(sphere.normals)
},
elements: regl.elements(sphere.cells)
});
// Return a render function that draws the current snapshot of the animation
return ({ viewportWidth, viewportHeight, time }) => {
// On each tick, update regl timers and sizes
regl.poll();
// Clear backbuffer with black
regl.clear({
color: backgroundRGBA,
depth: 1,
stencil: 0
});
camera.viewport = [ 0, 0, viewportWidth, viewportHeight ];
camera.update();
drawMesh({
projectionMatrix: camera.projection,
modelViewMatrix: camera.view,
time
});
};
};
sketcher(sketch, settings);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment