diff --git a/7_ice_over.frag b/7_ice_over.frag index f25aacd..3c08843 100644 --- a/7_ice_over.frag +++ b/7_ice_over.frag @@ -24,14 +24,29 @@ float fbm(vec2 p) { for (int i = 0; i < 3; i++) { sum += amp * noise(p); p = mat2(1.62, 1.17, -1.17, 1.62) * p; amp *= 0.5; } return sum; } +float fbm2(vec2 p) { + return 0.5 * noise(p) + 0.25 * noise(mat2(1.62, 1.17, -1.17, 1.62) * p); +} vec2 atlasUV(vec2 world, float layer) { vec2 uv = world / (2.0 * EXTENT) + 0.5; uv = clamp(uv, 0.5 / u_tile_res, 1.0 - 0.5 / u_tile_res); // keep bilinear taps inside the tile return (vec2(mod(layer, 4.0), floor(layer / 4.0)) + uv) / vec2(4.0, 2.0); } -// Smooth frozen surface: two octaves, no bubbles, no grain. +// The slab's fracture lines: two families of long, slightly wandering cracks. Shared +// by the height field and the sheen, so each crack is both a bright seam and a step. +vec2 fracture(vec2 x) { + float d = fbm(x * 0.62 + 3.0) - 0.5; // zero set: long winding cracks + float e = fbm(x * 1.35 - 7.0) - 0.5; // a sparser second family + return vec2(d, e); +} +// Frozen surface: fine ridged grain over the plates the cracks have lifted apart. +// Static in world space; only the plasma behind it moves. float height(vec2 x) { - return 0.15 * fbm(x * 1.3 + 2.0) + 0.05 * fbm(x * 3.7 - 5.0); + vec2 fr = fracture(x); + return 0.085 * (1.0 - abs(2.0 * fbm2(x * 1.9 + 2.0) - 1.0)) + + 0.030 * (1.0 - abs(2.0 * fbm2(x * 4.6 - 5.0) - 1.0)) + + 0.020 * clamp(fr.x * 60.0, -1.0, 1.0) // the slab sits at a different level + + 0.012 * clamp(fr.y * 60.0, -1.0, 1.0); // on each side of a crack } void main() { vec2 screen = gl_FragCoord.xy / u_res; @@ -57,7 +72,7 @@ void main() { vec2 shearR = (refract(ray, normal, 1.0 / (IOR - 0.012)) - inside).xy; vec2 shearB = (refract(ray, normal, 1.0 / (IOR + 0.016)) - inside).xy; inside = normalize(inside); - if (inside.z > -0.05) inside = vec3(0.0, 0.0, -1.0); + inside.z = min(inside.z, -0.50); // bound grazing rays at the crack steps // Recentre the parallax on the middle of the sheet stack: near and far sheets // then slide past each other at different rates as the pointer moves. @@ -89,6 +104,14 @@ void main() { float fresnel = pow(1.0 - facing, 5.0); vec3 sky = mix(vec3(0.004, 0.016, 0.030), vec3(0.075, 0.220, 0.320), normal.y * 0.5 + 0.5); col += sky * (fresnel * 1.2 + bevel * 0.03); + // Steep faces of the crests catch a thin sheen: the edge of the frozen surface, + // painted only where the ice actually tilts, so the open water stays black. + float tilt = smoothstep(0.45, 0.95, length(slope)); + col += vec3(0.030, 0.085, 0.105) * tilt; + vec2 fr = fracture(hit.xy); + float crack = (1.0 - smoothstep(0.0, 0.006, abs(fr.x))) + + (1.0 - smoothstep(0.0, 0.006, abs(fr.y))); + col += vec3(0.020, 0.055, 0.070) * crack * smoothstep(0.58, 0.80, fbm2(hit.xy * 0.30 + 21.0)); gl_FragColor = vec4(pow(clamp(col, 0.0, 1.0), vec3(0.4545)), 1.0); } diff --git a/ice_plasma.html b/ice_plasma.html index d01b8c9..c052e06 100644 --- a/ice_plasma.html +++ b/ice_plasma.html @@ -17,7 +17,7 @@ canvas{display:block;width:100%;height:100%;touch-action:none}