1const FFT_RESOLUTION: u32 = 256u; 2const LOD_COUNT: u32 = 5u; 3const ATTENUATION_BINS: u32 = 4u; 4const FIELD_LAYERS: u32 = LOD_COUNT * ATTENUATION_BINS; 5const GRAVITY: f32 = 9.81; 18 cascades: array<CascadeParams, 6>, 21 bed_transform: vec4<f32>, 26 cascade_layout: CascadeLayout, 32@group(0) @binding(0) var initial_spectrum: texture_2d_array<f32>; 33@group(0) @binding(1) var height_x: texture_storage_2d_array<rgba32float, write>; 34@group(0) @binding(2) var z_field: texture_storage_2d_array<rgba32float, write>; 35@group(0) @binding(3) var<uniform> fft: FftUniform; 37fn complex_multiply(a: vec2<f32>, b: vec2<f32>) -> vec2<f32> { 38 return vec2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); 40fn conjugate(value: vec2<f32>) -> vec2<f32> { return vec2(value.x, -value.y); } 41fn signed_frequency(value: u32) -> i32 { 42 return select(i32(value), i32(value) - i32(FFT_RESOLUTION), value > FFT_RESOLUTION / 2u); 45@compute @workgroup_size(8, 8, 1) 46fn evolve(@builtin(global_invocation_id) id: vec3<u32>) { 47 let bins = u32(fft.mode.x); 48 if any(id.xy >= vec2<u32>(FFT_RESOLUTION)) || id.z >= LOD_COUNT * bins { return; } 49 let cascade_index = id.z / bins; 50 let attenuation_bin = id.z % bins; 51 let cascade = fft.cascade_layout.cascades[cascade_index]; 52 let period = cascade.texel_width * cascade.texture_res; 53 let k = (2.0 * 3.141592653589793 / period) 54 * vec2<f32>(f32(signed_frequency(id.x)), f32(signed_frequency(id.y))); 55 let k_length = length(k); 56 let mirror = vec2<i32>( 57 i32((FFT_RESOLUTION - id.x) % FFT_RESOLUTION), 58 i32((FFT_RESOLUTION - id.y) % FFT_RESOLUTION), 60 let h0 = textureLoad(initial_spectrum, vec2<i32>(id.xy), i32(cascade_index), 0).xy; 61 let h0_mirror = textureLoad(initial_spectrum, mirror, i32(cascade_index), 0).xy; 62 let omega = sqrt(GRAVITY * k_length); 63 let phase = omega * fft.params.x; 64 let negative_phase = vec2(cos(phase), -sin(phase)); 65 let positive_phase = vec2(cos(phase), sin(phase)); 66 var height = complex_multiply(h0, negative_phase) 67 + complex_multiply(conjugate(h0_mirror), positive_phase); 72 if k_length <= 0.0 { height = vec2(0.0); } 73 } else if k_length > 0.0 { 74 let wavelength = 2.0 * 3.141592653589793 / k_length; 75 let octave = log2(wavelength / (0.5 * cascade.max_wavelength)); 76 let component_bin = min(u32(max(floor(octave * f32(ATTENUATION_BINS)), 0.0)), 77 ATTENUATION_BINS - 1u); 78 if component_bin != attenuation_bin { height = vec2(0.0); } 82 let origin = cascade.center - vec2(0.5 * period) 83 + vec2(0.5 * cascade.texel_width); 84 let origin_phase = dot(k, origin); 85 height = complex_multiply(height, vec2(cos(origin_phase), sin(origin_phase))); 86 height *= fft.params.z; 87 var horizontal_x = vec2(0.0); 88 var horizontal_z = vec2(0.0); 90 let i_height = vec2(-height.y, height.x); 91 let chop = CHOP * (1.0 - fft.params.w); 92 horizontal_x = chop * k.x / k_length * i_height; 93 horizontal_z = chop * k.y / k_length * i_height; 95 textureStore(height_x, vec2<i32>(id.xy), i32(id.z), vec4(height, horizontal_x)); 96 textureStore(z_field, vec2<i32>(id.xy), i32(id.z), vec4(horizontal_z, 0.0, 0.0));