#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;
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, vec2(12.9898, 78.233));
float noise = fract(sin(seed) * 43758.5453123 + t);
noise = gaussian(noise, 0.0, 0.5);
return vec3(noise);
}
void main(void) {
vec2 uv = gl_FragCoord.xy / resolution.xy;
vec4 tex = texture(prgm1Texture, uv);
float frameScale = 29.97;
float frameTime = floor(time * frameScale) / frameScale;
vec3 grain = gaussGrain(frameTime) * 0.025;
tex.rgb -= grain;
float fadeIn = smoothstep(0.0, 2.0 * 2.125, time);
fragColor = tex * fadeIn;
}
#version 300 es
// ╔═════════════╦════════════════╗
// ║ Marco Gomez ║ https://mgz.me ║
// ╚═════════════╩════════════════╝
precision highp float;
uniform sampler2D eTexture0; // https://i.imgur.com/s1sWDwl.png
uniform vec2 resolution;
uniform vec2 mouselerp;
uniform float time;
out vec4 fragColor;
#define antialias 0 // change to 1 to enable super-sampling anti-aliasing
#define softshadow 1 // change to 1 to render the soft shadow
#define showReference 0 // change to 1 to show the image used as a reference to code the signed distance functions
#define marchSteps 70
const int materialGround = 1;
const int materialRecognizer = 2;
const float PI = acos(-1.0);
const float TAU = PI * 2.0;
const float SQRT3 = sqrt(3.0);
const float COS30 = SQRT3 / 2.0;
const float TAN30 = 1.0 / SQRT3;
mat4 rotationMatrix(vec3 axis, float angle) {
axis = normalize(axis);
float s = sin(angle);
float c = cos(angle);
float oc = 1.0 - c;
return mat4(
oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0,
oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0,
oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0,
0.0, 0.0, 0.0, 1.0
);
}
vec3 rotate(vec3 v, vec3 axis, float angle) {
mat4 m = rotationMatrix(axis, angle);
return (m * vec4(v, 1.0)).xyz;
}
float boxSDF(vec3 p, vec3 b) {
vec3 q = abs(p) - b;
return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0);
}
float halfBoxSDF(vec3 p, vec3 b, float cutRotation) {
const vec3 zAxis = vec3(0.0, 0.0, 1.0);
float base = boxSDF(p, b);
vec3 cut = rotate(p, zAxis, cutRotation);
return max(cut.x, base);
}
float parallelogramSDF(vec2 p, float wi, float he, float sk) {
vec2 e = vec2(sk, he);
p = (p.y < 0.0) ? -p : p;
vec2 w = p - e;
w.x -= clamp(w.x, -wi, wi);
vec2 d = vec2(dot(w, w), -w.y);
float s = p.x * e.y - p.y * e.x;
p = (s < 0.0) ? -p : p;
vec2 v = p - vec2(wi,0);
v -= e * clamp(dot(v, e) / dot(e, e), -1.0, 1.0);
d = min(d, vec2(dot(v, v), wi * he - abs(s)));
return sqrt(d.x) * sign(-d.y);
}
float hexagonalSDF(vec3 p, vec2 h, float xMult) {
const vec3 k = vec3(-COS30, 0.2, 1e4); //TAN30);
p = abs(vec3(p.x * xMult, p.y, p.z));
p.xy -= 2.0 * min(dot(k.xy, p.xy), 0.0) * k.xy;
vec2 d = vec2(
length(p.xy - vec2(clamp(p.x, -k.z * h.x, k.z * h.x), h.x)) * sign(p.y - h.x),
p.z - h.y
);
return min(max(d.x, d.y), 0.0) + length(max(d, 0.0));
}
float pyramidalSDF(vec3 p, float h, float sMult) {
p /= sMult;
float m2 = h * h + 0.25;
p.xz = abs(p.xz);
p.xz = (p.z > p.x) ? p.zx : p.xz;
p.xz -= 0.5;
vec3 q = vec3(p.z, h * p.y - 0.5 * p.x, h * p.x + 0.5 * p.y);
float s = max(-q.x, 0.0);
float t = clamp((q.y - 0.5 * q.x) / (m2 + 0.25), 0.0, 1.0);
float a = m2 * (q.x + s) * (q.x + s) + q.y * q.y;
float b = m2 * (q.x + 0.5 * t) * (q.x + 0.5 * t) + (q.y - m2 * t) * (q.y - m2 * t);
float d2 = max(-q.y, q.x * m2 + q.y * 0.5) < 0.0 ? 0.0 : min(a, b);
return sqrt((d2 + q.z * q.z) / m2) * sign(max(q.z, -p.y));
}
float legsSDF(vec3 p) {
vec3 absp = vec3(abs(p.x), p.y, p.z);
float legA = boxSDF(absp + vec3(-1.33, -1.00, 0.0), vec3(0.20, 0.55, 0.2) );
float legB = boxSDF(absp + vec3(-1.33, -2.00, 0.0), vec3(0.20, 0.20, 0.2) );
float feet = halfBoxSDF(absp + vec3(-0.97, -0.36, 0.0), vec3(0.56, 0.10, 0.2), radians(120.0));
float legs = min(legA, legB);
legs = min(legs, feet);
return legs;
}
float torsoSDF(vec3 p) {
vec3 absp = vec3(abs(p.x), p.y, p.z);
float hipCenter = boxSDF( p + vec3( 0.0, -1.67, 0.0), vec3(1.53, 0.050, 0.2) );
float hipTop = boxSDF( p + vec3( 0.0, -1.79, 0.0), vec3(0.30, 0.035, 0.2) );
float hipBottom = halfBoxSDF( absp + vec3(-0.26, -1.55, 0.0), vec3(0.27, 0.035, 0.2), radians(-45.0));
float chestCenter = halfBoxSDF( absp + vec3(-0.39, -2.12, 0.0), vec3(0.40, 0.120, 0.2), radians(-45.0));
float chestRibsLim = boxSDF( p + vec3( 0.0, -2.09, 0.0), vec3(1.10, 0.235, 0.2) );
float chestRibsSDF = parallelogramSDF(vec3(absp + vec3(-0.55, -2.00, 0.0)).xy, 0.2, 0.45, radians(25.0));
float chestBottom = boxSDF( absp + vec3(-0.17, -1.90, 0.0), vec3(0.13, 0.045, 0.2) );
float chestTop = boxSDF( absp + vec3(-0.85, -2.34, 0.0), vec3(0.81, 0.045, 0.2) );
float arms = boxSDF( absp + vec3(-0.94, -2.00, 0.0), vec3(0.12, 0.050, 0.2) );
float chestRibs = max(chestRibsLim, chestRibsSDF);
float hip = min(hipCenter, hipTop);
hip = min(hip, hipBottom);
float chest = min(chestCenter, chestRibs);
chest = min(chest, chestBottom);
chest = min(chest, chestTop);
float torso = min(hip, chest);
torso = min(torso, arms);
return torso;
}
float headSDF(vec3 p) {
float headLimit = boxSDF(p + vec3( 0.0, -2.58, 0.0), vec3(0.90, 0.15, 0.2) );
float headShape = hexagonalSDF(p + vec3( 0.0, -2.43, 0.0), vec2(0.32, 0.20), 1.0 );
float headHole = boxSDF(p + vec3( 0.0, -2.64, 0.0), vec3(0.65, 0.03, 0.07));
float headZCutA = pyramidalSDF(p + vec3(0.0, -2.4, 0.8), 1.7, 2.0);
float headZCutB = pyramidalSDF(p + vec3(0.0, -2.4, -0.8), 1.7, 2.0);
float head = max(headShape, headLimit);
head = max(head, -headHole);
head = max(head, headZCutA);
head = max(head, headZCutB);
return head;
}
float bodySDF(vec3 p) {
float legs = legsSDF(p);
float torso = torsoSDF(p);
float head = headSDF(p);
float body = min(legs, torso);
body = min(body, head);
return body;
}
vec3 rotateBody(vec3 q) {
float t = time * 0.25;
q.y -= 0.35;
q.xy += vec2(cos(t), sin(t) * cos(t));
q = rotate(q, vec3(0.0, 0.0, 1.0), sin(time * 0.125) * 0.0625);
q = rotate(q, vec3(0.0, 1.0, 0.0), sin(time * 0.250) * 0.2);
return q;
}
float mapDistance(vec3 p) {
#if showReference == 0
vec3 q = rotateBody(p);
float body = bodySDF(q);
#else
float body = bodySDF(p);
#endif
float groundDist = p.y + 0.38;
float dist = min(body, groundDist);
return dist;
}
int mapMaterial(vec3 p) {
#if showReference == 0
vec3 q = rotateBody(p);
float body = bodySDF(q);
#else
float body = bodySDF(p);
#endif
float groundDist = p.y;
float dist = min(body, groundDist);
int material = 0;
if (dist == groundDist) {
material = materialGround;
} else if (dist == body) {
material = materialRecognizer;
}
return material;
}
float rayMarch(vec3 ro, vec3 rd) {
float dist = 0.0;
for (int i = 0; i < marchSteps; i++) {
vec3 p = ro + rd * dist;
float sceneDist = mapDistance(p);
dist += sceneDist;
if (dist > 40.0 || sceneDist <= 0.003) { break; }
}
return dist;
}
vec3 getNormal(vec3 p) {
float dist = mapDistance(p);
vec2 closeSample = vec2(0.01, 0.0);
vec3 closeSampleV3 = vec3(
mapDistance(p - closeSample.xyy),
mapDistance(p - closeSample.yxy),
mapDistance(p - closeSample.yyx)
);
vec3 normal = dist - closeSampleV3;
return normalize(normal);
}
float getAmbientOcclusion(vec3 pos, vec3 nor) {
float occ = 0.0;
float sca = 1.0;
for (int i = 0; i < 5; i++) {
float h = 0.001 + 0.25 * float(i) / 4.0;
float d = mapDistance(pos + h * nor);
occ += (h - d) * sca;
sca *= 0.98;
}
return clamp(1.0 - 1.6 * occ, 0.0, 1.0);
}
float getSoftShadow(vec3 ro, vec3 rd, float tmin, float tmax) {
float res = 1.0;
float t = tmin;
float ph = 1e10;
for (int i = 0; i < 32; i++) {
float h = mapDistance(ro + rd * t);
res = min(res, 10.0 * h / t);
t += h;
if (res < tmin || t > tmax) { break; }
}
return clamp(res, 0.0, 1.0);
}
vec3 getCameraPosition(void) {
vec3 ro = vec3(0.0);
#if showReference == 1
ro = vec3(0.0, 1.5, 6.0);
#else
const float camHeight = 1.5;
const float camDistanceRadius = 6.0;
float camPosX = sin(mouselerp.x * PI * 1.5) * camDistanceRadius;
float camPosY = clamp(camHeight + mouselerp.y * PI, 1.5, 7.0);
float camPosZ = cos(mouselerp.x * PI * 1.5) * camDistanceRadius;
ro = vec3(camPosX, camPosY, camPosZ);
float t = time * 0.125;
ro.xy += vec2(cos(t), sin(t) * cos(t)) * 2.0;
#endif
return ro;
}
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);
}
vec3 getCameraTarget(vec2 uv, vec3 ro) {
vec3 camTarget = vec3(0.0, 1.5, 0.0);
float fov = 1.5; float camRoll = 0.0;
#if showReference == 0
camRoll = 0.0 + sin(time * 0.25) * 0.0625;
fov = 1.2;
vec3 q = camTarget;
float t = time * 0.25;
q.y -= 0.35;
q.xy -= vec2(cos(t), sin(t) * cos(t)) * 0.5;
camTarget = q;
#endif
mat3 camMatrix = calcLookAtMatrix(ro, camTarget, camRoll);
vec3 rd = normalize(camMatrix * vec3(uv.x, uv.y, fov));
return rd;
}
vec4 render(vec2 uv) {
float ambient = 0.3;
float spec = 0.3;
vec3 col = vec3(0.0);
vec3 baseMat = vec3(0.2, 0.5, 1.0);
vec3 ro = getCameraPosition();
vec3 rd = getCameraTarget(uv, ro);
float dist = rayMarch(ro, rd);
vec3 p = ro + rd * dist;
vec3 normal = getNormal(p);
int material = mapMaterial(p);
vec3 light = normalize(vec3(0.7, 1.5, 4.0));
float l = clamp(dot(light, normal), 0.0, 1.0);
vec3 ref = normalize(reflect(rd, normal));
float r = clamp(dot(ref, light), 0.0, 1.0);
float occlusion = getAmbientOcclusion(p, normal) * 0.7;
float shadow = 1.0;
#if softshadow == 1
shadow = getSoftShadow(p, light, 0.2, 10.0);
#endif
if (material == materialGround) {
vec3 q = vec3(p.x, p.y, p.z + time * 5.0);
float w = 0.05;
float sx = step(mod(q.x, 1.0), w);
float sz = step(mod(q.z, 1.0), w);
baseMat = mix(
baseMat,
baseMat + vec3(sx + sz - (sx * sz)) * 3.0 * 1.0 - length(p * 0.25),
0.125
) * (baseMat + shadow);
}
col = (
min(vec3(max(shadow, ambient)), max(l, ambient)) *
max(occlusion, ambient) * baseMat + pow(abs(r), 64.0) * spec
) + occlusion * 0.3;
col -= vec3(length(p * 0.125)) * 0.25;
col += baseMat * vec3(occlusion) * 0.3;
return vec4(col, 1.0);
}
vec4 superSample(vec2 uv, float res, int steps, float offset) {
vec4 rv = vec4(0.0);
vec2 stepSize = vec2(offset) / res / float(steps);
uv -= vec2(0.5) / res;
for (int x = 0; x < steps; ++x) {
for (int y = 0; y < steps; ++y) {
vec2 off = vec2(float(x), float(y)) + vec2(0.5);
rv += render(uv + off * stepSize);
}
}
return rv / float(steps * steps);
}
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, vec2(12.9898, 78.233));
float noise = fract(sin(seed) * 43758.5453123 + t);
noise = gaussian(noise, 0.0, 0.5);
return vec3(noise);
}
void main(void) {
vec2 uv = (gl_FragCoord.xy - resolution.xy * 0.5) / resolution.y;
float minRes = min(resolution.x, resolution.y);
#if antialias == 1
vec4 color = superSample(uv, minRes, 2, 1.25);
#else
vec4 color = render(uv);
#endif
vec3 grain = gaussGrain(time) * 0.03;
color.xyz -= grain;
fragColor = color;
#if showReference == 1
vec2 suv = gl_FragCoord.xy / resolution.xy;
suv -= 0.5; suv *= vec2(1.5, 1.35); suv += 0.5;
vec4 reference = texture(eTexture0, suv);
fragColor = mix(fragColor, reference, reference.r);
#endif
}