From ff7b09adef4e355aa5e06123b5f8e6d5748f50aa Mon Sep 17 00:00:00 2001 From: emil28092005 Date: Fri, 30 Oct 2026 20:18:28 +0300 Subject: [PATCH] Sharpen the static ice surface Ridged grain replaces the smooth swells, and two families of fracture lines step the height, so the refracted sheets break across hard seams instead of drifting. The height field stays time-independent: only the plasma moves. Grazing rays at a step are clamped so the sheets never smear off the atlas. --- 7_ice_over.frag | 29 ++++++++++++++++++++++++++--- ice_plasma.html | 2 +- 2 files changed, 27 insertions(+), 4 deletions(-) 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}