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.
1// Composed cascade surface material: vertex snap/morph/displacement, the
2// transmission/reflection/foam shading pipeline, and both stage entry
3// points. Imports every feature module over the aqua::cascade contract.
4// Owned by bevy-aqua-core (the material is part of the published contract).
6#import bevy_pbr::{
7 forward_io::Vertex,
8 mesh_functions,
9 mesh_view_types,
10 clustered_forward as clustering,
11 lighting,
12 prepass_utils,
13 shadows,
14 atmosphere::functions::{calculate_visible_sun_ratio, clamp_to_surface},
15 atmosphere::bruneton_functions::transmittance_lut_r_mu_to_uv,
16 mesh_view_bindings::{globals, light_probes, lights, view},
17 view_transformations::position_world_to_clip,
19#import bevy_pbr::mesh_view_bindings as view_bindings
21#import aqua::cascade::{CREST_SSS_RANGE, CREST_SSS_UNCOMPRESSED, DEBUG_MODE_BEAUTY, DEBUG_MODE_BEER_LAMBERT, DEBUG_MODE_FAR_TIER, DEBUG_MODE_FOAM, DEBUG_MODE_LIGHT_RADIANCE, DEBUG_MODE_REFLECTION, DEBUG_MODE_REFLECTION_FRACTION, DEBUG_MODE_REFRACTION_VALIDITY, DEBUG_MODE_SEA_FLOOR, DEBUG_MODE_TRANSMISSION, DEBUG_MODE_UNREFRACTED, DEBUG_MODE_WATER_PATH, DEBUG_MODE_WAVE_HEIGHT, LUMINANCE_EPSILON, LocalLightSample, MIN_NORMAL_Y, SAFE_LENGTH_SQUARED, advected_world, begin_invocation, capillary_resolved_weight, cascade_layout, effective_flow, far_tier_weight, field_params, godot_fresnel, invocation_extinction, invocation_ripple, invocation_river_state, invocation_scatter_scale, lod_count, owning_body, sample_displacement, sample_field_flow, sample_field_level, sample_planar_reflection, set_body_optics, set_effective_flow, set_effective_time, set_fragment_river, set_river_ripple, set_xz_footprint, snap_and_transition, surface}
23#import aqua::waves::displace::{FFT_JONSWAP_SLOPE_VARIANCE, GERSTNER_SLOPE_VARIANCE, WAVE_NORMALS_SLOPE_VARIANCE, capillary_normal_slope, crest_sss, detail_normal_sample, far_displacement, far_normal_cross, sample_fft_normal_cross}
25#import aqua::foam::contract::FOAM_PATTERN_RESOLUTION
26#import aqua::foam::shade::{CREST_FOAM_NORMAL_STRENGTH, CREST_FOAM_SPECULAR_BOOST, INV_PI, CREST_FOAM_SPECULAR_FALLOFF, CREST_FOAM_WHITE_COLOR, foam_bubble_colour, local_foam_light, river_streak_density, sample_foam_density, surface_foam_mask}
28#import aqua::shore::water::{blended_water_depth, caustic_bed_radiance}
29#import bevy_aqua_core::deform::{deform_current}
30#import bevy_aqua_core::material::{BodyLightingState, CameraDepthDebug, CameraDepthPath, FoamState, LocalLightingState, MediumState, NearSurface, PrimaryLightState, SurfaceVertexOutput, TransmissionState}
31#import aqua::light::incident::{GODOT_NORMAL_FADE_RATE, GODOT_NORMAL_MINIMUM_STRENGTH, GODOT_SSS_MODIFIER, GODOT_WATER_ALBEDO, LUMINANCE_WEIGHTS, filtered_primary_light_color, ggx_distribution, local_light_contribution, resolve_primary_light, safe_normalize, sample_diffuse_environment, sample_environment, sample_local_light, smith_masking_shadowing, strongest_incident_directional_light, view_direction}
32#import aqua::optics::{camera_depth_path, deep_water_weight, empty_camera_depth_path, far_field_water, resolve_near_surface, resolve_transmission, sample_water_medium, unresolved_wave_roughness}
34@vertex
35fn vertex(vertex: Vertex) -> SurfaceVertexOutput {
36 let deformation = deform_current(vertex, globals.time);
37 var out: SurfaceVertexOutput;
38 out.world_position = deformation.world_position;
39 out.position = position_world_to_clip(deformation.world_position.xyz);
40 out.world_normal = deformation.world_normal;
41 out.undisplaced_xz = deformation.undisplaced_xz;
42 out.sample_data = vec3(
43 f32(deformation.lod),
44 deformation.sample_alpha,
45 deformation.wave_height,
46 );
47 out.base_world_position = deformation.base_world_position;
48 return out;
51fn prepare_surface_foam(
52 in: SurfaceVertexOutput,
53 surface_lod: u32,
54 foam_density: f32,
55 lighting_distance: f32,
56) -> FoamState {
57 let foam_distance_fade = exp(-lighting_distance * 0.0075);
58 let visible_foam_density = foam_density * foam_distance_fade;
59 var white_foam_density = visible_foam_density;
60 var white_foam = 0.0;
61 var shared_depth_path = empty_camera_depth_path();
62 var has_shared_depth_path = false;
63 if visible_foam_density > 0.0 {
64 shared_depth_path = camera_depth_path(in);
65 has_shared_depth_path = true;
66 let foam_depth = shared_depth_path;
67 let shoreline_fade = select(
68 1.0,
69 clamp(foam_depth.path_length / 0.27, 0.0, 1.0),
70 foam_depth.has_background,
71 );
72 white_foam_density *= shoreline_fade;
73 white_foam = surface_foam_mask(
74 advected_world(in.undisplaced_xz),
75 surface_lod,
76 in.sample_data.y,
77 white_foam_density,
78 vec2(0.0),
79 );
80 }
81 // River bank streaks are independent of the persistent foam buffer:
82 // they exist wherever fast water runs close to a bank.
83 let streak = river_streak_density(
84 invocation_river_state(),
85 in.world_position.xz,
86 surface_lod,
87 in.sample_data.y,
88 );
89 if streak > 0.0 {
90 white_foam_density += streak;
91 white_foam += surface_foam_mask(
92 advected_world(in.undisplaced_xz),
93 surface_lod,
94 in.sample_data.y,
95 streak,
96 vec2(0.0),
97 );
98 }
99 return FoamState(
100 visible_foam_density,
101 white_foam_density,
102 white_foam,
103 shared_depth_path,
104 has_shared_depth_path,
105 );
108fn directional_scatter(
109 in: SurfaceVertexOutput,
110 surface_lod: u32,
111 near: NearSurface,
112 primary: PrimaryLightState,
113 medium: MediumState,
114 to_view: vec3<f32>,
115 mode: u32,
116) -> vec3<f32> {
117 // Crest's authored deep/grazing colours are volume-scatter albedos. They
118 // carry no radiance until the scene environment illuminates them.
119 var scatter_colour = medium.deep_body_albedo * medium.diffuse_irradiance;
120 // Crest `OceanEmission.hlsl::ScatterColour`: backlit subsurface tint is
121 // driven by horizontal-displacement pinch, not absolute wave height.
122 if mode >= DEBUG_MODE_BEAUTY
123 && lights.n_directional_lights > 0u {
124 let light = lights.directional_lights[0u];
125 let light_direction = safe_normalize(
126 light.direction_to_light,
127 vec3(0.0, 1.0, 0.0),
128 );
129 // Crest consumes Unity's `_LightColor0` scene radiance directly. Bevy's
130 // GPU light is linear RGB times lux, so one view-exposure multiplication
131 // converts it to the same pre-tonemap domain as Bevy PBR and atmosphere.
132 let light_radiance = primary.radiance;
133 let towards_sun = pow(
134 max(dot(light_direction, -to_view), 0.0),
135 surface.sss.z,
136 );
137 let pinch = crest_sss(in.undisplaced_xz, surface_lod, in.sample_data.y);
138 // Crest keeps a 0.48 SSS pedestal to hide outer-LOD transitions. With
139 // physical Bevy lux this would light the entire uncompressed surface;
140 // retain only its compression contrast so radiance pierces wave crests.
141 let crest_transmission = clamp(
142 (pinch - CREST_SSS_UNCOMPRESSED) / CREST_SSS_RANGE,
143 0.0,
144 1.0,
145 );
146 let view_vertical = abs(to_view.y);
147 let grazing = max(1.0 - view_vertical * view_vertical, 0.0);
148 let dot_nv = max(dot(near.lighting_normal, to_view), 2e-5);
149 let sss_light_mask = smith_masking_shadowing(surface.sun.y, dot_nv);
150 let sss_near = 0.5 * pow(dot_nv, 2.0);
151 let sss_height = max(0.0, in.sample_data.z + 2.5)
152 * pow(max(dot(light_direction, -to_view), 0.0), 4.0)
153 * pow(
154 0.5 - 0.5 * dot(light_direction, near.lighting_normal),
155 3.0,
156 );
157 // GodotOceanWaves supplies broad view/body and height/backlight lanes;
158 // Crest's Jacobian term remains the concentrated crest variation.
159 scatter_colour += (sss_height + sss_near)
160 * GODOT_SSS_MODIFIER
161 / (1.0 + sss_light_mask)
162 * light_radiance
163 * GODOT_WATER_ALBEDO;
164 scatter_colour += (surface.sss.x + surface.sss.y * towards_sun)
165 * surface.sss_tint.rgb
166 * light_radiance
167 * grazing
168 * crest_transmission
169 // Budget transmission by irradiance incident on the mean water
170 // plane. This preserves Crest pinch while preventing a grazing sun
171 // from lighting every compressed crest at normal-incidence energy.
172 * max(light_direction.y, 0.0);
173 }
174 return scatter_colour;
177fn shade_water_body(
178 in: SurfaceVertexOutput,
179 near: NearSurface,
180 primary: PrimaryLightState,
181 medium: MediumState,
182 foam: FoamState,
183 to_view: vec3<f32>,
184 input_body: vec3<f32>,
185 mode: u32,
186) -> BodyLightingState {
187 var body = input_body;
188 // Godot's engine substrate adds diffuse sky irradiance behind every
189 // material light() function. Crest consumes the same scene ambient for
190 // both the white-foam and sub-surface bubble lanes.
191 var foam_ambient_radiance = vec3(0.0);
192 if mode >= DEBUG_MODE_BEAUTY {
193 // Crest consumes Unity SH L0 for foam. Bevy's up-facing diffuse
194 // irradiance is the scene-driven equivalent; no constant radiance
195 // floor is allowed when that environment is dark or absent.
196 foam_ambient_radiance = sample_diffuse_environment(vec3(0.0, 1.0, 0.0));
197 body += medium.diffuse_irradiance * GODOT_WATER_ALBEDO;
198 if foam.visible_density > 0.0 {
199 body += foam_bubble_colour(
200 in.world_position.xz,
201 in.undisplaced_xz,
202 u32(round(in.sample_data.x)),
203 in.sample_data.y,
204 foam.visible_density,
205 near.normal,
206 to_view,
207 foam_ambient_radiance,
208 );
209 }
210 }
211 // Crest's final Fresnel composition supplies the `(1.0 - fresnel)`
212 // modulation exactly once.
213 if mode >= DEBUG_MODE_BEAUTY && lights.n_directional_lights > 0u {
214 let light = lights.directional_lights[0u];
215 let light_direction = safe_normalize(
216 light.direction_to_light,
217 vec3(0.0, 1.0, 0.0),
218 );
219 let light_radiance = primary.radiance;
220 let lambertian = 0.5 * max(dot(near.lighting_normal, light_direction), 2e-5);
221 body += lambertian * light_radiance * GODOT_WATER_ALBEDO;
222 }
224 let perceptual_roughness = unresolved_wave_roughness(
225 in.undisplaced_xz,
226 to_view,
227 in.sample_data.y,
228 near.lighting_normal_strength,
229 near.filtered_detail_variance,
230 );
231 let view_alignment = clamp(dot(near.lighting_normal, to_view), 0.0, 1.0);
232 let fresnel = godot_fresnel(view_alignment);
233 let foam_distance_fade = exp(-near.lighting_distance * 0.0075);
234 let foam_factor = smoothstep(
235 0.0,
236 1.0,
237 medium.foam_density * 0.75,
238 ) * foam_distance_fade;
239 let foam_roughness = (1.0 - fresnel) * foam_factor;
240 let environment_roughness = clamp(
241 perceptual_roughness + foam_roughness,
242 0.0,
243 1.0,
244 );
245 let has_local_lights = primary.point_start < primary.light_end;
246 // Godot uses a 0.4 alpha floor. Aqua's unresolved slope variance adds
247 // in quadrature so every direct emitter softens consistently with distance.
248 // Skip this emitter-only work when the clustered fragment has no lights.
249 var sun_roughness = surface.sun.y;
250 if lights.n_directional_lights > 0u || has_local_lights {
251 let foam_surface_roughness = clamp(
252 surface.sun.y + foam_roughness,
253 surface.sun.y,
254 1.0,
255 );
256 sun_roughness = min(sqrt(
257 foam_surface_roughness * foam_surface_roughness
258 + perceptual_roughness * perceptual_roughness,
259 ), 1.0);
260 }
261 return BodyLightingState(
262 body,
263 foam_ambient_radiance,
264 fresnel,
265 foam_roughness,
266 environment_roughness,
267 sun_roughness,
268 );
271fn shade_environment_and_sun(
272 world_position: vec3<f32>,
273 surface_level: f32,
274 near: NearSurface,
275 primary: PrimaryLightState,
276 body_lighting: BodyLightingState,
277 to_view: vec3<f32>,
278) -> vec3<f32> {
279 let reflection = reflect(-to_view, near.lighting_normal);
280 var reflected_radiance = sample_environment(
281 reflection,
282 near.lighting_normal,
283 body_lighting.environment_roughness,
284 );
285 let planar = sample_planar_reflection(world_position, surface_level, near.lighting_normal);
286 reflected_radiance = mix(reflected_radiance, planar.color, planar.weight);
288 // Crest `OceanReflection.hlsl::ApplyReflectionSky`: the directional light
289 // is a bounded reflection-vector lobe added before the Fresnel blend.
290 if lights.n_directional_lights > 0u {
291 let light = lights.directional_lights[0u];
292 let light_direction = safe_normalize(
293 light.direction_to_light,
294 vec3(0.0, 1.0, 0.0),
295 );
296 let light_luminance = max(
297 dot(primary.color, LUMINANCE_WEIGHTS),
298 LUMINANCE_EPSILON,
299 );
300 let light_color = primary.color / light_luminance;
301 let light_strength = clamp(light_luminance / surface.reflection.z, 0.0, 1.0);
302 let halfway = safe_normalize(
303 light_direction + to_view,
304 near.lighting_normal,
305 );
306 let dot_nl = max(dot(near.lighting_normal, light_direction), 2e-5);
307 let dot_nv = max(dot(near.lighting_normal, to_view), 2e-5);
308 let light_mask = smith_masking_shadowing(body_lighting.sun_roughness, dot_nv);
309 let view_mask = smith_masking_shadowing(body_lighting.sun_roughness, dot_nl);
310 let distribution = ggx_distribution(
311 clamp(dot(near.lighting_normal, halfway), 0.0, 1.0),
312 body_lighting.sun_roughness,
313 );
314 let geometric_attenuation = 1.0 / (1.0 + light_mask + view_mask);
315 let sun_specular = distribution
316 * geometric_attenuation / (4.0 * dot_nv + 0.1);
317 reflected_radiance += sun_specular
318 * surface.sun.x
319 * light_color
320 * light_strength
321 * primary.shadow;
322 }
323 return reflected_radiance;
326fn shade_local_lights(
327 in: SurfaceVertexOutput,
328 near: NearSurface,
329 primary: PrimaryLightState,
330 body_lighting: BodyLightingState,
331 foam: FoamState,
332 to_view: vec3<f32>,
333 reflected_input: vec3<f32>,
334 mode: u32,
335) -> LocalLightingState {
336 let has_local_lights = primary.point_start < primary.light_end;
337 let local_sss_enabled = mode >= DEBUG_MODE_BEAUTY && has_local_lights;
338 var local_crest_transmission = 0.0;
339 if local_sss_enabled {
340 let local_pinch = crest_sss(
341 in.undisplaced_xz,
342 u32(round(in.sample_data.x)),
343 in.sample_data.y,
344 );
345 local_crest_transmission = clamp(
346 (local_pinch - CREST_SSS_UNCOMPRESSED) / CREST_SSS_RANGE,
347 0.0,
348 1.0,
349 );
350 }
351 let view_vertical = abs(to_view.y);
352 let local_grazing = max(1.0 - view_vertical * view_vertical, 0.0);
353 let foam_active = foam.visible_density > 0.0;
354 var foam_normal = near.normal;
355 if foam_active {
356 let pixel_z = max(-primary.view_z, 0.0);
357 let foam_delta = 0.25 * pixel_z / FOAM_PATTERN_RESOLUTION;
358 let foam_x = surface_foam_mask(
359 advected_world(in.undisplaced_xz),
360 u32(round(in.sample_data.x)),
361 in.sample_data.y,
362 foam.white_density,
363 vec2(foam_delta, 0.0),
364 );
365 let foam_z = surface_foam_mask(
366 advected_world(in.undisplaced_xz),
367 u32(round(in.sample_data.x)),
368 in.sample_data.y,
369 foam.white_density,
370 vec2(0.0, foam_delta),
371 );
372 let foam_gradient = vec2(foam_x - foam.white_mask, foam_z - foam.white_mask);
373 foam_normal = safe_normalize(
374 near.normal + CREST_FOAM_NORMAL_STRENGTH
375 * vec3(-foam_gradient.x, 0.0, -foam_gradient.y),
376 near.normal,
377 );
378 }
379 var body = body_lighting.body;
380 var reflected_radiance = reflected_input;
381 var local_foam_radiance = vec3(0.0);
382 for (
383 var local_index = primary.point_start;
384 local_index < primary.spot_start;
385 local_index += 1u
386 ) {
387 let light_id = clustering::get_clusterable_object_id(local_index);
388 let sample = sample_local_light(
389 light_id,
390 false,
391 in.world_position.xyz,
392 near.normal,
393 in.position.xy,
394 );
395 let contribution = local_light_contribution(
396 sample,
397 near.lighting_normal,
398 to_view,
399 in.sample_data.z,
400 body_lighting.sun_roughness,
401 local_grazing,
402 local_crest_transmission,
403 local_sss_enabled,
404 );
405 body += contribution.body;
406 reflected_radiance += contribution.reflection;
407 if foam_active {
408 local_foam_radiance += local_foam_light(sample, foam_normal, to_view);
409 }
410 }
411 for (
412 var local_index = primary.spot_start;
413 local_index < primary.light_end;
414 local_index += 1u
415 ) {
416 let light_id = clustering::get_clusterable_object_id(local_index);
417 let sample = sample_local_light(
418 light_id,
419 true,
420 in.world_position.xyz,
421 near.normal,
422 in.position.xy,
423 );
424 let contribution = local_light_contribution(
425 sample,
426 near.lighting_normal,
427 to_view,
428 in.sample_data.z,
429 body_lighting.sun_roughness,
430 local_grazing,
431 local_crest_transmission,
432 local_sss_enabled,
433 );
434 body += contribution.body;
435 reflected_radiance += contribution.reflection;
436 if foam_active {
437 local_foam_radiance += local_foam_light(sample, foam_normal, to_view);
438 }
439 }
440 return LocalLightingState(
441 body,
442 reflected_radiance,
443 local_foam_radiance,
444 foam_normal,
445 );
448fn compose_water(
449 primary: PrimaryLightState,
450 body_lighting: BodyLightingState,
451 local: LocalLightingState,
452 foam: FoamState,
453 to_view: vec3<f32>,
454 far_water: vec3<f32>,
455 far_tier: f32,
456 mode: u32,
457) -> vec4<f32> {
458 // Foam is dielectric diffuse froth, not a second glossy water layer.
459 // Godot's foam roughness therefore damps both environment and sun glints.
460 let reflected_radiance = local.reflected * (1.0 - body_lighting.foam_roughness);
462 // GodotOceanWaves roughness-damped Fresnel; Crest owns final composition.
463 let reflection_weight = clamp(body_lighting.fresnel * surface.fresnel.z, 0.0, 1.0);
464 if mode == DEBUG_MODE_REFLECTION_FRACTION {
465 return vec4(vec3(reflection_weight), 1.0);
466 }
467 if mode == DEBUG_MODE_REFLECTION {
468 return vec4(reflected_radiance * reflection_weight, 1.0);
469 }
470 var water = mix(local.body, reflected_radiance, reflection_weight);
471 if foam.visible_density > 0.0 {
472 let mask = CREST_FOAM_WHITE_COLOR.a * foam.white_mask;
474 // Crest `OceanFoam.hlsl`: shipped 3D foam lighting. Bevy's scene
475 // diffuse irradiance replaces Unity SH L0; no constant ambient term
476 // enters the same pre-exposed radiance domain as SSS.
477 var foam_light = CREST_FOAM_WHITE_COLOR.rgb * body_lighting.foam_ambient;
478 if lights.n_directional_lights > 0u {
479 let light = lights.directional_lights[0u];
480 let light_direction = safe_normalize(
481 light.direction_to_light,
482 vec3(0.0, 1.0, 0.0),
483 );
484 // Stay in the established pre-exposed primary-light lane, but do
485 // not let a below-horizon light illuminate wave-facing foam.
486 let light_radiance = primary.radiance * max(light_direction.y, 0.0);
487 let foam_ndl = max(dot(local.foam_normal, light_direction), 0.0);
488 foam_light += CREST_FOAM_WHITE_COLOR.rgb
489 * INV_PI * surface.foam.z * light_radiance * foam_ndl;
490 let foam_reflection = reflect(-to_view, local.foam_normal);
491 foam_light += pow(
492 max(dot(foam_reflection, light_direction), 0.0),
493 CREST_FOAM_SPECULAR_FALLOFF,
494 ) * CREST_FOAM_SPECULAR_BOOST * light_radiance;
495 }
496 foam_light += local.foam_radiance;
497 water = mix(water, foam_light, mask);
498 }
499 return vec4(mix(water, far_water, far_tier), 1.0);
502@fragment
503fn fragment(in: SurfaceVertexOutput) -> @location(0) vec4<f32> {
504 // Position derivatives measure adjacent fragments in world XZ. Their
505 // maximum length conservatively bounds metres per screen pixel for LOD.
506 set_xz_footprint(max(
507 length(dpdx(in.world_position.xz)),
508 length(dpdy(in.world_position.xz)),
509 ));
510 // One shared tile set renders every scene: unclaimed texels discard
511 // unless the Ocean resource is present, so localized scenes pay fill
512 // only where their bodies (and the tiles themselves) exist.
513 var slot = 0u;
514 if field_params.info.x > 0.5 {
515 slot = u32(sample_field_level(in.world_position.xz).y + 0.5);
516 }
517 let bounded = slot > 0u;
518 if !bounded && field_params.info.y < 0.5 {
519 discard;
520 }
521 let params = owning_body(slot);
522 let surface_level = select(
523 cascade_layout.bed_range.z,
524 sample_field_level(in.undisplaced_xz).x,
525 bounded,
526 );
527 begin_invocation(bounded, params);
528 set_effective_time(globals.time);
529 if bounded {
530 // Bodies entirely beyond the far tier cull by extent-vs-distance.
531 let distance_to_extent = length(
532 params.extent.xy - view.world_position.xz,
533 ) - params.extent.w;
534 if distance_to_extent > surface.far_tier.y {
535 discard;
536 }
537 }
538 // River bodies clip to the baked bank distance. z is the SIGNED margin
539 // in metres: negative is outside the channel; w is channel half-width.
540 var fragment_river_flow = vec4(0.0, 0.0, 1024.0, 0.0);
541 if bounded {
542 fragment_river_flow = sample_field_flow(in.world_position.xz);
543 }
544 if bounded && params.flags.y > 0.5 && fragment_river_flow.z < 0.0 {
545 discard;
546 }
547 set_fragment_river(fragment_river_flow);
548 set_effective_flow(select(
549 surface.advection.xy,
550 fragment_river_flow.xy,
551 bounded && params.flags.y > 0.5,
552 ));
553 // Fresh-water optics override the ocean profile when authored.
554 let body_optics = bounded && params.optics_a.w > 0.5;
555 set_body_optics(
556 select(surface.fog_density.rgb, params.optics_a.rgb, body_optics),
557 select(1.0, params.optics_b.x, body_optics),
558 );
559 // Discharge reads as roughness: faster narrows break up more, banks and
560 // pools stay glassy. Bank fade eases the multiplier to zero at the edge.
561 if bounded && params.flags.y > 0.5 {
562 let speed = length(fragment_river_flow.xy);
563 // Bank proximity over an 8 m band: ripples calm to the waterline.
564 let bank_fade = clamp(fragment_river_flow.z / 8.0, 0.0, 1.0);
565 set_river_ripple(bank_fade * clamp(0.8 + 0.75 * speed, 0.8, 2.6));
566 } else {
567 set_river_ripple(1.0);
568 }
569 let mode = u32(round(surface.debug.x));
570 let surface_lod = u32(round(in.sample_data.x));
571 let geometric_normal = safe_normalize(in.world_normal, vec3(0.0, 1.0, 0.0));
572 let to_view = view_direction(in.world_position.xyz);
573 let far_diagnostic = mode == DEBUG_MODE_FAR_TIER;
574 var far_tier = select(
575 0.0,
576 far_tier_weight(in.base_world_position),
577 mode == DEBUG_MODE_BEAUTY || far_diagnostic,
578 );
579 if far_diagnostic {
580 return vec4(vec3(far_tier), 1.0);
581 }
582 var far_water = vec3(0.0);
583 var far_water_depth = 0.0;
584 if far_tier > 0.0 {
585 // Shallow water keeps the depth-aware near optics. As the bed becomes
586 // deep enough to disappear, the cheap far tier becomes fully active.
587 far_water_depth = blended_water_depth(in.world_position.xz);
588 far_tier *= deep_water_weight(far_water_depth);
589 }
590 if far_tier > 0.0 {
591 far_water = far_field_water(
592 in.world_position,
593 surface_level,
594 geometric_normal,
595 to_view,
596 in.sample_data.z,
597 far_water_depth,
598 );
599 if far_tier >= 1.0 {
600 // Fully far beauty skips detail, clusters, shadows, foam, sampled
601 // SSS, local lights, and every transmission/camera-depth read.
602 return vec4(far_water, 1.0);
603 }
604 }
606 let near = resolve_near_surface(in, surface_lod, geometric_normal, far_tier, mode);
607 let primary = resolve_primary_light(in, near.normal);
608 let medium = sample_water_medium(in, surface_lod, near.lighting_normal, to_view, mode);
609 if mode == DEBUG_MODE_SEA_FLOOR {
610 let depth = clamp(medium.water_depth / surface.sea_floor.y, 0.0, 1.0);
611 return vec4(1.0 - depth, 0.0, depth, 1.0);
612 }
613 if mode == DEBUG_MODE_LIGHT_RADIANCE {
614 if lights.n_directional_lights == 0u {
615 return vec4(0.0, 0.0, 0.0, 1.0);
616 }
617 let light_radiance = lights.directional_lights[0u].color.rgb * view.exposure;
618 return vec4(light_radiance / 16.0, 1.0);
619 }
620 if mode == DEBUG_MODE_WAVE_HEIGHT {
621 let height = clamp(0.5 + 0.5 * in.world_position.y, 0.0, 1.0);
622 return vec4(vec3(height), 1.0);
623 }
624 if mode == DEBUG_MODE_FOAM {
625 return vec4(vec3(medium.foam_density), 1.0);
626 }
628 let foam = prepare_surface_foam(
629 in,
630 surface_lod,
631 medium.foam_density,
632 near.lighting_distance,
633 );
634 let scatter = directional_scatter(
635 in,
636 surface_lod,
637 near,
638 primary,
639 medium,
640 to_view,
641 mode,
642 );
643 // Deep-pool darkness: bodies scale the ocean scatter endpoint down so
644 // colour comes from the bed through low-extinction water, not from a
645 // turquoise volume endpoint.
646 let scaled_scatter = scatter * invocation_scatter_scale();
647 let transmission =
648 resolve_transmission(in, near.normal, scaled_scatter, medium, foam, primary, mode);
649 if transmission.handled {
650 return transmission.output;
651 }
652 let body_lighting = shade_water_body(
653 in,
654 near,
655 primary,
656 medium,
657 foam,
658 to_view,
659 transmission.body,
660 mode,
661 );
662 let reflected = shade_environment_and_sun(
663 in.world_position.xyz,
664 surface_level,
665 near,
666 primary,
667 body_lighting,
668 to_view,
669 );
670 let local = shade_local_lights(
671 in,
672 near,
673 primary,
674 body_lighting,
675 foam,
676 to_view,
677 reflected,
678 mode,
679 );
680 return compose_water(
681 primary,
682 body_lighting,
683 local,
684 foam,
685 to_view,
686 far_water,
687 far_tier,
688 mode,
689 );
Contents