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
28@group(0) @binding(0) var<uniform> view: View;
29@group(0) @binding(1) var<uniform> sim_params : SimParams;
31@group(1) @binding(0) var<storage, read> particle_buffer : ParticleBuffer;
32@group(1) @binding(1) var<storage, read> indirect_buffer : IndirectBuffer;
33@group(1) @binding(2) var<storage, read> spawner : Spawner;
35{{MATERIAL_BINDINGS}}
37fn get_camera_position_effect_space() -> vec3<f32> {
38 let view_pos = view.world_from_view[3].xyz;
39#ifdef LOCAL_SPACE_SIMULATION
40 let inverse_transform = transpose(
41 mat3x3(
42 spawner.inverse_transform[0].xyz,
43 spawner.inverse_transform[1].xyz,
44 spawner.inverse_transform[2].xyz,
45 )
46 );
47 return inverse_transform * view_pos;
48#else
49 return view_pos;
50#endif
53fn get_camera_rotation_effect_space() -> mat3x3<f32> {
54 let view_rot = mat3x3(view.world_from_view[0].xyz, view.world_from_view[1].xyz, view.world_from_view[2].xyz);
55#ifdef LOCAL_SPACE_SIMULATION
56 let inverse_transform = transpose(
57 mat3x3(
58 spawner.inverse_transform[0].xyz,
59 spawner.inverse_transform[1].xyz,
60 spawner.inverse_transform[2].xyz,
61 )
62 );
63 return inverse_transform * view_rot;
64#else
65 return view_rot;
66#endif
69/// Unpack a compressed transform stored in transposed row-major form.
70fn unpack_compressed_transform(compressed_transform: mat3x4<f32>) -> mat4x4<f32> {
71 return transpose(
72 mat4x4(
73 compressed_transform[0],
74 compressed_transform[1],
75 compressed_transform[2],
76 vec4<f32>(0.0, 0.0, 0.0, 1.0)
77 )
78 );
81// Unpacks a compressed transform and transposes is.
82fn unpack_compressed_transform_3x3_transpose(compressed_transform: mat3x4<f32>) -> mat3x3<f32> {
83 return mat3x3(
84 compressed_transform[0].xyz,
85 compressed_transform[1].xyz,
86 compressed_transform[2].xyz,
87 );
90/// Transform a simulation space position into a world space position.
91///
92/// The simulation space depends on the effect's SimulationSpace value, and is either
93/// the effect space (SimulationSpace::Local) or the world space (SimulationSpace::Global).
94fn transform_position_simulation_to_world(sim_position: vec3<f32>) -> vec4<f32> {
95#ifdef LOCAL_SPACE_SIMULATION
96 let transform = unpack_compressed_transform(spawner.transform);
97 return transform * vec4<f32>(sim_position, 1.0);
98#else
99 return vec4<f32>(sim_position, 1.0);
100#endif
103fn transform_normal_simulation_to_world(sim_normal: vec3<f32>) -> vec3<f32> {
104#ifdef LOCAL_SPACE_SIMULATION
105 // We use the inverse transpose transform to transform normals.
106 // The inverse transpose is the same as the transposed inverse, so we can
107 // safely use the inverse transform.
108 let transform = unpack_compressed_transform_3x3_transpose(spawner.inverse_transform);
109 return transform * sim_normal;
110#else
111 return sim_normal;
112#endif
115/// Transform a simulation space position into a clip space position.
116///
117/// The simulation space depends on the effect's SimulationSpace value, and is either
118/// the effect space (SimulationSpace::Local) or the world space (SimulationSpace::Global).
119/// The clip space is the final [-1:1]^3 space output from the vertex shader, before
120/// perspective divide and viewport transform are applied.
121fn transform_position_simulation_to_clip(sim_position: vec3<f32>) -> vec4<f32> {
122 return view.clip_from_world * transform_position_simulation_to_world(sim_position);
125fn inverse_transpose_mat3(m: mat3x3<f32>) -> mat3x3<f32> {
126 let tmp0 = cross(m[1], m[2]);
127 let tmp1 = cross(m[2], m[0]);
128 let tmp2 = cross(m[0], m[1]);
129 let inv_det = 1.0 / dot(m[2], tmp2);
130 return mat3x3<f32>(tmp0 * inv_det, tmp1 * inv_det, tmp2 * inv_det);
133{{RENDER_EXTRA}}
135@vertex
136fn vertex(
137 @builtin(instance_index) instance_index: u32,
138 @location(0) vertex_position: vec3<f32>,
139#ifdef NEEDS_UV
140 @location(1) vertex_uv: vec2<f32>,
141#endif
142#ifdef NEEDS_NORMAL
143 @location(2) vertex_normal: vec3<f32>,
144#endif
145 // @location(1) vertex_color: u32,
146 // @location(1) vertex_velocity: vec3<f32>,
147) -> VertexOutput {
148 // Fetch particle
149 let pong = spawner.render_pong;
150 let particle_index = indirect_buffer.indices[3u * instance_index + pong];
151 var particle = particle_buffer.particles[particle_index];
153 var out: VertexOutput;
155#ifdef RIBBONS
156 // Discard first instance; we draw from second one, and link to previous one
157 if (instance_index == 0) {
158 out.position = vec4(0.0);
159 return out;
160 }
162 // Fetch previous particle
163 let prev_index = indirect_buffer.indices[3u * (instance_index - 1u) + pong];
164 let prev_particle = particle_buffer.particles[prev_index];
166 // Discard this instance if previous one is from a different ribbon. Again,
167 // we draw from second one of each ribbon.
168 if (prev_particle.ribbon_id != particle.ribbon_id) {
169 out.position = vec4(0.0);
170 return out;
171 }
172#endif // RIBBONS
174#ifdef NEEDS_UV
175 // Compute UVs
176 var uv = vertex_uv;
177#ifdef FLIPBOOK
178 let row_count = {{FLIPBOOK_ROW_COUNT}};
179 let ij = vec2<f32>(f32(particle.sprite_index % row_count), f32(particle.sprite_index / row_count));
180 uv = (ij + uv) * {{FLIPBOOK_SCALE}};
181#endif
182 out.uv = uv;
183#endif // NEEDS_UV
185{{INPUTS}}
187{{VERTEX_MODIFIERS}}
189#ifdef RIBBONS
190 var delta = particle.position - prev_particle.position;
192 axis_x = normalize(delta);
193 axis_y = normalize(cross(axis_x, axis_z));
194 axis_z = cross(axis_x, axis_y);
196 position = mix(particle.position, prev_particle.position, 0.5);
197 size = vec3(length(delta), size.y, 1.0);
198#endif // RIBBONS
200 // Expand particle mesh vertex based on particle position ("origin"), and local
201 // orientation and size of the particle mesh.
202 let vpos = vertex_position * size;
203 let sim_position = position + axis_x * vpos.x + axis_y * vpos.y + axis_z * vpos.z;
204 out.position = transform_position_simulation_to_clip(sim_position);
206 out.color = color;
208#ifdef NEEDS_NORMAL
209 let normal = inverse_transpose_mat3(mat3x3(axis_x, axis_y, axis_z)) * vertex_normal;
210 out.normal = transform_normal_simulation_to_world(normal);
211#endif // NEEDS_NORMAL
213 return out;
216@fragment
217fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
218 // Read fragment inputs
219#ifdef USE_ALPHA_MASK
220 var alpha_cutoff: f32 = {{ALPHA_CUTOFF}};
221#endif
222 var color = in.color;
223#ifdef NEEDS_UV
224 var uv = in.uv;
225#endif
226#ifdef NEEDS_NORMAL
227 var normal = in.normal;
228#endif
230{{FRAGMENT_MODIFIERS}}
232#ifdef USE_ALPHA_MASK
233 if color.a >= alpha_cutoff {
234 color.a = 1.0;
235 } else {
236 discard;
237 }
238#endif
240 return color;
Contents