Search across packages, versions, files, declarations, and comments. Use multiple terms to narrow results (AND), and add @vertex, @fragment, or @compute to filter by shader stage.
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;
6const CHOP: f32 = 0.8;
8struct CascadeParams {
9 center: vec2<f32>,
10 scale: f32,
11 texture_res: f32,
12 inv_texture_res: f32,
13 texel_width: f32,
14 weight: f32,
15 max_wavelength: f32,
17struct CascadeLayout {
18 cascades: array<CascadeParams, 6>,
19 center: vec4<f32>,
20 // XY bed-map first-texel world origin, ZW inverse world extent.
21 bed_transform: vec4<f32>,
22 // X height minimum, Y height span (negative = no bed map), Z sea level.
23 bed_range: vec4<f32>,
25struct FftUniform {
26 cascade_layout: CascadeLayout,
27 params: vec4<f32>,
28 // x: active attenuation-bin count (1 or ATTENUATION_BINS).
29 mode: vec4<f32>,
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),
59 );
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);
68 // Keep each Fourier component in a narrow quarter-octave field. Resolve can then
69 // apply local half-wavelength attenuation before summing the fields.
70 if bins == 1u {
71 // Single-bin deep-water path keeps every component; DC stays suppressed.
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); }
79 } else {
80 height = vec2(0.0);
81 }
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);
89 if k_length > 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;
94 }
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));
Contents