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_sprite::{
2 mesh2d_functions as mesh_functions,
3 mesh2d_vertex_output::VertexOutput,
4 mesh2d_view_bindings::view,
5}
7#ifdef TONEMAP_IN_SHADER
8#import bevy_core_pipeline::tonemapping
9#endif
10#ifdef SRGB_OUTPUT
11#import bevy_render::color_operations::linear_to_srgb
12#endif
13#ifdef OKLAB_OUTPUT
14#import bevy_render::color_operations::linear_rgb_to_oklab
15#endif
17struct Vertex {
18 @builtin(instance_index) instance_index: u32,
19#ifdef VERTEX_POSITIONS
20 @location(0) position: vec3<f32>,
21#endif
22#ifdef VERTEX_NORMALS
23 @location(1) normal: vec3<f32>,
24#endif
25#ifdef VERTEX_UVS
26 @location(2) uv: vec2<f32>,
27#endif
28#ifdef VERTEX_TANGENTS
29 @location(3) tangent: vec4<f32>,
30#endif
31#ifdef VERTEX_COLORS
32 @location(4) color: vec4<f32>,
33#endif
34};
36@vertex
37fn vertex(vertex: Vertex) -> VertexOutput {
38 var out: VertexOutput;
40#ifdef VERTEX_UVS
41 out.uv = vertex.uv;
42#endif
44#ifdef VERTEX_POSITIONS
45 var world_from_local = mesh_functions::get_world_from_local(vertex.instance_index);
46 let position = vec4<f32>(vertex.position * vec3<f32>(material.vertex_scale, 1.0) + vec3<f32>(material.vertex_offset, 0.0), 1.0);
48 out.world_position = mesh_functions::mesh2d_position_local_to_world(
49 world_from_local,
50 position
51 );
52 out.position = mesh_functions::mesh2d_position_world_to_clip(out.world_position);
53#endif
55#ifdef VERTEX_NORMALS
56 out.world_normal = mesh_functions::mesh2d_normal_local_to_world(vertex.normal, vertex.instance_index);
57#endif
59#ifdef VERTEX_TANGENTS
60 out.world_tangent = mesh_functions::mesh2d_tangent_local_to_world(
61 world_from_local,
62 vertex.tangent
63 );
64#endif
66#ifdef VERTEX_COLORS
67 out.color = vertex.color;
68#endif
69 return out;
72struct SpriteMaterial {
73 color: vec4<f32>,
74 flags: u32,
75 alpha_cutoff: f32,
76 vertex_scale: vec2<f32>,
77 vertex_offset: vec2<f32>,
78 uv_transform: mat3x3<f32>,
80 tile_stretch_value: vec2<f32>,
82 scale: vec2<f32>,
83 min_inset: vec2<f32>,
84 max_inset: vec2<f32>,
85 side_stretch_value: vec2<f32>,
86 center_stretch_value: vec2<f32>,
87};
89const SPRITE_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS: u32 = 3221225472u; // (0b11u32 << 30)
90const SPRITE_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE: u32 = 0u; // (0u32 << 30)
91const SPRITE_MATERIAL_FLAGS_ALPHA_MODE_MASK: u32 = 1073741824u; // (1u32 << 30)
92const SPRITE_MATERIAL_FLAGS_ALPHA_MODE_BLEND: u32 = 2147483648u; // (2u32 << 30)
94const SPRITE_MATERIAL_FLAGS_FLIP_X: u32 = 1u;
95const SPRITE_MATERIAL_FLAGS_FLIP_Y: u32 = 2u;
96const SPRITE_MATERIAL_FLAGS_TILE_X: u32 = 4u;
97const SPRITE_MATERIAL_FLAGS_TILE_Y: u32 = 8u;
99@group(#{MATERIAL_BIND_GROUP}) @binding(0) var<uniform> material: SpriteMaterial;
100@group(#{MATERIAL_BIND_GROUP}) @binding(1) var texture: texture_2d<f32>;
101@group(#{MATERIAL_BIND_GROUP}) @binding(2) var texture_sampler: sampler;
103@fragment
104fn fragment(
105 mesh: VertexOutput,
106) -> @location(0) vec4<f32> {
108 var uv = mesh.uv;
110 if (material.flags & SPRITE_MATERIAL_FLAGS_FLIP_X) != 0u {
111 uv.x = 1.0 - uv.x;
112 }
113 if (material.flags & SPRITE_MATERIAL_FLAGS_FLIP_Y) != 0u {
114 uv.y = 1.0 - uv.y;
115 }
117 if (material.flags & SPRITE_MATERIAL_FLAGS_TILE_X) != 0u {
118 uv.x = (uv.x - material.tile_stretch_value.x * floor(uv.x / material.tile_stretch_value.x)) / material.tile_stretch_value.x;
119 }
120 if (material.flags & SPRITE_MATERIAL_FLAGS_TILE_Y) != 0u {
121 uv.y = (uv.y - material.tile_stretch_value.y * floor(uv.y / material.tile_stretch_value.y)) / material.tile_stretch_value.y;
122 }
124 // using this as a temp check for slicing
125 if material.scale.x != 0.0 {
126 uv = apply_slicing(uv);
127 }
129 uv = (material.uv_transform * vec3(uv, 1.0)).xy;
131 let sprite_color = textureSample(texture, texture_sampler, uv);
132 var output_color = alpha_discard(sprite_color * material.color);
134#ifdef TONEMAP_IN_SHADER
135 output_color = tonemapping::tone_mapping(output_color, view.color_grading);
136#endif
138#ifdef SRGB_OUTPUT
139 output_color = vec4(linear_to_srgb(output_color.rgb), output_color.a);
140#endif
142#ifdef OKLAB_OUTPUT
143 output_color = vec4(linear_rgb_to_oklab(output_color.rgb), output_color.a);
144#endif
146 return output_color;
149fn apply_slicing(uv: vec2<f32>) -> vec2<f32> {
150 let min_inset_scaled = material.min_inset / material.scale;
151 let max_inset_scaled = material.max_inset / material.scale;
153 let left = uv.x < min_inset_scaled.x;
154 let right = uv.x > 1.0 - max_inset_scaled.x;
155 let top = uv.y < min_inset_scaled.y;
156 let bottom = uv.y > 1.0 - max_inset_scaled.y;
158 // top-left corner
159 if top && left {
160 return uv * material.scale;
161 }
163 // top-right corner
164 if top && right {
165 return vec2<f32>(
166 1.0 - (1.0 - uv.x) * material.scale.x,
167 uv.y * material.scale.y,
168 );
169 }
171 // bottom-left corner
172 if bottom && left {
173 return vec2<f32>(
174 uv.x * material.scale.x,
175 1.0 - (1.0 - uv.y) * material.scale.y
176 );
177 }
179 // bottom-right corner
180 if bottom && right {
181 return vec2<f32>(1.0) - (vec2<f32>(1.0) - uv) * material.scale;
182 }
184 // top edge
185 if top {
186 return vec2<f32>(
187 tile_or_stretch(uv.x, min_inset_scaled.x, 1.0 - max_inset_scaled.x, material.min_inset.x, 1.0 - material.max_inset.x, material.side_stretch_value.x),
188 uv.y * material.scale.y
189 );
190 }
192 // bottom edge
193 if bottom {
194 return vec2<f32>(
195 tile_or_stretch(uv.x, min_inset_scaled.x, 1.0 - max_inset_scaled.x, material.min_inset.x, 1.0 - material.max_inset.x, material.side_stretch_value.x),
196 1.0 - (1.0 - uv.y) * material.scale.y
197 );
198 }
200 // left edge
201 if left {
202 return vec2<f32>(
203 uv.x * material.scale.x,
204 tile_or_stretch(uv.y, min_inset_scaled.y, 1.0 - max_inset_scaled.y, material.min_inset.y, 1.0 - material.max_inset.y, material.side_stretch_value.y)
205 );
206 }
208 // right edge
209 if right {
210 return vec2<f32>(
211 1.0 - (1.0 - uv.x) * material.scale.x,
212 tile_or_stretch(uv.y, min_inset_scaled.y, 1.0 - max_inset_scaled.y, material.min_inset.y, 1.0 - material.max_inset.y, material.side_stretch_value.y)
213 );
214 }
216 // center
217 return vec2<f32>(
218 tile_or_stretch(uv.x, min_inset_scaled.x, 1.0 - max_inset_scaled.x, material.min_inset.x, 1.0 - material.max_inset.x, material.center_stretch_value.x),
219 tile_or_stretch(uv.y, min_inset_scaled.y, 1.0 - max_inset_scaled.y, material.min_inset.y, 1.0 - material.max_inset.y, material.center_stretch_value.y)
220 );
223// Maps a point p from [a, b] to [c, d], tiling it if stretch_value is not 0.
224fn tile_or_stretch(p: f32, a: f32, b: f32, c: f32, d: f32, stretch_value: f32) -> f32 {
225 if stretch_value == 0.0 {
226 return stretch_interval(p, a, b, c, d);
227 }
228 return tile_interval(p, a, b, c, d, stretch_value);
231// Takes a point p from an interval [a, b] and maps it to a portion of the tile [c, d]
232fn tile_interval(p: f32, a: f32, b: f32, c: f32, d: f32, stretch_value: f32) -> f32 {
233 let value = (p - a) / (b - a);
234 let tile_value = (value - stretch_value * floor(value / stretch_value)) / stretch_value;
235 return tile_value * (d - c) + c;
238// Takes a point p from an interval [a, b] and translates it to the interval [c, d]
239fn stretch_interval(p: f32, a: f32, b: f32, c: f32, d: f32) -> f32 {
240 return (p - a) / (b - a) * (d - c) + c;
243fn alpha_discard(output_color: vec4<f32>) -> vec4<f32> {
244 var color = output_color;
245 let alpha_mode = material.flags & SPRITE_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS;
247 if alpha_mode == SPRITE_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE {
248 // NOTE: If rendering as opaque, alpha should be ignored so set to 1.0
249 color.a = 1.0;
250 }
252#ifdef MAY_DISCARD
253 else if alpha_mode == SPRITE_MATERIAL_FLAGS_ALPHA_MODE_MASK {
254 if color.a >= material.alpha_cutoff {
255 // NOTE: If rendering as masked alpha and >= the cutoff, render as fully opaque
256 color.a = 1.0;
257 } else {
258 // NOTE: output_color.a < in.material.alpha_cutoff should not be rendered
259 discard;
260 }
261 }
262#endif // MAY_DISCARD
264 return color;
Contents