5#define_import_path aqua::waves::displace 7#import bevy_pbr::mesh_view_bindings::globals 9#import aqua::cascade::{CREST_SSS_MAXIMUM, CREST_SSS_RANGE, MIN_NORMAL_Y, MIN_SAMPLE_WEIGHT, advected_world, cascade_layout, detail_normal, detail_sampler, fft_surface, flow_frame, lod_alpha, lod_count, lod_data, lod_sampler, screen_texture_lod, surface, world_to_uv} 11const NORMAL_DIRECTION_0: vec2<f32> = vec2(0.94, 0.34); 12const NORMAL_DIRECTION_1: vec2<f32> = vec2(-0.85, -0.53); 13const CAPILLARY_DIRECTION: vec2<f32> = vec2(0.4226, 0.9063); 14const CAPILLARY_ROTATION: mat2x2<f32> = mat2x2( 15 vec2(0.819152, 0.573576), 16 vec2(-0.573576, 0.819152), 21const WAVE_NORMALS_SLOPE_VARIANCE: f32 = 0.05602466; 22const NORMAL_SCROLL_MULTIPLIER: f32 = 1.875; 23const NORMAL_SCROLL_POWER: f32 = 1.4; 27const FFT_JONSWAP_SLOPE_VARIANCE: array<f32, 5> = array( 28 0.06393624, 0.06369724, 0.06306524, 0.06170338, 0.06048288, 30const GERSTNER_SLOPE_VARIANCE: array<f32, 5> = array( 31 0.0194041, 0.027484603, 0.015417861, 0.0066486476, 0.010458008, 34fn direct_displacement(world_xz: vec2<f32>, lod: u32) -> vec3<f32> { 35 let sampled_xz = advected_world(world_xz); 36 let cascade = cascade_layout.cascades[lod]; 37 return textureSampleLevel( 40 world_to_uv(sampled_xz, cascade), 46fn direct_fft_normal_cross(world_xz: vec2<f32>, lod: u32) -> vec3<f32> { 47 let sampled_xz = advected_world(world_xz); 48 let cascade = cascade_layout.cascades[lod]; 49 return textureSampleLevel( 52 world_to_uv(sampled_xz, cascade), 58fn sample_fft_normal_cross(world_xz: vec2<f32>, lod: u32, alpha: f32) -> vec3<f32> { 59 let smaller = cascade_layout.cascades[lod]; 60 let bigger = cascade_layout.cascades[lod + 1u]; 61 let smaller_weight = (1.0 - alpha) * smaller.weight; 62 let bigger_weight = (1.0 - smaller_weight) * bigger.weight; 63 var normal_cross = vec3(0.0); 64 var sampled_weight = 0.0; 65 if smaller_weight > MIN_SAMPLE_WEIGHT { 66 normal_cross += smaller_weight * direct_fft_normal_cross(world_xz, lod); 67 sampled_weight += smaller_weight; 69 if bigger_weight > MIN_SAMPLE_WEIGHT && lod + 1u < lod_count() { 70 normal_cross += bigger_weight * direct_fft_normal_cross(world_xz, lod + 1u); 71 sampled_weight += bigger_weight; 73 normal_cross.y += max(1.0 - sampled_weight, 0.0); 77fn outer_cascade_weight(world_xz: vec2<f32>) -> f32 { 78 let outer_lod = lod_count() - 1u; 79 return 1.0 - lod_alpha(world_xz, cascade_layout.cascades[outer_lod]); 82fn far_displacement(world_xz: vec2<f32>) -> vec3<f32> { 83 let weight = outer_cascade_weight(world_xz); 84 if weight <= MIN_SAMPLE_WEIGHT { 87 return weight * direct_displacement(world_xz, lod_count() - 1u); 90fn far_normal_cross(world_xz: vec2<f32>) -> vec3<f32> { 91 let outer_lod = lod_count() - 1u; 92 let cascade = cascade_layout.cascades[outer_lod]; 93 let weight = outer_cascade_weight(world_xz); 94 if weight <= MIN_SAMPLE_WEIGHT { 95 return vec3(0.0, 1.0, 0.0); 97 if surface.reflection.x > 0.5 { 98 let cached = direct_fft_normal_cross(world_xz, outer_lod); 99 return mix(vec3(0.0, 1.0, 0.0), cached, weight); 101 let center = weight * direct_displacement(world_xz, outer_lod); 102 let offset_x = weight * direct_displacement( 103 world_xz + vec2(cascade.texel_width, 0.0), 106 let offset_z = weight * direct_displacement( 107 world_xz + vec2(0.0, cascade.texel_width), 110 let tangent_x = vec3(cascade.texel_width, 0.0, 0.0) + offset_x - center; 111 let tangent_z = vec3(0.0, 0.0, cascade.texel_width) + offset_z - center; 112 var normal_cross = cross(tangent_z, tangent_x); 113 normal_cross.y = max(normal_cross.y, MIN_NORMAL_Y); 119fn displacement_jacobian(world_xz: vec2<f32>, lod: u32) -> f32 { 120 if surface.reflection.x > 0.5 { 121 let cascade = cascade_layout.cascades[lod]; 122 return textureSampleLevel( 125 world_to_uv(advected_world(world_xz), cascade), 130 let texel_width = cascade_layout.cascades[lod].texel_width; 131 let center = direct_displacement(world_xz, lod); 132 let offset_x = direct_displacement(world_xz + vec2(texel_width, 0.0), lod); 133 let offset_z = direct_displacement(world_xz + vec2(0.0, texel_width), lod); 134 let tangent_x = vec2(texel_width, 0.0) + offset_x.xz - center.xz; 135 let tangent_z = vec2(0.0, texel_width) + offset_z.xz - center.xz; 136 return (tangent_x.x * tangent_z.y - tangent_x.y * tangent_z.x) 137 / (texel_width * texel_width); 140fn crest_sss(world_xz: vec2<f32>, lod: u32, alpha: f32) -> f32 { 141 let smaller = cascade_layout.cascades[lod]; 142 let bigger = cascade_layout.cascades[lod + 1u]; 143 let smaller_weight = (1.0 - alpha) * smaller.weight; 144 let bigger_weight = (1.0 - smaller_weight) * bigger.weight; 145 var determinant = 0.0; 146 if smaller_weight > MIN_SAMPLE_WEIGHT { 147 determinant += smaller_weight * displacement_jacobian(world_xz, lod); 149 if bigger_weight > MIN_SAMPLE_WEIGHT && lod + 1u < lod_count() { 150 determinant += bigger_weight * displacement_jacobian(world_xz, lod + 1u); 152 if lod + 1u >= lod_count() { 153 determinant += 1.0 - smaller_weight; 155 return clamp(CREST_SSS_MAXIMUM - CREST_SSS_RANGE * determinant, 0.0, 1.0); 158fn sample_detail_normal(uv: vec2<f32>, lod: f32) -> vec3<f32> { 159 let packed = textureSampleLevel(detail_normal, detail_sampler, uv, lod); 160 let slope = 2.0 * packed.xy - vec2(1.0); 161 let second_moment = 2.0 * packed.z; 162 return vec3(slope, max(second_moment - dot(slope, slope), 0.0)); 168fn detail_normal_sample(world_xz: vec2<f32>, lod: u32, alpha: f32, ripple: f32) -> vec3<f32> { 171 let scrolled_xz = flow_frame(advected_world(world_xz)); 172 let cascade = cascade_layout.cascades[lod]; 173 let stretch = surface.detail.x * cascade.scale / 100.0; 174 let speed_near = pow( 175 log(1.0 + 2.0 * cascade.texel_width) * NORMAL_SCROLL_MULTIPLIER, 179 log(1.0 + 4.0 * cascade.texel_width) * NORMAL_SCROLL_MULTIPLIER, 182 let texture_width = textureDimensions(detail_normal).x; 183 let near_lod = screen_texture_lod(stretch, texture_width); 184 let near_a = sample_detail_normal( 185 (scrolled_xz + NORMAL_DIRECTION_0 * globals.time * speed_near) / stretch, 188 let near_b = sample_detail_normal( 189 (scrolled_xz + NORMAL_DIRECTION_1 * globals.time * speed_near) / stretch, 192 let far_stretch = 2.0 * stretch; 193 let far_lod = screen_texture_lod(far_stretch, texture_width); 194 let far_a = sample_detail_normal( 195 (scrolled_xz + NORMAL_DIRECTION_0 * globals.time * speed_far) / far_stretch, 198 let far_b = sample_detail_normal( 199 (scrolled_xz + NORMAL_DIRECTION_1 * globals.time * speed_far) / far_stretch, 202 let near = near_a.xy + near_b.xy; 203 let far = far_a.xy + far_b.xy; 204 let near_weight = 1.0 - alpha; 205 let variance = near_weight * near_weight * (near_a.z + near_b.z) 206 + alpha * alpha * (far_a.z + far_b.z); 207 let strength = ripple * surface.detail.y * surface.detail.z; 208 return vec3(strength * mix(near, far, alpha), strength * strength * variance); 215fn capillary_normal_slope(world_xz: vec2<f32>, ripple: f32) -> vec2<f32> { 216 let cascade = cascade_layout.cascades[0u]; 217 let stretch = surface.detail.x * cascade.scale 218 / (100.0 * surface.capillary.x); 220 log(1.0 + 2.0 * cascade.texel_width) * NORMAL_SCROLL_MULTIPLIER, 223 let scrolled = advected_world(world_xz) 224 + CAPILLARY_DIRECTION * globals.time * speed; 225 let uv = CAPILLARY_ROTATION * scrolled / stretch; 226 let lod = screen_texture_lod(stretch, textureDimensions(detail_normal).x); 227 return ripple * surface.capillary.y * sample_detail_normal(uv, lod).xy;