A Weird Lawn

A tiny ray marched render using a simple noise texture to provide a cheap grass resemblance, with a big goo composed of sphere distances bound by a smooth minimum union operator raising out of it.

Created by marcogomez on Thu, 21 Oct 2021 12:30:01 GMT.


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

uniform sampler2D prgm2Texture;
uniform vec2 resolution;

#define inputTexture prgm2Texture

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec4 tex = texture2D(inputTexture, uv);
  gl_FragColor = tex;
}

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

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

#define marchSteps 70
#define maxDist 30.0
#define minDist 0.1
const float PI  = acos(-1.0);
const float TAU = PI * 2.0;

vec3 hue(vec3 col, float a) {
  const vec3 k = vec3(0.57735, 0.57735, 0.57735);
  float c = cos(a);
  return col * c + cross(k, col) * sin(a) + k * dot(k, col) * (1.0 - c);
}

float disturb(float radius, vec3 axis, float t) {
  float minRes = min (resolution.x, resolution.y);
  vec2 fc = gl_FragCoord.xy / minRes;
  float wt = t * 2.0;
  vec2 distortOffset = vec2(sin(wt + fc.y * TAU), sin(wt + fc.x * TAU)) * vec2(0.5);
  axis.xy += distortOffset;
  float disturbed = (
    radius * (0.9 + fft * 0.5) +
    0.05 * sin(4.0 * t + axis.x * 7.0) +
    0.03 * sin(3.0 * t + axis.y * 6.0) +
    0.07 * sin(2.0 * t + axis.z * 5.0)
  );
  return disturbed;
}

float smin(float a, float b, float k) {
  float h = max(k - abs(a - b), 0.0) / k;
  return min(a, b) - h * h * k * (1.0 / 4.0);
}

vec3 movePattern(float w, float h, float t) {
  return vec3(
    2.5 * h * sin(t),
    0.4 * w + sin(t) * cos(t),
    1.6 * cos(t * 0.25) * sin(t * 0.5)
  );
}

float getDist(vec3 p) {
  float dstb = disturb(1.0, p, time * 0.25);
  float planeDist = p.y - texture2D(noiseTexture, time * 0.01 + p.xz * 0.2, 1.0).x * 0.75;
  float sphereDist1 = length(p - movePattern(1.2, 0.3, time * 0.30)) - disturb(1.0, p, time * 0.25) - 0.3;
  float sphereDist2 = length(p - movePattern(2.5, 0.6, time * 0.45)) - disturb(0.7, p, time * 0.40) - 0.1;
  float sphereDist3 = length(p - movePattern(2.1, 0.5, time * 0.40)) - disturb(0.5, p, time * 0.35) - 0.2;
  float sphereDist4 = length(p - movePattern(2.9, 0.4, time * 0.35)) - disturb(0.4, p, time * 0.30) - 0.3;
  float dist = smin(planeDist, sphereDist1, 1.3);
  dist = smin(dist, sphereDist2, 1.1);
  dist = smin(dist, sphereDist3, 0.9);
  dist = smin(dist, sphereDist4, 0.6);
  return smoothstep(0.0, 3.0, dist);
}

vec3 getNormal(vec3 p) {
  return normalize(vec3(0.0, 1.0, 0.0) - p);
}

float getLight(vec3 p, vec3 lightPos) {
  vec3 lightdir = normalize(lightPos - p);
  vec3 normal = getNormal(p);
  float diff = dot(normal, lightdir);
  return diff;
}

float raymarch(vec3 camera, vec3 dir) {
  float dist = 0.0;
  for (int i = 0; i < marchSteps; i++) {
    vec3 pos = camera + dir * dist;
    float stepdist = getDist(pos);
    dist += stepdist;
    if (dist > maxDist || dist < minDist) { break; }
  }
  return dist;
}

mat3 calcLookAtMatrix(vec3 origin, vec3 target, float roll) {
  vec3 rr = vec3(sin(roll), cos(roll), 0.0);
  vec3 ww = normalize(target - origin);
  vec3 uu = normalize(cross(ww, rr));
  vec3 vv = normalize(cross(uu, ww));
  return mat3(uu, vv, ww);
}

void main(void) {
  vec2 uv = (gl_FragCoord.xy - resolution.xy * 0.5) / resolution.y;
  vec3 col = vec3(0.0);
  float t = time * 0.3;
  float camRadius = 7.0;
  vec3 ro = vec3(
    sin(mouselerp.x * PI) * camRadius,
    2.5,
    cos(mouselerp.x * PI) * camRadius
  );
  vec3 camTarget = vec3(0.0, 1.5, 0.0);
  float camRoll = 0.0;
  mat3 camMatrix = calcLookAtMatrix(ro, camTarget, camRoll);
  vec3 rd = normalize(camMatrix * vec3(uv.x, uv.y, 1.0));
  float d = raymarch(ro, rd);
  vec3 dist = ro + rd * d;
  vec3 lightPos = vec3(-ro.x, ro.y + 3.5, -ro.z);
  float lightDist = getLight(dist, lightPos);
  col += vec3(1.0 - lightDist);
  col = col * 0.4;
  vec3 color = mix(
    vec3(0.1, -0.1, 0.3),
    vec3(1.5, -0.6, 0.7),
    col
  );
  color -= lightDist * 0.125;
  color = hue(mix(color, col, 0.7), 2.9);
  gl_FragColor = vec4(color, 1.0);
}

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

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

#define inputTexture prgm1Texture

const float amount = 1.0;
const float reinhardAmount = 1.0;
const float contrast = 1.0;
const float brightness = 1.2;
const float saturation = 0.9;
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) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec4 tex = texture2D(inputTexture, uv);
  vec3 reinhard = filmicReinhard(tex.rgb);
  vec3 color = tex.rgb;
  color = mix(tex.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(tex.xyz, color, amount);
  color = clamp(color, 0.0, 1.0);
  gl_FragColor = vec4(color, 1.0);
}