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// Aqua's motion-vector prepass.
2// Current geometry uses the shared forward deformation. Previous geometry is
3// evaluated at the same stable world anchor from the retained wave history.
5#import bevy_pbr::{
6 forward_io,
7 prepass_bindings,
8 mesh_functions,
9 prepass_io::{Vertex, VertexOutput, FragmentOutput},
10 mesh_view_bindings::view,
11 view_transformations::position_world_to_clip,
13#import bevy_aqua_core::deform::{deform_current}
14#import bevy_render::globals::Globals
15#import aqua::cascade::{DEBUG_MODE_BEAUTY, DEBUG_MODE_FAR_TIER, MIN_SAMPLE_WEIGHT, begin_invocation, CascadeLayout, cascade_layout, far_tier_weight, field_params, lod_alpha, lod_count, owning_body, sample_field_flow, sample_field_level, surface, world_to_uv}
16#import bevy_aqua_core::river::river_analytic_displacement
18// Retained AnimWaves output and the exact layout/time/flow that produced it.
19struct AquaPreviousFrame {
20 previous_layout: CascadeLayout,
21 time: vec4<f32>,
22 flow: vec4<f32>,
23 valid: vec4<f32>,
25@group(4) @binding(0) var aqua_previous_displacement: texture_2d_array<f32>;
26@group(4) @binding(1) var aqua_previous_sampler: sampler;
27@group(4) @binding(2) var<uniform> aqua_previous_frame: AquaPreviousFrame;
28@group(0) @binding(1) var<uniform> prepass_globals: Globals;
30fn previous_direct_displacement(world_xz: vec2<f32>, lod: u32) -> vec3<f32> {
31 // History contains the already-evolved wave field. Only global current
32 // advection remains to place that field in world space at the saved time.
33 let sampled_xz = world_xz
34 - aqua_previous_frame.flow.xy * aqua_previous_frame.time.x;
35 let previous_cascade = aqua_previous_frame.previous_layout.cascades[lod];
36 return textureSampleLevel(
37 aqua_previous_displacement,
38 aqua_previous_sampler,
39 world_to_uv(sampled_xz, previous_cascade),
40 i32(lod),
41 0.0,
42 ).xyz;
45fn previous_near_displacement(
46 world_xz: vec2<f32>,
47 lod: u32,
48 alpha: f32,
49) -> vec3<f32> {
50 // LOD selection and weights describe today's mesh representation. History
51 // layout is used only to map the stable world sample into retained texels.
52 let smaller = cascade_layout.cascades[lod];
53 let bigger = cascade_layout.cascades[lod + 1u];
54 let smaller_weight = (1.0 - alpha) * smaller.weight;
55 let bigger_weight = (1.0 - smaller_weight) * bigger.weight;
56 var displacement = vec3(0.0);
57 if smaller_weight > MIN_SAMPLE_WEIGHT {
58 displacement += smaller_weight
59 * previous_direct_displacement(world_xz, lod);
60 }
61 if bigger_weight > MIN_SAMPLE_WEIGHT {
62 displacement += bigger_weight
63 * previous_direct_displacement(world_xz, lod + 1u);
64 }
65 return displacement;
68fn previous_far_displacement(world_xz: vec2<f32>) -> vec3<f32> {
69 let outer_lod = lod_count() - 1u;
70 // Keep the current representation's outer-ring fade, but read its wave
71 // value from the retained outer history layer in the retained layout.
72 let weight = 1.0 - lod_alpha(
73 world_xz,
74 cascade_layout.cascades[outer_lod],
75 );
76 if weight <= MIN_SAMPLE_WEIGHT {
77 return vec3(0.0);
78 }
79 return weight * previous_direct_displacement(world_xz, outer_lod);
82@vertex
83fn vertex(vertex_in: Vertex) -> VertexOutput {
84 var out: VertexOutput;
85 // The shared deform contract uses the forward vertex ABI. Translate the
86 // prepass input explicitly because Bevy assigns different attribute
87 // locations to the two stage ABIs.
88 var forward_vertex: forward_io::Vertex;
89 forward_vertex.instance_index = vertex_in.instance_index;
90 forward_vertex.position = vertex_in.position;
91#ifdef VERTEX_UVS_A
92 forward_vertex.uv = vertex_in.uv;
93#endif
94#ifdef VERTEX_UVS_B
95 forward_vertex.uv_b = vertex_in.uv_b;
96#endif
97#ifdef VERTEX_COLORS
98 forward_vertex.color = vertex_in.color;
99#endif
100#ifdef SKINNED
101 forward_vertex.joint_indices = vertex_in.joint_indices;
102 forward_vertex.joint_weights = vertex_in.joint_weights;
103#endif
104#ifdef MORPH_TARGETS
105 forward_vertex.index = vertex_in.index;
106#endif
107 let deformation = deform_current(forward_vertex, prepass_globals.time);
108 out.world_position = deformation.world_position;
109 out.position = position_world_to_clip(out.world_position.xyz);
110#ifdef MOTION_VECTOR_PREPASS
111 var previous_world_position = out.world_position;
112 // Do not touch history until it contains a completed prior AnimWaves frame.
113 if aqua_previous_frame.valid.x >= 0.5 {
114 if deformation.river {
115 // Re-evaluate only height at the saved time. XZ and the water level
116 // are current stable anchors, never a previous ring transform.
117 let previous_river = river_analytic_displacement(
118 deformation.sample_xz,
119 deformation.river_flow,
120 aqua_previous_frame.time.x,
121 );
122 previous_world_position = vec4(
123 deformation.sample_xz.x,
124 deformation.body_level + previous_river.x,
125 deformation.sample_xz.y,
126 1.0,
127 );
128 } else if !deformation.bounded {
129 let mode = u32(round(surface.debug.x));
130 let far_diagnostic = mode == DEBUG_MODE_FAR_TIER;
131 let far_tier = select(
132 0.0,
133 far_tier_weight(deformation.base_world_position),
134 mode == DEBUG_MODE_BEAUTY || far_diagnostic,
135 );
136 var previous_displacement: vec3<f32>;
137 if far_tier >= 1.0 {
138 previous_displacement = previous_far_displacement(
139 deformation.sample_xz,
140 );
141 } else {
142 previous_displacement = previous_near_displacement(
143 deformation.sample_xz,
144 deformation.lod,
145 deformation.sample_alpha,
146 );
147 if far_tier > 0.0 {
148 previous_displacement = mix(
149 previous_displacement,
150 previous_far_displacement(deformation.sample_xz),
151 far_tier,
152 );
153 }
154 }
155 previous_world_position = vec4(
156 deformation.base_world_position + previous_displacement,
157 1.0,
158 );
159 }
160 // Bounded flat water intentionally retains current == previous.
161 }
162 out.previous_world_position = previous_world_position;
163#endif
164#ifdef VERTEX_OUTPUT_INSTANCE_INDEX
165 out.instance_index = vertex_in.instance_index;
166#endif
167#ifdef VISIBILITY_RANGE_DITHER
168 let world_from_local = mesh_functions::get_world_from_local(vertex_in.instance_index);
169 out.visibility_range_dither = mesh_functions::get_visibility_range_dither_level(
170 vertex_in.instance_index,
171 world_from_local[3],
172 );
173#endif
174 return out;
177@fragment
178fn fragment(in: VertexOutput) -> FragmentOutput {
179 // Match forward coverage for the shared ocean/body tile set. In the main
180 // ocean showcase this is exactly the current forward runtime path.
181 var slot = 0u;
182 if field_params.info.x > 0.5 {
183 slot = u32(sample_field_level(in.world_position.xz).y + 0.5);
184 }
185 let bounded = slot > 0u;
186 if !bounded && field_params.info.y < 0.5 {
187 discard;
188 }
189 let params = owning_body(slot);
190 begin_invocation(bounded, params);
191 if bounded {
192 let distance_to_extent = length(
193 params.extent.xy - view.world_position.xz,
194 ) - params.extent.w;
195 if distance_to_extent > surface.far_tier.y {
196 discard;
197 }
198 let river_flow = sample_field_flow(in.world_position.xz);
199 if params.flags.y > 0.5 && river_flow.z < 0.0 {
200 discard;
201 }
202 }
204 var out: FragmentOutput;
205#ifdef MOTION_VECTOR_PREPASS
206 let clip_t = view.unjittered_clip_from_world * in.world_position;
207 let clip = clip_t.xy / clip_t.w;
208 let previous_clip_t = prepass_bindings::previous_view_uniforms.clip_from_world
209 * in.previous_world_position;
210 let saved_previous_clip = previous_clip_t.xy / previous_clip_t.w;
211 // Invalid history is a full temporal reset, including camera teleport.
212 let previous_clip = select(
213 clip,
214 saved_previous_clip,
215 aqua_previous_frame.valid.x >= 0.5,
216 );
217 // Bevy/Terra ABI: UV-space offset; clip Y is inverted and NDC is halved.
218 let motion = (clip - previous_clip) * vec2(0.5, -0.5);
219 // Canonical positive zero is part of the raw Rg16Float reset contract.
220 out.motion_vector = select(motion, vec2(0.0), motion == vec2(0.0));
221#endif
222 return out;
Contents