// Scene1 effect shader - ShaderToy conversion (raymarching cube & sphere) // Source: Saturday cubism experiment by skal #include "common_uniforms" @group(0) @binding(0) var uniforms: CommonUniforms; const PI: f32 = 3.141592654; const TAU: f32 = 6.283185307; const TOLERANCE: f32 = 0.0005; const MAX_RAY_LENGTH: f32 = 20.0; const MAX_RAY_MARCHES: i32 = 80; const MAX_SHD_MARCHES: i32 = 20; const NORM_OFF: f32 = 0.005; fn rot(a: f32) -> mat2x2 { let c = cos(a); let s = sin(a); return mat2x2(c, s, -s, c); } // HSV to RGB conversion const hsv2rgb_K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); fn hsv2rgb(c: vec3) -> vec3 { let p = abs(fract(c.xxx + hsv2rgb_K.xyz) * 6.0 - hsv2rgb_K.www); return c.z * mix(hsv2rgb_K.xxx, clamp(p - hsv2rgb_K.xxx, vec3(0.0), vec3(1.0)), c.y); } // Colors (precomputed HSV conversions) const skyCol = vec3(0.176, 0.235, 0.25); // HSV(0.57, 0.90, 0.25) const skylineCol = vec3(0.5, 0.125, 0.025); // HSV(0.02, 0.95, 0.5) const sunCol = vec3(0.5, 0.163, 0.025); // HSV(0.07, 0.95, 0.5) const diffCol1 = vec3(0.4, 1.0, 1.0); // HSV(0.60, 0.90, 1.0) const diffCol2 = vec3(0.325, 1.0, 0.975); // HSV(0.55, 0.90, 1.0) // Lighting (normalized manually) const sunDir1 = vec3(0.0, 0.04997, -0.99875); // normalize(0, 0.05, -1) const lightPos1 = vec3(10.0, 10.0, 10.0); const lightPos2 = vec3(-10.0, 10.0, -10.0); fn sRGB(t: vec3) -> vec3 { return mix(1.055 * pow(t, vec3(1.0/2.4)) - 0.055, 12.92 * t, step(t, vec3(0.0031308))); } fn aces_approx(v_in: vec3) -> vec3 { var v = max(v_in, vec3(0.0)); v *= 0.6; let a = 2.51; let b = 0.03; let c = 2.43; let d = 0.59; let e = 0.14; return clamp((v * (a * v + b)) / (v * (c * v + d) + e), vec3(0.0), vec3(1.0)); } fn tanh_approx(x: f32) -> f32 { let x2 = x * x; return clamp(x * (27.0 + x2) / (27.0 + 9.0 * x2), -1.0, 1.0); } fn rayPlane(ro: vec3, rd: vec3, plane: vec4) -> f32 { return -(dot(ro, plane.xyz) + plane.w) / dot(rd, plane.xyz); } fn box2d(p: vec2, b: vec2) -> f32 { let d = abs(p) - b; return length(max(d, vec2(0.0))) + min(max(d.x, d.y), 0.0); } fn box3d(p: vec3, b: vec3) -> f32 { let q = abs(p) - b; return length(max(q, vec3(0.0))) + min(max(q.x, max(q.y, q.z)), 0.0); } fn sphere(p: vec3, r: f32) -> f32 { return length(p) - r; } var g_rot0: mat2x2; fn render0(ro: vec3, rd: vec3) -> vec3 { var col = vec3(0.0); var sf = 1.0001 - max(dot(sunDir1, rd), 0.0); col += skyCol * pow((1.0 - abs(rd.y)), 8.0); col += clamp(vec3(mix(0.0025, 0.125, tanh_approx(0.005 / sf)) / abs(rd.y)) * skylineCol, vec3(0.0), vec3(10.0)); sf *= sf; col += sunCol * 0.00005 / sf; let tp1 = rayPlane(ro, rd, vec4(0.0, -1.0, 0.0, 6.0)); if (tp1 > 0.0) { let pos = ro + tp1 * rd; let pp = pos.xz; let db = box2d(pp, vec2(5.0, 9.0)) - 3.0; col += vec3(4.0) * skyCol * rd.y * rd.y * smoothstep(0.25, 0.0, db); col += vec3(0.8) * skyCol * exp(-0.5 * max(db, 0.0)); } return clamp(col, vec3(0.0), vec3(10.0)); } fn df(p_in: vec3) -> f32 { var p = p_in; p.x = p_in.x * g_rot0[0][0] + p_in.z * g_rot0[0][1]; p.z = p_in.x * g_rot0[1][0] + p_in.z * g_rot0[1][1]; // Cube var pc = p; pc -= vec3(-1.9, 0.0, 0.0); let dCube = box3d(pc, vec3(1.6)); // Sphere var ps = p; ps -= vec3(1.3, 0.0, 0.0); let dSphere = sphere(ps, 1.2); // Ground plane let dPlane = p.y + 1.0; // Union var d = min(dCube, dSphere); d = min(d, dPlane); return d; } fn normal(pos: vec3) -> vec3 { let eps = vec2(NORM_OFF, 0.0); var nor: vec3; nor.x = df(pos + eps.xyy) - df(pos - eps.xyy); nor.y = df(pos + eps.yxy) - df(pos - eps.yxy); nor.z = df(pos + eps.yyx) - df(pos - eps.yyx); return normalize(nor); } fn rayMarch(ro: vec3, rd: vec3, initt: f32) -> f32 { var t = initt; for (var i = 0; i < MAX_RAY_MARCHES; i++) { if (t > MAX_RAY_LENGTH) { t = MAX_RAY_LENGTH; break; } let d = df(ro + rd * t); if (d < TOLERANCE) { break; } t += d; } return t; } fn shadow(lp: vec3, ld: vec3, mint: f32, maxt: f32) -> f32 { let ds = 1.0 - 0.4; var t = mint; var nd = 1e6; let soff = 0.05; let smul = 1.5; for (var i = 0; i < MAX_SHD_MARCHES; i++) { let p = lp + ld * t; let d = df(p); if (d < TOLERANCE || t >= maxt) { let sd = 1.0 - exp(-smul * max(t / maxt - soff, 0.0)); return select(mix(sd, 1.0, smoothstep(0.0, 0.025, nd)), sd, t >= maxt); } nd = min(nd, d); t += ds * d; } let sd = 1.0 - exp(-smul * max(t / maxt - soff, 0.0)); return sd; } fn boxCol(col: vec3, nsp: vec3, rd: vec3, nnor: vec3, nrcol: vec3, nshd1: f32, nshd2: f32) -> vec3 { var nfre = 1.0 + dot(rd, nnor); nfre *= nfre; let nld1 = normalize(lightPos1 - nsp); let nld2 = normalize(lightPos2 - nsp); var ndif1 = max(dot(nld1, nnor), 0.0); ndif1 *= ndif1; var ndif2 = max(dot(nld2, nnor), 0.0); ndif2 *= ndif2; var scol = vec3(0.0); let rf = smoothstep(1.0, 0.9, nfre); scol += diffCol1 * ndif1 * nshd1; scol += diffCol2 * ndif2 * nshd2; scol += 0.1 * (skyCol + skylineCol); scol += nrcol * 0.75 * mix(vec3(0.25), vec3(0.5, 0.5, 1.0), nfre); return mix(col, scol, rf * smoothstep(90.0, 20.0, dot(nsp, nsp))); } fn render1(ro: vec3, rd: vec3) -> vec3 { let skyCol_local = render0(ro, rd); var col = skyCol_local; let nt = rayMarch(ro, rd, 0.0); if (nt < MAX_RAY_LENGTH) { let nsp = ro + rd * nt; let nnor = normal(nsp); let nref = reflect(rd, nnor); let nrt = rayMarch(nsp, nref, 0.2); var nrcol = render0(nsp, nref); if (nrt < MAX_RAY_LENGTH) { let nrsp = nsp + nref * nrt; let nrnor = normal(nrsp); let nrref = reflect(nref, nrnor); nrcol = boxCol(nrcol, nrsp, nref, nrnor, render0(nrsp, nrref), 1.0, 1.0); } let nshd1 = mix(0.0, 1.0, shadow(nsp, normalize(lightPos1 - nsp), 0.1, distance(lightPos1, nsp))); let nshd2 = mix(0.0, 1.0, shadow(nsp, normalize(lightPos2 - nsp), 0.1, distance(lightPos2, nsp))); col = boxCol(col, nsp, rd, nnor, nrcol, nshd1, nshd2); } return col; } fn effect(p: vec2) -> vec3 { g_rot0 = rot(-0.2 * uniforms.time); let fov = tan(TAU / 6.0); let ro = vec3(0.0, 2.5, 5.0); let la = vec3(0.0, 0.0, 0.0); let up = vec3(0.1, 1.0, 0.0); let ww = normalize(la - ro); let uu = normalize(cross(up, ww)); let vv = cross(ww, uu); let rd = normalize(-p.x * uu + p.y * vv + fov * ww); return render1(ro, rd); } @vertex fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4 { var pos = array, 3>( vec2(-1.0, -1.0), vec2(3.0, -1.0), vec2(-1.0, 3.0) ); return vec4(pos[i], 0.0, 1.0); } @fragment fn fs_main(@builtin(position) p: vec4) -> @location(0) vec4 { let q = p.xy / uniforms.resolution; var coord = -1.0 + 2.0 * q; coord.x *= uniforms.resolution.x / uniforms.resolution.y; var col = effect(coord); col = aces_approx(col); col = sRGB(col); return vec4(col, 1.0); }