@yali-engine/water 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,6 @@ export declare const waterWaves: {
5
5
  };
6
6
  /** Linked from shaders/water/surface.wesl. */
7
7
  export declare const waterSurface: {
8
- readonly hash: "d76b79846f4edbf0";
9
- readonly wgsl: "const PI = 3.14159265;\nconst EARTH_RADIUS = 6371000.0;\nconst MAX_CASCADES = 4u;\nstruct SurfaceParams {\ncascade: array<vec4f, MAX_CASCADES>,\ngrid: vec4f,\nsea: vec4f,\nbody: vec4f,\nsss: vec4f,\nsize: vec4f,\noptics: vec4f,\nshore: vec4f,\nmirror: vec4f,\nsurf0: vec4f,\nsurf1: vec4f,\nsurf2: vec4f,\nsurf3: vec4f,\n}\n@group(0) @binding(0) var<uniform> camera: Camera;\n@group(0) @binding(1) var<uniform> lighting: Lighting;\n@group(0) @binding(2) var<uniform> sp: SurfaceParams;\n@group(0) @binding(3) var displacement: texture_2d_array<f32>;\n@group(0) @binding(4) var slopes: texture_2d_array<f32>;\n@group(0) @binding(5) var wrap: sampler;\n@group(0) @binding(6) var env_cube: texture_cube<f32>;\n@group(0) @binding(7) var under: texture_2d<f32>;\n@group(0) @binding(8) var under_depth: texture_2d<f32>;\n@group(0) @binding(9) var clamp_smp: sampler;\n@group(0) @binding(10) var scene_depth: texture_depth_2d;\n@group(0) @binding(11) var depth_out: texture_storage_2d<r32float, write>;\nstruct WaterMotion {\nview_proj: mat4x4f,\nprev_view_proj: mat4x4f,\neye_move: vec4f,\n}\n@group(0) @binding(12) var prev_displacement: texture_2d_array<f32>;\n@group(0) @binding(13) var<uniform> wm: WaterMotion;\n@group(0) @binding(14) var shore_map: texture_2d<f32>;\n@group(0) @binding(15) var spill_map: texture_2d<f32>;\n@group(0) @binding(16) var cloud_pano: texture_2d<f32>;\nstruct Surf {\nd: vec3f,\nslope: vec2f,\nbroken: f32,\nfoam: f32,\nfade: f32,\nground: f32,\nreach: f32,\ncover: f32,\nwet: f32,\nfilm: f32,\nfront: f32,\nface: f32,\namp: f32,\nworld: vec2f,\ndir: vec2f,\nphase: f32,\nonset: f32,\n}\nfn pcg(v: u32) -> u32 {\nlet state = v * 747796405u + 2891336453u;\nlet word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;\nreturn (word >> 22u) ^ word;\n}\nfn hash1(n: f32) -> f32 {\nreturn f32(pcg(bitcast<u32>(i32(n)))) / 4294967296.0;\n}\nfn hash2(p: vec2f) -> f32 {\nlet q = vec2<i32>(p);\nreturn f32(pcg(bitcast<u32>(q.x) ^ pcg(bitcast<u32>(q.y) + 0x9e3779b9u))) / 4294967296.0;\n}\nfn vnoise(p: vec2f) -> f32 {\nlet i = floor(p);\nlet f = fract(p);\nlet u = f * f * (3.0 - 2.0 * f);\nreturn mix(mix(hash2(i), hash2(i + vec2f(1.0, 0.0)), u.x), mix(hash2(i + vec2f(0.0, 1.0)), hash2(i + vec2f(1.0, 1.0)), u.x), u.y);\n}\nfn worley(p: vec2f) -> vec2f {\nlet i = floor(p);\nlet f = fract(p);\nvar d1 = 8.0;\nvar d2 = 8.0;\nfor (var y = -1; y <= 1; y++) {\nfor (var x = -1; x <= 1; x++) {\nlet o = vec2f(f32(x), f32(y));\nlet c = i + o;\nlet j = vec2f(hash2(c), hash2(c + vec2f(1013.0, 7919.0)));\nlet d = length(o + j - f) / (0.55 + 0.9 * hash2(c + vec2f(4241.0, 557.0)));\nif (d < d1) {\nd2 = d1;\nd1 = d;\n} else if (d < d2) {\nd2 = d;\n}\n}\n}\nreturn vec2f(d1, d2);\n}\nfn foam_net(p: vec2f, amount: f32, fw: f32) -> vec2f {\nlet a = saturate(amount);\nlet q = p + vec2f(vnoise(p * 2.0), vnoise(p * 2.0 + vec2f(5.2, 1.3))) * 0.36;\nlet big = worley(q * 3.2);\nvar small = vec2f(0.5, 1.0);\nif (sp.size.w > 1.5) {\nsmall = worley(q * 10.0 + vec2f(3.7, 1.9));\n}\nlet r = mix(0.72, 0.0, pow(a, 0.65));\nlet eb = fw * 3.2;\nlet es = fw * 10.0;\nlet mean = saturate(1.0 - 2.6 * r * r);\nlet hb = mix(smoothstep(r - 0.05 - eb, r + 0.05 + eb, big.x), mean, smoothstep(0.15, 0.6, eb));\nlet rs = r * 0.8 + 0.1;\nlet hs = mix(smoothstep(rs - 0.06 - es, rs + 0.06 + es, small.x), saturate(1.0 - 2.6 * rs * rs), smoothstep(0.15, 0.6, es));\nlet net = hb * mix(1.0, hs, 0.55 * (1.0 - a));\nlet cover = max(net, smoothstep(0.8, 1.0, a)) * smoothstep(0.0, 0.06, a);\nlet lumps = mix(0.66, 1.06, saturate(1.0 - small.x * 0.9)) * mix(0.84, 1.0, saturate(1.0 - big.x * 0.8));\nlet light = mix(lumps, 0.92, smoothstep(0.15, 0.6, es));\nreturn vec2f(cover, light);\n}\nfn wave_amp(n: f32, world: vec2f) -> f32 {\nlet group = 1.0 + 0.3 * sin(2.0 * PI * n / 6.3 + 1.3) + 0.25 * (hash1(n) - 0.5);\nlet section = 0.6 + 0.8 * vnoise(world / 28.0 + vec2f(n * 7.13, n * 3.71));\nreturn mix(1.0, group * section, sp.surf3.y);\n}\nfn surf_at(rest: vec2f, time: f32) -> Surf {\nvar s: Surf;\ns.fade = 1.0;\ns.amp = 1.0;\nif (sp.surf0.w < 0.5) {\nreturn s;\n}\nlet uv = sp.surf0.xy + rest * sp.surf0.z;\nif (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0))) {\nreturn s;\n}\nlet f = textureSampleLevel(shore_map, clamp_smp, uv, 0.0);\nlet sp2 = textureSampleLevel(spill_map, clamp_smp, uv, 0.0);\nlet depth = f.x;\nlet period = sp.surf1.y;\nlet clock = 2.0 * PI * fract(time / period);\nlet th = clock - atan2(f.z, f.y);\nlet u = fract(th / (2.0 * PI));\ns.phase = u;\nlet ff = fract(u + 0.5);\nlet nw = round((time - sp2.y) / period + 0.5 - ff);\ns.world = uv / sp.surf0.z;\ns.amp = mix(wave_amp(nw, s.world), wave_amp(nw + 1.0, s.world), smoothstep(0.82, 1.0, ff));\ns.fade = smoothstep(0.2, sp.surf2.y, depth);\nlet hs = max(depth, 0.15);\nlet start = sp.surf2.z;\nlet shoaled = sp.surf1.x * s.amp * pow(start / hs, 0.25) * smoothstep(start, start * 0.6, depth);\nlet hb = sp.surf1.z * hs;\nlet ratio = shoaled / max(hb, 1e-3);\ns.broken = saturate((ratio - 1.0) * 3.0);\nlet H = min(shoaled, hb) * smoothstep(-0.05, 0.25, depth);\nlet stokes = 0.5 * (cos(th) + 0.3 * cos(2.0 * th));\nlet dstokes = 0.5 * (-sin(th) - 0.6 * sin(2.0 * th));\nlet w = 0.12;\nlet bore = select(1.0 - (u - w) / (1.0 - w), u / w, u < w) - 0.5;\nlet dbore = select(-1.0 / (1.0 - w), 1.0 / w, u < w) / (2.0 * PI);\nvar eta = H * mix(stokes, bore, s.broken);\nlet deta = H * mix(dstokes, dbore, s.broken);\nlet dir = vec2f(cos(f.w), sin(f.w));\ns.dir = dir;\nlet q = sp.surf1.w * (1.0 - 0.6 * s.broken);\nvar lean = dir * (q * H * 0.5 * sin(th));\nlet onset = s.broken * (1.0 - s.broken) * 4.0 * saturate((ratio - 0.9) * 10.0);\ns.onset = onset;\nlet top = smoothstep(0.1, 0.5, eta / max(H, 1e-3));\nlean += dir * (sp.surf3.z * H * 0.55 * onset * top);\neta *= 1.0 + 0.25 * onset * top;\nlet k = 2.0 * PI / (period * sqrt(9.81 * hs));\ns.slope = -k * deta * dir;\ns.d = vec3f(lean.x, eta, lean.y);\ns.face = saturate(-dot(s.slope, dir) * 1.5) * smoothstep(-0.2, 0.45, eta / max(H, 1e-3)) * saturate(H * 1.2) * (1.0 - 0.65 * s.broken + 0.5 * onset);\ns.foam = s.broken * smoothstep(0.0, w, u) * (exp(-(u - w) * 9.0) + 0.3 * exp(-(u - w) * 3.0)) * saturate(H * 3.0);\ns.ground = -depth;\ns.reach = max(sp2.x, s.ground);\nif (s.ground > -0.05) {\nlet e = s.reach;\nlet run = sp.surf1.x * s.amp * 0.8;\nlet front = run * pow(saturate(1.0 - u / 0.55), 1.6);\ns.cover = saturate((front - e) / 0.03);\nlet passed = 0.55 * (1.0 - pow(saturate(e / max(run, 1e-3)), 1.0 / 1.6));\nlet since = select(0.0, (u - passed) * sp.surf1.y, u > passed);\ns.wet = select(0.0, max(exp(-since / 4.0) * 0.75, 0.35 * saturate(1.0 - e / max(run, 1e-3))), e < run);\ns.film = 0.02 + 0.04 * s.cover * saturate((front - e) / 0.15);\ns.front = s.cover * (1.0 - saturate((front - e) / 0.015)) * saturate(front * 6.0);\n}\nreturn s;\n}\nfn on_beach(d: vec3f, s: Surf) -> vec3f {\nif (s.ground <= 0.0) {\nreturn d;\n}\nlet reach = sp.surf1.x * 0.8 * 2.0 + 0.05;\nlet y = select(s.ground - 1.0, s.ground + s.film, s.reach < reach);\nlet k = smoothstep(0.0, 0.04, s.ground);\nreturn vec3f(d.x * (1.0 - k), mix(d.y, y, k), d.z * (1.0 - k));\n}\nconst SPRAY_SIDE = 192u;\nconst SPRAY_SPACING = 0.75;\nstruct SprayOut {\n@builtin(position) clip: vec4f,\n@location(0) q: vec4f,\n@location(1) p: vec3f,\n}\n@vertex fn vs_spray(@builtin(vertex_index) vi: u32, @builtin(instance_index) ii: u32) -> SprayOut {\nvar o: SprayOut;\no.clip = vec4f(0.0, 0.0, -1.0, 1.0);\nif (sp.surf0.w < 0.5 || sp.size.y <= 0.0) {\nreturn o;\n}\nlet extent = 1.0 / sp.surf0.z;\nlet eye = sp.surf0.xy * extent;\nlet gx = f32(ii % SPRAY_SIDE) - f32(SPRAY_SIDE / 2u);\nlet gz = f32(ii / SPRAY_SIDE) - f32(SPRAY_SIDE / 2u);\nlet cell = floor(eye / SPRAY_SPACING) + vec2f(gx, gz);\nlet j = vec2f(hash2(cell), hash2(cell + vec2f(91.0, 37.0)));\nlet rest = (cell + j) * SPRAY_SPACING - eye;\nlet s = surf_at(rest, sp.surf3.x);\nlet age = (s.phase - 0.12) * sp.surf1.y;\nlet life = 1.2 + 0.8 * hash2(cell + vec2f(13.0, 71.0));\nif (s.broken < 0.05 || age < 0.0 || age > life || s.ground > 0.3) {\nreturn o;\n}\nlet H = sp.surf1.x * s.amp;\nlet strength = (0.2 * s.broken + 0.8 * s.onset) * saturate(H * 1.2) * (0.4 + 1.2 * hash2(cell + vec2f(5.0, 3.0)));\nlet v0 = (1.5 + 2.5 * hash2(cell + vec2f(2.0, 9.0))) * saturate(H);\nlet k = 1.8;\nlet rise = v0 / k * (1.0 - exp(-k * age)) - 4.0 * age * age * 0.5;\nlet fwd = s.dir * (1.2 * (1.0 - exp(-1.5 * age)));\nlet base = s.d.y + 0.15;\nlet centre = vec3f(rest.x + fwd.x, sp.sea.x + base + max(rise, 0.0), rest.y + fwd.y);\nlet t = age / life;\nlet size = (0.25 + 0.9 * t) * (0.7 + 0.6 * hash2(cell + vec2f(17.0, 23.0))) * (0.6 + 0.6 * saturate(H));\nlet corner = array<vec2f, 6>(vec2f(-1.0, -1.0), vec2f(1.0, -1.0), vec2f(1.0, 1.0), vec2f(-1.0, -1.0), vec2f(1.0, 1.0), vec2f(-1.0, 1.0))[vi];\nlet fwdv = normalize(centre);\nlet right = normalize(cross(vec3f(0.0, 1.0, 0.0), fwdv));\nlet up = cross(fwdv, right);\nlet pos = centre + (right * corner.x + up * corner.y) * size;\no.clip = camera.view_proj * vec4f(pos, 1.0);\nlet alpha = strength * smoothstep(0.0, 0.1, t) * (1.0 - smoothstep(0.4, 1.0, t)) * sp.size.y * 0.22;\no.q = vec4f(corner, alpha, length(centre));\no.p = centre;\nreturn o;\n}\n@fragment fn fs_spray(in: SprayOut) -> @location(0) vec4f {\nlet r2 = dot(in.q.xy, in.q.xy);\nif (r2 > 1.0) {\ndiscard;\n}\nlet wisp = vnoise(in.q.xy * 1.8 + in.p.xz * 3.1) * 0.6 + vnoise(in.q.xy * 4.3 + in.p.xz * 1.7) * 0.4;\nvar a = in.q.z * (1.0 - r2) * (1.0 - r2) * smoothstep(0.25, 0.8, wisp) * 1.6;\nlet d = textureLoad(under_depth, vec2i(in.clip.xy), 0).x;\nif (d > 0.0) {\nlet gap = in.q.w * (in.clip.z / d - 1.0);\na *= saturate(gap / 0.5);\n}\nlet v = normalize(in.p);\nlet l = camera.light_dir.xyz;\nlet g = 0.6;\nlet cosv = dot(v, l);\nlet hg = (1.0 - g * g) / (4.0 * PI * pow(1.0 + g * g - 2.0 * g * cosv, 1.5));\nlet light = sh_irradiance(vec3f(0.0, 1.0, 0.0)) * 0.9 + lighting.sun_color.rgb * (hg * 2.0 + 0.08);\nlet fogged = apply_fog(light, in.p, lighting.fog, lighting.fog2, lighting.fog_air.rgb, sh_irradiance(v), lighting.sun_color.rgb, l);\nreturn vec4f(fogged * lighting.env.z * a, a);\n}\nstruct VsOut {\n@builtin(position) clip: vec4f,\n@location(0) p: vec3f,\n@location(1) rest: vec2f,\n@location(2) height: f32,\n}\nfn ring_radius(i: f32) -> f32 {\nlet k = sp.grid.y;\nreturn sp.grid.x * (pow(1.0 + k, i) - 1.0) / k;\n}\nfn grid_point(vi: u32) -> vec3f {\nlet segs = u32(sp.grid.w);\nlet quad = vi / 6u;\nlet corner = vi % 6u;\nlet dr = select(select(0u, 1u, corner == 1u || corner == 2u || corner == 4u), 0u, corner == 0u || corner == 3u);\nlet ds = select(0u, 1u, corner == 2u || corner == 4u || corner == 5u);\nlet ring = f32(quad / segs + dr);\nlet seg = f32(quad % segs + ds);\nlet r = ring_radius(ring);\nlet a = 2.0 * PI * seg / f32(segs);\nreturn vec3f(vec2f(cos(a), sin(a)) * r, max(ring_radius(ring + 1.0) - r, 2.0 * PI * r / f32(segs)));\n}\nfn displaced(field: texture_2d_array<f32>, rest: vec2f, spacing: f32) -> vec3f {\nvar d = vec3f(0.0);\nfor (var c = 0u; c < u32(sp.sea.y); c++) {\nlet cc = sp.cascade[c];\nlet texel = 1.0 / (cc.z * sp.size.x);\nlet w = 1.0 - smoothstep(1.0, 4.0, spacing / texel);\nif (w <= 0.0) {\ncontinue;\n}\nd += textureSampleLevel(field, wrap, cc.xy + rest * cc.z, c, 0.0).xyz * w;\n}\nreturn d;\n}\nstruct MotionOut {\n@builtin(position) clip: vec4f,\n@location(0) now: vec4f,\n@location(1) then: vec4f,\n}\n@vertex fn vs_motion(@builtin(vertex_index) vi: u32) -> MotionOut {\nlet g = grid_point(vi);\nlet rest = g.xy;\nlet drop = dot(rest, rest) / (2.0 * EARTH_RADIUS);\nlet sn = surf_at(rest, sp.surf3.x);\nlet sq = surf_at(rest, select(sp.surf3.x, sp.surf3.w, wm.eye_move.w > 0.5));\nlet dn = on_beach(displaced(displacement, rest, g.z) * sn.fade + sn.d, sn);\nlet dp = select(dn, on_beach(displaced(prev_displacement, rest, g.z) * sq.fade + sq.d, sq), wm.eye_move.w > 0.5);\nlet p = vec3f(rest.x + dn.x, sp.sea.x + dn.y - drop, rest.y + dn.z);\nlet q = vec3f(rest.x + dp.x, sp.sea.x + dp.y - drop, rest.y + dp.z) + wm.eye_move.xyz;\nvar o: MotionOut;\no.now = wm.view_proj * vec4f(p, 1.0);\no.clip = camera.view_proj * vec4f(p, 1.0);\no.then = wm.prev_view_proj * vec4f(q, 1.0);\nreturn o;\n}\n@fragment fn fs_motion(in: MotionOut) -> @location(0) vec4f {\nlet now = in.now.xy / in.now.w;\nlet then = in.then.xy / max(in.then.w, 1e-6);\nreturn vec4f((now - then) * vec2f(0.5, -0.5), in.then.w, 1.0);\n}\n@vertex fn vs(@builtin(vertex_index) vi: u32) -> VsOut {\nlet segs = u32(sp.grid.w);\nlet quad = vi / 6u;\nlet corner = vi % 6u;\nlet dr = select(select(0u, 1u, corner == 1u || corner == 2u || corner == 4u), 0u, corner == 0u || corner == 3u);\nlet ds = select(0u, 1u, corner == 2u || corner == 4u || corner == 5u);\nlet ring = f32(quad / segs + dr);\nlet seg = f32(quad % segs + ds);\nlet r = ring_radius(ring);\nlet a = 2.0 * PI * seg / f32(segs);\nlet rest = vec2f(cos(a), sin(a)) * r;\nlet spacing = max(ring_radius(ring + 1.0) - r, 2.0 * PI * r / f32(segs));\nvar d = vec3f(0.0);\nfor (var c = 0u; c < u32(sp.sea.y); c++) {\nlet cc = sp.cascade[c];\nlet texel = 1.0 / (cc.z * sp.size.x);\nlet w = 1.0 - smoothstep(1.0, 4.0, spacing / texel);\nif (w <= 0.0) {\ncontinue;\n}\nd += textureSampleLevel(displacement, wrap, cc.xy + rest * cc.z, c, 0.0).xyz * w;\n}\nlet sf = surf_at(rest, sp.surf3.x);\nd = on_beach(d * sf.fade + sf.d, sf);\nlet drop = dot(rest, rest) / (2.0 * EARTH_RADIUS);\nlet p = vec3f(rest.x + d.x, sp.sea.x + d.y - drop, rest.y + d.z);\nvar o: VsOut;\no.clip = camera.view_proj * vec4f(p, 1.0);\no.p = p;\no.rest = rest;\no.height = d.y;\nreturn o;\n}\nfn sh_irradiance(n: vec3f) -> vec3f {\nreturn max(lighting.sh[0].rgb * 0.282095 + lighting.sh[1].rgb * (0.488603 * n.y) + lighting.sh[2].rgb * (0.488603 * n.z) + lighting.sh[3].rgb * (0.488603 * n.x) + lighting.sh[4].rgb * (1.092548 * n.x * n.y) + lighting.sh[5].rgb * (1.092548 * n.y * n.z) + lighting.sh[6].rgb * (0.315392 * (3.0 * n.z * n.z - 1.0)) + lighting.sh[7].rgb * (1.092548 * n.x * n.z) + lighting.sh[8].rgb * (0.546274 * (n.x * n.x - n.y * n.y)), vec3f(0.0));\n}\n@compute @workgroup_size(8, 8) fn copy_depth(@builtin(global_invocation_id) id: vec3u) {\nlet size = textureDimensions(scene_depth);\nif (id.x >= size.x || id.y >= size.y) {\nreturn;\n}\ntextureStore(depth_out, vec2i(id.xy), vec4f(textureLoad(scene_depth, vec2i(id.xy), 0), 0.0, 0.0, 0.0));\n}\nfn thickness(dist: f32, ds: f32, dop: f32) -> f32 {\nreturn select(1.0e9, dist * max(ds / dop - 1.0, 0.0), dop > 0.0);\n}\nfn mirror_probe(p: vec3f, r: vec3f, t: f32, size: vec2f) -> vec4f {\nlet c = camera.view_proj * vec4f(p + r * t, 1.0);\nif (c.w <= 0.0) {\nreturn vec4f(0.0);\n}\nlet ndc = c.xyz / c.w;\nlet uv = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);\nif (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0))) {\nreturn vec4f(0.0);\n}\nlet d = textureLoad(under_depth, vec2i(uv * size), 0).x;\nlet gap = select(-1.0e9, c.w * (1.0 - ndc.z / d), d > 0.0);\nreturn vec4f(uv, gap, 1.0);\n}\nfn mirror_scene(p: vec3f, r: vec3f, size: vec2f, jitter: f32) -> vec4f {\nlet len = sp.mirror.x;\nlet steps = u32(sp.mirror.y);\nif (len <= 0.0 || steps == 0u || sp.shore.y < 0.5 || r.y <= 0.0) {\nreturn vec4f(0.0);\n}\nvar prev = 0.0;\nvar was_behind = false;\nfor (var i = 1u; i <= steps; i++) {\nlet f = (f32(i) - jitter) / f32(steps);\nlet t = len * f * f;\nlet s = mirror_probe(p, r, t, size);\nif (s.w < 0.5) {\nbreak;\n}\nlet behind = s.z > 0.0;\nif (behind && !was_behind) {\nvar a = prev;\nvar b = t;\nvar hit = s;\nfor (var k = 0u; k < 6u; k++) {\nlet m = 0.5 * (a + b);\nlet sm = mirror_probe(p, r, m, size);\nif (sm.w > 0.5 && sm.z > 0.0) {\nb = m;\nhit = sm;\n} else {\na = m;\n}\n}\nif (hit.z < sp.mirror.z + 0.03 * b) {\nlet uv = hit.xy;\nlet edge = min(min(uv.x, 1.0 - uv.x), min(uv.y, 1.0 - uv.y));\nlet fade = saturate(edge * 12.0) * (1.0 - smoothstep(0.7, 1.0, b / len));\nreturn vec4f(textureSampleLevel(under, clamp_smp, uv, 0.0).rgb * sp.shore.z, fade);\n}\n}\nwas_behind = behind;\nprev = t;\n}\nreturn vec4f(0.0);\n}\nfn ign(px: vec2f) -> f32 {\nlet frame = floor(camera.light_dir.w * 60.0) % 64.0;\nlet q = px + 5.588238 * frame;\nreturn fract(52.9829189 * fract(dot(q, vec2f(0.06711056, 0.00583715))));\n}\n@fragment fn fs(in: VsOut) -> @location(0) vec4f {\nlet frag = in.clip;\nvar sx = 0.0;\nvar sz = 0.0;\nvar lost = 0.0;\nvar fold = 0.0;\nfor (var c = 0u; c < u32(sp.sea.y); c++) {\nlet cc = sp.cascade[c];\nlet uv = cc.xy + in.rest * cc.z;\nlet texels = length(fwidth(uv)) * sp.size.x;\nlet w = 1.0 - smoothstep(0.5, 2.0, texels);\nlet s = textureSampleLevel(slopes, wrap, uv, c, 0.0);\nsx += s.x * w;\nsz += s.y * w;\nlost += cc.w * (1.0 - w);\nfold = max(fold, max(saturate((sp.sea.z - s.z) * 2.0), s.w) * w);\n}\nlet sf = surf_at(in.rest, sp.surf3.x);\nsx = sx * sf.fade + sf.slope.x;\nsz = sz * sf.fade + sf.slope.y;\nfold *= sf.fade;\nlet n = normalize(vec3f(-sx, 1.0, -sz));\nlet v = normalize(-in.p);\nlet l = camera.light_dir.xyz;\nlet nv = max(dot(n, v), 1e-3);\nlet nl = max(dot(n, l), 0.0);\nlet a2 = max(sp.body.w * sp.body.w + 2.0 * lost, 1e-4);\nlet f0 = 0.02;\nlet fresnel = f0 + (1.0 - f0) * pow(1.0 - nv, 5.0);\nvar r = reflect(-v, n);\nr.y = abs(r.y);\nlet lod = clamp(sqrt(sqrt(a2)) * (lighting.env.x - 1.0), 0.0, lighting.env.x - 1.0);\nvar sky = textureSampleLevel(env_cube, wrap, r, lod).rgb * lighting.view.rgb;\nif (sp.size.z > 0.5) {\nlet pn = vec2f(textureDimensions(cloud_pano));\nlet pv = clamp(sqrt(max(r.y, 0.0)), 0.5 / pn.y, 1.0 - 0.5 / pn.y);\nlet cl = textureSampleLevel(cloud_pano, wrap, vec2f(fract(atan2(r.z, r.x) / (2.0 * PI)), pv), 0.0);\nsky = sky * cl.a + cl.rgb;\n}\nlet seen = mirror_scene(in.p, reflect(-v, n), vec2f(textureDimensions(under)), ign(frag.xy));\nsky = mix(sky, seen.rgb, seen.a);\nif (sp.mirror.w > 0.5) {\nreturn vec4f(select(vec3f(seen.a * 0.2, 0.0, 0.0) + seen.rgb * seen.a, vec3f(0.3, 0.0, 0.0), seen.a <= 0.0) * lighting.env.z, 1.0);\n}\nlet h = normalize(v + l);\nlet nh = max(dot(n, h), 0.0);\nlet dd = nh * nh * (a2 - 1.0) + 1.0;\nlet ggx = a2 / (PI * dd * dd);\nlet vis = 0.5 / (nl * sqrt(nv * nv * (1.0 - a2) + a2) + nv * sqrt(nl * nl * (1.0 - a2) + a2) + 1e-5);\nlet fh = f0 + (1.0 - f0) * pow(1.0 - max(dot(v, h), 0.0), 5.0);\nlet sun = lighting.sun_color.rgb;\nlet spec = sun * (ggx * vis * fh * nl);\nlet up = sh_irradiance(vec3f(0.0, 1.0, 0.0));\nlet body = sp.body.rgb * (up + sun * (max(l.y, 0.0) / PI) * 0.5);\nlet behind = pow(saturate(dot(-v, l)), 8.0);\nlet crest = saturate(in.height * 0.5) * saturate(1.0 - n.y * 0.9 + 0.2);\nvar sss = sp.sss.rgb * sun * (behind * crest * sp.sss.w);\nlet wide = 0.35 + 0.65 * pow(saturate(dot(-v, l) * 0.5 + 0.5), 3.0);\nlet thin = sf.face * (0.6 + 0.4 * saturate(1.0 - abs(n.y) * 1.2));\nsss += sp.sss.rgb * (up * 4.5 + sun * (0.8 * wide)) * (thin * sp.sss.w * 2.5);\nvar below = body;\nvar shore_foam = 0.0;\nif (sp.shore.y > 0.5) {\nlet px = vec2i(frag.xy);\nlet size = vec2f(textureDimensions(under));\nlet dist = length(in.p);\nlet t0 = thickness(dist, frag.z, textureLoad(under_depth, px, 0).x);\nlet bend = n.xz * sp.optics.w * min(t0, 1.0);\nvar uv = (frag.xy + bend) / size;\nif (any(uv < vec2f(0.0)) || any(uv >= vec2f(1.0))) {\nuv = frag.xy / size;\n}\nvar t = thickness(dist, frag.z, textureLoad(under_depth, vec2i(uv * size), 0).x);\nif (textureLoad(under_depth, vec2i(uv * size), 0).x > frag.z) {\nuv = frag.xy / size;\nt = t0;\n}\nvar behind_light = textureSampleLevel(under, clamp_smp, uv, 0.0).rgb * sp.shore.z;\nlet down = max(t * v.y, 0.0);\nlet sun_in = sqrt(max(1.0 - (1.0 - l.y * l.y) / 1.7689, 0.04));\nlet above = (in.p - v * t).xz + vec2f(l.x, l.z) / sun_in * down * (1.0 / 1.33);\nvar lap = 0.0;\nlet cn = u32(sp.sea.y);\nlet c0 = select(0u, cn - 2u, cn >= 2u);\nfor (var c = c0; c < select(c0, select(1u, cn - 1u, cn >= 2u), sp.size.w > 0.5); c++) {\nlet cc = sp.cascade[c];\nlet e = 2.0 / (cc.z * sp.size.x);\nlet q = cc.xy + above * cc.z;\nlet ex = vec2f(cc.z * e, 0.0);\nlet ez = vec2f(0.0, cc.z * e);\nlet dsx = textureSampleLevel(slopes, wrap, q + ex, c, 0.0).x - textureSampleLevel(slopes, wrap, q - ex, c, 0.0).x;\nlet dsz = textureSampleLevel(slopes, wrap, q + ez, c, 0.0).y - textureSampleLevel(slopes, wrap, q - ez, c, 0.0).y;\nlap += (dsx + dsz) / (2.0 * e);\n}\nlet focus = 1.0 / max(abs(1.0 - down * 0.248 * lap), 0.25);\nlet caustic = mix(1.0, focus, saturate(down * 3.0) * exp(-down / 4.0) * saturate(l.y * 4.0));\nbehind_light *= mix(1.0, caustic, 0.65);\nlet trans = exp(-sp.optics.rgb * (t + down / sun_in));\nbelow = behind_light * trans + body * (vec3f(1.0) - exp(-sp.optics.rgb * t));\nlet vertical = t0 * max(v.y, 0.02);\nshore_foam = saturate(1.0 - vertical / sp.shore.x) * saturate(vertical * 40.0);\n}\nlet sigma = sqrt(lost);\nlet mean_f = f0 + (1.0 - f0) * pow(1.0 - nv, 5.0 * exp(-2.69 * sigma)) / (1.0 + 22.7 * pow(sigma, 1.5));\nlet fr = min(fresnel, mean_f);\nlet water = sky * fr + (below + sss) * (1.0 - fr) + spec;\nlet foam_light = (sh_irradiance(n) + sun * (nl / PI)) * 0.85;\nvar fp = (sp.cascade[0].xy + in.rest * sp.cascade[0].z) / sp.cascade[0].z;\nif (sf.foam > 0.001 || sf.front > 0.001) {\nlet c = sqrt(9.81 * max(1.0 - sf.ground, 0.3));\nfp = sf.world - sf.dir * (0.5 * c * sp.surf1.y * sf.phase);\n}\nlet beach = smoothstep(0.0, 0.04, sf.ground);\nvar cover = saturate(fold * sp.sea.w + shore_foam * (1.0 - beach) + sf.foam + sf.front * 0.9);\nif (cover > 0.002) {\nlet spots = vnoise(fp * 0.22) * 0.65 + vnoise(fp * 0.61 + vec2f(9.0, 4.0)) * 0.35;\ncover *= mix(smoothstep(0.35, 0.75, spots), 1.0, smoothstep(0.35, 0.8, cover));\n}\nlet fw = length(fwidth(fp));\nvar foam = 0.0;\nvar bubbles = 1.0;\nif (cover > 0.002) {\nlet net = foam_net(fp, cover, fw);\nfoam = net.x;\nbubbles = net.y;\n}\nvar lit = mix(water, foam_light * bubbles, foam);\nif (sf.ground > 0.0 && sp.shore.y > 0.5) {\nlet sand = textureLoad(under, vec2i(frag.xy), 0).rgb * sp.shore.z;\nlet wet_rough = 0.12;\nlet sheen = textureSampleLevel(env_cube, wrap, r, clamp(sqrt(wet_rough) * (lighting.env.x - 1.0), 0.0, lighting.env.x - 1.0)).rgb * lighting.view.rgb;\nlet wet_sand = sand * mix(1.0, 0.5, sf.wet) + sheen * fresnel * sf.wet * 0.8;\nlit = mix(wet_sand, lit, max(sf.cover, foam));\n}\nlet fogged = apply_fog(lit, in.p, lighting.fog, lighting.fog2, lighting.fog_air.rgb, sh_irradiance(normalize(in.p)), sun, l);\nreturn vec4f(fogged * lighting.env.z, 1.0);\n}\nstruct Camera {\nview_proj: mat4x4f,\nfrustum: array<vec4f, 6>,\nlight_dir: vec4f,\norigin: vec4f,\n}\nstruct Lighting {\nsun_color: vec4f,\nenv: vec4f,\nsh: array<vec4f, 9>,\nscreen: vec4f,\nview: vec4f,\nao_reproject: mat4x4f,\nao: vec4f,\nfog: vec4f,\nfog2: vec4f,\nfog_air: vec4f,\ngi_grid: vec4f,\ngi_counts: vec4f,\nssr: vec4f,\ngi_cascades: array<vec4f, 3>,\n}\nfn apply_fog(color: vec3f, p: vec3f, fog: vec4f, fog2: vec4f, air: vec3f, ambient: vec3f, sun: vec3f, sun_dir: vec3f) -> vec3f {\nlet dist = length(p);\nif (dist <= 0.0 || (fog.x <= 0.0 && fog2.x <= 0.0)) {\nreturn color;\n}\nlet dir = p / dist;\nlet t = exp(-(air * (fog2.x * dist) + vec3f(height_fog_depth(fog, dir, dist))));\nreturn color * t + fog_inscatter(fog2, dir, ambient, sun, sun_dir) * (vec3f(1.0) - t);\n}\nfn height_fog_depth(fog: vec4f, dir: vec3f, dist: f32) -> f32 {\nif (fog.x <= 0.0) {\nreturn 0.0;\n}\nlet falloff = max(fog.y, 1e-5);\nlet len = max(dist - fog.w, 0.0);\nlet h0 = fog.z + dir.y * min(dist, fog.w);\nlet k = falloff * dir.y * len;\nlet shape = select((1.0 - exp(-k)) / k, 1.0 - 0.5 * k, abs(k) < 1e-3);\nreturn fog.x * exp(-falloff * h0) * len * shape;\n}\nfn fog_inscatter(fog2: vec4f, dir: vec3f, ambient: vec3f, sun: vec3f, sun_dir: vec3f) -> vec3f {\nlet g = fog2.z;\nlet c = dot(dir, sun_dir);\nlet phase = (1.0 - g * g) / (FOUR_PI * pow(max(1.0 + g * g - 2.0 * g * c, 1e-4), 1.5));\nreturn ambient + sun * (phase * fog2.y);\n}\nconst FOUR_PI = 12.5663706;";
8
+ readonly hash: "b6a83798dfc992f7";
9
+ readonly wgsl: "const PI = 3.14159265;\nconst EARTH_RADIUS = 6371000.0;\nconst MAX_CASCADES = 4u;\nstruct SurfaceParams {\ncascade: array<vec4f, MAX_CASCADES>,\ngrid: vec4f,\nsea: vec4f,\nbody: vec4f,\nsss: vec4f,\nsize: vec4f,\noptics: vec4f,\nshore: vec4f,\nmirror: vec4f,\nsurf0: vec4f,\nsurf1: vec4f,\nsurf2: vec4f,\nsurf3: vec4f,\nglint: vec4f,\ncshadow: vec4f,\n}\n@group(0) @binding(0) var<uniform> camera: Camera;\n@group(0) @binding(1) var<uniform> lighting: Lighting;\n@group(0) @binding(2) var<uniform> sp: SurfaceParams;\n@group(0) @binding(3) var displacement: texture_2d_array<f32>;\n@group(0) @binding(4) var slopes: texture_2d_array<f32>;\n@group(0) @binding(5) var wrap: sampler;\n@group(0) @binding(6) var env_cube: texture_cube<f32>;\n@group(0) @binding(7) var under: texture_2d<f32>;\n@group(0) @binding(8) var under_depth: texture_2d<f32>;\n@group(0) @binding(9) var clamp_smp: sampler;\n@group(0) @binding(10) var scene_depth: texture_depth_2d;\n@group(0) @binding(11) var depth_out: texture_storage_2d<r32float, write>;\nstruct WaterMotion {\nview_proj: mat4x4f,\nprev_view_proj: mat4x4f,\neye_move: vec4f,\nreactive: vec4f,\n}\n@group(0) @binding(12) var prev_displacement: texture_2d_array<f32>;\n@group(0) @binding(13) var<uniform> wm: WaterMotion;\n@group(0) @binding(14) var shore_map: texture_2d<f32>;\n@group(0) @binding(15) var spill_map: texture_2d<f32>;\n@group(0) @binding(16) var cloud_pano: texture_2d<f32>;\n@group(0) @binding(27) var cloud_shadow: texture_2d<f32>;\nfn cloud_light(p: vec3f, l: vec3f) -> f32 {\nif (sp.cshadow.w < 0.5) {\nreturn 1.0;\n}\nlet at = p - l * ((p.y - sp.sea.x) / max(l.y, 0.05));\nlet uv = (at.xz - sp.cshadow.xy) * sp.cshadow.z + 0.5;\nlet edge = min(min(uv.x, 1.0 - uv.x), min(uv.y, 1.0 - uv.y));\nreturn mix(1.0, textureSampleLevel(cloud_shadow, clamp_smp, uv, 0.0).r, saturate(edge * 16.0));\n}\nstruct Surf {\nd: vec3f,\nslope: vec2f,\nbroken: f32,\nfoam: f32,\nfade: f32,\nground: f32,\nreach: f32,\ncover: f32,\nwet: f32,\nfilm: f32,\nfront: f32,\nface: f32,\namp: f32,\nworld: vec2f,\ndir: vec2f,\nphase: f32,\nonset: f32,\nfresh: f32,\nfront_h: f32,\nwet_h1: f32,\nwet_h2: f32,\nrun: f32,\nmark: f32,\n}\nfn pcg(v: u32) -> u32 {\nlet state = v * 747796405u + 2891336453u;\nlet word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;\nreturn (word >> 22u) ^ word;\n}\nfn hash1(n: f32) -> f32 {\nreturn f32(pcg(bitcast<u32>(i32(n)))) / 4294967296.0;\n}\nfn hash2(p: vec2f) -> f32 {\nlet q = vec2<i32>(p);\nreturn f32(pcg(bitcast<u32>(q.x) ^ pcg(bitcast<u32>(q.y) + 0x9e3779b9u))) / 4294967296.0;\n}\nfn vnoise(p: vec2f) -> f32 {\nlet i = floor(p);\nlet f = fract(p);\nlet u = f * f * (3.0 - 2.0 * f);\nreturn mix(mix(hash2(i), hash2(i + vec2f(1.0, 0.0)), u.x), mix(hash2(i + vec2f(0.0, 1.0)), hash2(i + vec2f(1.0, 1.0)), u.x), u.y);\n}\nfn worley(p: vec2f) -> vec2f {\nlet i = floor(p);\nlet f = fract(p);\nvar d1 = 8.0;\nvar d2 = 8.0;\nfor (var y = -1; y <= 1; y++) {\nfor (var x = -1; x <= 1; x++) {\nlet o = vec2f(f32(x), f32(y));\nlet c = i + o;\nlet j = vec2f(hash2(c), hash2(c + vec2f(1013.0, 7919.0)));\nlet d = length(o + j - f) / (0.55 + 0.9 * hash2(c + vec2f(4241.0, 557.0)));\nif (d < d1) {\nd2 = d1;\nd1 = d;\n} else if (d < d2) {\nd2 = d;\n}\n}\n}\nreturn vec2f(d1, d2);\n}\nfn foam_net(p: vec2f, amount: f32, fw: f32) -> vec2f {\nlet a = saturate(amount);\nlet q = p + vec2f(vnoise(p * 2.0), vnoise(p * 2.0 + vec2f(5.2, 1.3))) * 0.36;\nlet big = worley(q * 3.2);\nvar small = vec2f(0.5, 1.0);\nif (sp.size.w > 1.5) {\nsmall = worley(q * 10.0 + vec2f(3.7, 1.9));\n}\nlet r = mix(0.82, 0.12, pow(a, 1.1));\nlet eb = fw * 3.2;\nlet es = fw * 10.0;\nlet mean = saturate(1.0 - 2.6 * r * r);\nlet hb = mix(smoothstep(r - 0.05 - eb, r + 0.05 + eb, big.x), mean, smoothstep(0.15, 0.6, eb));\nlet rs = r * 0.8 + 0.1;\nlet hs = mix(smoothstep(rs - 0.06 - es, rs + 0.06 + es, small.x), saturate(1.0 - 2.6 * rs * rs), smoothstep(0.15, 0.6, es));\nlet net = hb * mix(1.0, hs, 0.55 * (1.0 - a));\nlet cover = net * smoothstep(0.0, 0.06, a);\nlet lumps = mix(0.8, 1.04, saturate(1.0 - small.x * 0.9)) * mix(0.9, 1.0, saturate(1.0 - big.x * 0.8));\nlet light = mix(lumps, 0.92, smoothstep(0.15, 0.6, es));\nreturn vec2f(cover, light);\n}\nstruct Train {\nth: f32,\nu: f32,\nnw: f32,\namp: f32,\nshoaled: f32,\nperiod: f32,\nblend: f32,\n}\nconst TRAINS = array<vec4f, 3>(vec4f(1.0, 1.0, 0.0, 0.0), vec4f(1.37, 0.45, 0.0, 0.31), vec4f(0.79, 0.3, 0.0, 0.67));\nfn wobble(n: f32, i: u32, world: vec2f) -> f32 {\nlet o = vec2f(n * 0.61 + f32(i) * 7.0, n * 0.37 + f32(i) * 3.0);\nreturn ((vnoise(world / 45.0 + o) - 0.5) * 1.2 + (vnoise(world / 14.0 + o * 1.7) - 0.5) * 0.35) * sp.surf3.y;\n}\nfn wave_amp(n: f32, world: vec2f) -> f32 {\nlet group = 1.0 + 0.3 * sin(2.0 * PI * n / 6.3 + 1.3) + 0.25 * (hash1(n) - 0.5);\nlet section = 0.6 + 0.8 * vnoise(world / 28.0 + vec2f(n * 7.13, n * 3.71));\nreturn mix(1.0, group * section, sp.surf3.y);\n}\nfn swash_front(u: f32, run: f32) -> f32 {\nlet rise = 0.18;\nlet up = 1.0 - (1.0 - saturate(u / rise)) * (1.0 - saturate(u / rise));\nlet down = pow(saturate(1.0 - (u - rise) / 0.57), 1.6);\nreturn run * select(down, up, u < rise);\n}\nfn swash_high(u: f32, run: f32, window: f32) -> f32 {\nlet rise = 0.18;\nlet u0 = u - window;\nif (u0 >= rise) {\nreturn swash_front(u0, run);\n}\nif (u >= rise) {\nreturn run;\n}\nreturn max(swash_front(u, run), select(0.0, swash_front(u0 + 1.0, run), u0 < 0.0));\n}\nfn swash_state(e: f32, front_h: f32, wet_h1: f32, wet_h2: f32, run: f32) -> vec4f {\nlet cover = saturate((front_h - e) / 0.03);\nlet r1 = saturate((wet_h1 - e) / 0.06 + 0.4);\nlet r2 = saturate((wet_h2 - e) / 0.12 + 0.4);\nlet damp = 0.3 * saturate(1.0 - e / max(run, 1e-3));\nlet wet = max(0.75 * (0.55 * r1 + 0.45 * r2), damp) * saturate((run - e) / 0.08 + 0.5);\nlet film = 0.02 + 0.04 * cover * saturate((front_h - e) / 0.15);\nlet front = cover * (1.0 - saturate((front_h - e) / 0.015)) * saturate(front_h * 6.0);\nreturn vec4f(cover, wet, film, front);\n}\nfn surf_at(rest: vec2f, time: f32) -> Surf {\nvar s: Surf;\ns.fade = 1.0;\ns.amp = 1.0;\nif (sp.surf0.w < 0.5) {\nreturn s;\n}\nlet uv = sp.surf0.xy + rest * sp.surf0.z;\nif (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0))) {\nreturn s;\n}\nlet f = textureSampleLevel(shore_map, clamp_smp, uv, 0.0);\nlet sp2 = textureSampleLevel(spill_map, clamp_smp, uv, 0.0);\nlet depth = f.x;\nlet period = sp.surf1.y;\ns.world = uv / sp.surf0.z;\ns.fade = smoothstep(0.2, sp.surf2.y, depth);\nlet hs = max(depth, 0.15);\nlet start = sp.surf2.z;\nlet shoal = pow(start / hs, 0.25) * smoothstep(start, start * 0.6, depth);\nlet dir = vec2f(cos(f.w), sin(f.w));\ns.dir = dir;\nvar tr: array<Train, 3>;\nlet trains = select(1u, 3u, sp.size.w > 0.5);\nfor (var i = 0u; i < trains; i++) {\nlet ti = TRAINS[i];\nlet pi = period * ti.x;\nvar th: f32;\nvar u: f32;\nvar nw: f32;\nif (i == 0u) {\nth = 2.0 * PI * fract(time / period) - atan2(f.z, f.y);\nu = fract(th / (2.0 * PI));\nnw = round((time - sp2.y) / period + 0.5 - fract(u + 0.5));\n} else {\nlet cyc = (time - sp2.y) / pi + ti.w;\nu = fract(cyc);\nth = 2.0 * PI * u;\nnw = round(cyc + 0.5 - fract(u + 0.5));\n}\nlet ff = fract(u + 0.5);\nlet blend = smoothstep(0.82, 1.0, ff);\nvar amp = wave_amp(nw + f32(i) * 101.0, s.world);\nvar wob = wobble(nw, i, s.world);\nif (blend > 0.0) {\namp = mix(amp, wave_amp(nw + 1.0 + f32(i) * 101.0, s.world), blend);\nwob = mix(wob, wobble(nw + 1.0, i, s.world), blend);\n}\nth -= wob;\ntr[i].th = th;\ntr[i].u = fract(th / (2.0 * PI));\ntr[i].nw = nw;\ntr[i].amp = amp;\ntr[i].shoaled = sp.surf1.x * ti.y * amp * shoal;\ntr[i].blend = blend;\ntr[i].period = pi;\n}\nlet gnoise = mix(vnoise(s.world / 35.0 + vec2f(tr[0].nw * 2.3, 0.0)), vnoise(s.world / 35.0 + vec2f((tr[0].nw + 1.0) * 2.3, 0.0)), tr[0].blend);\nlet gamma = sp.surf1.z * (0.85 + 0.3 * gnoise * sp.surf3.y + 0.15 * (1.0 - sp.surf3.y));\nlet hb = gamma * hs;\nvar eta = 0.0;\nvar lean = vec2f(0.0);\nvar slope = 0.0;\nvar face_k = 0.0;\nvar foam = 0.0;\ns.broken = 0.0;\ns.onset = 0.0;\nfor (var i = 0u; i < trains; i++) {\nvar others = 0.0;\nfor (var j = 0u; j < trains; j++) {\nif (j != i) {\nothers += 0.4 * tr[j].shoaled * (0.5 + 0.5 * cos(tr[j].th));\n}\n}\nlet ratio = (tr[i].shoaled + others) / max(hb, 1e-3);\nlet broken = saturate((ratio - 1.0) * 3.0);\nlet share = tr[i].shoaled / max(tr[i].shoaled + others, 1e-4);\nlet H = min(tr[i].shoaled, hb * share + tr[i].shoaled * (1.0 - broken)) * smoothstep(-0.05, 0.25, depth);\nlet th = tr[i].th;\nlet u = tr[i].u;\nlet wn = mix(vnoise(s.world / 22.0 + vec2f(tr[i].nw * 5.1, f32(i) * 9.0)), vnoise(s.world / 22.0 + vec2f((tr[i].nw + 1.0) * 5.1, f32(i) * 9.0)), tr[i].blend);\nlet w = 0.12 * mix(1.0, 0.6 + 0.8 * wn, sp.surf3.y);\nlet stokes = 0.5 * (cos(th) + 0.3 * cos(2.0 * th));\nlet dstokes = 0.5 * (-sin(th) - 0.6 * sin(2.0 * th));\nlet bore = select(1.0 - (u - w) / (1.0 - w), u / w, u < w) - 0.5;\nlet dw = min(0.6 * w, 1.2 / (tr[i].period * sqrt(9.81 * hs)));\nlet rise = saturate(0.5 + u / dw) * saturate(0.5 - (u - w) / dw) + saturate(0.5 + (u - 1.0) / dw);\nlet dbore = (-1.0 / (1.0 - w) + (1.0 / w + 1.0 / (1.0 - w)) * rise) / (2.0 * PI);\nvar e = H * mix(stokes, bore, broken);\nlet de = H * mix(dstokes, dbore, broken);\nlet q = sp.surf1.w * (1.0 - 0.6 * broken);\nvar l = dir * (q * H * 0.5 * sin(th));\nlet onset = broken * (1.0 - broken) * 4.0 * saturate((ratio - 0.9) * 10.0);\nlet top = smoothstep(0.1, 0.5, e / max(H, 1e-3));\nl += dir * (sp.surf3.z * H * 0.55 * onset * top);\ne *= 1.0 + 0.25 * onset * top;\nlet k = 2.0 * PI / (tr[i].period * sqrt(9.81 * hs));\neta += e;\nlean += l;\nslope += -k * de;\nface_k += smoothstep(-0.2, 0.45, e / max(H, 1e-3)) * saturate(H * 1.2) * (1.0 - 0.65 * broken + 0.5 * onset) * TRAINS[i].y;\nlet fo = broken * smoothstep(0.0, w, u) * (exp(-(u - w) * 9.0) + 0.15 * exp(-(u - w) * 3.0)) * saturate(H * 3.0) * TRAINS[i].y;\nfoam = 1.0 - (1.0 - foam) * (1.0 - fo);\ns.broken = max(s.broken, broken);\ns.onset = max(s.onset, onset * TRAINS[i].y);\n}\ns.slope = slope * dir;\ns.d = vec3f(lean.x, eta, lean.y);\ns.face = saturate(-slope * 1.5) * saturate(face_k);\ns.foam = foam;\nlet u = tr[0].u;\ns.phase = u;\ns.amp = tr[0].amp;\ns.ground = -depth;\ns.reach = max(sp2.x, s.ground);\nif (s.ground > -0.05) {\nlet e = s.reach;\nlet rn = mix(vnoise(s.world / 9.0 + vec2f(tr[0].nw * 3.3, 1.0)), vnoise(s.world / 9.0 + vec2f((tr[0].nw + 1.0) * 3.3, 1.0)), tr[0].blend);\nlet run = sp.surf1.x * s.amp * 0.8 * mix(1.0, 0.7 + 0.6 * rn, sp.surf3.y);\ns.run = run;\ns.front_h = swash_front(u, run);\ns.wet_h1 = swash_high(u, run, 1.5 / sp.surf1.y);\ns.wet_h2 = swash_high(u, run, 4.0 / sp.surf1.y);\nlet w = swash_state(e, s.front_h, s.wet_h1, s.wet_h2, run);\ns.cover = w.x;\ns.wet = w.y;\ns.film = w.z;\ns.front = w.w;\n}\nreturn s;\n}\nfn on_beach(d: vec3f, s: Surf, spacing: f32) -> vec3f {\nif (s.ground <= 0.0) {\nreturn d;\n}\nlet reach = sp.surf1.x * 0.8 * 2.0 + 0.05;\nlet y = select(s.ground - 1.0, s.ground + s.film + 0.03 + 0.03 * spacing, s.reach < reach);\nlet k = smoothstep(0.0, 0.04, s.ground);\nreturn vec3f(d.x * (1.0 - k), mix(d.y, y, k), d.z * (1.0 - k));\n}\n@group(0) @binding(17) var surf_a: texture_2d<f32>;\n@group(0) @binding(18) var surf_b: texture_2d<f32>;\n@group(0) @binding(19) var surf_c: texture_2d<f32>;\n@group(0) @binding(20) var surf_d: texture_2d<f32>;\n@group(0) @binding(21) var surf_a_prev: texture_2d<f32>;\n@group(0) @binding(22) var surf_d_prev: texture_2d<f32>;\n@group(0) @binding(23) var out_a: texture_storage_2d<rgba16float, write>;\n@group(0) @binding(24) var out_b: texture_storage_2d<rgba16float, write>;\n@group(0) @binding(25) var out_c: texture_storage_2d<rgba16float, write>;\n@group(0) @binding(26) var out_d: texture_storage_2d<rgba16float, write>;\n@compute @workgroup_size(8, 8) fn surf_field(@builtin(global_invocation_id) id: vec3u) {\nlet n = textureDimensions(out_a);\nif (any(id.xy >= n)) {\nreturn;\n}\nlet uv = (vec2f(id.xy) + 0.5) / vec2f(n);\nlet rest = (uv - sp.surf0.xy) / sp.surf0.z;\nlet s = surf_at(rest, sp.surf3.x);\ntextureStore(out_a, vec2i(id.xy), vec4f(s.d, s.broken));\ntextureStore(out_b, vec2i(id.xy), vec4f(s.slope, s.face, s.onset));\ntextureStore(out_c, vec2i(id.xy), vec4f(s.foam, s.front_h, s.wet_h1, s.wet_h2));\nlet dt = clamp(sp.surf3.x - sp.surf3.w, 0.0, 0.1);\nlet v = surf_flow(s);\nlet extent = 1.0 / sp.surf0.z;\nlet back = uv - v * dt / extent;\nlet texel = 1.0 / vec2f(n);\nlet f0 = textureSampleLevel(surf_d_prev, clamp_smp, back, 0.0).w;\nlet fs = textureSampleLevel(surf_d_prev, clamp_smp, back + vec2f(texel.x, 0.0), 0.0).w + textureSampleLevel(surf_d_prev, clamp_smp, back - vec2f(texel.x, 0.0), 0.0).w + textureSampleLevel(surf_d_prev, clamp_smp, back + vec2f(0.0, texel.y), 0.0).w + textureSampleLevel(surf_d_prev, clamp_smp, back - vec2f(0.0, texel.y), 0.0).w;\nlet carried = mix(f0, fs * 0.25, 0.12 * saturate(dt * 60.0));\nlet life = mix(3.0, 1.2, saturate(carried));\nvar f = carried * exp(-dt / life) * select(1.0, 0.0, s.ground > 0.0 && s.cover < 0.01 && s.wet < 0.05) * select(0.0, 1.0, dt > 0.0);\nf = max(f, saturate(s.foam * 0.9 + s.front * 0.8));\ntextureStore(out_d, vec2i(id.xy), vec4f(s.run, s.phase, s.amp, f));\n}\nfn smooth_sample(t: texture_2d<f32>, uv: vec2f) -> vec4f {\nlet size = vec2f(textureDimensions(t));\nlet p = uv * size - 0.5;\nlet i = floor(p);\nlet f = p - i;\nlet w0 = (1.0 - f) * (1.0 - f) * (1.0 - f) / 6.0;\nlet w1 = (4.0 - 6.0 * f * f + 3.0 * f * f * f) / 6.0;\nlet w2 = (1.0 + 3.0 * f + 3.0 * f * f - 3.0 * f * f * f) / 6.0;\nlet w3 = f * f * f / 6.0;\nlet g0 = w0 + w1;\nlet g1 = w2 + w3;\nlet h0 = (i - 0.5 + w1 / g0) / size;\nlet h1 = (i + 1.5 + w3 / g1) / size;\nlet a = textureSampleLevel(t, clamp_smp, vec2f(h0.x, h0.y), 0.0);\nlet b = textureSampleLevel(t, clamp_smp, vec2f(h1.x, h0.y), 0.0);\nlet c = textureSampleLevel(t, clamp_smp, vec2f(h0.x, h1.y), 0.0);\nlet d = textureSampleLevel(t, clamp_smp, vec2f(h1.x, h1.y), 0.0);\nreturn mix(mix(a, b, g1.x), mix(c, d, g1.x), g1.y);\n}\nfn surf_tex(rest: vec2f, prev: bool) -> Surf {\nvar s: Surf;\ns.fade = 1.0;\ns.amp = 1.0;\nif (sp.surf0.w < 0.5) {\nreturn s;\n}\nlet uv = sp.surf0.xy + rest * sp.surf0.z;\nif (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0))) {\nreturn s;\n}\nlet f = textureSampleLevel(shore_map, clamp_smp, uv, 0.0);\nlet depth = f.x;\ns.fade = smoothstep(0.2, sp.surf2.y, depth);\ns.dir = vec2f(cos(f.w), sin(f.w));\ns.world = uv / sp.surf0.z;\ns.ground = -depth;\ns.reach = max(textureSampleLevel(spill_map, clamp_smp, uv, 0.0).x, s.ground);\nlet a = select(textureSampleLevel(surf_a, clamp_smp, uv, 0.0), textureSampleLevel(surf_a_prev, clamp_smp, uv, 0.0), prev);\ns.d = a.xyz;\ns.broken = a.w;\nlet b = textureSampleLevel(surf_b, clamp_smp, uv, 0.0);\ns.slope = b.xy;\ns.face = b.z;\ns.onset = b.w;\nlet c = textureSampleLevel(surf_c, clamp_smp, uv, 0.0);\nlet dd = textureSampleLevel(surf_d, clamp_smp, uv, 0.0);\nif (s.ground > -0.05) {\nlet e = max(smooth_sample(spill_map, uv).x, -smooth_sample(shore_map, uv).x);\nlet w = swash_state(e, c.y, c.z, c.w, dd.x);\ns.cover = w.x;\ns.wet = w.y;\ns.film = w.z;\ns.front = w.w;\nlet band = saturate(1.0 - abs(c.w - 0.008 - e) / 0.012);\nlet age = saturate(1.0 - (c.w - c.z) / max(dd.x * 0.6, 0.02));\ns.mark = band * (1.0 - w.x) * mix(0.35, 1.0, age) * saturate(c.w * 8.0);\n}\ns.front_h = c.y;\ns.run = dd.x;\ns.phase = dd.y;\ns.amp = dd.z;\ns.fresh = c.x;\ns.foam = max(c.x, dd.w);\nreturn s;\n}\nfn surf_disp(rest: vec2f, spacing: f32, prev: bool) -> vec3f {\nif (sp.surf0.w < 0.5) {\nreturn vec3f(0.0);\n}\nlet uv = sp.surf0.xy + rest * sp.surf0.z;\nif (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0))) {\nreturn vec3f(0.0);\n}\nlet r = 0.35 * spacing * sp.surf0.z;\nvar d = vec3f(0.0);\nfor (var k = 0u; k < 5u; k++) {\nlet o = array<vec2f, 5>(vec2f(0.0), vec2f(r, 0.0), vec2f(-r, 0.0), vec2f(0.0, r), vec2f(0.0, -r))[k];\nlet w = select(0.125, 0.5, k == 0u);\nlet t = select(textureSampleLevel(surf_a, clamp_smp, uv + o, 0.0), textureSampleLevel(surf_a_prev, clamp_smp, uv + o, 0.0), prev);\nd += t.xyz * w;\n}\nreturn d;\n}\nfn surf_flow(s: Surf) -> vec2f {\nlet hs = max(-s.ground, 0.15);\nif (s.ground > -0.05) {\nreturn s.dir * select(-0.9, 1.6, s.phase < 0.18) * s.cover;\n}\nreturn s.dir * clamp(sqrt(9.81 * hs) * max(s.d.y, 0.0) / hs, 0.0, 3.0);\n}\nconst SPRAY_SIDE = 192u;\nconst SPRAY_SPACING = 0.75;\nstruct SprayOut {\n@builtin(position) clip: vec4f,\n@location(0) q: vec4f,\n@location(1) p: vec3f,\n}\n@vertex fn vs_spray(@builtin(vertex_index) vi: u32, @builtin(instance_index) ii: u32) -> SprayOut {\nvar o: SprayOut;\no.clip = vec4f(0.0, 0.0, -1.0, 1.0);\nif (sp.surf0.w < 0.5 || sp.size.y <= 0.0) {\nreturn o;\n}\nlet extent = 1.0 / sp.surf0.z;\nlet eye = sp.surf0.xy * extent;\nlet gx = f32(ii % SPRAY_SIDE) - f32(SPRAY_SIDE / 2u);\nlet gz = f32(ii / SPRAY_SIDE) - f32(SPRAY_SIDE / 2u);\nlet cell = floor(eye / SPRAY_SPACING) + vec2f(gx, gz);\nlet j = vec2f(hash2(cell), hash2(cell + vec2f(91.0, 37.0)));\nlet rest = (cell + j) * SPRAY_SPACING - eye;\nvar s = surf_tex(rest, false);\nlet tuv = sp.surf0.xy + rest * sp.surf0.z;\ns.phase = textureLoad(surf_d, vec2i(clamp(tuv, vec2f(0.0), vec2f(0.9999)) * vec2f(textureDimensions(surf_d))), 0).y;\nlet age = (s.phase - 0.12) * sp.surf1.y;\nlet life = 0.6 + 0.6 * hash2(cell + vec2f(13.0, 71.0));\nif (s.broken < 0.05 || age < 0.0 || age > life || s.ground > 0.3) {\nreturn o;\n}\nlet H = sp.surf1.x * s.amp;\nlet strength = (0.05 * s.broken + s.onset) * saturate(H * 1.2) * (0.4 + 1.2 * hash2(cell + vec2f(5.0, 3.0)));\nlet v0 = (2.0 + 3.0 * hash2(cell + vec2f(2.0, 9.0))) * saturate(H);\nlet k = 1.8;\nlet rise = v0 / k * (1.0 - exp(-k * age)) - 4.0 * age * age * 0.5;\nlet fwd = s.dir * (1.2 * (1.0 - exp(-1.5 * age)));\nlet base = s.d.y + 0.15;\nlet centre = vec3f(rest.x + fwd.x, sp.sea.x + base + max(rise, 0.0), rest.y + fwd.y);\nlet t = age / life;\nlet size = (0.12 + 0.5 * t) * (0.7 + 0.6 * hash2(cell + vec2f(17.0, 23.0))) * (0.6 + 0.6 * saturate(H));\nlet corner = array<vec2f, 6>(vec2f(-1.0, -1.0), vec2f(1.0, -1.0), vec2f(1.0, 1.0), vec2f(-1.0, -1.0), vec2f(1.0, 1.0), vec2f(-1.0, 1.0))[vi];\nlet fwdv = normalize(centre);\nlet right = normalize(cross(vec3f(0.0, 1.0, 0.0), fwdv));\nlet up = cross(fwdv, right);\nlet pos = centre + (right * corner.x + up * corner.y) * size;\no.clip = camera.view_proj * vec4f(pos, 1.0);\nlet alpha = strength * smoothstep(0.0, 0.1, t) * (1.0 - smoothstep(0.4, 1.0, t)) * sp.size.y * 0.32;\no.q = vec4f(corner, alpha, length(centre));\no.p = centre;\nreturn o;\n}\nfn spray_alpha(in: SprayOut) -> f32 {\nlet r2 = dot(in.q.xy, in.q.xy);\nif (r2 > 1.0) {\nreturn 0.0;\n}\nlet wisp = vnoise(in.q.xy * 1.8 + in.p.xz * 3.1) * 0.6 + vnoise(in.q.xy * 4.3 + in.p.xz * 1.7) * 0.4;\nreturn in.q.z * (1.0 - r2) * (1.0 - r2) * smoothstep(0.38, 0.62, wisp) * 1.6;\n}\n@fragment fn fs_spray_reactive(in: SprayOut) -> @location(0) vec4f {\nlet a = spray_alpha(in);\nif (a <= 0.002) {\ndiscard;\n}\nreturn vec4f(saturate(a * 2.0) * wm.reactive.x);\n}\n@fragment fn fs_spray(in: SprayOut) -> @location(0) vec4f {\nvar a = spray_alpha(in);\nif (a <= 0.0) {\ndiscard;\n}\nlet d = textureLoad(under_depth, vec2i(in.clip.xy), 0).x;\nif (d > 0.0) {\nlet gap = in.q.w * (in.clip.z / d - 1.0);\na *= saturate(gap / 0.5);\n}\nlet v = normalize(in.p);\nlet l = camera.light_dir.xyz;\nlet g = 0.75;\nlet cosv = dot(v, l);\nlet hg = (1.0 - g * g) / (4.0 * PI * pow(1.0 + g * g - 2.0 * g * cosv, 1.5));\nlet light = sh_irradiance(vec3f(0.0, 1.0, 0.0)) * 0.6 + lighting.sun_color.rgb * cloud_light(in.p, l) * (hg * 3.0 + 0.05);\nlet fogged = apply_fog(light, in.p, lighting.fog, lighting.fog2, lighting.fog_air.rgb, sh_irradiance(v), lighting.sun_color.rgb, l);\nreturn vec4f(fogged * lighting.env.z * a, a);\n}\nstruct VsOut {\n@builtin(position) clip: vec4f,\n@location(0) p: vec3f,\n@location(1) rest: vec2f,\n@location(2) height: f32,\n}\nfn ring_radius(i: f32) -> f32 {\nlet k = sp.grid.y;\nreturn sp.grid.x * (pow(1.0 + k, i) - 1.0) / k;\n}\nfn grid_point(vi: u32) -> vec3f {\nlet segs = u32(sp.grid.w);\nlet ring = f32(vi / (segs + 1u));\nlet seg = f32(vi % (segs + 1u));\nlet r = ring_radius(ring);\nlet a = 2.0 * PI * seg / f32(segs);\nreturn vec3f(vec2f(cos(a), sin(a)) * r, max(ring_radius(ring + 1.0) - r, 2.0 * PI * r / f32(segs)));\n}\nfn calm(c: u32, fade: f32) -> f32 {\nreturn mix(1.0, fade, saturate(1.0 / (sp.cascade[c].z * 150.0)));\n}\nfn displaced(field: texture_2d_array<f32>, rest: vec2f, spacing: f32, fade: f32) -> vec3f {\nvar d = vec3f(0.0);\nfor (var c = 0u; c < u32(sp.sea.y); c++) {\nlet cc = sp.cascade[c];\nlet texel = 1.0 / (cc.z * sp.size.x);\nlet w = (1.0 - smoothstep(1.0, 4.0, spacing / texel)) * calm(c, fade);\nif (w <= 0.0) {\ncontinue;\n}\nd += textureSampleLevel(field, wrap, cc.xy + rest * cc.z, c, 0.0).xyz * w;\n}\nreturn d;\n}\nstruct MotionOut {\n@builtin(position) clip: vec4f,\n@location(0) now: vec4f,\n@location(1) then: vec4f,\n@location(2) rest: vec2f,\n}\n@vertex fn vs_motion(@builtin(vertex_index) vi: u32) -> MotionOut {\nlet g = grid_point(vi);\nlet rest = g.xy;\nlet drop = dot(rest, rest) / (2.0 * EARTH_RADIUS);\nlet sn = surf_tex(rest, false);\nlet sq = surf_tex(rest, wm.eye_move.w > 0.5);\nlet dn = on_beach(displaced(displacement, rest, g.z, sn.fade) + surf_disp(rest, g.z, false), sn, g.z);\nlet dp = select(dn, on_beach(displaced(prev_displacement, rest, g.z, sq.fade) + surf_disp(rest, g.z, true), sq, g.z), wm.eye_move.w > 0.5);\nlet p = vec3f(rest.x + dn.x, sp.sea.x + dn.y - drop, rest.y + dn.z);\nlet q = vec3f(rest.x + dp.x, sp.sea.x + dp.y - drop, rest.y + dp.z) + wm.eye_move.xyz;\nvar o: MotionOut;\no.now = wm.view_proj * vec4f(p, 1.0);\no.clip = camera.view_proj * vec4f(p, 1.0);\no.then = wm.prev_view_proj * vec4f(q, 1.0);\no.rest = rest;\nreturn o;\n}\nfn motion_of(in: MotionOut) -> vec4f {\nlet now = in.now.xy / in.now.w;\nlet then = in.then.xy / max(in.then.w, 1e-6);\nreturn vec4f((now - then) * vec2f(0.5, -0.5), in.then.w, 1.0);\n}\n@fragment fn fs_motion(in: MotionOut) -> @location(0) vec4f {\nreturn motion_of(in);\n}\nstruct MotionReactive {\n@location(0) motion: vec4f,\n@location(1) reactive: vec4f,\n}\n@fragment fn fs_motion_reactive(in: MotionOut) -> MotionReactive {\nvar o: MotionReactive;\no.motion = motion_of(in);\nlet s = surf_tex(in.rest, false);\no.reactive = vec4f(saturate(s.fresh * 0.9 + s.front * 0.7 + s.onset * 0.6) * wm.reactive.x);\nreturn o;\n}\n@vertex fn vs(@builtin(vertex_index) vi: u32) -> VsOut {\nlet segs = u32(sp.grid.w);\nlet ring = f32(vi / (segs + 1u));\nlet seg = f32(vi % (segs + 1u));\nlet r = ring_radius(ring);\nlet a = 2.0 * PI * seg / f32(segs);\nlet rest = vec2f(cos(a), sin(a)) * r;\nlet spacing = max(ring_radius(ring + 1.0) - r, 2.0 * PI * r / f32(segs));\nlet sf = surf_tex(rest, false);\nvar d = vec3f(0.0);\nfor (var c = 0u; c < u32(sp.sea.y); c++) {\nlet cc = sp.cascade[c];\nlet texel = 1.0 / (cc.z * sp.size.x);\nlet w = (1.0 - smoothstep(1.0, 4.0, spacing / texel)) * calm(c, sf.fade);\nif (w <= 0.0) {\ncontinue;\n}\nd += textureSampleLevel(displacement, wrap, cc.xy + rest * cc.z, c, 0.0).xyz * w;\n}\nd = on_beach(d + surf_disp(rest, spacing, false), sf, spacing);\nlet drop = dot(rest, rest) / (2.0 * EARTH_RADIUS);\nlet p = vec3f(rest.x + d.x, sp.sea.x + d.y - drop, rest.y + d.z);\nvar o: VsOut;\no.clip = camera.view_proj * vec4f(p, 1.0);\no.p = p;\no.rest = rest;\no.height = d.y;\nreturn o;\n}\nfn sh_irradiance(n: vec3f) -> vec3f {\nreturn max(lighting.sh[0].rgb * 0.282095 + lighting.sh[1].rgb * (0.488603 * n.y) + lighting.sh[2].rgb * (0.488603 * n.z) + lighting.sh[3].rgb * (0.488603 * n.x) + lighting.sh[4].rgb * (1.092548 * n.x * n.y) + lighting.sh[5].rgb * (1.092548 * n.y * n.z) + lighting.sh[6].rgb * (0.315392 * (3.0 * n.z * n.z - 1.0)) + lighting.sh[7].rgb * (1.092548 * n.x * n.z) + lighting.sh[8].rgb * (0.546274 * (n.x * n.x - n.y * n.y)), vec3f(0.0));\n}\n@compute @workgroup_size(8, 8) fn copy_depth(@builtin(global_invocation_id) id: vec3u) {\nlet size = textureDimensions(scene_depth);\nif (id.x >= size.x || id.y >= size.y) {\nreturn;\n}\ntextureStore(depth_out, vec2i(id.xy), vec4f(textureLoad(scene_depth, vec2i(id.xy), 0), 0.0, 0.0, 0.0));\n}\nfn thickness(dist: f32, ds: f32, dop: f32) -> f32 {\nreturn select(1.0e9, dist * max(ds / dop - 1.0, 0.0), dop > 0.0);\n}\nfn mirror_probe(p: vec3f, r: vec3f, t: f32, size: vec2f) -> vec4f {\nlet c = camera.view_proj * vec4f(p + r * t, 1.0);\nif (c.w <= 0.0) {\nreturn vec4f(0.0);\n}\nlet ndc = c.xyz / c.w;\nlet uv = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);\nif (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0))) {\nreturn vec4f(0.0);\n}\nlet d = textureLoad(under_depth, vec2i(uv * size), 0).x;\nlet gap = select(-1.0e9, c.w * (1.0 - ndc.z / d), d > 0.0);\nreturn vec4f(uv, gap, 1.0);\n}\nfn mirror_scene(p: vec3f, r: vec3f, size: vec2f, jitter: f32) -> vec4f {\nlet len = sp.mirror.x;\nlet steps = u32(sp.mirror.y);\nif (len <= 0.0 || steps == 0u || sp.shore.y < 0.5 || r.y <= 0.0) {\nreturn vec4f(0.0);\n}\nvar prev = 0.0;\nvar was_behind = false;\nfor (var i = 1u; i <= steps; i++) {\nlet f = (f32(i) - jitter) / f32(steps);\nlet t = len * f * f;\nlet s = mirror_probe(p, r, t, size);\nif (s.w < 0.5) {\nbreak;\n}\nlet behind = s.z > 0.0;\nif (behind && !was_behind) {\nvar a = prev;\nvar b = t;\nvar hit = s;\nfor (var k = 0u; k < 6u; k++) {\nlet m = 0.5 * (a + b);\nlet sm = mirror_probe(p, r, m, size);\nif (sm.w > 0.5 && sm.z > 0.0) {\nb = m;\nhit = sm;\n} else {\na = m;\n}\n}\nif (hit.z < sp.mirror.z + 0.03 * b) {\nlet uv = hit.xy;\nlet edge = min(min(uv.x, 1.0 - uv.x), min(uv.y, 1.0 - uv.y));\nlet fade = saturate(edge * 12.0) * (1.0 - smoothstep(0.7, 1.0, b / len));\nreturn vec4f(textureSampleLevel(under, clamp_smp, uv, 0.0).rgb * sp.shore.z, fade);\n}\n}\nwas_behind = behind;\nprev = t;\n}\nreturn vec4f(0.0);\n}\nstruct WetParams { inv_view_proj: mat4x4f }\n@group(0) @binding(28) var<uniform> wp: WetParams;\nstruct WetOut { @builtin(position) clip: vec4f }\n@vertex fn vs_wet(@builtin(vertex_index) i: u32) -> WetOut {\nlet p = vec2f(f32((i << 1u) & 2u), f32(i & 2u)) * 2.0 - 1.0;\nvar o: WetOut;\no.clip = vec4f(p, 0.0, 1.0);\nreturn o;\n}\n@fragment fn fs_wet(in: WetOut) -> @location(0) vec4f {\nlet px = vec2i(in.clip.xy);\nlet d = textureLoad(scene_depth, px, 0);\nif (d <= 0.0 || sp.surf0.w < 0.5) {\ndiscard;\n}\nlet size = vec2f(textureDimensions(scene_depth));\nlet ndc = vec2f(in.clip.x / size.x * 2.0 - 1.0, 1.0 - in.clip.y / size.y * 2.0);\nlet w4 = wp.inv_view_proj * vec4f(ndc, d, 1.0);\nlet p = w4.xyz / w4.w;\nlet uv = sp.surf0.xy + p.xz * sp.surf0.z;\nif (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0))) {\ndiscard;\n}\nlet h = p.y - sp.sea.x;\nlet ground = -smooth_sample(shore_map, uv).x;\nif (h < ground + 0.05) {\ndiscard;\n}\nlet c = textureSampleLevel(surf_c, clamp_smp, uv, 0.0);\nlet dd = textureSampleLevel(surf_d, clamp_smp, uv, 0.0);\nlet wash = select(sp.surf1.x * 0.6 * dd.z, 0.0, ground > 0.0);\nlet world = uv / sp.surf0.z;\nlet ragged = (vnoise(world * 3.0) - 0.5) * 0.12 + (vnoise(vec2f(world.x * 9.0, world.y * 9.0) + vec2f(5.0, 1.0)) - 0.5) * 0.05;\nlet top = max(c.w, wash) + ragged;\nlet drip = smoothstep(0.55, 0.9, vnoise(vec2f(world.x * 14.0 + world.y * 14.0, 0.5))) * 0.08;\nlet wet = (1.0 - smoothstep(top - 0.02, top + 0.04 + drip, h)) * saturate(top * 6.0);\nif (wet <= 0.001) {\ndiscard;\n}\nreturn vec4f(vec3f(mix(1.0, 0.58, wet)), 1.0);\n}\nfn ign(px: vec2f) -> f32 {\nlet frame = floor(camera.light_dir.w * 60.0) % 64.0;\nlet q = px + 5.588238 * frame;\nreturn fract(52.9829189 * fract(dot(q, vec2f(0.06711056, 0.00583715))));\n}\nfn glint_hash(c: vec2i, lod: i32, bucket: i32, s: u32) -> f32 {\nlet h = pcg(bitcast<u32>(c.x) ^ pcg(bitcast<u32>(c.y) ^ pcg(bitcast<u32>(lod * 7919 + bucket) + s * 0x9e3779b9u)));\nreturn f32(h) / 4294967296.0;\n}\nfn poisson(mu: f32, u: f32, g: vec3f) -> f32 {\nif (mu > 12.0) {\nreturn max(0.0, round(mu + sqrt(mu) * (g.x + g.y + g.z - 1.5) * 2.0));\n}\nvar k = 0.0;\nvar pk = exp(-mu);\nvar cdf = pk;\nfor (var i = 0u; i < 32u; i++) {\nif (u <= cdf) {\nbreak;\n}\nk += 1.0;\npk *= mu / k;\ncdf += pk;\n}\nreturn k;\n}\nfn glint_level(w: vec2f, size: vec2f, lod: i32, mu: f32) -> f32 {\nlet c = vec2i(floor(w / size));\nlet t = sp.surf3.x / sp.glint.w;\nvar sum = 0.0;\nfor (var j = 0; j < 2; j++) {\nlet phase = t + 0.5 * f32(j);\nlet bucket = i32(floor(phase)) * 2 + j;\nlet wt = 1.0 - abs(2.0 * fract(phase) - 1.0);\nlet k = poisson(mu, glint_hash(c, lod, bucket, 0u), vec3f(glint_hash(c, lod, bucket, 1u), glint_hash(c, lod, bucket, 2u), glint_hash(c, lod, bucket, 3u)));\nsum += wt * k / max(mu, 1e-12);\n}\nreturn sum;\n}\nconst GLINT_ANGLE = 1.0e-5;\nfn glint_ratio(w: vec2f, ext: vec2f, p: f32) -> f32 {\nif (sp.glint.x <= 0.0 || p <= 0.0) {\nreturn 1.0;\n}\nlet facet = sp.glint.y;\nlet r = max(w.y, 1e-3);\nlet facets = ext.x * ext.y / (facet * facet);\nlet level = vec2f(log2(max(ext.x / (r * GLINT_ANGLE), 1.0)), log2(max(ext.y / facet, 1.0)));\nlet l0 = floor(level);\nlet f = level - l0;\nlet s0 = vec2f(GLINT_ANGLE, facet) * exp2(l0);\nlet mu = s0.x * r * s0.y / (facet * facet) * p;\nlet id = i32(l0.x) * 64 + i32(l0.y);\nlet r0 = mix(glint_level(w, s0, id, mu), glint_level(w, s0 * vec2f(2.0, 1.0), id + 64, mu * 2.0), f.x);\nlet r1 = mix(glint_level(w, s0 * vec2f(1.0, 2.0), id + 1, mu * 2.0), glint_level(w, s0 * 2.0, id + 65, mu * 4.0), f.x);\nreturn mix(1.0, mix(r0, r1, f.y), sp.glint.x * smoothstep(1.0, 16.0, facets));\n}\n@fragment fn fs(in: VsOut) -> @location(0) vec4f {\nlet frag = in.clip;\nvar sx = 0.0;\nvar sz = 0.0;\nvar lost = 0.0;\nvar fold = 0.0;\nlet sf = surf_tex(in.rest, false);\nfor (var c = 0u; c < u32(sp.sea.y); c++) {\nlet cc = sp.cascade[c];\nlet uv = cc.xy + in.rest * cc.z;\nlet texels = length(fwidth(uv)) * sp.size.x;\nlet resolved = 1.0 - smoothstep(0.5, 2.0, texels);\nlet w = resolved * calm(c, sf.fade);\nlet s = textureSampleLevel(slopes, wrap, uv, c, 0.0);\nsx += s.x * w;\nsz += s.y * w;\nlost += cc.w * (1.0 - resolved) * calm(c, sf.fade);\nfold = max(fold, max(saturate((sp.sea.z - s.z) * 2.0), s.w) * w);\n}\nlet fine = u32(sp.sea.y) - 1u;\nfor (var k = 0u; k < 2u; k++) {\nlet scale = select(8.3, 3.1, k == 0u);\nlet amp = select(0.5, 0.7, k == 0u) * sp.shore.w;\nlet ang = select(1.24, 0.65, k == 0u);\nlet rot = mat2x2f(cos(ang), sin(ang), -sin(ang), cos(ang));\nlet q = rot * (sp.cascade[fine].xy + in.rest * sp.cascade[fine].z) * scale + vec2f(0.37, 0.71) * f32(k + 1u);\nlet resolved = 1.0 - smoothstep(0.5, 2.0, length(fwidth(q)) * sp.size.x);\nlet t = textureSampleLevel(slopes, wrap, q, fine, 0.0).xy * amp;\nlet back = transpose(rot) * t;\nsx += back.x * resolved;\nsz += back.y * resolved;\nlost += sp.cascade[fine].w * amp * amp * (1.0 - resolved);\n}\nsx += sf.slope.x;\nsz += sf.slope.y;\nfold *= sf.fade;\nlet n = normalize(vec3f(-sx, 1.0, -sz));\nlet v = normalize(-in.p);\nlet l = camera.light_dir.xyz;\nlet nv = max(dot(n, v), 1e-3);\nlet nl = max(dot(n, l), 0.0);\nlet dnx = dpdx(n);\nlet dny = dpdy(n);\nlet kernel = min(0.25 * (dot(dnx, dnx) + dot(dny, dny)), 0.18);\nlet a2 = max(sp.body.w * sp.body.w + 2.0 * lost + kernel, 1e-4);\nlet rdx = dpdx(in.rest);\nlet rdy = dpdy(in.rest);\nlet gr = length(in.rest);\nlet ahead = select(vec2f(0.0, 1.0), in.rest / gr, gr > 1e-4);\nlet across = vec2f(-ahead.y, ahead.x);\nlet gpos = vec2f(atan2(in.rest.y, in.rest.x), gr);\nlet gext = vec2f(length(vec2f(dot(rdx, across), dot(rdy, across))), length(vec2f(dot(rdx, ahead), dot(rdy, ahead))));\nlet f0 = 0.02;\nlet fresnel = f0 + (1.0 - f0) * pow(1.0 - nv, 5.0);\nvar r = reflect(-v, n);\nr.y = abs(r.y);\nlet lod = clamp(sqrt(sqrt(a2)) * (lighting.env.x - 1.0), 0.0, lighting.env.x - 1.0);\nvar sky = textureSampleLevel(env_cube, wrap, r, lod).rgb * lighting.view.rgb;\nif (sp.size.z > 0.5) {\nlet pn = vec2f(textureDimensions(cloud_pano));\nlet pv = clamp(sqrt(max(r.y, 0.0)), 0.5 / pn.y, 1.0 - 0.5 / pn.y);\nlet cl = textureSampleLevel(cloud_pano, wrap, vec2f(fract(atan2(r.z, r.x) / (2.0 * PI)), pv), 0.0);\nsky = sky * cl.a + cl.rgb;\n}\nlet seen = mirror_scene(in.p, reflect(-v, n), vec2f(textureDimensions(under)), ign(frag.xy));\nsky = mix(sky, seen.rgb, seen.a);\nif (sp.mirror.w > 0.5) {\nreturn vec4f(select(vec3f(seen.a * 0.2, 0.0, 0.0) + seen.rgb * seen.a, vec3f(0.3, 0.0, 0.0), seen.a <= 0.0) * lighting.env.z, 1.0);\n}\nlet h = normalize(v + l);\nlet nh = max(dot(n, h), 0.0);\nlet dd = nh * nh * (a2 - 1.0) + 1.0;\nlet ggx = a2 / (PI * dd * dd);\nlet vis = 0.5 / (nl * sqrt(nv * nv * (1.0 - a2) + a2) + nv * sqrt(nl * nl * (1.0 - a2) + a2) + 1e-5);\nlet fh = f0 + (1.0 - f0) * pow(1.0 - max(dot(v, h), 0.0), 5.0);\nlet shade = cloud_light(in.p, l);\nlet sun = lighting.sun_color.rgb * shade;\nlet vh = max(dot(v, h), 1e-3);\nlet p_facet = min(ggx * nh * sp.glint.z / (4.0 * vh), 1.0);\nlet spec = sun * (ggx * vis * fh * nl * glint_ratio(gpos, gext, p_facet));\nlet up = sh_irradiance(vec3f(0.0, 1.0, 0.0));\nlet body = sp.body.rgb * (up + sun * (max(l.y, 0.0) / PI) * 0.5);\nlet behind = pow(saturate(dot(-v, l)), 8.0);\nlet crest = saturate(in.height * 0.5) * saturate(1.0 - n.y * 0.9 + 0.2);\nvar sss = sp.sss.rgb * sun * (behind * crest * sp.sss.w);\nlet wide = 0.35 + 0.65 * pow(saturate(dot(-v, l) * 0.5 + 0.5), 3.0);\nlet thin = sf.face * (0.6 + 0.4 * saturate(1.0 - abs(n.y) * 1.2));\nsss += sp.sss.rgb * (up * 4.5 + sun * (0.8 * wide)) * (thin * sp.sss.w * 2.5);\nvar below = body;\nvar shore_foam = 0.0;\nif (sp.shore.y > 0.5) {\nlet px = vec2i(frag.xy);\nlet size = vec2f(textureDimensions(under));\nlet dist = length(in.p);\nlet t0 = thickness(dist, frag.z, textureLoad(under_depth, px, 0).x);\nlet bend = n.xz * sp.optics.w * min(t0, 1.0);\nvar uv = (frag.xy + bend) / size;\nif (any(uv < vec2f(0.0)) || any(uv >= vec2f(1.0))) {\nuv = frag.xy / size;\n}\nvar t = thickness(dist, frag.z, textureLoad(under_depth, vec2i(uv * size), 0).x);\nif (textureLoad(under_depth, vec2i(uv * size), 0).x > frag.z) {\nuv = frag.xy / size;\nt = t0;\n}\nvar behind_light = textureSampleLevel(under, clamp_smp, uv, 0.0).rgb * sp.shore.z;\nlet down = max(t * v.y, 0.0);\nlet sun_in = sqrt(max(1.0 - (1.0 - l.y * l.y) / 1.7689, 0.04));\nlet above = (in.p - v * t).xz + vec2f(l.x, l.z) / sun_in * down * (1.0 / 1.33);\nvar lap = 0.0;\nlet cn = u32(sp.sea.y);\nlet c0 = select(0u, cn - 2u, cn >= 2u);\nfor (var c = c0; c < select(c0, select(1u, cn - 1u, cn >= 2u), sp.size.w > 0.5); c++) {\nlet cc = sp.cascade[c];\nlet e = 2.0 / (cc.z * sp.size.x);\nlet q = cc.xy + above * cc.z;\nlet ex = vec2f(cc.z * e, 0.0);\nlet ez = vec2f(0.0, cc.z * e);\nlet dsx = textureSampleLevel(slopes, wrap, q + ex, c, 0.0).x - textureSampleLevel(slopes, wrap, q - ex, c, 0.0).x;\nlet dsz = textureSampleLevel(slopes, wrap, q + ez, c, 0.0).y - textureSampleLevel(slopes, wrap, q - ez, c, 0.0).y;\nlap += (dsx + dsz) / (2.0 * e);\n}\nlet focus = 1.0 / max(abs(1.0 - down * 0.248 * lap), 0.25);\nlet caustic = mix(1.0, focus, saturate(down * 3.0) * exp(-down / 4.0) * saturate(l.y * 4.0) * shade);\nbehind_light *= mix(1.0, caustic, 0.65);\nlet trans = exp(-sp.optics.rgb * (t + down / sun_in));\nbelow = behind_light * trans + body * (vec3f(1.0) - exp(-sp.optics.rgb * t));\nlet vertical = t0 * max(v.y, 0.02);\nshore_foam = saturate(1.0 - vertical / sp.shore.x) * saturate(vertical * 40.0);\nif (sp.surf0.w > 0.5) {\nlet object = saturate((-sf.ground - vertical - 0.15) / 0.3);\nshore_foam = mix(shore_foam, saturate(1.0 - vertical / 0.035) * saturate(vertical * 60.0) * 0.8, object);\n}\n}\nlet sigma = sqrt(lost);\nlet mean_f = f0 + (1.0 - f0) * pow(1.0 - nv, 5.0 * exp(-2.69 * sigma)) / (1.0 + 22.7 * pow(sigma, 1.5));\nlet fr = min(fresnel, mean_f);\nlet water = sky * fr + (below + sss) * (1.0 - fr) + spec;\nlet foam_light = (sh_irradiance(n) + sun * (nl / PI)) * 0.85;\nvar fp = (sp.cascade[0].xy + in.rest * sp.cascade[0].z) / sp.cascade[0].z;\nif (sf.foam > 0.001 || sf.front > 0.001) {\nfp = sf.world;\n}\nlet flow = surf_flow(sf);\nlet cyc = 1.0;\nlet ph0 = fract(sp.surf3.x / cyc);\nlet ph1 = fract(sp.surf3.x / cyc + 0.5);\nlet fp0 = fp - flow * (ph0 * cyc) + vec2f(13.7, 5.1) * floor(sp.surf3.x / cyc);\nlet fp1 = fp - flow * (ph1 * cyc) + vec2f(7.3, 11.9) * floor(sp.surf3.x / cyc + 0.5);\nlet wflow = abs(2.0 * ph0 - 1.0);\nlet beach = smoothstep(0.0, 0.04, sf.ground);\nvar drain = 1.0;\nif (beach > 0.0 && sf.cover > 0.0 && sf.phase > 0.18) {\nlet along = dot(sf.world, sf.dir);\nlet across = dot(sf.world, vec2f(-sf.dir.y, sf.dir.x)) + (vnoise(vec2f(along * 0.6, 0.0)) - 0.5) * 1.4 + (vnoise(vec2f(along * 1.7, 5.0)) - 0.5) * 0.5;\nlet rill = vnoise(vec2f(across * 1.5, along * 0.12)) * 0.6 + vnoise(vec2f(across * 4.3, along * 0.3) + vec2f(17.0, 3.0)) * 0.4;\nlet k = smoothstep(0.2, 0.5, sf.phase) * beach * smoothstep(0.35, 0.65, vnoise(sf.world / 7.0 + vec2f(3.0, 9.0)));\ndrain = mix(1.0, smoothstep(0.38, 0.62, rill), k);\n}\nvar cover = saturate(fold * sp.sea.w + shore_foam * (1.0 - beach) + (sf.foam + sf.front * 0.9) * mix(1.0, drain, beach) + sf.mark * 0.55);\nif (cover > 0.002) {\nlet spots = vnoise(fp * 0.22) * 0.65 + vnoise(fp * 0.61 + vec2f(9.0, 4.0)) * 0.35;\ncover *= mix(smoothstep(0.35, 0.75, spots), 1.0, smoothstep(0.35, 0.8, cover));\nlet clumps = vnoise(fp * 0.35 + vec2f(3.0, 7.0)) * 0.6 + vnoise(fp * 1.1 + vec2f(11.0, 2.0)) * 0.4;\ncover *= mix(1.0, 0.45 + 0.75 * clumps, smoothstep(0.3, 0.7, cover));\n}\nlet fw = length(fwidth(fp0));\nvar foam = 0.0;\nvar bubbles = 1.0;\nif (cover > 0.002) {\nvar net = foam_net(fp0, cover, fw);\nif (dot(flow, flow) > 1e-4) {\nnet = mix(net, foam_net(fp1, cover, fw), wflow);\n}\nfoam = net.x;\nbubbles = net.y;\n}\nlet aerated = saturate(sf.fresh * 1.6) * (1.0 - beach);\nlet milky = mix(sp.sss.rgb * 2.5, vec3f(0.85, 0.95, 0.95), 0.55) * (sh_irradiance(n) + sun * (nl / PI));\nvar foam_lit = foam_light;\nif (foam > 0.01 && sp.surf0.w > 0.5) {\nlet fuv = sp.surf0.xy + in.rest * sp.surf0.z;\nlet ft = 1.0 / vec2f(textureDimensions(surf_d));\nlet gx = textureSampleLevel(surf_d, clamp_smp, fuv + vec2f(ft.x, 0.0), 0.0).w - textureSampleLevel(surf_d, clamp_smp, fuv - vec2f(ft.x, 0.0), 0.0).w;\nlet gz = textureSampleLevel(surf_d, clamp_smp, fuv + vec2f(0.0, ft.y), 0.0).w - textureSampleLevel(surf_d, clamp_smp, fuv - vec2f(0.0, ft.y), 0.0).w;\nlet per_m = 0.1 / (2.0 * ft.x / sp.surf0.z);\nlet nf = normalize(n + vec3f(-gx, 0.0, -gz) * per_m * 0.5);\nfoam_lit = (sh_irradiance(nf) + sun * (max(dot(nf, l), 0.0) / PI)) * 0.85;\n}\nvar lit = mix(mix(water, milky, aerated * 0.65), foam_lit * bubbles, foam);\nif (sf.ground > 0.0 && sp.shore.y > 0.5) {\nlet sand = textureLoad(under, vec2i(frag.xy), 0).rgb * sp.shore.z;\nlet wet_rough = 0.12;\nlet sheen = textureSampleLevel(env_cube, wrap, r, clamp(sqrt(wet_rough) * (lighting.env.x - 1.0), 0.0, lighting.env.x - 1.0)).rgb * lighting.view.rgb;\nlet wet_sand = sand * mix(1.0, 0.5, sf.wet) + sheen * fresnel * sf.wet * 0.8;\nlit = mix(wet_sand, lit, max(sf.cover * drain, foam));\n}\nlet fogged = apply_fog(lit, in.p, lighting.fog, lighting.fog2, lighting.fog_air.rgb, sh_irradiance(normalize(in.p)), sun, l);\nreturn vec4f(fogged * lighting.env.z, 1.0);\n}\nstruct Camera {\nview_proj: mat4x4f,\nfrustum: array<vec4f, 6>,\nlight_dir: vec4f,\norigin: vec4f,\n}\nstruct Lighting {\nsun_color: vec4f,\nenv: vec4f,\nsh: array<vec4f, 9>,\nscreen: vec4f,\nview: vec4f,\nao_reproject: mat4x4f,\nao: vec4f,\nfog: vec4f,\nfog2: vec4f,\nfog_air: vec4f,\ngi_grid: vec4f,\ngi_counts: vec4f,\nssr: vec4f,\ngi_cascades: array<vec4f, 3>,\nclouds: vec4f,\n}\nfn apply_fog(color: vec3f, p: vec3f, fog: vec4f, fog2: vec4f, air: vec3f, ambient: vec3f, sun: vec3f, sun_dir: vec3f) -> vec3f {\nlet dist = length(p);\nif (dist <= 0.0 || (fog.x <= 0.0 && fog2.x <= 0.0)) {\nreturn color;\n}\nlet dir = p / dist;\nlet t = exp(-(air * (fog2.x * dist) + vec3f(height_fog_depth(fog, dir, dist))));\nreturn color * t + fog_inscatter(fog2, dir, ambient, sun, sun_dir) * (vec3f(1.0) - t);\n}\nfn height_fog_depth(fog: vec4f, dir: vec3f, dist: f32) -> f32 {\nif (fog.x <= 0.0) {\nreturn 0.0;\n}\nlet falloff = max(fog.y, 1e-5);\nlet len = max(dist - fog.w, 0.0);\nlet h0 = fog.z + dir.y * min(dist, fog.w);\nlet k = falloff * dir.y * len;\nlet shape = select((1.0 - exp(-k)) / k, 1.0 - 0.5 * k, abs(k) < 1e-3);\nreturn fog.x * exp(-falloff * h0) * len * shape;\n}\nfn fog_inscatter(fog2: vec4f, dir: vec3f, ambient: vec3f, sun: vec3f, sun_dir: vec3f) -> vec3f {\nlet g = fog2.z;\nlet c = dot(dir, sun_dir);\nlet phase = (1.0 - g * g) / (FOUR_PI * pow(max(1.0 + g * g - 2.0 * g * c, 1e-4), 1.5));\nreturn ambient + sun * (phase * fog2.y);\n}\nconst FOUR_PI = 12.5663706;";
10
10
  };
@@ -7,7 +7,7 @@ export const waterWaves = {
7
7
  };
8
8
  /** Linked from shaders/water/surface.wesl. */
9
9
  export const waterSurface = {
10
- hash: 'd76b79846f4edbf0',
11
- wgsl: "const PI = 3.14159265;\nconst EARTH_RADIUS = 6371000.0;\nconst MAX_CASCADES = 4u;\nstruct SurfaceParams {\ncascade: array<vec4f, MAX_CASCADES>,\ngrid: vec4f,\nsea: vec4f,\nbody: vec4f,\nsss: vec4f,\nsize: vec4f,\noptics: vec4f,\nshore: vec4f,\nmirror: vec4f,\nsurf0: vec4f,\nsurf1: vec4f,\nsurf2: vec4f,\nsurf3: vec4f,\n}\n@group(0) @binding(0) var<uniform> camera: Camera;\n@group(0) @binding(1) var<uniform> lighting: Lighting;\n@group(0) @binding(2) var<uniform> sp: SurfaceParams;\n@group(0) @binding(3) var displacement: texture_2d_array<f32>;\n@group(0) @binding(4) var slopes: texture_2d_array<f32>;\n@group(0) @binding(5) var wrap: sampler;\n@group(0) @binding(6) var env_cube: texture_cube<f32>;\n@group(0) @binding(7) var under: texture_2d<f32>;\n@group(0) @binding(8) var under_depth: texture_2d<f32>;\n@group(0) @binding(9) var clamp_smp: sampler;\n@group(0) @binding(10) var scene_depth: texture_depth_2d;\n@group(0) @binding(11) var depth_out: texture_storage_2d<r32float, write>;\nstruct WaterMotion {\nview_proj: mat4x4f,\nprev_view_proj: mat4x4f,\neye_move: vec4f,\n}\n@group(0) @binding(12) var prev_displacement: texture_2d_array<f32>;\n@group(0) @binding(13) var<uniform> wm: WaterMotion;\n@group(0) @binding(14) var shore_map: texture_2d<f32>;\n@group(0) @binding(15) var spill_map: texture_2d<f32>;\n@group(0) @binding(16) var cloud_pano: texture_2d<f32>;\nstruct Surf {\nd: vec3f,\nslope: vec2f,\nbroken: f32,\nfoam: f32,\nfade: f32,\nground: f32,\nreach: f32,\ncover: f32,\nwet: f32,\nfilm: f32,\nfront: f32,\nface: f32,\namp: f32,\nworld: vec2f,\ndir: vec2f,\nphase: f32,\nonset: f32,\n}\nfn pcg(v: u32) -> u32 {\nlet state = v * 747796405u + 2891336453u;\nlet word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;\nreturn (word >> 22u) ^ word;\n}\nfn hash1(n: f32) -> f32 {\nreturn f32(pcg(bitcast<u32>(i32(n)))) / 4294967296.0;\n}\nfn hash2(p: vec2f) -> f32 {\nlet q = vec2<i32>(p);\nreturn f32(pcg(bitcast<u32>(q.x) ^ pcg(bitcast<u32>(q.y) + 0x9e3779b9u))) / 4294967296.0;\n}\nfn vnoise(p: vec2f) -> f32 {\nlet i = floor(p);\nlet f = fract(p);\nlet u = f * f * (3.0 - 2.0 * f);\nreturn mix(mix(hash2(i), hash2(i + vec2f(1.0, 0.0)), u.x), mix(hash2(i + vec2f(0.0, 1.0)), hash2(i + vec2f(1.0, 1.0)), u.x), u.y);\n}\nfn worley(p: vec2f) -> vec2f {\nlet i = floor(p);\nlet f = fract(p);\nvar d1 = 8.0;\nvar d2 = 8.0;\nfor (var y = -1; y <= 1; y++) {\nfor (var x = -1; x <= 1; x++) {\nlet o = vec2f(f32(x), f32(y));\nlet c = i + o;\nlet j = vec2f(hash2(c), hash2(c + vec2f(1013.0, 7919.0)));\nlet d = length(o + j - f) / (0.55 + 0.9 * hash2(c + vec2f(4241.0, 557.0)));\nif (d < d1) {\nd2 = d1;\nd1 = d;\n} else if (d < d2) {\nd2 = d;\n}\n}\n}\nreturn vec2f(d1, d2);\n}\nfn foam_net(p: vec2f, amount: f32, fw: f32) -> vec2f {\nlet a = saturate(amount);\nlet q = p + vec2f(vnoise(p * 2.0), vnoise(p * 2.0 + vec2f(5.2, 1.3))) * 0.36;\nlet big = worley(q * 3.2);\nvar small = vec2f(0.5, 1.0);\nif (sp.size.w > 1.5) {\nsmall = worley(q * 10.0 + vec2f(3.7, 1.9));\n}\nlet r = mix(0.72, 0.0, pow(a, 0.65));\nlet eb = fw * 3.2;\nlet es = fw * 10.0;\nlet mean = saturate(1.0 - 2.6 * r * r);\nlet hb = mix(smoothstep(r - 0.05 - eb, r + 0.05 + eb, big.x), mean, smoothstep(0.15, 0.6, eb));\nlet rs = r * 0.8 + 0.1;\nlet hs = mix(smoothstep(rs - 0.06 - es, rs + 0.06 + es, small.x), saturate(1.0 - 2.6 * rs * rs), smoothstep(0.15, 0.6, es));\nlet net = hb * mix(1.0, hs, 0.55 * (1.0 - a));\nlet cover = max(net, smoothstep(0.8, 1.0, a)) * smoothstep(0.0, 0.06, a);\nlet lumps = mix(0.66, 1.06, saturate(1.0 - small.x * 0.9)) * mix(0.84, 1.0, saturate(1.0 - big.x * 0.8));\nlet light = mix(lumps, 0.92, smoothstep(0.15, 0.6, es));\nreturn vec2f(cover, light);\n}\nfn wave_amp(n: f32, world: vec2f) -> f32 {\nlet group = 1.0 + 0.3 * sin(2.0 * PI * n / 6.3 + 1.3) + 0.25 * (hash1(n) - 0.5);\nlet section = 0.6 + 0.8 * vnoise(world / 28.0 + vec2f(n * 7.13, n * 3.71));\nreturn mix(1.0, group * section, sp.surf3.y);\n}\nfn surf_at(rest: vec2f, time: f32) -> Surf {\nvar s: Surf;\ns.fade = 1.0;\ns.amp = 1.0;\nif (sp.surf0.w < 0.5) {\nreturn s;\n}\nlet uv = sp.surf0.xy + rest * sp.surf0.z;\nif (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0))) {\nreturn s;\n}\nlet f = textureSampleLevel(shore_map, clamp_smp, uv, 0.0);\nlet sp2 = textureSampleLevel(spill_map, clamp_smp, uv, 0.0);\nlet depth = f.x;\nlet period = sp.surf1.y;\nlet clock = 2.0 * PI * fract(time / period);\nlet th = clock - atan2(f.z, f.y);\nlet u = fract(th / (2.0 * PI));\ns.phase = u;\nlet ff = fract(u + 0.5);\nlet nw = round((time - sp2.y) / period + 0.5 - ff);\ns.world = uv / sp.surf0.z;\ns.amp = mix(wave_amp(nw, s.world), wave_amp(nw + 1.0, s.world), smoothstep(0.82, 1.0, ff));\ns.fade = smoothstep(0.2, sp.surf2.y, depth);\nlet hs = max(depth, 0.15);\nlet start = sp.surf2.z;\nlet shoaled = sp.surf1.x * s.amp * pow(start / hs, 0.25) * smoothstep(start, start * 0.6, depth);\nlet hb = sp.surf1.z * hs;\nlet ratio = shoaled / max(hb, 1e-3);\ns.broken = saturate((ratio - 1.0) * 3.0);\nlet H = min(shoaled, hb) * smoothstep(-0.05, 0.25, depth);\nlet stokes = 0.5 * (cos(th) + 0.3 * cos(2.0 * th));\nlet dstokes = 0.5 * (-sin(th) - 0.6 * sin(2.0 * th));\nlet w = 0.12;\nlet bore = select(1.0 - (u - w) / (1.0 - w), u / w, u < w) - 0.5;\nlet dbore = select(-1.0 / (1.0 - w), 1.0 / w, u < w) / (2.0 * PI);\nvar eta = H * mix(stokes, bore, s.broken);\nlet deta = H * mix(dstokes, dbore, s.broken);\nlet dir = vec2f(cos(f.w), sin(f.w));\ns.dir = dir;\nlet q = sp.surf1.w * (1.0 - 0.6 * s.broken);\nvar lean = dir * (q * H * 0.5 * sin(th));\nlet onset = s.broken * (1.0 - s.broken) * 4.0 * saturate((ratio - 0.9) * 10.0);\ns.onset = onset;\nlet top = smoothstep(0.1, 0.5, eta / max(H, 1e-3));\nlean += dir * (sp.surf3.z * H * 0.55 * onset * top);\neta *= 1.0 + 0.25 * onset * top;\nlet k = 2.0 * PI / (period * sqrt(9.81 * hs));\ns.slope = -k * deta * dir;\ns.d = vec3f(lean.x, eta, lean.y);\ns.face = saturate(-dot(s.slope, dir) * 1.5) * smoothstep(-0.2, 0.45, eta / max(H, 1e-3)) * saturate(H * 1.2) * (1.0 - 0.65 * s.broken + 0.5 * onset);\ns.foam = s.broken * smoothstep(0.0, w, u) * (exp(-(u - w) * 9.0) + 0.3 * exp(-(u - w) * 3.0)) * saturate(H * 3.0);\ns.ground = -depth;\ns.reach = max(sp2.x, s.ground);\nif (s.ground > -0.05) {\nlet e = s.reach;\nlet run = sp.surf1.x * s.amp * 0.8;\nlet front = run * pow(saturate(1.0 - u / 0.55), 1.6);\ns.cover = saturate((front - e) / 0.03);\nlet passed = 0.55 * (1.0 - pow(saturate(e / max(run, 1e-3)), 1.0 / 1.6));\nlet since = select(0.0, (u - passed) * sp.surf1.y, u > passed);\ns.wet = select(0.0, max(exp(-since / 4.0) * 0.75, 0.35 * saturate(1.0 - e / max(run, 1e-3))), e < run);\ns.film = 0.02 + 0.04 * s.cover * saturate((front - e) / 0.15);\ns.front = s.cover * (1.0 - saturate((front - e) / 0.015)) * saturate(front * 6.0);\n}\nreturn s;\n}\nfn on_beach(d: vec3f, s: Surf) -> vec3f {\nif (s.ground <= 0.0) {\nreturn d;\n}\nlet reach = sp.surf1.x * 0.8 * 2.0 + 0.05;\nlet y = select(s.ground - 1.0, s.ground + s.film, s.reach < reach);\nlet k = smoothstep(0.0, 0.04, s.ground);\nreturn vec3f(d.x * (1.0 - k), mix(d.y, y, k), d.z * (1.0 - k));\n}\nconst SPRAY_SIDE = 192u;\nconst SPRAY_SPACING = 0.75;\nstruct SprayOut {\n@builtin(position) clip: vec4f,\n@location(0) q: vec4f,\n@location(1) p: vec3f,\n}\n@vertex fn vs_spray(@builtin(vertex_index) vi: u32, @builtin(instance_index) ii: u32) -> SprayOut {\nvar o: SprayOut;\no.clip = vec4f(0.0, 0.0, -1.0, 1.0);\nif (sp.surf0.w < 0.5 || sp.size.y <= 0.0) {\nreturn o;\n}\nlet extent = 1.0 / sp.surf0.z;\nlet eye = sp.surf0.xy * extent;\nlet gx = f32(ii % SPRAY_SIDE) - f32(SPRAY_SIDE / 2u);\nlet gz = f32(ii / SPRAY_SIDE) - f32(SPRAY_SIDE / 2u);\nlet cell = floor(eye / SPRAY_SPACING) + vec2f(gx, gz);\nlet j = vec2f(hash2(cell), hash2(cell + vec2f(91.0, 37.0)));\nlet rest = (cell + j) * SPRAY_SPACING - eye;\nlet s = surf_at(rest, sp.surf3.x);\nlet age = (s.phase - 0.12) * sp.surf1.y;\nlet life = 1.2 + 0.8 * hash2(cell + vec2f(13.0, 71.0));\nif (s.broken < 0.05 || age < 0.0 || age > life || s.ground > 0.3) {\nreturn o;\n}\nlet H = sp.surf1.x * s.amp;\nlet strength = (0.2 * s.broken + 0.8 * s.onset) * saturate(H * 1.2) * (0.4 + 1.2 * hash2(cell + vec2f(5.0, 3.0)));\nlet v0 = (1.5 + 2.5 * hash2(cell + vec2f(2.0, 9.0))) * saturate(H);\nlet k = 1.8;\nlet rise = v0 / k * (1.0 - exp(-k * age)) - 4.0 * age * age * 0.5;\nlet fwd = s.dir * (1.2 * (1.0 - exp(-1.5 * age)));\nlet base = s.d.y + 0.15;\nlet centre = vec3f(rest.x + fwd.x, sp.sea.x + base + max(rise, 0.0), rest.y + fwd.y);\nlet t = age / life;\nlet size = (0.25 + 0.9 * t) * (0.7 + 0.6 * hash2(cell + vec2f(17.0, 23.0))) * (0.6 + 0.6 * saturate(H));\nlet corner = array<vec2f, 6>(vec2f(-1.0, -1.0), vec2f(1.0, -1.0), vec2f(1.0, 1.0), vec2f(-1.0, -1.0), vec2f(1.0, 1.0), vec2f(-1.0, 1.0))[vi];\nlet fwdv = normalize(centre);\nlet right = normalize(cross(vec3f(0.0, 1.0, 0.0), fwdv));\nlet up = cross(fwdv, right);\nlet pos = centre + (right * corner.x + up * corner.y) * size;\no.clip = camera.view_proj * vec4f(pos, 1.0);\nlet alpha = strength * smoothstep(0.0, 0.1, t) * (1.0 - smoothstep(0.4, 1.0, t)) * sp.size.y * 0.22;\no.q = vec4f(corner, alpha, length(centre));\no.p = centre;\nreturn o;\n}\n@fragment fn fs_spray(in: SprayOut) -> @location(0) vec4f {\nlet r2 = dot(in.q.xy, in.q.xy);\nif (r2 > 1.0) {\ndiscard;\n}\nlet wisp = vnoise(in.q.xy * 1.8 + in.p.xz * 3.1) * 0.6 + vnoise(in.q.xy * 4.3 + in.p.xz * 1.7) * 0.4;\nvar a = in.q.z * (1.0 - r2) * (1.0 - r2) * smoothstep(0.25, 0.8, wisp) * 1.6;\nlet d = textureLoad(under_depth, vec2i(in.clip.xy), 0).x;\nif (d > 0.0) {\nlet gap = in.q.w * (in.clip.z / d - 1.0);\na *= saturate(gap / 0.5);\n}\nlet v = normalize(in.p);\nlet l = camera.light_dir.xyz;\nlet g = 0.6;\nlet cosv = dot(v, l);\nlet hg = (1.0 - g * g) / (4.0 * PI * pow(1.0 + g * g - 2.0 * g * cosv, 1.5));\nlet light = sh_irradiance(vec3f(0.0, 1.0, 0.0)) * 0.9 + lighting.sun_color.rgb * (hg * 2.0 + 0.08);\nlet fogged = apply_fog(light, in.p, lighting.fog, lighting.fog2, lighting.fog_air.rgb, sh_irradiance(v), lighting.sun_color.rgb, l);\nreturn vec4f(fogged * lighting.env.z * a, a);\n}\nstruct VsOut {\n@builtin(position) clip: vec4f,\n@location(0) p: vec3f,\n@location(1) rest: vec2f,\n@location(2) height: f32,\n}\nfn ring_radius(i: f32) -> f32 {\nlet k = sp.grid.y;\nreturn sp.grid.x * (pow(1.0 + k, i) - 1.0) / k;\n}\nfn grid_point(vi: u32) -> vec3f {\nlet segs = u32(sp.grid.w);\nlet quad = vi / 6u;\nlet corner = vi % 6u;\nlet dr = select(select(0u, 1u, corner == 1u || corner == 2u || corner == 4u), 0u, corner == 0u || corner == 3u);\nlet ds = select(0u, 1u, corner == 2u || corner == 4u || corner == 5u);\nlet ring = f32(quad / segs + dr);\nlet seg = f32(quad % segs + ds);\nlet r = ring_radius(ring);\nlet a = 2.0 * PI * seg / f32(segs);\nreturn vec3f(vec2f(cos(a), sin(a)) * r, max(ring_radius(ring + 1.0) - r, 2.0 * PI * r / f32(segs)));\n}\nfn displaced(field: texture_2d_array<f32>, rest: vec2f, spacing: f32) -> vec3f {\nvar d = vec3f(0.0);\nfor (var c = 0u; c < u32(sp.sea.y); c++) {\nlet cc = sp.cascade[c];\nlet texel = 1.0 / (cc.z * sp.size.x);\nlet w = 1.0 - smoothstep(1.0, 4.0, spacing / texel);\nif (w <= 0.0) {\ncontinue;\n}\nd += textureSampleLevel(field, wrap, cc.xy + rest * cc.z, c, 0.0).xyz * w;\n}\nreturn d;\n}\nstruct MotionOut {\n@builtin(position) clip: vec4f,\n@location(0) now: vec4f,\n@location(1) then: vec4f,\n}\n@vertex fn vs_motion(@builtin(vertex_index) vi: u32) -> MotionOut {\nlet g = grid_point(vi);\nlet rest = g.xy;\nlet drop = dot(rest, rest) / (2.0 * EARTH_RADIUS);\nlet sn = surf_at(rest, sp.surf3.x);\nlet sq = surf_at(rest, select(sp.surf3.x, sp.surf3.w, wm.eye_move.w > 0.5));\nlet dn = on_beach(displaced(displacement, rest, g.z) * sn.fade + sn.d, sn);\nlet dp = select(dn, on_beach(displaced(prev_displacement, rest, g.z) * sq.fade + sq.d, sq), wm.eye_move.w > 0.5);\nlet p = vec3f(rest.x + dn.x, sp.sea.x + dn.y - drop, rest.y + dn.z);\nlet q = vec3f(rest.x + dp.x, sp.sea.x + dp.y - drop, rest.y + dp.z) + wm.eye_move.xyz;\nvar o: MotionOut;\no.now = wm.view_proj * vec4f(p, 1.0);\no.clip = camera.view_proj * vec4f(p, 1.0);\no.then = wm.prev_view_proj * vec4f(q, 1.0);\nreturn o;\n}\n@fragment fn fs_motion(in: MotionOut) -> @location(0) vec4f {\nlet now = in.now.xy / in.now.w;\nlet then = in.then.xy / max(in.then.w, 1e-6);\nreturn vec4f((now - then) * vec2f(0.5, -0.5), in.then.w, 1.0);\n}\n@vertex fn vs(@builtin(vertex_index) vi: u32) -> VsOut {\nlet segs = u32(sp.grid.w);\nlet quad = vi / 6u;\nlet corner = vi % 6u;\nlet dr = select(select(0u, 1u, corner == 1u || corner == 2u || corner == 4u), 0u, corner == 0u || corner == 3u);\nlet ds = select(0u, 1u, corner == 2u || corner == 4u || corner == 5u);\nlet ring = f32(quad / segs + dr);\nlet seg = f32(quad % segs + ds);\nlet r = ring_radius(ring);\nlet a = 2.0 * PI * seg / f32(segs);\nlet rest = vec2f(cos(a), sin(a)) * r;\nlet spacing = max(ring_radius(ring + 1.0) - r, 2.0 * PI * r / f32(segs));\nvar d = vec3f(0.0);\nfor (var c = 0u; c < u32(sp.sea.y); c++) {\nlet cc = sp.cascade[c];\nlet texel = 1.0 / (cc.z * sp.size.x);\nlet w = 1.0 - smoothstep(1.0, 4.0, spacing / texel);\nif (w <= 0.0) {\ncontinue;\n}\nd += textureSampleLevel(displacement, wrap, cc.xy + rest * cc.z, c, 0.0).xyz * w;\n}\nlet sf = surf_at(rest, sp.surf3.x);\nd = on_beach(d * sf.fade + sf.d, sf);\nlet drop = dot(rest, rest) / (2.0 * EARTH_RADIUS);\nlet p = vec3f(rest.x + d.x, sp.sea.x + d.y - drop, rest.y + d.z);\nvar o: VsOut;\no.clip = camera.view_proj * vec4f(p, 1.0);\no.p = p;\no.rest = rest;\no.height = d.y;\nreturn o;\n}\nfn sh_irradiance(n: vec3f) -> vec3f {\nreturn max(lighting.sh[0].rgb * 0.282095 + lighting.sh[1].rgb * (0.488603 * n.y) + lighting.sh[2].rgb * (0.488603 * n.z) + lighting.sh[3].rgb * (0.488603 * n.x) + lighting.sh[4].rgb * (1.092548 * n.x * n.y) + lighting.sh[5].rgb * (1.092548 * n.y * n.z) + lighting.sh[6].rgb * (0.315392 * (3.0 * n.z * n.z - 1.0)) + lighting.sh[7].rgb * (1.092548 * n.x * n.z) + lighting.sh[8].rgb * (0.546274 * (n.x * n.x - n.y * n.y)), vec3f(0.0));\n}\n@compute @workgroup_size(8, 8) fn copy_depth(@builtin(global_invocation_id) id: vec3u) {\nlet size = textureDimensions(scene_depth);\nif (id.x >= size.x || id.y >= size.y) {\nreturn;\n}\ntextureStore(depth_out, vec2i(id.xy), vec4f(textureLoad(scene_depth, vec2i(id.xy), 0), 0.0, 0.0, 0.0));\n}\nfn thickness(dist: f32, ds: f32, dop: f32) -> f32 {\nreturn select(1.0e9, dist * max(ds / dop - 1.0, 0.0), dop > 0.0);\n}\nfn mirror_probe(p: vec3f, r: vec3f, t: f32, size: vec2f) -> vec4f {\nlet c = camera.view_proj * vec4f(p + r * t, 1.0);\nif (c.w <= 0.0) {\nreturn vec4f(0.0);\n}\nlet ndc = c.xyz / c.w;\nlet uv = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);\nif (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0))) {\nreturn vec4f(0.0);\n}\nlet d = textureLoad(under_depth, vec2i(uv * size), 0).x;\nlet gap = select(-1.0e9, c.w * (1.0 - ndc.z / d), d > 0.0);\nreturn vec4f(uv, gap, 1.0);\n}\nfn mirror_scene(p: vec3f, r: vec3f, size: vec2f, jitter: f32) -> vec4f {\nlet len = sp.mirror.x;\nlet steps = u32(sp.mirror.y);\nif (len <= 0.0 || steps == 0u || sp.shore.y < 0.5 || r.y <= 0.0) {\nreturn vec4f(0.0);\n}\nvar prev = 0.0;\nvar was_behind = false;\nfor (var i = 1u; i <= steps; i++) {\nlet f = (f32(i) - jitter) / f32(steps);\nlet t = len * f * f;\nlet s = mirror_probe(p, r, t, size);\nif (s.w < 0.5) {\nbreak;\n}\nlet behind = s.z > 0.0;\nif (behind && !was_behind) {\nvar a = prev;\nvar b = t;\nvar hit = s;\nfor (var k = 0u; k < 6u; k++) {\nlet m = 0.5 * (a + b);\nlet sm = mirror_probe(p, r, m, size);\nif (sm.w > 0.5 && sm.z > 0.0) {\nb = m;\nhit = sm;\n} else {\na = m;\n}\n}\nif (hit.z < sp.mirror.z + 0.03 * b) {\nlet uv = hit.xy;\nlet edge = min(min(uv.x, 1.0 - uv.x), min(uv.y, 1.0 - uv.y));\nlet fade = saturate(edge * 12.0) * (1.0 - smoothstep(0.7, 1.0, b / len));\nreturn vec4f(textureSampleLevel(under, clamp_smp, uv, 0.0).rgb * sp.shore.z, fade);\n}\n}\nwas_behind = behind;\nprev = t;\n}\nreturn vec4f(0.0);\n}\nfn ign(px: vec2f) -> f32 {\nlet frame = floor(camera.light_dir.w * 60.0) % 64.0;\nlet q = px + 5.588238 * frame;\nreturn fract(52.9829189 * fract(dot(q, vec2f(0.06711056, 0.00583715))));\n}\n@fragment fn fs(in: VsOut) -> @location(0) vec4f {\nlet frag = in.clip;\nvar sx = 0.0;\nvar sz = 0.0;\nvar lost = 0.0;\nvar fold = 0.0;\nfor (var c = 0u; c < u32(sp.sea.y); c++) {\nlet cc = sp.cascade[c];\nlet uv = cc.xy + in.rest * cc.z;\nlet texels = length(fwidth(uv)) * sp.size.x;\nlet w = 1.0 - smoothstep(0.5, 2.0, texels);\nlet s = textureSampleLevel(slopes, wrap, uv, c, 0.0);\nsx += s.x * w;\nsz += s.y * w;\nlost += cc.w * (1.0 - w);\nfold = max(fold, max(saturate((sp.sea.z - s.z) * 2.0), s.w) * w);\n}\nlet sf = surf_at(in.rest, sp.surf3.x);\nsx = sx * sf.fade + sf.slope.x;\nsz = sz * sf.fade + sf.slope.y;\nfold *= sf.fade;\nlet n = normalize(vec3f(-sx, 1.0, -sz));\nlet v = normalize(-in.p);\nlet l = camera.light_dir.xyz;\nlet nv = max(dot(n, v), 1e-3);\nlet nl = max(dot(n, l), 0.0);\nlet a2 = max(sp.body.w * sp.body.w + 2.0 * lost, 1e-4);\nlet f0 = 0.02;\nlet fresnel = f0 + (1.0 - f0) * pow(1.0 - nv, 5.0);\nvar r = reflect(-v, n);\nr.y = abs(r.y);\nlet lod = clamp(sqrt(sqrt(a2)) * (lighting.env.x - 1.0), 0.0, lighting.env.x - 1.0);\nvar sky = textureSampleLevel(env_cube, wrap, r, lod).rgb * lighting.view.rgb;\nif (sp.size.z > 0.5) {\nlet pn = vec2f(textureDimensions(cloud_pano));\nlet pv = clamp(sqrt(max(r.y, 0.0)), 0.5 / pn.y, 1.0 - 0.5 / pn.y);\nlet cl = textureSampleLevel(cloud_pano, wrap, vec2f(fract(atan2(r.z, r.x) / (2.0 * PI)), pv), 0.0);\nsky = sky * cl.a + cl.rgb;\n}\nlet seen = mirror_scene(in.p, reflect(-v, n), vec2f(textureDimensions(under)), ign(frag.xy));\nsky = mix(sky, seen.rgb, seen.a);\nif (sp.mirror.w > 0.5) {\nreturn vec4f(select(vec3f(seen.a * 0.2, 0.0, 0.0) + seen.rgb * seen.a, vec3f(0.3, 0.0, 0.0), seen.a <= 0.0) * lighting.env.z, 1.0);\n}\nlet h = normalize(v + l);\nlet nh = max(dot(n, h), 0.0);\nlet dd = nh * nh * (a2 - 1.0) + 1.0;\nlet ggx = a2 / (PI * dd * dd);\nlet vis = 0.5 / (nl * sqrt(nv * nv * (1.0 - a2) + a2) + nv * sqrt(nl * nl * (1.0 - a2) + a2) + 1e-5);\nlet fh = f0 + (1.0 - f0) * pow(1.0 - max(dot(v, h), 0.0), 5.0);\nlet sun = lighting.sun_color.rgb;\nlet spec = sun * (ggx * vis * fh * nl);\nlet up = sh_irradiance(vec3f(0.0, 1.0, 0.0));\nlet body = sp.body.rgb * (up + sun * (max(l.y, 0.0) / PI) * 0.5);\nlet behind = pow(saturate(dot(-v, l)), 8.0);\nlet crest = saturate(in.height * 0.5) * saturate(1.0 - n.y * 0.9 + 0.2);\nvar sss = sp.sss.rgb * sun * (behind * crest * sp.sss.w);\nlet wide = 0.35 + 0.65 * pow(saturate(dot(-v, l) * 0.5 + 0.5), 3.0);\nlet thin = sf.face * (0.6 + 0.4 * saturate(1.0 - abs(n.y) * 1.2));\nsss += sp.sss.rgb * (up * 4.5 + sun * (0.8 * wide)) * (thin * sp.sss.w * 2.5);\nvar below = body;\nvar shore_foam = 0.0;\nif (sp.shore.y > 0.5) {\nlet px = vec2i(frag.xy);\nlet size = vec2f(textureDimensions(under));\nlet dist = length(in.p);\nlet t0 = thickness(dist, frag.z, textureLoad(under_depth, px, 0).x);\nlet bend = n.xz * sp.optics.w * min(t0, 1.0);\nvar uv = (frag.xy + bend) / size;\nif (any(uv < vec2f(0.0)) || any(uv >= vec2f(1.0))) {\nuv = frag.xy / size;\n}\nvar t = thickness(dist, frag.z, textureLoad(under_depth, vec2i(uv * size), 0).x);\nif (textureLoad(under_depth, vec2i(uv * size), 0).x > frag.z) {\nuv = frag.xy / size;\nt = t0;\n}\nvar behind_light = textureSampleLevel(under, clamp_smp, uv, 0.0).rgb * sp.shore.z;\nlet down = max(t * v.y, 0.0);\nlet sun_in = sqrt(max(1.0 - (1.0 - l.y * l.y) / 1.7689, 0.04));\nlet above = (in.p - v * t).xz + vec2f(l.x, l.z) / sun_in * down * (1.0 / 1.33);\nvar lap = 0.0;\nlet cn = u32(sp.sea.y);\nlet c0 = select(0u, cn - 2u, cn >= 2u);\nfor (var c = c0; c < select(c0, select(1u, cn - 1u, cn >= 2u), sp.size.w > 0.5); c++) {\nlet cc = sp.cascade[c];\nlet e = 2.0 / (cc.z * sp.size.x);\nlet q = cc.xy + above * cc.z;\nlet ex = vec2f(cc.z * e, 0.0);\nlet ez = vec2f(0.0, cc.z * e);\nlet dsx = textureSampleLevel(slopes, wrap, q + ex, c, 0.0).x - textureSampleLevel(slopes, wrap, q - ex, c, 0.0).x;\nlet dsz = textureSampleLevel(slopes, wrap, q + ez, c, 0.0).y - textureSampleLevel(slopes, wrap, q - ez, c, 0.0).y;\nlap += (dsx + dsz) / (2.0 * e);\n}\nlet focus = 1.0 / max(abs(1.0 - down * 0.248 * lap), 0.25);\nlet caustic = mix(1.0, focus, saturate(down * 3.0) * exp(-down / 4.0) * saturate(l.y * 4.0));\nbehind_light *= mix(1.0, caustic, 0.65);\nlet trans = exp(-sp.optics.rgb * (t + down / sun_in));\nbelow = behind_light * trans + body * (vec3f(1.0) - exp(-sp.optics.rgb * t));\nlet vertical = t0 * max(v.y, 0.02);\nshore_foam = saturate(1.0 - vertical / sp.shore.x) * saturate(vertical * 40.0);\n}\nlet sigma = sqrt(lost);\nlet mean_f = f0 + (1.0 - f0) * pow(1.0 - nv, 5.0 * exp(-2.69 * sigma)) / (1.0 + 22.7 * pow(sigma, 1.5));\nlet fr = min(fresnel, mean_f);\nlet water = sky * fr + (below + sss) * (1.0 - fr) + spec;\nlet foam_light = (sh_irradiance(n) + sun * (nl / PI)) * 0.85;\nvar fp = (sp.cascade[0].xy + in.rest * sp.cascade[0].z) / sp.cascade[0].z;\nif (sf.foam > 0.001 || sf.front > 0.001) {\nlet c = sqrt(9.81 * max(1.0 - sf.ground, 0.3));\nfp = sf.world - sf.dir * (0.5 * c * sp.surf1.y * sf.phase);\n}\nlet beach = smoothstep(0.0, 0.04, sf.ground);\nvar cover = saturate(fold * sp.sea.w + shore_foam * (1.0 - beach) + sf.foam + sf.front * 0.9);\nif (cover > 0.002) {\nlet spots = vnoise(fp * 0.22) * 0.65 + vnoise(fp * 0.61 + vec2f(9.0, 4.0)) * 0.35;\ncover *= mix(smoothstep(0.35, 0.75, spots), 1.0, smoothstep(0.35, 0.8, cover));\n}\nlet fw = length(fwidth(fp));\nvar foam = 0.0;\nvar bubbles = 1.0;\nif (cover > 0.002) {\nlet net = foam_net(fp, cover, fw);\nfoam = net.x;\nbubbles = net.y;\n}\nvar lit = mix(water, foam_light * bubbles, foam);\nif (sf.ground > 0.0 && sp.shore.y > 0.5) {\nlet sand = textureLoad(under, vec2i(frag.xy), 0).rgb * sp.shore.z;\nlet wet_rough = 0.12;\nlet sheen = textureSampleLevel(env_cube, wrap, r, clamp(sqrt(wet_rough) * (lighting.env.x - 1.0), 0.0, lighting.env.x - 1.0)).rgb * lighting.view.rgb;\nlet wet_sand = sand * mix(1.0, 0.5, sf.wet) + sheen * fresnel * sf.wet * 0.8;\nlit = mix(wet_sand, lit, max(sf.cover, foam));\n}\nlet fogged = apply_fog(lit, in.p, lighting.fog, lighting.fog2, lighting.fog_air.rgb, sh_irradiance(normalize(in.p)), sun, l);\nreturn vec4f(fogged * lighting.env.z, 1.0);\n}\nstruct Camera {\nview_proj: mat4x4f,\nfrustum: array<vec4f, 6>,\nlight_dir: vec4f,\norigin: vec4f,\n}\nstruct Lighting {\nsun_color: vec4f,\nenv: vec4f,\nsh: array<vec4f, 9>,\nscreen: vec4f,\nview: vec4f,\nao_reproject: mat4x4f,\nao: vec4f,\nfog: vec4f,\nfog2: vec4f,\nfog_air: vec4f,\ngi_grid: vec4f,\ngi_counts: vec4f,\nssr: vec4f,\ngi_cascades: array<vec4f, 3>,\n}\nfn apply_fog(color: vec3f, p: vec3f, fog: vec4f, fog2: vec4f, air: vec3f, ambient: vec3f, sun: vec3f, sun_dir: vec3f) -> vec3f {\nlet dist = length(p);\nif (dist <= 0.0 || (fog.x <= 0.0 && fog2.x <= 0.0)) {\nreturn color;\n}\nlet dir = p / dist;\nlet t = exp(-(air * (fog2.x * dist) + vec3f(height_fog_depth(fog, dir, dist))));\nreturn color * t + fog_inscatter(fog2, dir, ambient, sun, sun_dir) * (vec3f(1.0) - t);\n}\nfn height_fog_depth(fog: vec4f, dir: vec3f, dist: f32) -> f32 {\nif (fog.x <= 0.0) {\nreturn 0.0;\n}\nlet falloff = max(fog.y, 1e-5);\nlet len = max(dist - fog.w, 0.0);\nlet h0 = fog.z + dir.y * min(dist, fog.w);\nlet k = falloff * dir.y * len;\nlet shape = select((1.0 - exp(-k)) / k, 1.0 - 0.5 * k, abs(k) < 1e-3);\nreturn fog.x * exp(-falloff * h0) * len * shape;\n}\nfn fog_inscatter(fog2: vec4f, dir: vec3f, ambient: vec3f, sun: vec3f, sun_dir: vec3f) -> vec3f {\nlet g = fog2.z;\nlet c = dot(dir, sun_dir);\nlet phase = (1.0 - g * g) / (FOUR_PI * pow(max(1.0 + g * g - 2.0 * g * c, 1e-4), 1.5));\nreturn ambient + sun * (phase * fog2.y);\n}\nconst FOUR_PI = 12.5663706;",
10
+ hash: 'b6a83798dfc992f7',
11
+ wgsl: "const PI = 3.14159265;\nconst EARTH_RADIUS = 6371000.0;\nconst MAX_CASCADES = 4u;\nstruct SurfaceParams {\ncascade: array<vec4f, MAX_CASCADES>,\ngrid: vec4f,\nsea: vec4f,\nbody: vec4f,\nsss: vec4f,\nsize: vec4f,\noptics: vec4f,\nshore: vec4f,\nmirror: vec4f,\nsurf0: vec4f,\nsurf1: vec4f,\nsurf2: vec4f,\nsurf3: vec4f,\nglint: vec4f,\ncshadow: vec4f,\n}\n@group(0) @binding(0) var<uniform> camera: Camera;\n@group(0) @binding(1) var<uniform> lighting: Lighting;\n@group(0) @binding(2) var<uniform> sp: SurfaceParams;\n@group(0) @binding(3) var displacement: texture_2d_array<f32>;\n@group(0) @binding(4) var slopes: texture_2d_array<f32>;\n@group(0) @binding(5) var wrap: sampler;\n@group(0) @binding(6) var env_cube: texture_cube<f32>;\n@group(0) @binding(7) var under: texture_2d<f32>;\n@group(0) @binding(8) var under_depth: texture_2d<f32>;\n@group(0) @binding(9) var clamp_smp: sampler;\n@group(0) @binding(10) var scene_depth: texture_depth_2d;\n@group(0) @binding(11) var depth_out: texture_storage_2d<r32float, write>;\nstruct WaterMotion {\nview_proj: mat4x4f,\nprev_view_proj: mat4x4f,\neye_move: vec4f,\nreactive: vec4f,\n}\n@group(0) @binding(12) var prev_displacement: texture_2d_array<f32>;\n@group(0) @binding(13) var<uniform> wm: WaterMotion;\n@group(0) @binding(14) var shore_map: texture_2d<f32>;\n@group(0) @binding(15) var spill_map: texture_2d<f32>;\n@group(0) @binding(16) var cloud_pano: texture_2d<f32>;\n@group(0) @binding(27) var cloud_shadow: texture_2d<f32>;\nfn cloud_light(p: vec3f, l: vec3f) -> f32 {\nif (sp.cshadow.w < 0.5) {\nreturn 1.0;\n}\nlet at = p - l * ((p.y - sp.sea.x) / max(l.y, 0.05));\nlet uv = (at.xz - sp.cshadow.xy) * sp.cshadow.z + 0.5;\nlet edge = min(min(uv.x, 1.0 - uv.x), min(uv.y, 1.0 - uv.y));\nreturn mix(1.0, textureSampleLevel(cloud_shadow, clamp_smp, uv, 0.0).r, saturate(edge * 16.0));\n}\nstruct Surf {\nd: vec3f,\nslope: vec2f,\nbroken: f32,\nfoam: f32,\nfade: f32,\nground: f32,\nreach: f32,\ncover: f32,\nwet: f32,\nfilm: f32,\nfront: f32,\nface: f32,\namp: f32,\nworld: vec2f,\ndir: vec2f,\nphase: f32,\nonset: f32,\nfresh: f32,\nfront_h: f32,\nwet_h1: f32,\nwet_h2: f32,\nrun: f32,\nmark: f32,\n}\nfn pcg(v: u32) -> u32 {\nlet state = v * 747796405u + 2891336453u;\nlet word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;\nreturn (word >> 22u) ^ word;\n}\nfn hash1(n: f32) -> f32 {\nreturn f32(pcg(bitcast<u32>(i32(n)))) / 4294967296.0;\n}\nfn hash2(p: vec2f) -> f32 {\nlet q = vec2<i32>(p);\nreturn f32(pcg(bitcast<u32>(q.x) ^ pcg(bitcast<u32>(q.y) + 0x9e3779b9u))) / 4294967296.0;\n}\nfn vnoise(p: vec2f) -> f32 {\nlet i = floor(p);\nlet f = fract(p);\nlet u = f * f * (3.0 - 2.0 * f);\nreturn mix(mix(hash2(i), hash2(i + vec2f(1.0, 0.0)), u.x), mix(hash2(i + vec2f(0.0, 1.0)), hash2(i + vec2f(1.0, 1.0)), u.x), u.y);\n}\nfn worley(p: vec2f) -> vec2f {\nlet i = floor(p);\nlet f = fract(p);\nvar d1 = 8.0;\nvar d2 = 8.0;\nfor (var y = -1; y <= 1; y++) {\nfor (var x = -1; x <= 1; x++) {\nlet o = vec2f(f32(x), f32(y));\nlet c = i + o;\nlet j = vec2f(hash2(c), hash2(c + vec2f(1013.0, 7919.0)));\nlet d = length(o + j - f) / (0.55 + 0.9 * hash2(c + vec2f(4241.0, 557.0)));\nif (d < d1) {\nd2 = d1;\nd1 = d;\n} else if (d < d2) {\nd2 = d;\n}\n}\n}\nreturn vec2f(d1, d2);\n}\nfn foam_net(p: vec2f, amount: f32, fw: f32) -> vec2f {\nlet a = saturate(amount);\nlet q = p + vec2f(vnoise(p * 2.0), vnoise(p * 2.0 + vec2f(5.2, 1.3))) * 0.36;\nlet big = worley(q * 3.2);\nvar small = vec2f(0.5, 1.0);\nif (sp.size.w > 1.5) {\nsmall = worley(q * 10.0 + vec2f(3.7, 1.9));\n}\nlet r = mix(0.82, 0.12, pow(a, 1.1));\nlet eb = fw * 3.2;\nlet es = fw * 10.0;\nlet mean = saturate(1.0 - 2.6 * r * r);\nlet hb = mix(smoothstep(r - 0.05 - eb, r + 0.05 + eb, big.x), mean, smoothstep(0.15, 0.6, eb));\nlet rs = r * 0.8 + 0.1;\nlet hs = mix(smoothstep(rs - 0.06 - es, rs + 0.06 + es, small.x), saturate(1.0 - 2.6 * rs * rs), smoothstep(0.15, 0.6, es));\nlet net = hb * mix(1.0, hs, 0.55 * (1.0 - a));\nlet cover = net * smoothstep(0.0, 0.06, a);\nlet lumps = mix(0.8, 1.04, saturate(1.0 - small.x * 0.9)) * mix(0.9, 1.0, saturate(1.0 - big.x * 0.8));\nlet light = mix(lumps, 0.92, smoothstep(0.15, 0.6, es));\nreturn vec2f(cover, light);\n}\nstruct Train {\nth: f32,\nu: f32,\nnw: f32,\namp: f32,\nshoaled: f32,\nperiod: f32,\nblend: f32,\n}\nconst TRAINS = array<vec4f, 3>(vec4f(1.0, 1.0, 0.0, 0.0), vec4f(1.37, 0.45, 0.0, 0.31), vec4f(0.79, 0.3, 0.0, 0.67));\nfn wobble(n: f32, i: u32, world: vec2f) -> f32 {\nlet o = vec2f(n * 0.61 + f32(i) * 7.0, n * 0.37 + f32(i) * 3.0);\nreturn ((vnoise(world / 45.0 + o) - 0.5) * 1.2 + (vnoise(world / 14.0 + o * 1.7) - 0.5) * 0.35) * sp.surf3.y;\n}\nfn wave_amp(n: f32, world: vec2f) -> f32 {\nlet group = 1.0 + 0.3 * sin(2.0 * PI * n / 6.3 + 1.3) + 0.25 * (hash1(n) - 0.5);\nlet section = 0.6 + 0.8 * vnoise(world / 28.0 + vec2f(n * 7.13, n * 3.71));\nreturn mix(1.0, group * section, sp.surf3.y);\n}\nfn swash_front(u: f32, run: f32) -> f32 {\nlet rise = 0.18;\nlet up = 1.0 - (1.0 - saturate(u / rise)) * (1.0 - saturate(u / rise));\nlet down = pow(saturate(1.0 - (u - rise) / 0.57), 1.6);\nreturn run * select(down, up, u < rise);\n}\nfn swash_high(u: f32, run: f32, window: f32) -> f32 {\nlet rise = 0.18;\nlet u0 = u - window;\nif (u0 >= rise) {\nreturn swash_front(u0, run);\n}\nif (u >= rise) {\nreturn run;\n}\nreturn max(swash_front(u, run), select(0.0, swash_front(u0 + 1.0, run), u0 < 0.0));\n}\nfn swash_state(e: f32, front_h: f32, wet_h1: f32, wet_h2: f32, run: f32) -> vec4f {\nlet cover = saturate((front_h - e) / 0.03);\nlet r1 = saturate((wet_h1 - e) / 0.06 + 0.4);\nlet r2 = saturate((wet_h2 - e) / 0.12 + 0.4);\nlet damp = 0.3 * saturate(1.0 - e / max(run, 1e-3));\nlet wet = max(0.75 * (0.55 * r1 + 0.45 * r2), damp) * saturate((run - e) / 0.08 + 0.5);\nlet film = 0.02 + 0.04 * cover * saturate((front_h - e) / 0.15);\nlet front = cover * (1.0 - saturate((front_h - e) / 0.015)) * saturate(front_h * 6.0);\nreturn vec4f(cover, wet, film, front);\n}\nfn surf_at(rest: vec2f, time: f32) -> Surf {\nvar s: Surf;\ns.fade = 1.0;\ns.amp = 1.0;\nif (sp.surf0.w < 0.5) {\nreturn s;\n}\nlet uv = sp.surf0.xy + rest * sp.surf0.z;\nif (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0))) {\nreturn s;\n}\nlet f = textureSampleLevel(shore_map, clamp_smp, uv, 0.0);\nlet sp2 = textureSampleLevel(spill_map, clamp_smp, uv, 0.0);\nlet depth = f.x;\nlet period = sp.surf1.y;\ns.world = uv / sp.surf0.z;\ns.fade = smoothstep(0.2, sp.surf2.y, depth);\nlet hs = max(depth, 0.15);\nlet start = sp.surf2.z;\nlet shoal = pow(start / hs, 0.25) * smoothstep(start, start * 0.6, depth);\nlet dir = vec2f(cos(f.w), sin(f.w));\ns.dir = dir;\nvar tr: array<Train, 3>;\nlet trains = select(1u, 3u, sp.size.w > 0.5);\nfor (var i = 0u; i < trains; i++) {\nlet ti = TRAINS[i];\nlet pi = period * ti.x;\nvar th: f32;\nvar u: f32;\nvar nw: f32;\nif (i == 0u) {\nth = 2.0 * PI * fract(time / period) - atan2(f.z, f.y);\nu = fract(th / (2.0 * PI));\nnw = round((time - sp2.y) / period + 0.5 - fract(u + 0.5));\n} else {\nlet cyc = (time - sp2.y) / pi + ti.w;\nu = fract(cyc);\nth = 2.0 * PI * u;\nnw = round(cyc + 0.5 - fract(u + 0.5));\n}\nlet ff = fract(u + 0.5);\nlet blend = smoothstep(0.82, 1.0, ff);\nvar amp = wave_amp(nw + f32(i) * 101.0, s.world);\nvar wob = wobble(nw, i, s.world);\nif (blend > 0.0) {\namp = mix(amp, wave_amp(nw + 1.0 + f32(i) * 101.0, s.world), blend);\nwob = mix(wob, wobble(nw + 1.0, i, s.world), blend);\n}\nth -= wob;\ntr[i].th = th;\ntr[i].u = fract(th / (2.0 * PI));\ntr[i].nw = nw;\ntr[i].amp = amp;\ntr[i].shoaled = sp.surf1.x * ti.y * amp * shoal;\ntr[i].blend = blend;\ntr[i].period = pi;\n}\nlet gnoise = mix(vnoise(s.world / 35.0 + vec2f(tr[0].nw * 2.3, 0.0)), vnoise(s.world / 35.0 + vec2f((tr[0].nw + 1.0) * 2.3, 0.0)), tr[0].blend);\nlet gamma = sp.surf1.z * (0.85 + 0.3 * gnoise * sp.surf3.y + 0.15 * (1.0 - sp.surf3.y));\nlet hb = gamma * hs;\nvar eta = 0.0;\nvar lean = vec2f(0.0);\nvar slope = 0.0;\nvar face_k = 0.0;\nvar foam = 0.0;\ns.broken = 0.0;\ns.onset = 0.0;\nfor (var i = 0u; i < trains; i++) {\nvar others = 0.0;\nfor (var j = 0u; j < trains; j++) {\nif (j != i) {\nothers += 0.4 * tr[j].shoaled * (0.5 + 0.5 * cos(tr[j].th));\n}\n}\nlet ratio = (tr[i].shoaled + others) / max(hb, 1e-3);\nlet broken = saturate((ratio - 1.0) * 3.0);\nlet share = tr[i].shoaled / max(tr[i].shoaled + others, 1e-4);\nlet H = min(tr[i].shoaled, hb * share + tr[i].shoaled * (1.0 - broken)) * smoothstep(-0.05, 0.25, depth);\nlet th = tr[i].th;\nlet u = tr[i].u;\nlet wn = mix(vnoise(s.world / 22.0 + vec2f(tr[i].nw * 5.1, f32(i) * 9.0)), vnoise(s.world / 22.0 + vec2f((tr[i].nw + 1.0) * 5.1, f32(i) * 9.0)), tr[i].blend);\nlet w = 0.12 * mix(1.0, 0.6 + 0.8 * wn, sp.surf3.y);\nlet stokes = 0.5 * (cos(th) + 0.3 * cos(2.0 * th));\nlet dstokes = 0.5 * (-sin(th) - 0.6 * sin(2.0 * th));\nlet bore = select(1.0 - (u - w) / (1.0 - w), u / w, u < w) - 0.5;\nlet dw = min(0.6 * w, 1.2 / (tr[i].period * sqrt(9.81 * hs)));\nlet rise = saturate(0.5 + u / dw) * saturate(0.5 - (u - w) / dw) + saturate(0.5 + (u - 1.0) / dw);\nlet dbore = (-1.0 / (1.0 - w) + (1.0 / w + 1.0 / (1.0 - w)) * rise) / (2.0 * PI);\nvar e = H * mix(stokes, bore, broken);\nlet de = H * mix(dstokes, dbore, broken);\nlet q = sp.surf1.w * (1.0 - 0.6 * broken);\nvar l = dir * (q * H * 0.5 * sin(th));\nlet onset = broken * (1.0 - broken) * 4.0 * saturate((ratio - 0.9) * 10.0);\nlet top = smoothstep(0.1, 0.5, e / max(H, 1e-3));\nl += dir * (sp.surf3.z * H * 0.55 * onset * top);\ne *= 1.0 + 0.25 * onset * top;\nlet k = 2.0 * PI / (tr[i].period * sqrt(9.81 * hs));\neta += e;\nlean += l;\nslope += -k * de;\nface_k += smoothstep(-0.2, 0.45, e / max(H, 1e-3)) * saturate(H * 1.2) * (1.0 - 0.65 * broken + 0.5 * onset) * TRAINS[i].y;\nlet fo = broken * smoothstep(0.0, w, u) * (exp(-(u - w) * 9.0) + 0.15 * exp(-(u - w) * 3.0)) * saturate(H * 3.0) * TRAINS[i].y;\nfoam = 1.0 - (1.0 - foam) * (1.0 - fo);\ns.broken = max(s.broken, broken);\ns.onset = max(s.onset, onset * TRAINS[i].y);\n}\ns.slope = slope * dir;\ns.d = vec3f(lean.x, eta, lean.y);\ns.face = saturate(-slope * 1.5) * saturate(face_k);\ns.foam = foam;\nlet u = tr[0].u;\ns.phase = u;\ns.amp = tr[0].amp;\ns.ground = -depth;\ns.reach = max(sp2.x, s.ground);\nif (s.ground > -0.05) {\nlet e = s.reach;\nlet rn = mix(vnoise(s.world / 9.0 + vec2f(tr[0].nw * 3.3, 1.0)), vnoise(s.world / 9.0 + vec2f((tr[0].nw + 1.0) * 3.3, 1.0)), tr[0].blend);\nlet run = sp.surf1.x * s.amp * 0.8 * mix(1.0, 0.7 + 0.6 * rn, sp.surf3.y);\ns.run = run;\ns.front_h = swash_front(u, run);\ns.wet_h1 = swash_high(u, run, 1.5 / sp.surf1.y);\ns.wet_h2 = swash_high(u, run, 4.0 / sp.surf1.y);\nlet w = swash_state(e, s.front_h, s.wet_h1, s.wet_h2, run);\ns.cover = w.x;\ns.wet = w.y;\ns.film = w.z;\ns.front = w.w;\n}\nreturn s;\n}\nfn on_beach(d: vec3f, s: Surf, spacing: f32) -> vec3f {\nif (s.ground <= 0.0) {\nreturn d;\n}\nlet reach = sp.surf1.x * 0.8 * 2.0 + 0.05;\nlet y = select(s.ground - 1.0, s.ground + s.film + 0.03 + 0.03 * spacing, s.reach < reach);\nlet k = smoothstep(0.0, 0.04, s.ground);\nreturn vec3f(d.x * (1.0 - k), mix(d.y, y, k), d.z * (1.0 - k));\n}\n@group(0) @binding(17) var surf_a: texture_2d<f32>;\n@group(0) @binding(18) var surf_b: texture_2d<f32>;\n@group(0) @binding(19) var surf_c: texture_2d<f32>;\n@group(0) @binding(20) var surf_d: texture_2d<f32>;\n@group(0) @binding(21) var surf_a_prev: texture_2d<f32>;\n@group(0) @binding(22) var surf_d_prev: texture_2d<f32>;\n@group(0) @binding(23) var out_a: texture_storage_2d<rgba16float, write>;\n@group(0) @binding(24) var out_b: texture_storage_2d<rgba16float, write>;\n@group(0) @binding(25) var out_c: texture_storage_2d<rgba16float, write>;\n@group(0) @binding(26) var out_d: texture_storage_2d<rgba16float, write>;\n@compute @workgroup_size(8, 8) fn surf_field(@builtin(global_invocation_id) id: vec3u) {\nlet n = textureDimensions(out_a);\nif (any(id.xy >= n)) {\nreturn;\n}\nlet uv = (vec2f(id.xy) + 0.5) / vec2f(n);\nlet rest = (uv - sp.surf0.xy) / sp.surf0.z;\nlet s = surf_at(rest, sp.surf3.x);\ntextureStore(out_a, vec2i(id.xy), vec4f(s.d, s.broken));\ntextureStore(out_b, vec2i(id.xy), vec4f(s.slope, s.face, s.onset));\ntextureStore(out_c, vec2i(id.xy), vec4f(s.foam, s.front_h, s.wet_h1, s.wet_h2));\nlet dt = clamp(sp.surf3.x - sp.surf3.w, 0.0, 0.1);\nlet v = surf_flow(s);\nlet extent = 1.0 / sp.surf0.z;\nlet back = uv - v * dt / extent;\nlet texel = 1.0 / vec2f(n);\nlet f0 = textureSampleLevel(surf_d_prev, clamp_smp, back, 0.0).w;\nlet fs = textureSampleLevel(surf_d_prev, clamp_smp, back + vec2f(texel.x, 0.0), 0.0).w + textureSampleLevel(surf_d_prev, clamp_smp, back - vec2f(texel.x, 0.0), 0.0).w + textureSampleLevel(surf_d_prev, clamp_smp, back + vec2f(0.0, texel.y), 0.0).w + textureSampleLevel(surf_d_prev, clamp_smp, back - vec2f(0.0, texel.y), 0.0).w;\nlet carried = mix(f0, fs * 0.25, 0.12 * saturate(dt * 60.0));\nlet life = mix(3.0, 1.2, saturate(carried));\nvar f = carried * exp(-dt / life) * select(1.0, 0.0, s.ground > 0.0 && s.cover < 0.01 && s.wet < 0.05) * select(0.0, 1.0, dt > 0.0);\nf = max(f, saturate(s.foam * 0.9 + s.front * 0.8));\ntextureStore(out_d, vec2i(id.xy), vec4f(s.run, s.phase, s.amp, f));\n}\nfn smooth_sample(t: texture_2d<f32>, uv: vec2f) -> vec4f {\nlet size = vec2f(textureDimensions(t));\nlet p = uv * size - 0.5;\nlet i = floor(p);\nlet f = p - i;\nlet w0 = (1.0 - f) * (1.0 - f) * (1.0 - f) / 6.0;\nlet w1 = (4.0 - 6.0 * f * f + 3.0 * f * f * f) / 6.0;\nlet w2 = (1.0 + 3.0 * f + 3.0 * f * f - 3.0 * f * f * f) / 6.0;\nlet w3 = f * f * f / 6.0;\nlet g0 = w0 + w1;\nlet g1 = w2 + w3;\nlet h0 = (i - 0.5 + w1 / g0) / size;\nlet h1 = (i + 1.5 + w3 / g1) / size;\nlet a = textureSampleLevel(t, clamp_smp, vec2f(h0.x, h0.y), 0.0);\nlet b = textureSampleLevel(t, clamp_smp, vec2f(h1.x, h0.y), 0.0);\nlet c = textureSampleLevel(t, clamp_smp, vec2f(h0.x, h1.y), 0.0);\nlet d = textureSampleLevel(t, clamp_smp, vec2f(h1.x, h1.y), 0.0);\nreturn mix(mix(a, b, g1.x), mix(c, d, g1.x), g1.y);\n}\nfn surf_tex(rest: vec2f, prev: bool) -> Surf {\nvar s: Surf;\ns.fade = 1.0;\ns.amp = 1.0;\nif (sp.surf0.w < 0.5) {\nreturn s;\n}\nlet uv = sp.surf0.xy + rest * sp.surf0.z;\nif (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0))) {\nreturn s;\n}\nlet f = textureSampleLevel(shore_map, clamp_smp, uv, 0.0);\nlet depth = f.x;\ns.fade = smoothstep(0.2, sp.surf2.y, depth);\ns.dir = vec2f(cos(f.w), sin(f.w));\ns.world = uv / sp.surf0.z;\ns.ground = -depth;\ns.reach = max(textureSampleLevel(spill_map, clamp_smp, uv, 0.0).x, s.ground);\nlet a = select(textureSampleLevel(surf_a, clamp_smp, uv, 0.0), textureSampleLevel(surf_a_prev, clamp_smp, uv, 0.0), prev);\ns.d = a.xyz;\ns.broken = a.w;\nlet b = textureSampleLevel(surf_b, clamp_smp, uv, 0.0);\ns.slope = b.xy;\ns.face = b.z;\ns.onset = b.w;\nlet c = textureSampleLevel(surf_c, clamp_smp, uv, 0.0);\nlet dd = textureSampleLevel(surf_d, clamp_smp, uv, 0.0);\nif (s.ground > -0.05) {\nlet e = max(smooth_sample(spill_map, uv).x, -smooth_sample(shore_map, uv).x);\nlet w = swash_state(e, c.y, c.z, c.w, dd.x);\ns.cover = w.x;\ns.wet = w.y;\ns.film = w.z;\ns.front = w.w;\nlet band = saturate(1.0 - abs(c.w - 0.008 - e) / 0.012);\nlet age = saturate(1.0 - (c.w - c.z) / max(dd.x * 0.6, 0.02));\ns.mark = band * (1.0 - w.x) * mix(0.35, 1.0, age) * saturate(c.w * 8.0);\n}\ns.front_h = c.y;\ns.run = dd.x;\ns.phase = dd.y;\ns.amp = dd.z;\ns.fresh = c.x;\ns.foam = max(c.x, dd.w);\nreturn s;\n}\nfn surf_disp(rest: vec2f, spacing: f32, prev: bool) -> vec3f {\nif (sp.surf0.w < 0.5) {\nreturn vec3f(0.0);\n}\nlet uv = sp.surf0.xy + rest * sp.surf0.z;\nif (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0))) {\nreturn vec3f(0.0);\n}\nlet r = 0.35 * spacing * sp.surf0.z;\nvar d = vec3f(0.0);\nfor (var k = 0u; k < 5u; k++) {\nlet o = array<vec2f, 5>(vec2f(0.0), vec2f(r, 0.0), vec2f(-r, 0.0), vec2f(0.0, r), vec2f(0.0, -r))[k];\nlet w = select(0.125, 0.5, k == 0u);\nlet t = select(textureSampleLevel(surf_a, clamp_smp, uv + o, 0.0), textureSampleLevel(surf_a_prev, clamp_smp, uv + o, 0.0), prev);\nd += t.xyz * w;\n}\nreturn d;\n}\nfn surf_flow(s: Surf) -> vec2f {\nlet hs = max(-s.ground, 0.15);\nif (s.ground > -0.05) {\nreturn s.dir * select(-0.9, 1.6, s.phase < 0.18) * s.cover;\n}\nreturn s.dir * clamp(sqrt(9.81 * hs) * max(s.d.y, 0.0) / hs, 0.0, 3.0);\n}\nconst SPRAY_SIDE = 192u;\nconst SPRAY_SPACING = 0.75;\nstruct SprayOut {\n@builtin(position) clip: vec4f,\n@location(0) q: vec4f,\n@location(1) p: vec3f,\n}\n@vertex fn vs_spray(@builtin(vertex_index) vi: u32, @builtin(instance_index) ii: u32) -> SprayOut {\nvar o: SprayOut;\no.clip = vec4f(0.0, 0.0, -1.0, 1.0);\nif (sp.surf0.w < 0.5 || sp.size.y <= 0.0) {\nreturn o;\n}\nlet extent = 1.0 / sp.surf0.z;\nlet eye = sp.surf0.xy * extent;\nlet gx = f32(ii % SPRAY_SIDE) - f32(SPRAY_SIDE / 2u);\nlet gz = f32(ii / SPRAY_SIDE) - f32(SPRAY_SIDE / 2u);\nlet cell = floor(eye / SPRAY_SPACING) + vec2f(gx, gz);\nlet j = vec2f(hash2(cell), hash2(cell + vec2f(91.0, 37.0)));\nlet rest = (cell + j) * SPRAY_SPACING - eye;\nvar s = surf_tex(rest, false);\nlet tuv = sp.surf0.xy + rest * sp.surf0.z;\ns.phase = textureLoad(surf_d, vec2i(clamp(tuv, vec2f(0.0), vec2f(0.9999)) * vec2f(textureDimensions(surf_d))), 0).y;\nlet age = (s.phase - 0.12) * sp.surf1.y;\nlet life = 0.6 + 0.6 * hash2(cell + vec2f(13.0, 71.0));\nif (s.broken < 0.05 || age < 0.0 || age > life || s.ground > 0.3) {\nreturn o;\n}\nlet H = sp.surf1.x * s.amp;\nlet strength = (0.05 * s.broken + s.onset) * saturate(H * 1.2) * (0.4 + 1.2 * hash2(cell + vec2f(5.0, 3.0)));\nlet v0 = (2.0 + 3.0 * hash2(cell + vec2f(2.0, 9.0))) * saturate(H);\nlet k = 1.8;\nlet rise = v0 / k * (1.0 - exp(-k * age)) - 4.0 * age * age * 0.5;\nlet fwd = s.dir * (1.2 * (1.0 - exp(-1.5 * age)));\nlet base = s.d.y + 0.15;\nlet centre = vec3f(rest.x + fwd.x, sp.sea.x + base + max(rise, 0.0), rest.y + fwd.y);\nlet t = age / life;\nlet size = (0.12 + 0.5 * t) * (0.7 + 0.6 * hash2(cell + vec2f(17.0, 23.0))) * (0.6 + 0.6 * saturate(H));\nlet corner = array<vec2f, 6>(vec2f(-1.0, -1.0), vec2f(1.0, -1.0), vec2f(1.0, 1.0), vec2f(-1.0, -1.0), vec2f(1.0, 1.0), vec2f(-1.0, 1.0))[vi];\nlet fwdv = normalize(centre);\nlet right = normalize(cross(vec3f(0.0, 1.0, 0.0), fwdv));\nlet up = cross(fwdv, right);\nlet pos = centre + (right * corner.x + up * corner.y) * size;\no.clip = camera.view_proj * vec4f(pos, 1.0);\nlet alpha = strength * smoothstep(0.0, 0.1, t) * (1.0 - smoothstep(0.4, 1.0, t)) * sp.size.y * 0.32;\no.q = vec4f(corner, alpha, length(centre));\no.p = centre;\nreturn o;\n}\nfn spray_alpha(in: SprayOut) -> f32 {\nlet r2 = dot(in.q.xy, in.q.xy);\nif (r2 > 1.0) {\nreturn 0.0;\n}\nlet wisp = vnoise(in.q.xy * 1.8 + in.p.xz * 3.1) * 0.6 + vnoise(in.q.xy * 4.3 + in.p.xz * 1.7) * 0.4;\nreturn in.q.z * (1.0 - r2) * (1.0 - r2) * smoothstep(0.38, 0.62, wisp) * 1.6;\n}\n@fragment fn fs_spray_reactive(in: SprayOut) -> @location(0) vec4f {\nlet a = spray_alpha(in);\nif (a <= 0.002) {\ndiscard;\n}\nreturn vec4f(saturate(a * 2.0) * wm.reactive.x);\n}\n@fragment fn fs_spray(in: SprayOut) -> @location(0) vec4f {\nvar a = spray_alpha(in);\nif (a <= 0.0) {\ndiscard;\n}\nlet d = textureLoad(under_depth, vec2i(in.clip.xy), 0).x;\nif (d > 0.0) {\nlet gap = in.q.w * (in.clip.z / d - 1.0);\na *= saturate(gap / 0.5);\n}\nlet v = normalize(in.p);\nlet l = camera.light_dir.xyz;\nlet g = 0.75;\nlet cosv = dot(v, l);\nlet hg = (1.0 - g * g) / (4.0 * PI * pow(1.0 + g * g - 2.0 * g * cosv, 1.5));\nlet light = sh_irradiance(vec3f(0.0, 1.0, 0.0)) * 0.6 + lighting.sun_color.rgb * cloud_light(in.p, l) * (hg * 3.0 + 0.05);\nlet fogged = apply_fog(light, in.p, lighting.fog, lighting.fog2, lighting.fog_air.rgb, sh_irradiance(v), lighting.sun_color.rgb, l);\nreturn vec4f(fogged * lighting.env.z * a, a);\n}\nstruct VsOut {\n@builtin(position) clip: vec4f,\n@location(0) p: vec3f,\n@location(1) rest: vec2f,\n@location(2) height: f32,\n}\nfn ring_radius(i: f32) -> f32 {\nlet k = sp.grid.y;\nreturn sp.grid.x * (pow(1.0 + k, i) - 1.0) / k;\n}\nfn grid_point(vi: u32) -> vec3f {\nlet segs = u32(sp.grid.w);\nlet ring = f32(vi / (segs + 1u));\nlet seg = f32(vi % (segs + 1u));\nlet r = ring_radius(ring);\nlet a = 2.0 * PI * seg / f32(segs);\nreturn vec3f(vec2f(cos(a), sin(a)) * r, max(ring_radius(ring + 1.0) - r, 2.0 * PI * r / f32(segs)));\n}\nfn calm(c: u32, fade: f32) -> f32 {\nreturn mix(1.0, fade, saturate(1.0 / (sp.cascade[c].z * 150.0)));\n}\nfn displaced(field: texture_2d_array<f32>, rest: vec2f, spacing: f32, fade: f32) -> vec3f {\nvar d = vec3f(0.0);\nfor (var c = 0u; c < u32(sp.sea.y); c++) {\nlet cc = sp.cascade[c];\nlet texel = 1.0 / (cc.z * sp.size.x);\nlet w = (1.0 - smoothstep(1.0, 4.0, spacing / texel)) * calm(c, fade);\nif (w <= 0.0) {\ncontinue;\n}\nd += textureSampleLevel(field, wrap, cc.xy + rest * cc.z, c, 0.0).xyz * w;\n}\nreturn d;\n}\nstruct MotionOut {\n@builtin(position) clip: vec4f,\n@location(0) now: vec4f,\n@location(1) then: vec4f,\n@location(2) rest: vec2f,\n}\n@vertex fn vs_motion(@builtin(vertex_index) vi: u32) -> MotionOut {\nlet g = grid_point(vi);\nlet rest = g.xy;\nlet drop = dot(rest, rest) / (2.0 * EARTH_RADIUS);\nlet sn = surf_tex(rest, false);\nlet sq = surf_tex(rest, wm.eye_move.w > 0.5);\nlet dn = on_beach(displaced(displacement, rest, g.z, sn.fade) + surf_disp(rest, g.z, false), sn, g.z);\nlet dp = select(dn, on_beach(displaced(prev_displacement, rest, g.z, sq.fade) + surf_disp(rest, g.z, true), sq, g.z), wm.eye_move.w > 0.5);\nlet p = vec3f(rest.x + dn.x, sp.sea.x + dn.y - drop, rest.y + dn.z);\nlet q = vec3f(rest.x + dp.x, sp.sea.x + dp.y - drop, rest.y + dp.z) + wm.eye_move.xyz;\nvar o: MotionOut;\no.now = wm.view_proj * vec4f(p, 1.0);\no.clip = camera.view_proj * vec4f(p, 1.0);\no.then = wm.prev_view_proj * vec4f(q, 1.0);\no.rest = rest;\nreturn o;\n}\nfn motion_of(in: MotionOut) -> vec4f {\nlet now = in.now.xy / in.now.w;\nlet then = in.then.xy / max(in.then.w, 1e-6);\nreturn vec4f((now - then) * vec2f(0.5, -0.5), in.then.w, 1.0);\n}\n@fragment fn fs_motion(in: MotionOut) -> @location(0) vec4f {\nreturn motion_of(in);\n}\nstruct MotionReactive {\n@location(0) motion: vec4f,\n@location(1) reactive: vec4f,\n}\n@fragment fn fs_motion_reactive(in: MotionOut) -> MotionReactive {\nvar o: MotionReactive;\no.motion = motion_of(in);\nlet s = surf_tex(in.rest, false);\no.reactive = vec4f(saturate(s.fresh * 0.9 + s.front * 0.7 + s.onset * 0.6) * wm.reactive.x);\nreturn o;\n}\n@vertex fn vs(@builtin(vertex_index) vi: u32) -> VsOut {\nlet segs = u32(sp.grid.w);\nlet ring = f32(vi / (segs + 1u));\nlet seg = f32(vi % (segs + 1u));\nlet r = ring_radius(ring);\nlet a = 2.0 * PI * seg / f32(segs);\nlet rest = vec2f(cos(a), sin(a)) * r;\nlet spacing = max(ring_radius(ring + 1.0) - r, 2.0 * PI * r / f32(segs));\nlet sf = surf_tex(rest, false);\nvar d = vec3f(0.0);\nfor (var c = 0u; c < u32(sp.sea.y); c++) {\nlet cc = sp.cascade[c];\nlet texel = 1.0 / (cc.z * sp.size.x);\nlet w = (1.0 - smoothstep(1.0, 4.0, spacing / texel)) * calm(c, sf.fade);\nif (w <= 0.0) {\ncontinue;\n}\nd += textureSampleLevel(displacement, wrap, cc.xy + rest * cc.z, c, 0.0).xyz * w;\n}\nd = on_beach(d + surf_disp(rest, spacing, false), sf, spacing);\nlet drop = dot(rest, rest) / (2.0 * EARTH_RADIUS);\nlet p = vec3f(rest.x + d.x, sp.sea.x + d.y - drop, rest.y + d.z);\nvar o: VsOut;\no.clip = camera.view_proj * vec4f(p, 1.0);\no.p = p;\no.rest = rest;\no.height = d.y;\nreturn o;\n}\nfn sh_irradiance(n: vec3f) -> vec3f {\nreturn max(lighting.sh[0].rgb * 0.282095 + lighting.sh[1].rgb * (0.488603 * n.y) + lighting.sh[2].rgb * (0.488603 * n.z) + lighting.sh[3].rgb * (0.488603 * n.x) + lighting.sh[4].rgb * (1.092548 * n.x * n.y) + lighting.sh[5].rgb * (1.092548 * n.y * n.z) + lighting.sh[6].rgb * (0.315392 * (3.0 * n.z * n.z - 1.0)) + lighting.sh[7].rgb * (1.092548 * n.x * n.z) + lighting.sh[8].rgb * (0.546274 * (n.x * n.x - n.y * n.y)), vec3f(0.0));\n}\n@compute @workgroup_size(8, 8) fn copy_depth(@builtin(global_invocation_id) id: vec3u) {\nlet size = textureDimensions(scene_depth);\nif (id.x >= size.x || id.y >= size.y) {\nreturn;\n}\ntextureStore(depth_out, vec2i(id.xy), vec4f(textureLoad(scene_depth, vec2i(id.xy), 0), 0.0, 0.0, 0.0));\n}\nfn thickness(dist: f32, ds: f32, dop: f32) -> f32 {\nreturn select(1.0e9, dist * max(ds / dop - 1.0, 0.0), dop > 0.0);\n}\nfn mirror_probe(p: vec3f, r: vec3f, t: f32, size: vec2f) -> vec4f {\nlet c = camera.view_proj * vec4f(p + r * t, 1.0);\nif (c.w <= 0.0) {\nreturn vec4f(0.0);\n}\nlet ndc = c.xyz / c.w;\nlet uv = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);\nif (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0))) {\nreturn vec4f(0.0);\n}\nlet d = textureLoad(under_depth, vec2i(uv * size), 0).x;\nlet gap = select(-1.0e9, c.w * (1.0 - ndc.z / d), d > 0.0);\nreturn vec4f(uv, gap, 1.0);\n}\nfn mirror_scene(p: vec3f, r: vec3f, size: vec2f, jitter: f32) -> vec4f {\nlet len = sp.mirror.x;\nlet steps = u32(sp.mirror.y);\nif (len <= 0.0 || steps == 0u || sp.shore.y < 0.5 || r.y <= 0.0) {\nreturn vec4f(0.0);\n}\nvar prev = 0.0;\nvar was_behind = false;\nfor (var i = 1u; i <= steps; i++) {\nlet f = (f32(i) - jitter) / f32(steps);\nlet t = len * f * f;\nlet s = mirror_probe(p, r, t, size);\nif (s.w < 0.5) {\nbreak;\n}\nlet behind = s.z > 0.0;\nif (behind && !was_behind) {\nvar a = prev;\nvar b = t;\nvar hit = s;\nfor (var k = 0u; k < 6u; k++) {\nlet m = 0.5 * (a + b);\nlet sm = mirror_probe(p, r, m, size);\nif (sm.w > 0.5 && sm.z > 0.0) {\nb = m;\nhit = sm;\n} else {\na = m;\n}\n}\nif (hit.z < sp.mirror.z + 0.03 * b) {\nlet uv = hit.xy;\nlet edge = min(min(uv.x, 1.0 - uv.x), min(uv.y, 1.0 - uv.y));\nlet fade = saturate(edge * 12.0) * (1.0 - smoothstep(0.7, 1.0, b / len));\nreturn vec4f(textureSampleLevel(under, clamp_smp, uv, 0.0).rgb * sp.shore.z, fade);\n}\n}\nwas_behind = behind;\nprev = t;\n}\nreturn vec4f(0.0);\n}\nstruct WetParams { inv_view_proj: mat4x4f }\n@group(0) @binding(28) var<uniform> wp: WetParams;\nstruct WetOut { @builtin(position) clip: vec4f }\n@vertex fn vs_wet(@builtin(vertex_index) i: u32) -> WetOut {\nlet p = vec2f(f32((i << 1u) & 2u), f32(i & 2u)) * 2.0 - 1.0;\nvar o: WetOut;\no.clip = vec4f(p, 0.0, 1.0);\nreturn o;\n}\n@fragment fn fs_wet(in: WetOut) -> @location(0) vec4f {\nlet px = vec2i(in.clip.xy);\nlet d = textureLoad(scene_depth, px, 0);\nif (d <= 0.0 || sp.surf0.w < 0.5) {\ndiscard;\n}\nlet size = vec2f(textureDimensions(scene_depth));\nlet ndc = vec2f(in.clip.x / size.x * 2.0 - 1.0, 1.0 - in.clip.y / size.y * 2.0);\nlet w4 = wp.inv_view_proj * vec4f(ndc, d, 1.0);\nlet p = w4.xyz / w4.w;\nlet uv = sp.surf0.xy + p.xz * sp.surf0.z;\nif (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0))) {\ndiscard;\n}\nlet h = p.y - sp.sea.x;\nlet ground = -smooth_sample(shore_map, uv).x;\nif (h < ground + 0.05) {\ndiscard;\n}\nlet c = textureSampleLevel(surf_c, clamp_smp, uv, 0.0);\nlet dd = textureSampleLevel(surf_d, clamp_smp, uv, 0.0);\nlet wash = select(sp.surf1.x * 0.6 * dd.z, 0.0, ground > 0.0);\nlet world = uv / sp.surf0.z;\nlet ragged = (vnoise(world * 3.0) - 0.5) * 0.12 + (vnoise(vec2f(world.x * 9.0, world.y * 9.0) + vec2f(5.0, 1.0)) - 0.5) * 0.05;\nlet top = max(c.w, wash) + ragged;\nlet drip = smoothstep(0.55, 0.9, vnoise(vec2f(world.x * 14.0 + world.y * 14.0, 0.5))) * 0.08;\nlet wet = (1.0 - smoothstep(top - 0.02, top + 0.04 + drip, h)) * saturate(top * 6.0);\nif (wet <= 0.001) {\ndiscard;\n}\nreturn vec4f(vec3f(mix(1.0, 0.58, wet)), 1.0);\n}\nfn ign(px: vec2f) -> f32 {\nlet frame = floor(camera.light_dir.w * 60.0) % 64.0;\nlet q = px + 5.588238 * frame;\nreturn fract(52.9829189 * fract(dot(q, vec2f(0.06711056, 0.00583715))));\n}\nfn glint_hash(c: vec2i, lod: i32, bucket: i32, s: u32) -> f32 {\nlet h = pcg(bitcast<u32>(c.x) ^ pcg(bitcast<u32>(c.y) ^ pcg(bitcast<u32>(lod * 7919 + bucket) + s * 0x9e3779b9u)));\nreturn f32(h) / 4294967296.0;\n}\nfn poisson(mu: f32, u: f32, g: vec3f) -> f32 {\nif (mu > 12.0) {\nreturn max(0.0, round(mu + sqrt(mu) * (g.x + g.y + g.z - 1.5) * 2.0));\n}\nvar k = 0.0;\nvar pk = exp(-mu);\nvar cdf = pk;\nfor (var i = 0u; i < 32u; i++) {\nif (u <= cdf) {\nbreak;\n}\nk += 1.0;\npk *= mu / k;\ncdf += pk;\n}\nreturn k;\n}\nfn glint_level(w: vec2f, size: vec2f, lod: i32, mu: f32) -> f32 {\nlet c = vec2i(floor(w / size));\nlet t = sp.surf3.x / sp.glint.w;\nvar sum = 0.0;\nfor (var j = 0; j < 2; j++) {\nlet phase = t + 0.5 * f32(j);\nlet bucket = i32(floor(phase)) * 2 + j;\nlet wt = 1.0 - abs(2.0 * fract(phase) - 1.0);\nlet k = poisson(mu, glint_hash(c, lod, bucket, 0u), vec3f(glint_hash(c, lod, bucket, 1u), glint_hash(c, lod, bucket, 2u), glint_hash(c, lod, bucket, 3u)));\nsum += wt * k / max(mu, 1e-12);\n}\nreturn sum;\n}\nconst GLINT_ANGLE = 1.0e-5;\nfn glint_ratio(w: vec2f, ext: vec2f, p: f32) -> f32 {\nif (sp.glint.x <= 0.0 || p <= 0.0) {\nreturn 1.0;\n}\nlet facet = sp.glint.y;\nlet r = max(w.y, 1e-3);\nlet facets = ext.x * ext.y / (facet * facet);\nlet level = vec2f(log2(max(ext.x / (r * GLINT_ANGLE), 1.0)), log2(max(ext.y / facet, 1.0)));\nlet l0 = floor(level);\nlet f = level - l0;\nlet s0 = vec2f(GLINT_ANGLE, facet) * exp2(l0);\nlet mu = s0.x * r * s0.y / (facet * facet) * p;\nlet id = i32(l0.x) * 64 + i32(l0.y);\nlet r0 = mix(glint_level(w, s0, id, mu), glint_level(w, s0 * vec2f(2.0, 1.0), id + 64, mu * 2.0), f.x);\nlet r1 = mix(glint_level(w, s0 * vec2f(1.0, 2.0), id + 1, mu * 2.0), glint_level(w, s0 * 2.0, id + 65, mu * 4.0), f.x);\nreturn mix(1.0, mix(r0, r1, f.y), sp.glint.x * smoothstep(1.0, 16.0, facets));\n}\n@fragment fn fs(in: VsOut) -> @location(0) vec4f {\nlet frag = in.clip;\nvar sx = 0.0;\nvar sz = 0.0;\nvar lost = 0.0;\nvar fold = 0.0;\nlet sf = surf_tex(in.rest, false);\nfor (var c = 0u; c < u32(sp.sea.y); c++) {\nlet cc = sp.cascade[c];\nlet uv = cc.xy + in.rest * cc.z;\nlet texels = length(fwidth(uv)) * sp.size.x;\nlet resolved = 1.0 - smoothstep(0.5, 2.0, texels);\nlet w = resolved * calm(c, sf.fade);\nlet s = textureSampleLevel(slopes, wrap, uv, c, 0.0);\nsx += s.x * w;\nsz += s.y * w;\nlost += cc.w * (1.0 - resolved) * calm(c, sf.fade);\nfold = max(fold, max(saturate((sp.sea.z - s.z) * 2.0), s.w) * w);\n}\nlet fine = u32(sp.sea.y) - 1u;\nfor (var k = 0u; k < 2u; k++) {\nlet scale = select(8.3, 3.1, k == 0u);\nlet amp = select(0.5, 0.7, k == 0u) * sp.shore.w;\nlet ang = select(1.24, 0.65, k == 0u);\nlet rot = mat2x2f(cos(ang), sin(ang), -sin(ang), cos(ang));\nlet q = rot * (sp.cascade[fine].xy + in.rest * sp.cascade[fine].z) * scale + vec2f(0.37, 0.71) * f32(k + 1u);\nlet resolved = 1.0 - smoothstep(0.5, 2.0, length(fwidth(q)) * sp.size.x);\nlet t = textureSampleLevel(slopes, wrap, q, fine, 0.0).xy * amp;\nlet back = transpose(rot) * t;\nsx += back.x * resolved;\nsz += back.y * resolved;\nlost += sp.cascade[fine].w * amp * amp * (1.0 - resolved);\n}\nsx += sf.slope.x;\nsz += sf.slope.y;\nfold *= sf.fade;\nlet n = normalize(vec3f(-sx, 1.0, -sz));\nlet v = normalize(-in.p);\nlet l = camera.light_dir.xyz;\nlet nv = max(dot(n, v), 1e-3);\nlet nl = max(dot(n, l), 0.0);\nlet dnx = dpdx(n);\nlet dny = dpdy(n);\nlet kernel = min(0.25 * (dot(dnx, dnx) + dot(dny, dny)), 0.18);\nlet a2 = max(sp.body.w * sp.body.w + 2.0 * lost + kernel, 1e-4);\nlet rdx = dpdx(in.rest);\nlet rdy = dpdy(in.rest);\nlet gr = length(in.rest);\nlet ahead = select(vec2f(0.0, 1.0), in.rest / gr, gr > 1e-4);\nlet across = vec2f(-ahead.y, ahead.x);\nlet gpos = vec2f(atan2(in.rest.y, in.rest.x), gr);\nlet gext = vec2f(length(vec2f(dot(rdx, across), dot(rdy, across))), length(vec2f(dot(rdx, ahead), dot(rdy, ahead))));\nlet f0 = 0.02;\nlet fresnel = f0 + (1.0 - f0) * pow(1.0 - nv, 5.0);\nvar r = reflect(-v, n);\nr.y = abs(r.y);\nlet lod = clamp(sqrt(sqrt(a2)) * (lighting.env.x - 1.0), 0.0, lighting.env.x - 1.0);\nvar sky = textureSampleLevel(env_cube, wrap, r, lod).rgb * lighting.view.rgb;\nif (sp.size.z > 0.5) {\nlet pn = vec2f(textureDimensions(cloud_pano));\nlet pv = clamp(sqrt(max(r.y, 0.0)), 0.5 / pn.y, 1.0 - 0.5 / pn.y);\nlet cl = textureSampleLevel(cloud_pano, wrap, vec2f(fract(atan2(r.z, r.x) / (2.0 * PI)), pv), 0.0);\nsky = sky * cl.a + cl.rgb;\n}\nlet seen = mirror_scene(in.p, reflect(-v, n), vec2f(textureDimensions(under)), ign(frag.xy));\nsky = mix(sky, seen.rgb, seen.a);\nif (sp.mirror.w > 0.5) {\nreturn vec4f(select(vec3f(seen.a * 0.2, 0.0, 0.0) + seen.rgb * seen.a, vec3f(0.3, 0.0, 0.0), seen.a <= 0.0) * lighting.env.z, 1.0);\n}\nlet h = normalize(v + l);\nlet nh = max(dot(n, h), 0.0);\nlet dd = nh * nh * (a2 - 1.0) + 1.0;\nlet ggx = a2 / (PI * dd * dd);\nlet vis = 0.5 / (nl * sqrt(nv * nv * (1.0 - a2) + a2) + nv * sqrt(nl * nl * (1.0 - a2) + a2) + 1e-5);\nlet fh = f0 + (1.0 - f0) * pow(1.0 - max(dot(v, h), 0.0), 5.0);\nlet shade = cloud_light(in.p, l);\nlet sun = lighting.sun_color.rgb * shade;\nlet vh = max(dot(v, h), 1e-3);\nlet p_facet = min(ggx * nh * sp.glint.z / (4.0 * vh), 1.0);\nlet spec = sun * (ggx * vis * fh * nl * glint_ratio(gpos, gext, p_facet));\nlet up = sh_irradiance(vec3f(0.0, 1.0, 0.0));\nlet body = sp.body.rgb * (up + sun * (max(l.y, 0.0) / PI) * 0.5);\nlet behind = pow(saturate(dot(-v, l)), 8.0);\nlet crest = saturate(in.height * 0.5) * saturate(1.0 - n.y * 0.9 + 0.2);\nvar sss = sp.sss.rgb * sun * (behind * crest * sp.sss.w);\nlet wide = 0.35 + 0.65 * pow(saturate(dot(-v, l) * 0.5 + 0.5), 3.0);\nlet thin = sf.face * (0.6 + 0.4 * saturate(1.0 - abs(n.y) * 1.2));\nsss += sp.sss.rgb * (up * 4.5 + sun * (0.8 * wide)) * (thin * sp.sss.w * 2.5);\nvar below = body;\nvar shore_foam = 0.0;\nif (sp.shore.y > 0.5) {\nlet px = vec2i(frag.xy);\nlet size = vec2f(textureDimensions(under));\nlet dist = length(in.p);\nlet t0 = thickness(dist, frag.z, textureLoad(under_depth, px, 0).x);\nlet bend = n.xz * sp.optics.w * min(t0, 1.0);\nvar uv = (frag.xy + bend) / size;\nif (any(uv < vec2f(0.0)) || any(uv >= vec2f(1.0))) {\nuv = frag.xy / size;\n}\nvar t = thickness(dist, frag.z, textureLoad(under_depth, vec2i(uv * size), 0).x);\nif (textureLoad(under_depth, vec2i(uv * size), 0).x > frag.z) {\nuv = frag.xy / size;\nt = t0;\n}\nvar behind_light = textureSampleLevel(under, clamp_smp, uv, 0.0).rgb * sp.shore.z;\nlet down = max(t * v.y, 0.0);\nlet sun_in = sqrt(max(1.0 - (1.0 - l.y * l.y) / 1.7689, 0.04));\nlet above = (in.p - v * t).xz + vec2f(l.x, l.z) / sun_in * down * (1.0 / 1.33);\nvar lap = 0.0;\nlet cn = u32(sp.sea.y);\nlet c0 = select(0u, cn - 2u, cn >= 2u);\nfor (var c = c0; c < select(c0, select(1u, cn - 1u, cn >= 2u), sp.size.w > 0.5); c++) {\nlet cc = sp.cascade[c];\nlet e = 2.0 / (cc.z * sp.size.x);\nlet q = cc.xy + above * cc.z;\nlet ex = vec2f(cc.z * e, 0.0);\nlet ez = vec2f(0.0, cc.z * e);\nlet dsx = textureSampleLevel(slopes, wrap, q + ex, c, 0.0).x - textureSampleLevel(slopes, wrap, q - ex, c, 0.0).x;\nlet dsz = textureSampleLevel(slopes, wrap, q + ez, c, 0.0).y - textureSampleLevel(slopes, wrap, q - ez, c, 0.0).y;\nlap += (dsx + dsz) / (2.0 * e);\n}\nlet focus = 1.0 / max(abs(1.0 - down * 0.248 * lap), 0.25);\nlet caustic = mix(1.0, focus, saturate(down * 3.0) * exp(-down / 4.0) * saturate(l.y * 4.0) * shade);\nbehind_light *= mix(1.0, caustic, 0.65);\nlet trans = exp(-sp.optics.rgb * (t + down / sun_in));\nbelow = behind_light * trans + body * (vec3f(1.0) - exp(-sp.optics.rgb * t));\nlet vertical = t0 * max(v.y, 0.02);\nshore_foam = saturate(1.0 - vertical / sp.shore.x) * saturate(vertical * 40.0);\nif (sp.surf0.w > 0.5) {\nlet object = saturate((-sf.ground - vertical - 0.15) / 0.3);\nshore_foam = mix(shore_foam, saturate(1.0 - vertical / 0.035) * saturate(vertical * 60.0) * 0.8, object);\n}\n}\nlet sigma = sqrt(lost);\nlet mean_f = f0 + (1.0 - f0) * pow(1.0 - nv, 5.0 * exp(-2.69 * sigma)) / (1.0 + 22.7 * pow(sigma, 1.5));\nlet fr = min(fresnel, mean_f);\nlet water = sky * fr + (below + sss) * (1.0 - fr) + spec;\nlet foam_light = (sh_irradiance(n) + sun * (nl / PI)) * 0.85;\nvar fp = (sp.cascade[0].xy + in.rest * sp.cascade[0].z) / sp.cascade[0].z;\nif (sf.foam > 0.001 || sf.front > 0.001) {\nfp = sf.world;\n}\nlet flow = surf_flow(sf);\nlet cyc = 1.0;\nlet ph0 = fract(sp.surf3.x / cyc);\nlet ph1 = fract(sp.surf3.x / cyc + 0.5);\nlet fp0 = fp - flow * (ph0 * cyc) + vec2f(13.7, 5.1) * floor(sp.surf3.x / cyc);\nlet fp1 = fp - flow * (ph1 * cyc) + vec2f(7.3, 11.9) * floor(sp.surf3.x / cyc + 0.5);\nlet wflow = abs(2.0 * ph0 - 1.0);\nlet beach = smoothstep(0.0, 0.04, sf.ground);\nvar drain = 1.0;\nif (beach > 0.0 && sf.cover > 0.0 && sf.phase > 0.18) {\nlet along = dot(sf.world, sf.dir);\nlet across = dot(sf.world, vec2f(-sf.dir.y, sf.dir.x)) + (vnoise(vec2f(along * 0.6, 0.0)) - 0.5) * 1.4 + (vnoise(vec2f(along * 1.7, 5.0)) - 0.5) * 0.5;\nlet rill = vnoise(vec2f(across * 1.5, along * 0.12)) * 0.6 + vnoise(vec2f(across * 4.3, along * 0.3) + vec2f(17.0, 3.0)) * 0.4;\nlet k = smoothstep(0.2, 0.5, sf.phase) * beach * smoothstep(0.35, 0.65, vnoise(sf.world / 7.0 + vec2f(3.0, 9.0)));\ndrain = mix(1.0, smoothstep(0.38, 0.62, rill), k);\n}\nvar cover = saturate(fold * sp.sea.w + shore_foam * (1.0 - beach) + (sf.foam + sf.front * 0.9) * mix(1.0, drain, beach) + sf.mark * 0.55);\nif (cover > 0.002) {\nlet spots = vnoise(fp * 0.22) * 0.65 + vnoise(fp * 0.61 + vec2f(9.0, 4.0)) * 0.35;\ncover *= mix(smoothstep(0.35, 0.75, spots), 1.0, smoothstep(0.35, 0.8, cover));\nlet clumps = vnoise(fp * 0.35 + vec2f(3.0, 7.0)) * 0.6 + vnoise(fp * 1.1 + vec2f(11.0, 2.0)) * 0.4;\ncover *= mix(1.0, 0.45 + 0.75 * clumps, smoothstep(0.3, 0.7, cover));\n}\nlet fw = length(fwidth(fp0));\nvar foam = 0.0;\nvar bubbles = 1.0;\nif (cover > 0.002) {\nvar net = foam_net(fp0, cover, fw);\nif (dot(flow, flow) > 1e-4) {\nnet = mix(net, foam_net(fp1, cover, fw), wflow);\n}\nfoam = net.x;\nbubbles = net.y;\n}\nlet aerated = saturate(sf.fresh * 1.6) * (1.0 - beach);\nlet milky = mix(sp.sss.rgb * 2.5, vec3f(0.85, 0.95, 0.95), 0.55) * (sh_irradiance(n) + sun * (nl / PI));\nvar foam_lit = foam_light;\nif (foam > 0.01 && sp.surf0.w > 0.5) {\nlet fuv = sp.surf0.xy + in.rest * sp.surf0.z;\nlet ft = 1.0 / vec2f(textureDimensions(surf_d));\nlet gx = textureSampleLevel(surf_d, clamp_smp, fuv + vec2f(ft.x, 0.0), 0.0).w - textureSampleLevel(surf_d, clamp_smp, fuv - vec2f(ft.x, 0.0), 0.0).w;\nlet gz = textureSampleLevel(surf_d, clamp_smp, fuv + vec2f(0.0, ft.y), 0.0).w - textureSampleLevel(surf_d, clamp_smp, fuv - vec2f(0.0, ft.y), 0.0).w;\nlet per_m = 0.1 / (2.0 * ft.x / sp.surf0.z);\nlet nf = normalize(n + vec3f(-gx, 0.0, -gz) * per_m * 0.5);\nfoam_lit = (sh_irradiance(nf) + sun * (max(dot(nf, l), 0.0) / PI)) * 0.85;\n}\nvar lit = mix(mix(water, milky, aerated * 0.65), foam_lit * bubbles, foam);\nif (sf.ground > 0.0 && sp.shore.y > 0.5) {\nlet sand = textureLoad(under, vec2i(frag.xy), 0).rgb * sp.shore.z;\nlet wet_rough = 0.12;\nlet sheen = textureSampleLevel(env_cube, wrap, r, clamp(sqrt(wet_rough) * (lighting.env.x - 1.0), 0.0, lighting.env.x - 1.0)).rgb * lighting.view.rgb;\nlet wet_sand = sand * mix(1.0, 0.5, sf.wet) + sheen * fresnel * sf.wet * 0.8;\nlit = mix(wet_sand, lit, max(sf.cover * drain, foam));\n}\nlet fogged = apply_fog(lit, in.p, lighting.fog, lighting.fog2, lighting.fog_air.rgb, sh_irradiance(normalize(in.p)), sun, l);\nreturn vec4f(fogged * lighting.env.z, 1.0);\n}\nstruct Camera {\nview_proj: mat4x4f,\nfrustum: array<vec4f, 6>,\nlight_dir: vec4f,\norigin: vec4f,\n}\nstruct Lighting {\nsun_color: vec4f,\nenv: vec4f,\nsh: array<vec4f, 9>,\nscreen: vec4f,\nview: vec4f,\nao_reproject: mat4x4f,\nao: vec4f,\nfog: vec4f,\nfog2: vec4f,\nfog_air: vec4f,\ngi_grid: vec4f,\ngi_counts: vec4f,\nssr: vec4f,\ngi_cascades: array<vec4f, 3>,\nclouds: vec4f,\n}\nfn apply_fog(color: vec3f, p: vec3f, fog: vec4f, fog2: vec4f, air: vec3f, ambient: vec3f, sun: vec3f, sun_dir: vec3f) -> vec3f {\nlet dist = length(p);\nif (dist <= 0.0 || (fog.x <= 0.0 && fog2.x <= 0.0)) {\nreturn color;\n}\nlet dir = p / dist;\nlet t = exp(-(air * (fog2.x * dist) + vec3f(height_fog_depth(fog, dir, dist))));\nreturn color * t + fog_inscatter(fog2, dir, ambient, sun, sun_dir) * (vec3f(1.0) - t);\n}\nfn height_fog_depth(fog: vec4f, dir: vec3f, dist: f32) -> f32 {\nif (fog.x <= 0.0) {\nreturn 0.0;\n}\nlet falloff = max(fog.y, 1e-5);\nlet len = max(dist - fog.w, 0.0);\nlet h0 = fog.z + dir.y * min(dist, fog.w);\nlet k = falloff * dir.y * len;\nlet shape = select((1.0 - exp(-k)) / k, 1.0 - 0.5 * k, abs(k) < 1e-3);\nreturn fog.x * exp(-falloff * h0) * len * shape;\n}\nfn fog_inscatter(fog2: vec4f, dir: vec3f, ambient: vec3f, sun: vec3f, sun_dir: vec3f) -> vec3f {\nlet g = fog2.z;\nlet c = dot(dir, sun_dir);\nlet phase = (1.0 - g * g) / (FOUR_PI * pow(max(1.0 + g * g - 2.0 * g * c, 1e-4), 1.5));\nreturn ambient + sun * (phase * fog2.y);\n}\nconst FOUR_PI = 12.5663706;",
12
12
  };
13
13
  //# sourceMappingURL=water-shaders.gen.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"water-shaders.gen.js","sourceRoot":"","sources":["../src/water-shaders.gen.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,yEAAyE;AAEzE,4CAA4C;AAC5C,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,kBAAkB;IACxB,IAAI,EAAE,8+GAA8+G;CAC5+G,CAAC;AAEX,8CAA8C;AAC9C,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,kBAAkB;IACxB,IAAI,EAAE,youBAAyouB;CACvouB,CAAC"}
1
+ {"version":3,"file":"water-shaders.gen.js","sourceRoot":"","sources":["../src/water-shaders.gen.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,yEAAyE;AAEzE,4CAA4C;AAC5C,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,kBAAkB;IACxB,IAAI,EAAE,8+GAA8+G;CAC5+G,CAAC;AAEX,8CAA8C;AAC9C,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,kBAAkB;IACxB,IAAI,EAAE,0+uCAA0+uC;CACx+uC,CAAC"}
package/dist/water.d.ts CHANGED
@@ -102,9 +102,29 @@ export declare class Water implements RenderFeature {
102
102
  surfLip: number;
103
103
  /** Spray thrown up by breaking waves (ADR-0218; live, 0 = none, 1 = default). */
104
104
  spray: number;
105
+ /** The ripples finer than the cascades (ADR-0223; live, 0 none, 1 Cox & Munk's slope for the wind). */
106
+ ripples: number;
107
+ /** Rocks the swash and the surf reach are wet: darker below the water's line (ADR-0228; live). */
108
+ wetRocks: boolean;
109
+ /**
110
+ * How strongly fresh whitewater, the swash's front, the lip and spray mark TAA's reactive mask (ADR-0229; live, 0–1,
111
+ * default 0.6), where TAA runs with `taa: { reactive: true }`: their shading moves unlike the surface's points, so TAA
112
+ * leans on this frame there instead of smearing them.
113
+ */
114
+ reactive: number;
115
+ /**
116
+ * Sun glints (ADR-0226; live, 0 = the smooth highlight only, 1 = default): far water mirrors the sun from a few ripples
117
+ * at a time, so the glitter path is sparkles that rise and fade, keeping the smooth highlight's mean.
118
+ */
119
+ glints: number;
120
+ /** A glinting facet's size, metres (ADR-0226; live, default 0.02: the capillary ripples). Smaller = more, finer sparkles. */
121
+ glintSize: number;
122
+ /** How long a glint lives, seconds (ADR-0226; live, default 0.3). */
123
+ glintLife: number;
105
124
  /**
106
125
  * The quality tier (ADR-0220; live): `high` everything; `medium` the coarse foam pattern only, 16 reflection steps, half
107
- * the spray; `low` no caustics, no scene reflections, no spray. Setting it sets those knobs (they stay live after).
126
+ * the spray; `low` no caustics, no scene reflections, no spray, and a 256² surf field (1 m texels, ADR-0232). Setting it
127
+ * sets those knobs (they stay live after).
108
128
  */
109
129
  get quality(): WaterQuality;
110
130
  set quality(q: WaterQuality);
@@ -113,6 +133,15 @@ export declare class Water implements RenderFeature {
113
133
  constructor(device: GPUDevice, o: WaterOptions);
114
134
  init(cache: PipelineCache, format: GPUTextureFormat): Promise<void>;
115
135
  prepare(f: RenderFeatureFrame): void;
136
+ /**
137
+ * The surf field's GPU cost, ms a frame (ADR-0223): one submission of `frames` + 1 dispatches less one of 1, over
138
+ * `frames`, each timed to `onSubmittedWorkDone` (the render loop's own frames land in either window, and the difference
139
+ * cancels them; the field runs in its own submission, which the frame graph's timers do not see). Null without a seabed.
140
+ *
141
+ * @example
142
+ * const ms = await ocean.measureSurf();
143
+ */
144
+ measureSurf(frames?: number): Promise<number | null>;
116
145
  /**
117
146
  * The seabed under the water (ADR-0213): bakes the shore field (`bakeShore`: depth, the crests' arrival phase for
118
147
  * `surfPeriod`, their direction) and uploads it; the surf then rises, bends to the beach and breaks. Call again after
@@ -124,7 +153,7 @@ export declare class Water implements RenderFeature {
124
153
  setSeabed(seabed: Seabed): ShoreField;
125
154
  /** The baked shore field, or null before `setSeabed`. */
126
155
  get shoreField(): ShoreField | null;
127
- addMotionPasses(graph: FrameGraph, motion: string, depth: string): void;
156
+ addMotionPasses(graph: FrameGraph, motion: string, depth: string, reactive?: string | null): void;
128
157
  addPasses(graph: FrameGraph, color: string, depth: string, scene: SceneRenderer): void;
129
158
  /**
130
159
  * Its agent tools (ADR-0204): `water.state` reads the sea, `water.set` turns its knobs, `water.sea` makes a new sea.