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#import bevy_render::view::View
2#import bevy_hanabi::vfx_common::{
3 IndirectBuffer, SimParams, Spawner,
4 seed, tau, pcg_hash, to_float01, frand, frand2, frand3, frand4,
5 rand_uniform_f, rand_uniform_vec2, rand_uniform_vec3, rand_uniform_vec4,
6 rand_normal_f, rand_normal_vec2, rand_normal_vec3, rand_normal_vec4, proj
7}
9struct Particle {
10{{ATTRIBUTES}}
13struct ParticleBuffer {
14 particles: array<Particle>,
17struct VertexOutput {
18 @builtin(position) position: vec4<f32>,
19 @location(0) color: vec4<f32>,
20#ifdef NEEDS_UV
21 @location(1) uv: vec2<f32>,
22#endif
23#ifdef NEEDS_NORMAL
24 @location(2) normal: vec3<f32>,
25#endif
26#ifdef NEEDS_PARTICLE_FRAGMENT
27 @location(3) slab_particle_index: u32,
28#endif
31@group(0) @binding(0) var<uniform> view: View;
32@group(0) @binding(1) var<uniform> sim_params : SimParams;
34@group(1) @binding(0) var<storage, read> particle_buffer : ParticleBuffer;
35@group(1) @binding(1) var<storage, read> indirect_buffer : IndirectBuffer;
36@group(1) @binding(2) var<storage, read> spawner : Spawner;
38{{MATERIAL_BINDINGS}}
40fn get_camera_position_effect_space() -> vec3<f32> {
41 let view_pos = view.world_from_view[3].xyz;
42#ifdef LOCAL_SPACE_SIMULATION
43 let inverse_transform = transpose(
44 mat3x3(
45 spawner.inverse_transform[0].xyz,
46 spawner.inverse_transform[1].xyz,
47 spawner.inverse_transform[2].xyz,
48 )
49 );
50 return inverse_transform * view_pos;
51#else
52 return view_pos;
53#endif
56fn get_camera_rotation_effect_space() -> mat3x3<f32> {
57 let view_rot = mat3x3(view.world_from_view[0].xyz, view.world_from_view[1].xyz, view.world_from_view[2].xyz);
58#ifdef LOCAL_SPACE_SIMULATION
59 let inverse_transform = transpose(
60 mat3x3(
61 spawner.inverse_transform[0].xyz,
62 spawner.inverse_transform[1].xyz,
63 spawner.inverse_transform[2].xyz,
64 )
65 );
66 return inverse_transform * view_rot;
67#else
68 return view_rot;
69#endif
72/// Unpack a compressed transform stored in transposed row-major form.
73fn unpack_compressed_transform(compressed_transform: mat3x4<f32>) -> mat4x4<f32> {
74 return transpose(
75 mat4x4(
76 compressed_transform[0],
77 compressed_transform[1],
78 compressed_transform[2],
79 vec4<f32>(0.0, 0.0, 0.0, 1.0)
80 )
81 );
84// Unpacks a compressed transform and transposes is.
85fn unpack_compressed_transform_3x3_transpose(compressed_transform: mat3x4<f32>) -> mat3x3<f32> {
86 return mat3x3(
87 compressed_transform[0].xyz,
88 compressed_transform[1].xyz,
89 compressed_transform[2].xyz,
90 );
93/// Transform a simulation space position into a world space position.
94///
95/// The simulation space depends on the effect's SimulationSpace value, and is either
96/// the effect space (SimulationSpace::Local) or the world space (SimulationSpace::Global).
97fn transform_position_simulation_to_world(sim_position: vec3<f32>) -> vec4<f32> {
98#ifdef LOCAL_SPACE_SIMULATION
99 let transform = unpack_compressed_transform(spawner.transform);
100 return transform * vec4<f32>(sim_position, 1.0);
101#else
102 return vec4<f32>(sim_position, 1.0);
103#endif
106fn transform_normal_simulation_to_world(sim_normal: vec3<f32>) -> vec3<f32> {
107#ifdef LOCAL_SPACE_SIMULATION
108 // We use the inverse transpose transform to transform normals.
109 // The inverse transpose is the same as the transposed inverse, so we can
110 // safely use the inverse transform.
111 let transform = unpack_compressed_transform_3x3_transpose(spawner.inverse_transform);
112 return transform * sim_normal;
113#else
114 return sim_normal;
115#endif
118/// Transform a simulation space position into a clip space position.
119///
120/// The simulation space depends on the effect's SimulationSpace value, and is either
121/// the effect space (SimulationSpace::Local) or the world space (SimulationSpace::Global).
122/// The clip space is the final [-1:1]^3 space output from the vertex shader, before
123/// perspective divide and viewport transform are applied.
124fn transform_position_simulation_to_clip(sim_position: vec3<f32>) -> vec4<f32> {
125 return view.clip_from_world * transform_position_simulation_to_world(sim_position);
128fn inverse_transpose_mat3(m: mat3x3<f32>) -> mat3x3<f32> {
129 let tmp0 = cross(m[1], m[2]);
130 let tmp1 = cross(m[2], m[0]);
131 let tmp2 = cross(m[0], m[1]);
132 let inv_det = 1.0 / dot(m[2], tmp2);
133 return mat3x3<f32>(tmp0 * inv_det, tmp1 * inv_det, tmp2 * inv_det);
136{{RENDER_EXTRA}}
138@vertex
139fn vertex(
140 @builtin(instance_index) instance_index: u32,
141 @location(0) vertex_position: vec3<f32>,
142#ifdef NEEDS_UV
143 @location(1) vertex_uv: vec2<f32>,
144#endif
145#ifdef NEEDS_NORMAL
146 @location(2) vertex_normal: vec3<f32>,
147#endif
148 // @location(1) vertex_color: u32,
149 // @location(1) vertex_velocity: vec3<f32>,
150) -> VertexOutput {
151 let base_particle = spawner.slab_offset;
153 // Fetch particle
154 let indirect_read_index = spawner.render_indirect_read_index;
155 let particle_index = indirect_buffer.rows[base_particle + instance_index].particle_index[indirect_read_index];
156 var particle = particle_buffer.particles[base_particle + particle_index];
158 var out: VertexOutput;
160#ifdef NEEDS_PARTICLE_FRAGMENT
161 out.slab_particle_index = base_particle + particle_index;
162#endif // NEEDS_PARTICLE_FRAGMENT
164#ifdef RIBBONS
165 // Discard first instance; we draw from second one, and link to previous one
166 if (instance_index == 0) {
167 out.position = vec4(0.0);
168 return out;
169 }
171 // Fetch previous particle
172 let prev_index = indirect_buffer.rows[base_particle + instance_index - 1u].particle_index[indirect_read_index];
173 let prev_particle = particle_buffer.particles[base_particle + prev_index];
175 // Discard this instance if previous one is from a different ribbon. Again,
176 // we draw from second one of each ribbon.
177 if (prev_particle.ribbon_id != particle.ribbon_id) {
178 out.position = vec4(0.0);
179 return out;
180 }
181#endif // RIBBONS
183#ifdef NEEDS_UV
184 // Compute UVs
185 var uv = vertex_uv;
186#ifdef FLIPBOOK
187 let row_count = {{FLIPBOOK_ROW_COUNT}};
188 let ij = vec2<f32>(f32(particle.sprite_index % row_count), f32(particle.sprite_index / row_count));
189 uv = (ij + uv) * {{FLIPBOOK_SCALE}};
190#endif
191 out.uv = uv;
192#endif // NEEDS_UV
194{{INPUTS}}
196{{VERTEX_MODIFIERS}}
198#ifdef RIBBONS
199 var delta = particle.position - prev_particle.position;
201 axis_x = normalize(delta);
202 axis_y = normalize(cross(axis_x, axis_z));
203 axis_z = cross(axis_x, axis_y);
205 position = mix(particle.position, prev_particle.position, 0.5);
206 size = vec3(length(delta), size.y, 1.0);
207#endif // RIBBONS
209 // Expand particle mesh vertex based on particle position ("origin"), and local
210 // orientation and size of the particle mesh.
211 let vpos = vertex_position * size;
212 let sim_position = position + axis_x * vpos.x + axis_y * vpos.y + axis_z * vpos.z;
213 out.position = transform_position_simulation_to_clip(sim_position);
215 out.color = color;
217#ifdef NEEDS_NORMAL
218 let normal = inverse_transpose_mat3(mat3x3(axis_x, axis_y, axis_z)) * vertex_normal;
219 out.normal = transform_normal_simulation_to_world(normal);
220#endif // NEEDS_NORMAL
222 return out;
225@fragment
226fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
227 // Read fragment inputs
228#ifdef USE_ALPHA_MASK
229 var alpha_cutoff: f32 = {{ALPHA_CUTOFF}};
230#endif
231 var color = in.color;
232#ifdef NEEDS_UV
233 var uv = in.uv;
234#endif
235#ifdef NEEDS_NORMAL
236 var normal = in.normal;
237#endif
238#ifdef NEEDS_PARTICLE_FRAGMENT
239 var particle = particle_buffer.particles[in.slab_particle_index];
240#endif // NEEDS_PARTICLE_FRAGMENT
242{{FRAGMENT_MODIFIERS}}
244#ifdef USE_ALPHA_MASK
245 if color.a >= alpha_cutoff {
246 color.a = 1.0;
247 } else {
248 discard;
249 }
250#endif
252 return color;
Contents