// Scene1 effect shader - ShaderToy conversion (raymarching cube & sphere) // Source: Saturday cubism experiment by skal #include "common_uniforms" #include "math/color" #include "math/utils" #include "math/sdf_shapes" #include "render/raymarching" @group(0) @binding(0) var uniforms: CommonUniforms; const PI: f32 = 3.141592654; const TAU: f32 = 6.283185307; // 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 rayPlane(ro: vec3, rd: vec3, plane: vec4) -> f32 { return -(dot(ro, plane.xyz) + plane.w) / dot(rd, plane.xyz); } 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 = sdBox2D(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 = sdBox(pc, vec3(1.6)); // Sphere var ps = p; ps -= vec3(1.3, 0.0, 0.0); let dSphere = sdSphere(ps, 1.2); // Ground plane let dPlane = p.y + 1.0; // Union var d = min(dCube, dSphere); d = min(d, dPlane); return d; } 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); } #include "render/fullscreen_vs" @fragment fn fs_main(@builtin(position) p: vec4) -> @location(0) vec4 { // Flip Y to match ShaderToy convention (origin at bottom-left) let flipped = vec2(p.x, uniforms.resolution.y - p.y); let q = flipped / 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); }