The Code Therapy

Drawing Pixels

Drawing pixels with the mouse or through infinite and spirograph-based patterns over time.

Created by marcogomez on Sat, 16 Oct 2021 16:54:27 GMT.


#version 300 es
// ╔═════════════╦════════════════╗
// ║ Marco Gomez ║ https://mgz.me ║
// ╚═════════════╩════════════════╝
precision highp float;

uniform sampler2D prgm3Texture;
uniform vec2 resolution;
uniform float time;

out vec4 fragColor;

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec4 prgm3 = texture(prgm3Texture, uv);
  fragColor = prgm3;
}

#version 300 es
// ╔═════════════╦════════════════╗
// ║ Marco Gomez ║ https://mgz.me ║
// ╚═════════════╩════════════════╝
precision highp float;

uniform sampler2D prgm1Texture;
uniform vec2 resolution;
uniform vec2 mouselerp;
uniform vec2 mouse;
uniform float time;
uniform float fft;

out vec4 fragColor;

const float PI = acos(-1.0);
const float TAU = PI * 2.0;
const float hk = 1.0 / sqrt(3.0);

#define cSize       12.0, 12.0
#define cPad        3.0, 3.0
#define baseColor   0.01, 0.01, 0.01, 1.0
#define mouseColor  0.1, 0.9, 0.7, 1.0
#define mouseRadius 64.0
#define fadeOut     0.0047

const vec2 neighbours[8] = vec2[8](
  vec2(-1.0, -1.0),
  vec2(-1.0, +0.0),
  vec2(-1.0, +1.0),
  vec2(+0.0, -1.0),
  vec2(+0.0, +1.0),
  vec2(+1.0, -1.0),
  vec2(+1.0, +0.0),
  vec2(+1.0, +1.0)
);

float ssin(float t) {
  return (2.0 / PI) * atan(sin(TAU * t * 0.5) / 0.1) * 2.0;
}

float osc(float s, float e, float t, float ts) {
  return (e - s) / 2.0 + s + sin(t * ts) * (e - s) * 0.5;
}

vec3 hueShift(vec3 col, float a) {
  const vec3 k = vec3(hk);
  float ca = cos(a);
  return vec3(col * ca + cross(k, col) * sin(a) + k * dot(k, col) * (1.0 - ca));
}

vec4 getBounds(vec2 pixel, vec2 cellSize, vec2 cellPad) {
  vec2 reach = cellSize + cellPad;
  vec2 index = floor(pixel / reach);
  vec2 start = reach * index;
  vec2 limit = reach * (index + vec2(1.0));
  return vec4(start, limit);
}

vec2 getCenter(vec2 pixel, vec2 cellSize, vec2 cellPad) {
  vec4 bounds = getBounds(pixel, cellSize, cellPad);
  vec2 start = bounds.xy;
  vec2 limit = bounds.zw;
  return (start + limit) * 0.5;
}

float isPixelInside(vec2 pixel, vec2 cellSize, vec2 cellPad) {
  vec4 bounds = getBounds(pixel, cellSize, cellPad);
  vec2 limit = bounds.zw;
  float outside = float(
    (pixel.x > (limit.x - cellPad.x)) ||
    (pixel.y > (limit.y - cellPad.y))
  );
  float inside = 1.0 - outside;
  return inside;
}

vec2 inRadius(vec2 canvas_cell, vec2 cursor_cell, float radius) {
  vec2 v = canvas_cell - cursor_cell;
  float d = dot(v, v);
  float rad = radius * radius;
  float inside = 1.0 - step(rad, d);
  return vec2(inside, d);
}

vec4 getColor(vec2 pixel, vec2 cellSize, vec2 cellPad, vec4 brushColor, float rad, vec2 mousePixel) {
  vec2 center = getCenter(pixel, cellSize, cellPad);
  vec2 mouseCenter = getCenter(mousePixel.xy, cellSize, cellPad);
  vec2 insideAndDistance = inRadius(center, mouseCenter, rad);
  float insideRadius = insideAndDistance[0];
  float dist = insideAndDistance[1];
  vec4 drawnColor = insideRadius * (1.0 - dist / rad / rad) * brushColor;
  return drawnColor;
}

vec4 getAvg(vec2 pixel, vec2 cellSize, vec2 cellPad, vec2 res) {
  vec2 curCenter = getCenter(pixel, cellSize, cellPad);
  vec2 fullCellSize = cellSize + cellPad;
  vec4 averageColor = vec4(0.0);
  for(int i = 0; i < 8; i++) {
    vec2 neighCellShift = neighbours[i] * fullCellSize;
    vec2 neighCellCenter = curCenter + neighCellShift;
    vec2 neighUV = neighCellCenter / res;
    vec4 neighColor = texture(prgm1Texture, neighUV);
    averageColor += neighColor;
    }
    return averageColor / 8.0;
}

vec2 getPattern(float rA, float rB, float angle) {
  float x = (rA - rB) * cos(angle) + rA * cos(((rA - rB) / rB) * angle);
  float y = (rA - rB) * sin(angle) - rA * sin(((rA - rB) / rB) * angle);
  return vec2(x, y) * 1.2;
}

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  float t = time * 1.5;
  float o = osc(-0.1, 0.2, time, 0.0625);
  vec2 v2o = vec2(0.75 + o, 0.85 + o);
  vec2 rawMouse = (mix(mouse.xy, mouselerp.xy, 0.6) * 0.5 + 0.5) * resolution.xy;
  vec2 infPattern = vec2(cos(t), sin(t) * cos(t));
  vec2 pattern = getPattern(0.4, 0.1, t);
  float transition = clamp(ssin(time * 0.05), 0.0, 1.0);
  vec2 pat = mix(infPattern, pattern, transition);
  vec2 inf = ((pat * v2o) * 0.5 + 0.5) * resolution.xy;
  vec2 mousePixel = (length(mouse) < 3.0) ? rawMouse : inf;
  vec4 prevFrame = texture(prgm1Texture, uv) * (1.0 - fadeOut);
  prevFrame *= float(prevFrame.a > 1e-5);
  vec4 cellColor = vec4(baseColor);
  vec4 brushColor = vec4(mouseColor);
  float brushRadius = mouseRadius;
  vec2 cellSize = vec2(cSize);
  vec2 cellPad = vec2(cPad);
  float inside = isPixelInside(gl_FragCoord.xy, cellSize, cellPad);
  vec4 curDrawnColor = (
    getColor(
      gl_FragCoord.xy, cellSize, cellPad,
      vec4(hueShift(brushColor.xyz, TAU * fft + time * 3.0) * 3.0, brushColor.a),
      brushRadius, mousePixel
    )
  );
  vec4 diff = curDrawnColor - prevFrame;
  diff *= float(diff.a > 0.0);
  vec4 straightBrushedColor = inside * (prevFrame + diff);
  vec4 averageColor = getAvg(gl_FragCoord.xy, cellSize, cellPad, resolution.xy);
  fragColor = mix(straightBrushedColor, averageColor, (fadeOut * cellSize.x * 0.25));
  fragColor = clamp(vec4(0.0), vec4(1.0), fragColor);
}

#version 300 es
// ╔═════════════╦════════════════╗
// ║ Marco Gomez ║ https://mgz.me ║
// ╚═════════════╩════════════════╝
precision highp float;

uniform sampler2D prgm1Texture;
uniform vec2 resolution;
uniform float time;

out vec4 fragColor;

const float PI = acos(-1.0);
const float TAU = PI * 2.0;
const vec2 hashv2 = vec2(12.9898, 78.233);
const float hashS = 43758.5453123;

#define cSize     12.0, 12.0
#define cPad      3.0, 3.0
#define baseColor 0.01, 0.02, 0.02, 1.0

#define try(new) old = mix (new, old, step (length (old-ref), length (new-ref)));
vec3 findClosest(vec3 ref) {
  vec3 old = vec3 (100.0 * 255.0);
  try(vec3(  0.0,   0.0,   0.0)); //  0 - black           (YPbPr = 0.0  ,  0.0 ,  0.0 )
  try(vec3(133.0,  59.0,  81.0)); //  1 - magenta         (YPbPr = 0.25 ,  0.0 ,  0.5 )
  try(vec3( 80.0,  71.0, 137.0)); //  2 - dark blue       (YPbPr = 0.25 ,  0.5 ,  0.0 )
  try(vec3(233.0,  93.0, 240.0)); //  3 - purple          (YPbPr = 0.5  ,  1.0 ,  1.0 )
  try(vec3(  0.0, 104.0,  82.0)); //  4 - dark green      (YPbPr = 0.25 ,  0.0 , -0.5 )
  try(vec3(146.0, 146.0, 146.0)); //  5 - gray #1         (YPbPr = 0.5  ,  0.0 ,  0.0 )
  try(vec3(  0.0, 168.0, 241.0)); //  6 - medium blue     (YPbPr = 0.5  ,  1.0 , -1.0 )
  try(vec3(202.0, 195.0, 248.0)); //  7 - light blue      (YPbPr = 0.75 ,  0.5 ,  0.0 )
  try(vec3( 81.0,  92.0,  15.0)); //  8 - brown           (YPbPr = 0.25 , -0.5 ,  0.0 )
  try(vec3(235.0, 127.0,  35.0)); //  9 - orange          (YPbPr = 0.5  , -1.0 ,  1.0 )
  try(vec3(241.0, 166.0, 191.0)); // 11 - pink            (YPbPr = 0.75 ,  0.0 ,  0.5 )
  try(vec3(  0.0, 201.0,  41.0)); // 12 - green           (YPbPr = 0.5  , -1.0 , -1.0 )
  try(vec3(203.0, 211.0, 155.0)); // 13 - yellow          (YPbPr = 0.75 , -0.5 ,  0.0 )
  try(vec3(154.0, 220.0, 203.0)); // 14 - aqua            (YPbPr = 0.75 ,  0.0 , -0.5 )
  try(vec3(255.0, 255.0, 255.0)); // 15 - white           (YPbPr = 1.0  ,  0.0 ,  0.0 )
  try(vec3(120.0,  41.0,  34.0)); //  2 - red             (YPbPr = 0.25 , -0.383 ,  0.924 )
  try(vec3(135.0, 214.0, 221.0)); //  3 - cyan            (YPbPr = 0.75 ,  0.383 , -0.924 )
  try(vec3(170.0,  95.0, 182.0)); //  4 - purple          (YPbPr = 0.5  ,  0.707 ,  0.707 )
  try(vec3( 85.0, 160.0,  73.0)); //  5 - green           (YPbPr = 0.5  , -0.707 , -0.707 )
  try(vec3( 64.0,  49.0, 141.0)); //  6 - blue            (YPbPr = 0.25 ,  1.0   ,  0.0   )
  try(vec3(191.0, 206.0, 114.0)); //  7 - yellow          (YPbPr = 0.75 , -1.0   ,  0.0   )
  try(vec3(170.0, 116.0,  73.0)); //  8 - orange          (YPbPr = 0.5  , -0.707 ,  0.707 )
  try(vec3(234.0, 180.0, 137.0)); //  9 - light orange    (YPbPr = 0.75 , -0.707 ,  0.707 )
  try(vec3(184.0, 105.0,  98.0)); // 10 - light red       (YPbPr = 0.5  , -0.383 ,  0.924 )
  try(vec3(199.0, 255.0, 255.0)); // 11 - light cyan      (YPbPr = 1.0  ,  0.383 , -0.924 )
  try(vec3(234.0, 159.0, 246.0)); // 12 - light purple    (YPbPr = 0.75 ,  0.707 ,  0.707 )
  try(vec3(148.0, 224.0, 137.0)); // 13 - light green     (YPbPr = 0.75 , -0.707 , -0.707 )
  try(vec3(128.0, 113.0, 204.0)); // 14 - light blue      (YPbPr = 0.5  ,  1.0   ,  0.0   )
  try(vec3(255.0, 255.0, 178.0)); // 15 - light yellow    (YPbPr = 1.0  , -1.0   ,  0.0   )
  try(vec3(161.0,  77.0,  67.0)); //  2 - red             (YPbPr = 0.313 , -0.383 ,  0.924 )
  try(vec3(106.0, 193.0, 200.0)); //  3 - cyan            (YPbPr = 0.625 ,  0.383 , -0.924 )
  try(vec3(162.0,  86.0, 165.0)); //  4 - purple          (YPbPr = 0.375 ,  0.707 ,  0.707 )
  try(vec3( 92.0, 173.0,  95.0)); //  5 - green           (YPbPr = 0.5   , -0.707 , -0.707 )
  try(vec3( 79.0,  68.0, 156.0)); //  6 - blue            (YPbPr = 0.25  ,  1.0   ,  0.0   )
  try(vec3(203.0, 214.0, 137.0)); //  7 - yellow          (YPbPr = 0.75  , -1.0   ,  0.0   )
  try(vec3(163.0, 104.0,  58.0)); //  8 - orange          (YPbPr = 0.375 , -0.707 ,  0.707 )
  try(vec3(110.0,  83.0,  11.0)); //  9 - brown           (YPbPr = 0.25  , -0.924 ,  0.383 )
  try(vec3(204.0, 127.0, 118.0)); // 10 - light red       (YPbPr = 0.5   , -0.383 ,  0.924 )
  try(vec3( 99.0,  99.0,  99.0)); // 11 - dark grey       (YPbPr = 0.313 ,  0.0   ,  0.0   )
  try(vec3(139.0, 139.0, 139.0)); // 12 - grey            (YPbPr = 0.469 ,  0.0   ,  0.0   )
  try(vec3(155.0, 227.0, 157.0)); // 13 - light green     (YPbPr = 0.75  , -0.707 , -0.707 )
  try(vec3(138.0, 127.0, 205.0)); // 14 - light blue      (YPbPr = 0.469 ,  1.0   ,  0.0   )
  try(vec3(175.0, 175.0, 175.0)); // 15 - light grey      (YPbPr = 0.625  , 0.0   ,  0.0   )
  try(vec3( 62.0, 184.0,  73.0)); //  2 - medium green    (YPbPr = 0.53 , -0.509 , -0.755 )
  try(vec3(116.0, 208.0, 125.0)); //  3 - light green     (YPbPr = 0.67 , -0.377 , -0.566 )
  try(vec3( 89.0,  85.0, 224.0)); //  4 - dark blue       (YPbPr = 0.40 ,  1.0   , -0.132 )
  try(vec3(128.0, 128.0, 241.0)); //  5 - light blue      (YPbPr = 0.53 ,  0.868 , -0.075 )
  try(vec3(185.0,  94.0,  81.0)); //  6 - dark red        (YPbPr = 0.47 , -0.321 ,  0.679 )
  try(vec3(101.0, 219.0, 239.0)); //  7 - cyan            (YPbPr = 0.73 ,  0.434 , -0.887 )
  try(vec3(219.0, 101.0,  89.0)); //  8 - medium red      (YPbPr = 0.53 , -0.377 ,  0.868 )
  try(vec3(255.0, 137.0, 125.0)); //  9 - light red       (YPbPr = 0.67 , -0.377 ,  0.868 )
  try(vec3(204.0, 195.0,  94.0)); // 10 - dark yellow     (YPbPr = 0.73 , -0.755 ,  0.189 )
  try(vec3(222.0, 208.0, 135.0)); // 11 - light yellow    (YPbPr = 0.80 , -0.566 ,  0.189 )
  try(vec3( 58.0, 162.0,  65.0)); // 12 - dark green      (YPbPr = 0.47 , -0.453 , -0.642 )
  try(vec3(183.0, 102.0, 181.0)); // 13 - magenta         (YPbPr = 0.53 ,  0.377 ,  0.491 )
  try(vec3(204.0, 204.0, 204.0)); // 14 - grey            (YPbPr = 0.80 ,  0.0   ,  0.0   )
  try(vec3(226.0, 226.0, 226.0)); // L90
  try(vec3(198.0, 198.0, 198.0)); // L80
  try(vec3(171.0, 171.0, 171.0)); // L70
  try(vec3(145.0, 145.0, 145.0)); // L60
  try(vec3(119.0, 119.0, 119.0)); // L50
  try(vec3( 94.0,  94.0,  94.0)); // L40
  try(vec3( 71.0,  71.0,  71.0)); // L30
  try(vec3( 48.0,  48.0,  48.0)); // L20
  try(vec3( 27.0,  27.0,  27.0)); // L10
  return old ;
}

float gaussian(float z, float u, float o) {
  return (
    (1.0 / (o * sqrt(TAU))) *
    (exp(-(((z - u) * (z - u)) / (2.0 * (o * o)))))
  );
}

vec3 gaussgrain(float t) {
  vec2 ps = vec2(1.0) / resolution.xy;
  vec2 uv = gl_FragCoord.xy * ps;
  float seed = dot(uv, hashv2);
  float noise = fract(sin(seed) * hashS + t);
  noise = gaussian(noise, 0.0, 0.5);
  return vec3(noise);
}

vec4 getBounds(vec2 pixel, vec2 cellSize, vec2 cellPad) {
  vec2 reach = cellSize + cellPad;
  vec2 index = floor(pixel / reach);
  vec2 start = reach * index;
  vec2 limit = reach * (index + vec2(1.0));
  return vec4(start, limit);
}

float isPixelInside(vec2 pixel, vec2 cellSize, vec2 cellPad) {
  vec4 bounds = getBounds(pixel, cellSize, cellPad);
  vec2 limit = bounds.zw;
  float outside = float(
    (pixel.x > (limit.x - cellPad.x)) ||
    (pixel.y > (limit.y - cellPad.y))
  );
  float inside_cell = 1.0 - outside;
  return inside_cell;
}

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  float frameScale = 29.97;
  float frameTime = floor(time * frameScale) / frameScale;
  vec3 gA = gaussgrain(frameTime) * 0.42;
  vec3 gB = gaussgrain(frameTime + 0.1) * 0.42;
  vec4 cellColor = vec4(baseColor);
  vec2 cellSize = vec2(cSize);
  vec2 cellPad = vec2(cPad);
  float inside = clamp(isPixelInside(gl_FragCoord.xy, cellSize, cellPad), 0.0, 1.0);
  vec4 color = texture(prgm1Texture, uv);
  color.xyz += (gA + gB) * 1e-2;
  color.xyz = mix(findClosest(color.xyz * 255.0) / 255.0, color.xyz, 0.42);
  fragColor = inside * (cellColor + color);
  fragColor.a = 1.0;
}

#version 300 es
// ╔═════════════╦════════════════╗
// ║ Marco Gomez ║ https://mgz.me ║
// ╚═════════════╩════════════════╝
precision highp float;

uniform sampler2D prgm2Texture;
uniform vec2 resolution;
uniform float time;

out vec4 fragColor;

const float PI = acos(-1.0);
const float TAU = PI * 2.0;
const vec2 hashv2 = vec2(12.9898, 78.233);
const float hashS = 43758.5453123;

float hash(vec2 p) {
  return fract(sin(dot(p, vec2(41.0, 289.0))) * hashS);
}

float gaussian(float z, float u, float o) {
  return (
    (1.0 / (o * sqrt(TAU))) *
    (exp(-(((z - u) * (z - u)) / (2.0 * (o * o)))))
  );
}

vec3 gaussgrain(float t) {
  vec2 ps = vec2(1.0) / resolution.xy;
  vec2 uv = gl_FragCoord.xy * ps;
  float seed = dot(uv, hashv2);
  float noise = fract(sin(seed) * hashS + t);
  noise = gaussian(noise, 0.0, 0.5);
  return vec3(noise);
}

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec4 prgm2 = texture(prgm2Texture, uv);
  float frameScale = 29.97;
  float frameTime = floor(time * frameScale) / frameScale;
  vec4 col = prgm2;
  float w = 0.1;
  vec2 a = vec2(uv.x - 0.5, uv.y - 0.66);
  vec2 b = a * 0.15 / float(10.0);
  uv += b * (hash(uv.xy + fract(time)) * 2.0);
  for (float i = 1.0; i > 0.995; i-= 0.0001) {
    uv -= 0.5; uv *= i; uv += 0.5;
    col += texture(prgm2Texture, uv) * w * 1.7;
    w *= 0.95;
  }
  vec3 gA = gaussgrain(frameTime) * 0.42;
  vec3 gB = gaussgrain(frameTime + 0.1) * 0.42;
  col *= 0.9; col.xyz += gA * gB * 0.0625;
  fragColor = mix(prgm2, col, 0.3);
}