4const LOD_COUNT: u32 = 5u; 5const CASCADE_COUNT: u32 = LOD_COUNT + 1u; 6const WAVE_COUNT: u32 = 40u; 7const TAU: f32 = 6.283185307179586; 8const PI: f32 = 3.141592653589793; 20 cascades: array<CascadeParams, CASCADE_COUNT>, 23 bed_transform: vec4<f32>, 31 angular_frequency: f32, 35struct AnimWavesUniform { 36 cascade_layout: CascadeLayout, 37 waves: array<GerstnerWave, WAVE_COUNT>, 38 ranges: array<vec4<u32>, LOD_COUNT>, 43@group(0) @binding(0) var output: texture_storage_2d_array<rgba16float, write>; 44@group(0) @binding(1) var<uniform> params: AnimWavesUniform; 45@group(0) @binding(2) var bed_height: texture_2d<f32>; 48const NO_BED_DEPTH: f32 = 256.0; 54fn bed_water_depth(world_xz: vec2<f32>) -> f32 { 55 let range = params.cascade_layout.bed_range; 59 let uv = (world_xz - params.cascade_layout.bed_transform.xy) 60 * params.cascade_layout.bed_transform.zw; 61 if any(uv < vec2(0.0)) || any(uv > vec2(1.0)) { 64 let dimensions = vec2<u32>(textureDimensions(bed_height)); 65 let maximum = vec2<i32>(dimensions - vec2<u32>(1u)); 66 let texel = clamp(vec2<i32>(round(uv * vec2<f32>(maximum))), vec2(i32(0)), maximum); 67 let height = f32(textureLoad(bed_height, texel, 0).r) * range.y + range.x; 68 return max(range.z - height, 0.0); 74fn shoaling_weights(depth: f32, wave_number: f32) -> vec2<f32> { 75 let relative_depth = clamp(depth * wave_number / PI, 0.0, 1.0); 76 let vertical_base = smoothstep(0.0, 1.0, relative_depth); 77 let breaker = 4.0 * vertical_base * (1.0 - vertical_base); 78 let vertical = vertical_base * (1.0 + 0.18 * breaker); 79 let chop_ratio = mix(0.55, 1.0, smoothstep(0.15, 0.85, vertical_base)); 80 return vec2(vertical * chop_ratio, vertical); 83@compute @workgroup_size(8, 8, 1) 84fn generate(@builtin(global_invocation_id) id: vec3<u32>) { 85 let cascade = params.cascade_layout.cascades[id.z]; 86 if any(id.xy >= vec2<u32>(u32(cascade.texture_res))) || id.z >= LOD_COUNT { 89 let uv = (vec2<f32>(id.xy) + vec2(0.5)) * cascade.inv_texture_res; 90 let coverage = cascade.texel_width * cascade.texture_res; 91 let world_xz = coverage * (uv - vec2(0.5)) + cascade.center; 92 let range = params.ranges[id.z]; 93 let water_depth = bed_water_depth(world_xz); 94 var displacement = vec3(0.0); 96 for (var index = range.x; index < range.y; index += 1u) { 97 let wave = params.waves[index]; 98 let temporal_phase = wave.phase + wave.angular_frequency * params.time.x; 99 let wrapped_phase = temporal_phase - floor(temporal_phase / TAU) * TAU; 100 let angle = wave.wave_number * dot(wave.direction, world_xz) + wrapped_phase; 104 let shoaling = shoaling_weights(water_depth, wave.wave_number); 105 let attenuation = params.time.z * mix(vec2(1.0), shoaling, params.time.y); 106 let horizontal = attenuation.x * wave.chop_amplitude * sin(angle); 107 displacement += vec3( 108 horizontal * wave.direction.x, 109 attenuation.y * wave.amplitude * cos(angle), 110 horizontal * wave.direction.y, 113 textureStore(output, vec2<i32>(id.xy), i32(id.z), vec4(displacement, 0.0));