The Code Therapy

The 16 Segment Display

This piece demonstrates an easy (but pricy to compile due to abuse of divergent branching) way to draw text emulating a 16 segment display.

Created by marcogomez on Fri, 30 Oct 2020 03:30:51 GMT.


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

uniform sampler2D prgm3Texture;
uniform vec2 resolution;

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec4 prgm3 = texture2D(prgm3Texture, uv);
  gl_FragColor = vec4(prgm3.xyz, 1.0);
}

// ╔═════════════╦════════════════╗
// ║ Marco Gomez ║ https://mgz.me ║
// ╚═════════════╩════════════════╝
/*
╔════════════════════════════════════════════════════════════════════╗
║        LET'S PLAY WITH 16 LINE SEGMENTS "DIGITAL DISPLAYS"         ║
╠════════════════════════════════════════════════════════════════════╣
║The display is composed by 16 "line segments" that can be ON and OFF║
║You can see a map below, showing the display 16 "positions" (bits)  ║
╠════════════════╦═══════════════════════════════════════════════════╣
║ bits positions ║ Following the positions map on the left, you can  ║
║                ║ start thinking on how to compose the capital A.   ║
║ ┌──2──┬──1──┐  ║ Think on the bits from 0 to 15, and observe which ║
║ │\    │    /│  ║ ones needs to be ON (1) and OFF (0), so...        ║
║ │ \   │   / │  ║ for the letter A we need the following positions  ║
║ 3  11 10 9  0  ║ on the left map to be ON (1):                     ║
║ │   \ │ /   │  ║ 0 1 2 3 4 7 8 12, and all other positions OFF (0) ║
║ │    \│/    │  ║                                                   ║
║ ├─12──X──8──┤  ║ So, let's think about all the positions in order, ║
║ │    /│\    │  ║ and remember that 1 is ON, and 0 is OFF           ║
║ │   / │ \   │  ║                                                   ║
║ 4 13 14  15 7  ║  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15   ║
║ │ /   │   \ │  ║  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |   ║
║ │/    │    \│  ║  1  1  1  1  1  0  0  1  1  0  0  0  1  0  0  0   ║
║ └──5──┴──6──┘  ║                                                   ║
║                ║      bits: 1111100110001000                       ║
╠════════════════╣  reversed: 0001000110011111                       ║
║                ║     split: 0001 0001 1001 1111                    ║
║ BINARY TO HEX  ║     group:    1    2    3    4                    ║
║                ║                                                   ║
║ DEC  HEX BIN   ║  So if you look at the quick reference table on   ║
║ 00   0   0000  ║  the left, you can convert our four 4-bits groups ║
║ 01   1   0001  ║  to the HEX representation, as each HEX value can ║
║ 02   2   0010  ║  represent 4 bits. So, in HEX, we have our letter ║
║ 03   3   0011  ║                                                   ║
║ 04   4   0100  ║  GROUP   BIN    HEX                               ║
║ 05   5   0101  ║      1   0001   1                                 ║
║ 06   6   0110  ║      2   0001   1                                 ║
║ 07   7   0111  ║      3   1001   9                                 ║
║ 08   8   1000  ║      4   1111   F                                 ║
║ 09   9   1001  ║                                                   ║
║ 10   A   1010  ║  So, the letter A would be HEX: 0x119F            ║
║ 11   B   1011  ║  Easy right? Now you can draw some old-school     ║
║ 12   C   1100  ║  stuff                                            ║
║ 13   D   1101  ║                                                   ║
║ 14   E   1110  ║                                                   ║
║ 15   F   1111  ║                                                   ║
╚════════════════╩═══════════════════════════════════════════════════╝*/

#define  A ddigit(0x119F);
#define  B ddigit(0x927E);
#define  C ddigit(0x007E);
#define  D ddigit(0x44E7);
#define  E ddigit(0x107E);
#define  F ddigit(0x101E);
#define  G ddigit(0x807E);
#define  H ddigit(0x1199);
#define  I ddigit(0x4466);
#define  J ddigit(0x4436);
#define  K ddigit(0x9218);
#define  L ddigit(0x0078);
#define  M ddigit(0x0A99);
#define  N ddigit(0x8899);
#define  O ddigit(0x00FF);
#define  P ddigit(0x111F);
#define  Q ddigit(0x80FF);
#define  R ddigit(0x911F);
#define  S ddigit(0x09E6);
#define  T ddigit(0x4406);
#define  U ddigit(0x00F9);
#define  V ddigit(0x2218);
#define  W ddigit(0xA099);
#define  X ddigit(0xAA00);
#define  Y ddigit(0x4A00);
#define  Z ddigit(0x2266);
#define n0 ddigit(0x22FF);
#define n1 ddigit(0x0281);
#define n2 ddigit(0x1177);
#define n3 ddigit(0x11E7);
#define n4 ddigit(0x5508);
#define n5 ddigit(0x11EE);
#define n6 ddigit(0x11FE);
#define n7 ddigit(0x2206);
#define n8 ddigit(0x11FF);
#define n9 ddigit(0x11EF);

#define  s_dot     ddots(0);
#define  s_ddot    ddots(1);
#define  s_minus   ddigit(0x1100);
#define  s_plus    ddigit(0x5500);
#define  s_greater ddigit(0x2800);
#define  s_less    ddigit(0x8200);
#define  s_open    ddigit(0x003C);
#define  s_close   ddigit(0x00C3);
#define  s_sqrt    ddigit(0x0C02);
#define  s_uline   ddigit(0x0060);
#define  s_apos    ddigit(0x0400);
#define  leftarrow ddigit(0x8300);
#define  s_slash   ddigit(0x2200);

#define  _  chPos.x += chSpace.x; // blank
#define  nl chPos.x = chStart.x; chPos.y -= 2.0;

#define EFFECT_LENGTH 6.0

precision highp float;

uniform vec2 resolution;
uniform vec2 mouse;
uniform float time;
uniform float fft;

const int starsPerPlane = 30;
const int planes = 4;

const vec3 blue =   vec3(0.00, 0.00, 0.99);
const vec3 purple = vec3(0.70, 0.00, 0.90);

const vec2 chSize  = vec2(0.7, 0.5);               // character size
const vec2 chSpace = chSize + vec2(0.4, 0.6);      // character distance
      vec2 chStart;
      vec2 chPos   = vec2(0.0, 0.0);               // character position
      vec3 chColor = vec3(0.6, 1.7, 0.8);          // character color
      vec3 bgColor = vec3(0.0, 0.0, 0.0);          // background color

vec2 tuv; // current text uv position

float drawSegment(vec2 p0, vec2 p1) {
  p0 *= chSize;
  p1 *= chSize;
  vec2 dir = normalize(p1 - p0);
  vec2 charPos = (tuv - chPos - p0) * mat2(dir.x, dir.y, -dir.y, dir.x);
  vec2 clampDist = clamp(charPos, vec2(0.0), vec2(distance(p0, p1), 0.0));
  return 2.0 * distance(charPos, clampDist);
}

bool bit(int n, int b) { // return true if bit b of n is set ( not 0 )
  return mod(floor(float(n) / exp2(floor(float(b)))), 2.0) != 0.0;
}

float d = 1.0;

void ddots(int n) {
  float v = 1.0;
  if      (n == 0) {
    v = min(v, drawSegment(vec2(-0.005, -1.000), vec2(0.000, -1.000)));
  }
  else if (n == 1) {
    v = min(v, drawSegment(vec2(0.005,-1.0), vec2(0.0, -1.0)));
    v = min(v, drawSegment(vec2(0.005, 0.0), vec2(0.0,  0.0)));
  }
  chPos.x += chSpace.x;
  d = min(d, v);
}

void ddigit(int n) {
  float v = 1.0;
  if (bit(n,  0)) { v = min(v, drawSegment(vec2( 0.500,  0.063), vec2( 0.500,  0.937))); }
  if (bit(n,  1)) { v = min(v, drawSegment(vec2( 0.438,  1.000), vec2( 0.063,  1.000))); }
  if (bit(n,  2)) { v = min(v, drawSegment(vec2(-0.063,  1.000), vec2(-0.438,  1.000))); }
  if (bit(n,  3)) { v = min(v, drawSegment(vec2(-0.500,  0.937), vec2(-0.500,  0.062))); }
  if (bit(n,  4)) { v = min(v, drawSegment(vec2(-0.500, -0.063), vec2(-0.500, -0.938))); }
  if (bit(n,  5)) { v = min(v, drawSegment(vec2(-0.438, -1.000), vec2(-0.063, -1.000))); }
  if (bit(n,  6)) { v = min(v, drawSegment(vec2( 0.063, -1.000), vec2( 0.438, -1.000))); }
  if (bit(n,  7)) { v = min(v, drawSegment(vec2( 0.500, -0.938), vec2( 0.500, -0.063))); }
  if (bit(n,  8)) { v = min(v, drawSegment(vec2( 0.063,  0.000), vec2( 0.438, -0.000))); }
  if (bit(n,  9)) { v = min(v, drawSegment(vec2( 0.063,  0.063), vec2( 0.438,  0.938))); }
  if (bit(n, 10)) { v = min(v, drawSegment(vec2( 0.000,  0.063), vec2( 0.000,  0.937))); }
  if (bit(n, 11)) { v = min(v, drawSegment(vec2(-0.063,  0.063), vec2(-0.438,  0.938))); }
  if (bit(n, 12)) { v = min(v, drawSegment(vec2(-0.438,  0.000), vec2(-0.063, -0.000))); }
  if (bit(n, 13)) { v = min(v, drawSegment(vec2(-0.063, -0.063), vec2(-0.438, -0.938))); }
  if (bit(n, 14)) { v = min(v, drawSegment(vec2( 0.000, -0.938), vec2( 0.000, -0.063))); }
  if (bit(n, 15)) { v = min(v, drawSegment(vec2( 0.063, -0.063), vec2( 0.438, -0.938))); }
  chPos.x += chSpace.x;
  d = min(d, v);
}

void showFloat(float value) {
  for (int ni = 4; ni > -3; ni--) {
    if (ni == -1) { s_dot; } // add dot
    float dd = (value / pow(10.0, float(ni)));
    dd = mod(floor(dd), 10.0);
    if (dd < 0.5) { n0 }
    else if (dd < 1.5) { n1 }
    else if (dd < 2.5) { n2 }
    else if (dd < 3.5) { n3 }
    else if (dd < 4.5) { n4 }
    else if (dd < 5.5) { n5 }
    else if (dd < 6.5) { n6 }
    else if (dd < 7.5) { n7 }
    else if (dd < 8.5) { n8 }
    else if (dd < 9.5) { n9 }
  }
}

void showInteger4Digits(int value) {
  float fv = float(value);
  for (int ni = 3; ni >= 0; ni--) {
    float dd = fv / pow(10.0, float(ni));
    dd = mod(floor(dd), 10.0);
    if (dd < 0.5) { n0 }
    else if (dd < 1.5) { n1 }
    else if (dd < 2.5) { n2 }
    else if (dd < 3.5) { n3 }
    else if (dd < 4.5) { n4 }
    else if (dd < 5.5) { n5 }
    else if (dd < 6.5) { n6 }
    else if (dd < 7.5) { n7 }
    else if (dd < 8.5) { n8 }
    else if (dd < 9.5) { n9 }
  }
}

void showInteger(int value) {
  bool startDisplay = false;
  float fv = float( value ) / 10000.0;
  for (int ni = 5; ni > 0; ni--) {
    float dd = mod(floor(fv), 10.0);
    if ( dd > 1.0 ) { startDisplay = true; }
    if ( startDisplay ) {
      if (dd < 0.5) { n0 }
      else if (dd < 1.5) { n1 }
      else if (dd < 2.5) { n2 }
      else if (dd < 3.5) { n3 }
      else if (dd < 4.5) { n4 }
      else if (dd < 5.5) { n5 }
      else if (dd < 6.5) { n6 }
      else if (dd < 7.5) { n7 }
      else if (dd < 8.5) { n8 }
      else if (dd < 9.5) { n9 }
      fv = fv * 10.0;
    }
  }
}

vec3 hsv2rgbSmooth(vec3 c) {
  vec3 rgb = abs(mod(c.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0;
  rgb = clamp(rgb, 0.0, 1.0);
  rgb = rgb * rgb * (3.0 - 2.0 * rgb); // cubic smoothing
  return c.z * mix(vec3(1.0), rgb, c.y);
}

float lineStep(float x0, float x1, float xn) {
  return (xn - x0) / (x1 - x0);
}

float dist(vec2 v0, vec2 v1) {
  v0 = abs(v0 - v1);
  return max(v0.x, v0.y);
}

vec2 rotate(vec2 uv, float a) {
  return vec2(uv.x * cos(a) - uv.y * sin(a), uv.x * sin(a) + uv.y * cos(a));
}

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

vec2 curve(vec2 uv) {
  uv = (uv - 0.5) * 2.0;
  uv *= 1.1;
  uv.x *= 1.0 + pow( ( abs( uv.y ) / 5.0 ), 2.0 );
  uv.y *= 1.0 + pow( ( abs( uv.x ) / 4.0 ), 2.0 );
  uv  = (uv / 2.0) + 0.5;
  uv =  uv * 0.92 + 0.04;
  return uv;
}

float rand(float n) {
  return fract(sin(n) * 43758.5453123);
}

float rand (vec2 uv) {
  return fract(sin(dot(uv, vec2(12.4124, 48.4124))) * 48512.41241);
}

float noise (vec2 uv) {
  vec2 b = floor(uv);
  return mix(
    mix(rand(b), rand(b + vec2(1.0, 0.0)), 0.5),
    mix(rand(b + vec2(0.0, 1.0)), rand(b + vec2(1.0, 1.0)), 0.5),
    0.5
  );
}

void main(void) {
  float ar = max(resolution.x, resolution.y) / min(resolution.x, resolution.y);
  tuv = (gl_FragCoord.xy / resolution.xy) * 2.0 - 1.0;
  chStart = vec2(tuv.x - 7.0 - chSpace.x, chSpace.y); // start position
  tuv.x *= ar;
  tuv *= 6.0;
  tuv.x += sin(time + tuv.y * 0.15) * 0.5;
  tuv.y += sin(time + tuv.x * 0.30) * 0.5;
  chPos = chStart + vec2(0.0, 0.0);  // set start position
  chColor = hsv2rgbSmooth(vec3(time * 0.75 + tuv.x * 0.1, 0.6, 0.012)) * 2.0;
  {T H E _ C O D E _ T H E R A P Y}
  chColor = mix(chColor, vec3(0.0), 1.5 - (2.8 / d));
  chColor *= clamp(0.1 / d, 0.0, 1.0);
  chColor = smoothstep(0.07, 0.9, chColor);
  vec2 suv = gl_FragCoord.xy / resolution.xy;
  const float speed = 42.0;
  const int layers = 12;
  float stars = 0.0;
  float fl, s;
  for (int layer = 0; layer < layers; layer++) {
    fl = float(layer);
    s = (500.0 - fl * 30.0);
    stars += step(
      0.1,
      pow(
        abs(noise(mod(vec2(suv.x * s + time * speed - fl * 100.0, suv.y * s), resolution.x))),
        21.0
      )
    ) * (fl / float(layers));
  }
  vec4 starsColor = vec4(stars);
  vec4 finalColor = vec4(chColor + stars, 1.0);
  finalColor.xyz *= mod(gl_FragCoord.y, 3.0);
  gl_FragColor = finalColor;
}

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

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

const float PI = acos(-1.0);
const float TAU = PI * 2.0;
const float h = 0.003;
const float v = 0.009;
const float g = 0.05;

float stepm(float a, float b, float c) {
  return step(c, sin(time + a * cos(time * b)));
}

vec3 badVHS(vec2 uv, sampler2D tex) {
  float tmod = mod(time * 0.25, 3.0);
  float lookyMod = uv.y - tmod;
  float window = 1.0 / (1.0 + 20.0 * lookyMod * lookyMod);
  float lookyStep = stepm(4.0, 4.0, 0.3);
  uv.x = uv.x + sin(uv.y * 10.0 + time) / 100.0 * lookyStep * (1.0 + cos(time * 80.0)) * window * 0.25;
  float vShift = v * stepm(2.0, 3.0, 0.9) * (sin(time) * sin(time * 20.0) + (0.5 + 0.1 * sin(time * 200.0) * cos(time)));
  uv.y = mod(uv.y + vShift, 5.0);
  vec3 desatColor;
  float _r, _g, _b;
  float x = sin(0.3 * time + uv.y * 21.0) * sin(0.7 * time + uv.y * 29.0) * sin(0.3 + 0.33 * time + uv.y * 31.0) * h;
  _r = texture2D(tex, vec2(x + uv.x + 0.001, uv.y + 0.001)).x + 0.003;
  _g = texture2D(tex, vec2(x + uv.x + 0.000, uv.y - 0.002)).y + 0.003;
  _b = texture2D(tex, vec2(x + uv.x - 0.002, uv.y + 0.000)).z + 0.003;
  _r += 0.08 * texture2D(tex, 0.75 * vec2(x +  0.006, -0.007) + vec2(uv.x + 0.001, uv.y + 0.001)).x;
  _g += 0.05 * texture2D(tex, 0.75 * vec2(x + -0.005, -0.005) + vec2(uv.x + 0.000, uv.y - 0.002)).y;
  _b += 0.08 * texture2D(tex, 0.75 * vec2(x + -0.005, -0.003) + vec2(uv.x - 0.002, uv.y + 0.000)).z;
  float _luma = 0.3 * _r + 0.6 * _g + 0.1 * _b;
  float _desat = 0.3;
  desatColor = vec3(
    _r + _desat * (_luma - _r),
    _g + _desat * (_luma - _g),
    _b + _desat * (_luma - _b)
  );
  desatColor = clamp(desatColor, 0.0, 1.0);
  return desatColor;
}

float oscillate(float s, float e, float t) {
  return (e - s) * 0.5 + s + sin(t) * (e - s) * 0.5;
}

float gaussian(float z, float u, float o) {
  return (
    (1.0 / (o * sqrt(2.0 * 3.14159265359))) *
    (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, vec2(12.9898, 78.233));
  float noise = fract(sin(seed) * 43758.5453123 + t);
  noise = gaussian(noise, 0.0, 0.5);
  return vec3(noise);
}

vec2 curve(vec2 uv) {
  uv = (uv - 0.5) * 2.0;
  uv *= 1.1;
  uv.x *= 1.0 + pow((abs(uv.y) / 5.0), 2.0);
  uv.y *= 1.0 + pow((abs(uv.x) / 4.0), 2.0);
  uv = (uv / 2.0) + 0.5;
  uv = uv * 0.92 + 0.04;
  return uv;
}

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec4 prgm1 = texture2D(prgm1Texture, uv);
  vec2 cuv = curve(uv);
  float frameScale = 29.97;
  float frameTime = floor(time * frameScale) / frameScale;
  vec3 grain = gaussgrain(frameTime * 2.0);
  vec3 vhsCol = badVHS(cuv, prgm1Texture) + (grain * grain) * g;
  float vig = (0.0 + 1.0 * 21.0 * cuv.x * cuv.y * (1.0 - cuv.x) * (1.0 - cuv.y));
  vhsCol *= vec3(pow(abs(vig), 1.4));
  float scans = clamp(0.35 + 0.35 * sin(3.5 * time + cuv.y * resolution.y * 1.5), 0.0, 1.0);
  float s = pow(scans, 1.33);
  vhsCol = vhsCol * vec3(1.4 + 1.7 * s);
  if (cuv.x < 0.0 || cuv.x > 1.0) { vhsCol *= 0.0; }
  if (cuv.y < 0.0 || cuv.y > 1.0) { vhsCol *= 0.0; }
  vhsCol *= 1.0 - 0.37 * vec3(clamp((mod(gl_FragCoord.x, 2.0) - 1.0) * 2.0, 0.0, 1.0));
  vec4 tvhs = vec4(vhsCol * 1.05, 1.0);
  vec3 finalColor = clamp(tvhs.xyz, 0.0, 1.0);
  gl_FragColor = vec4(finalColor, 1.0);
}

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

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

vec4 rgbShift(vec2 p , vec4 shift) {
  shift *= 2.0 * shift.w - 1.0;
  vec2 rs = vec2(shift.x, -shift.y);
  vec2 gs = vec2(shift.y, -shift.z);
  vec2 bs = vec2(shift.z, -shift.x);
  float r = texture2D(prgm2Texture, p + rs, 0.0).x;
  float g = texture2D(prgm2Texture, p + gs, 0.0).y;
  float b = texture2D(prgm2Texture, p + bs, 0.0).z;
  return vec4(r,g,b,1.0);
}

vec4 noise(vec2 uv) {
  return texture2D(noiseTexture, uv, 0.0);
}

vec4 vec4pow(vec4 v, float p) {
  return vec4(
    pow(v.x, p),
    pow(v.y, p),
    pow(v.z, p),
    v.w
  );
}

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec2 mo = uv * 2.0 - 1.0;
  mo *= 0.01;
  vec3 chromaticAberration;
  chromaticAberration.r = texture2D(prgm2Texture, uv - mo * 0.05, 0.0).r;
  chromaticAberration.g = texture2D(prgm2Texture, uv - mo * 0.15, 0.0).g;
  chromaticAberration.b = texture2D(prgm2Texture, uv - mo * 0.25, 0.0).b;
  vec4 color = vec4(vec3(0.0), 1.0);
  color.xyz = mix(color.xyz, chromaticAberration, 0.3);
  const float speed = 0.01;
  const float amplitude = 0.01;
  vec4 shift = vec4pow(
    noise(
      vec2(speed * time, speed * time / 25.0 )
    ), 8.0
  ) * vec4(vec3(amplitude), 1.0);
  color += rgbShift(uv, shift);
  gl_FragColor = color;
}