God Rays

This is a Piece inspired by a scene from the movie What Dreams May Come. I'm using an FBM (Fractal Brownian Motion) displacement and noise generated by a static texture with some mouse-position based color shifting.

Created by marcogomez on Tue, 14 Sep 2021 20:43:19 GMT.


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

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

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec4 prgm2 = texture2D(prgm2Texture, uv);
  vec4 prgm3 = texture2D(prgm3Texture, uv);
  gl_FragColor = vec4(mix(prgm2.xyz, prgm3.xyz, 0.4 + fft * 0.6), 1.0);
}

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

uniform sampler2D noiseTexture;
uniform vec2 resolution;
uniform vec2 mouselerp;
uniform float time;
uniform float fft;

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

const mat2 mtx = mat2( 0.80,  0.60, -0.60,  0.80 );

vec3 hueRotate(vec3 c, float angle) {
  vec3 rot = vec3(TAN30, sin(vec2(radians(angle)) + vec2(0.0, HALFPI)));
  return mix(rot.xxx * dot(rot.xxx, c), c, rot.z) + (cross(rot.xxx, c) * rot.y);
}

float distances(vec2 uv) {
  uv = ((gl_FragCoord.xy / resolution) - 0.5) * 2.0;
  float ar = max(resolution.x, resolution.y) / min(resolution.x, resolution.y);
  vec2 mp = vec2(mouselerp.x, mouselerp.y);
  if (resolution.x > resolution.y) { uv.x *= ar; mp.x *= ar; } else { uv.y *= ar; mp.y *= ar; }
  float magA = 13.0; float magB = magA * 0.01;
  float bdist = clamp(-magA * distance(mp, uv), -magA, magA);
  float tdist = smoothstep(-1.0, 5.0, 2.0 - magB * bdist);
  return tdist;
}

vec2 hash2(float n) {
  return fract(sin(vec2(n, n + 1.0)) * vec2(13.5453123, 31.1459123));
}

float noise(vec2 x) {
  vec2 p = floor(x);
  vec2 f = fract(x);
  f = f * f * (3.0 - 2.0 * f);
  float a = texture2D(noiseTexture, (p + vec2(0.5, 0.5 + fft * 0.25)) / 256.0, 0.0).x;
  float b = texture2D(noiseTexture, (p + vec2(1.5, 0.5 + fft * 0.25)) / 256.0, 0.0).x;
  float c = texture2D(noiseTexture, (p + vec2(0.5, 1.5 + fft * 0.25)) / 256.0, 0.0).x;
  float d = texture2D(noiseTexture, (p + vec2(1.5, 1.5 + fft * 0.25)) / 256.0, 0.0).x;
  return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
}

float fbm(vec2 p, vec2 m, vec2 uv) {
  uv *= distances(uv);
  float mp = (10.0 - length((((uv - 0.5) - m * 2.0) * 10.0))) * 0.25;
  float f = 0.0;
  f += 0.500000 * noise(p); p = mtx * (p - m * 3.6 * mp * 0.9) * 2.02;
  f += 0.250000 * noise(p); p = mtx * (p - m * 3.0 * mp * 0.7) * 2.03;
  f += 0.125000 * noise(p); p = mtx * (p - m * 2.4 * mp * 0.5) * 2.01;
  //f += 0.062500 * noise(p); p = mtx * (p - m * 1.8 * mp * 0.3) * 2.04;
  //f += 0.031250 * noise(p); p = mtx * (p - m * 1.2 * mp * 0.1) * 2.01;
  f += 0.015625 * noise(p - m);
  return f / 0.96875;
}

float pattern(in vec2 p, in float t, in vec2 uv, in vec2 m, out vec2 q, out vec2 r, out vec2 g) {
  uv *= distances(uv);
  q = vec2(fbm(p, m, uv), fbm(p + vec2(10.0, 1.3), m, uv));
  q.x += 0.3;
  float s = dot(uv.x + 0.5, uv.y + 0.5);
  r = vec2(fbm(p + 4.0 * q + vec2(t * 2.0) + vec2(1.7, 9.2), m, uv), fbm(p + 4.0 * q + vec2(t) + vec2(8.3, 2.8), m, uv));
  r.x += 0.3;
  g = vec2(fbm(p + 2.0 * r + vec2(t * 20.0) + vec2(2.0, 6.0), m, uv), fbm(p + 2.0 * r + vec2(t * 10.0) + vec2(5.0, 3.0), m, uv));
  return fbm((p - m) + 5.5 * g + vec2(-t * 7.0), m, uv);
}

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

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec2 mpos = vec2(mouselerp.x, mouselerp.y);
  vec2 m = mpos * 0.25;
  float l = length((((uv - 0.5) - m * 2.0) * 30.0));
  float mp = (6.0 - l) * 0.25;
  float smp = smoothstep(-0.5, 1.5, mp);
  vec2 q, r, g;
  uv *= distances(uv);
  float noise = pattern(gl_FragCoord.xy * vec2(0.007), time * 0.012, uv, m, q, r, g);
  vec3 col = mix(
    vec3(0.1, 0.4, 0.4),
    vec3(0.3, 0.7, 0.0),
    smoothstep(0.0, 1.0, noise)
  );
  vec3 colA = vec3(0.15, 0.00, 0.30);
  vec3 colB = vec3(0.00, 0.10, 1.00);
  vec3 colC = vec3(0.10, 0.40, 1.00);
  vec3 colD = vec3(0.40, 0.15, 0.60);
  colA = hueRotate(colA, abs(mpos.x) * 96.0);
  colB = hueRotate(colB, abs(mpos.x) * 96.0);
  colC = hueRotate(colC, abs(mpos.y) * 48.0);
  col = mix(col, colA, dot(q, q) * 1.0);
  col = mix(col, colB, 0.2 * g.y * g.y);
  col = mix(col, colC, smoothstep(0.0, 0.6, 0.6 * r.g * r.g - smp));
  col = mix(col, colD, 0.1 * g.x * mp);
  float np = noise * (2.0 - mp * 0.5);
  col = mix(col, vec3(0), smoothstep(0.3, 0.5, noise) * smoothstep(0.5, 0.3, noise));
  col = mix(col, vec3(0), smoothstep(0.7, 0.8, noise) * smoothstep(0.8, 0.7, noise));
  col *= noise * 4.0;
  col /= distances(uv);
  vec4 color = vec4(col * col, 1.0);
  gl_FragColor = color;
}

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

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

const float amount = 1.0;
const float saturation = 0.8;
const vec2 vignetteSize = vec2(0.25, 0.25);
const float vignetteRoundness = 0.12;
const float vignetteMix = 1.0;
const float vignetteSmoothness = 0.42;
const float W = 1.2;
const float T = 7.5;

float filmicReinhardCurve(float x) {
  float q = (T * T + 1.0) * x * x;
  return q / (q + x + T * T);
}

vec3 filmicReinhard(vec3 c) {
  float w = filmicReinhardCurve(W);
  return vec3(
    filmicReinhardCurve(c.r),
    filmicReinhardCurve(c.g),
    filmicReinhardCurve(c.b)
  ) / w;
}

vec3 ContrastSaturationBrightness(vec3 color, float brt, float sat, float con) {
  const float AvgLumR = 0.5;
  const float AvgLumG = 0.5;
  const float AvgLumB = 0.5;
  const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721);
  vec3 AvgLumin = vec3(AvgLumR, AvgLumG, AvgLumB);
  vec3 brtColor = color * brt;
  vec3 intensity = vec3(dot(brtColor, LumCoeff));
  vec3 satColor = mix(intensity, brtColor, sat);
  vec3 conColor = mix(AvgLumin, satColor, con);
  return conColor;
}

float sdSquare(vec2 point, float width) {
  vec2 d = abs(point) - width;
  return min(max(d.x, d.y), 0.0) + length(max(d, 0.0));
}

float vignette(vec2 uv, vec2 size, float roundness, float smoothness) {
  uv -= 0.5;
  float minWidth = min(size.x, size.y);
  uv.x = sign(uv.x) * clamp(abs(uv.x) - abs(minWidth - size.x), 0.0, 1.0);
  uv.y = sign(uv.y) * clamp(abs(uv.y) - abs(minWidth - size.y), 0.0, 1.0);
  float boxSize = minWidth * (1.0 - roundness);
  float dist = sdSquare(uv, boxSize) - (minWidth * roundness);
  return 1.0 - smoothstep(0.0, smoothness, dist);
}

void main(void) {
  float reinhardAmount = 0.4 + fft * 0.6;
  float contrast = 1.0 + fft * 0.25;
  float brightness = 1.0 + fft;

  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec4 prgm1 = texture2D(prgm1Texture, uv);
  vec3 reinhard = filmicReinhard(prgm1.rgb);
  vec3 color = prgm1.rgb;
  color = mix(prgm1.rgb, reinhard, reinhardAmount);
  color = ContrastSaturationBrightness(color, brightness, saturation, contrast);
  float v = vignette(uv, vignetteSize, vignetteRoundness, vignetteSmoothness);
  vec3 vig = color * v;
  color = mix(color, vig, vignetteMix);
  color = mix(prgm1.xyz, color, amount);
  color = clamp(color, 0.0, 1.0);
  gl_FragColor = vec4(color, 1.0);
}

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

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

const float PI = acos(-1.0);
const float TAU = PI * 2.0;

const int NUM_SAMPLES = 8;
const float Density = 0.5;
const float Weight = 0.7;
const float Exposure = 0.55;

float rand(vec2 co) {
  return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);
}

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec2 dist = (uv - (0.6 * mouselerp + 0.5));
  float Density = 0.75 + 0.2 * sin(2.0 * radians(360.0));
  dist *= 1.0 / float(NUM_SAMPLES) * Density;
  vec3 color = texture2D(prgm2Texture, uv).rgb;
  float illuminationDecay = 1.0;
  for (int i = 0; i < NUM_SAMPLES; i++)   {
    uv -= dist;
    vec3 sample_ = texture2D(prgm2Texture, uv + dist * rand(uv + time * 0.001)).rgb;
    sample_ *= illuminationDecay * Weight;
    color += sample_;
    float Decay = 0.7 + fft * 0.3;
    illuminationDecay *= Decay;
  }
  gl_FragColor = vec4(color * Exposure, 1.0);
}