Files
shaders/5_ice_refract.frag
T
emil28092005 27d238cbe6 Ice slab with layered plasma, two-pass refraction
Pass 1 renders eight plasma sheets into an atlas with linear depth in alpha;
pass 2 marches the refracted ray to each sheet and absorbs it by real path
length, so near and far layers separate under pointer parallax. Plasma cycle
repeats exactly every 30 s; - / = change speed.
2026-10-30 19:56:16 +03:00

108 lines
4.4 KiB
GLSL

// Pass B: the ice wall. Refracts pass A through a displaced surface using its depth buffer.
precision highp float;
uniform vec2 u_res; uniform float u_time; uniform vec2 u_mouse;
uniform sampler2D u_scene;
const float ICE_Z = 1.20; // front face of the ice
const vec3 SIG_A = vec3(0.13,0.042,0.020); // absorption in ice (red dies first -> blue)
const vec3 SIG_B = vec3(0.16,0.055,0.026); // absorption in the "depth fog" behind
float hash(vec2 p){ return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453); }
float noise(vec2 p){
vec2 i=floor(p), f=fract(p); f=f*f*(3.0-2.0*f);
return mix(mix(hash(i),hash(i+vec2(1,0)),f.x), mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),f.x), f.y);
}
float fbm(vec2 p){
float a=0.5, s=0.0;
for(int i=0;i<5;i++){ s+=a*noise(p); p*=2.02; a*=0.5; }
return s;
}
// height of the ice surface at (x,y) — almost frozen, drifts very slowly
float surf(vec2 p){
float t = u_time*0.04;
return 0.055*fbm(p*1.7 + vec2(t,-t)) + 0.022*fbm(p*4.3 - vec2(t*1.7,t));
}
vec3 surfaceNormal(vec2 p){
float e = 0.004;
float hx = surf(p+vec2(e,0.0)) - surf(p-vec2(e,0.0));
float hy = surf(p+vec2(0.0,e)) - surf(p-vec2(0.0,e));
return normalize(vec3(-hx/(2.0*e), -hy/(2.0*e), 1.0));
}
// project a world point with the scene camera (ro=(0,0,3.6), fov via 1.6)
vec2 project(vec3 p){
vec3 d = p - vec3(0.0,0.0,8.5);
return d.xy/(-d.z)*1.6;
}
void main(){
vec2 uv = (gl_FragCoord.xy - 0.5*u_res)/u_res.y;
vec3 ro = vec3(0.0,0.0,3.6);
vec3 rd = normalize(vec3(uv, -1.6));
// --- front surface: plane z=ICE_Z displaced by surf(), two fixed-point steps
float t = (ICE_Z - ro.z)/rd.z;
for(int i=0;i<2;i++){
vec3 p0 = ro + rd*t;
t = (ICE_Z + surf(p0.xy) - ro.z)/rd.z;
}
vec3 ps = ro + rd*t;
vec3 n = surfaceNormal(ps.xy);
if(dot(n,-rd) < 0.0) n = -n;
float cosi = clamp(dot(n,-rd), 0.0, 1.0);
float fres = 0.025 + 0.975*pow(1.0 - cosi, 5.0);
vec3 sky = mix(vec3(0.015,0.05,0.10), vec3(0.42,0.68,0.98), smoothstep(-0.35,0.95,n.y));
sky += vec3(0.9,0.97,1.0)*pow(cosi, 60.0); // specular glint
// --- refraction with chromatic dispersion down to the back face
float backZ = ICE_Z - (0.55 + 0.18*fbm(ps.xy*0.9));
vec3 sceneCol = vec3(0.0);
float pathIce = 0.0;
for(int c=0;c<3;c++){
float ior = 1.31 + float(c)*0.022; // 1.31 .. 1.354
vec3 d2 = refract(rd, n, 1.0/ior);
if(length(d2) < 0.5) d2 = rd; // total internal reflection guard
float t2 = (backZ - ps.z)/d2.z;
vec3 pe = ps + d2*t2;
pathIce = t2;
vec2 suv = project(pe);
vec2 texUV = (suv*u_res.y + 0.5*u_res)/u_res;
vec4 s = texture2D(u_scene, clamp(texUV, vec2(0.001), vec2(0.999)));
sceneCol[c] = s[c];
}
// --- z-buffer: how much ice/fog the view ray had to cross to the object
vec2 baseUV = gl_FragCoord.xy/u_res;
float dScene = texture2D(u_scene, baseUV).a * 12.0; // linear depth from pass A
float cell = max(0.0, dScene - (t + pathIce)); // ice-to-object gap
vec3 absorbA = exp(-SIG_A*pathIce); // inside the slab
vec3 absorbB = exp(-SIG_B*cell); // depth haze behind it
vec3 iceTint = vec3(0.80,0.94,1.0); // pure, clean blue ice
vec3 col = sceneCol * 1.10 * absorbA * absorbB * iceTint;
// --- body detail: small trapped bubbles, hair cracks, frozen sheets (static, they live in the slab)
vec2 q = ps.xy*2.2;
float zone = smoothstep(0.42, 0.62, fbm(q*0.7 + 21.0)); // clear ice vs bubbly ice
float bub = noise(q*19.0 + 3.1) * noise(q*17.0 - 1.7);
float dots = smoothstep(0.045, 0.075, bub) * mix(0.10, 1.0, zone) * 0.34;
float rid = abs(fbm(q*1.9 + 11.0)*2.0 - 1.0);
float crack = smoothstep(0.022, 0.0, rid) * 0.45;
float sheet = smoothstep(0.62, 0.78, fbm(vec2(q.y*1.3, q.x*0.35)) );
float streak = smoothstep(0.55,0.95, fbm(vec2(q.x*7.0, q.y*0.35) + 5.0));
float sheen = pow(max(0.0, dot(n, normalize(vec3(0.35,0.75,0.55)))), 40.0);
col += iceTint * (dots + crack*0.22 + streak*0.05 + sheen*0.5) * (0.25 + 0.75*cosi);
col += iceTint * sheet * 0.06 * smoothstep(0.0, 1.2, cell);
// --- reflection on top, thicker rim where the slab turns away
col = mix(col, sky, fres*0.85);
col += iceTint * pow(1.0 - cosi, 2.0) * 0.10; // frosted grazing edge
col = pow(clamp(col, 0.0, 1.0), vec3(0.4545));
gl_FragColor = vec4(col, 1.0);
}