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// Support code for clustered decals.
2//
3// This module provides an iterator API, which you may wish to use in your own
4// shaders if you want clustered decals to provide textures other than the base
5// color. The iterator API allows you to iterate over all decals affecting the
6// current fragment. Use `clustered_decal_iterator_new()` and
7// `clustered_decal_iterator_next()` as follows:
8//
9// let view_z = get_view_z(vec4(world_position, 1.0));
10// let is_orthographic = view_is_orthographic();
11//
12// let cluster_index =
13// clustered_forward::fragment_cluster_index(frag_coord, view_z, is_orthographic);
14// var clusterable_object_index_ranges =
15// clustered_forward::unpack_clusterable_object_index_ranges(cluster_index);
16//
17// var iterator = clustered_decal_iterator_new(world_position, &clusterable_object_index_ranges);
18// while (clustered_decal_iterator_next(&iterator)) {
19// ... sample from the texture at iterator.texture_index at iterator.uv ...
20// }
21//
22// In this way, in conjunction with a custom material, you can provide your own
23// texture arrays that mirror `mesh_view_bindings::clustered_decal_textures` in
24// order to support decals with normal maps, etc.
25//
26// Note that the order in which decals are returned is currently unpredictable,
27// though generally stable from frame to frame.
29#define_import_path bevy_pbr::decal::clustered
31#import bevy_pbr::clustered_forward
32#import bevy_pbr::clustered_forward::ClusterableObjectIndexRanges
33#import bevy_pbr::mesh_view_bindings
34#import bevy_render::maths
36// An object that allows stepping through all clustered decals that affect a
37// single fragment.
38struct ClusteredDecalIterator {
39 // Public fields follow:
40 // The index of the decal texture in the binding array.
41 texture_index: i32,
42 // The UV coordinates at which to sample that decal texture.
43 uv: vec2<f32>,
44 // A custom tag you can use for your own purposes.
45 tag: u32,
47 // Private fields follow:
48 // The current offset of the index in the `ClusterableObjectIndexRanges` list.
49 decal_index_offset: i32,
50 // The end offset of the index in the `ClusterableObjectIndexRanges` list.
51 end_offset: i32,
52 // The world-space position of the fragment.
53 world_position: vec3<f32>,
56#ifdef CLUSTERED_DECALS_ARE_USABLE
58// Creates a new iterator over the decals at the current fragment.
59//
60// You can retrieve `clusterable_object_index_ranges` as follows:
61//
62// let view_z = get_view_z(world_position);
63// let is_orthographic = view_is_orthographic();
64//
65// let cluster_index =
66// clustered_forward::fragment_cluster_index(frag_coord, view_z, is_orthographic);
67// var clusterable_object_index_ranges =
68// clustered_forward::unpack_clusterable_object_index_ranges(cluster_index);
69fn clustered_decal_iterator_new(
70 world_position: vec3<f32>,
71 clusterable_object_index_ranges: ptr<function, ClusterableObjectIndexRanges>
72) -> ClusteredDecalIterator {
73 return ClusteredDecalIterator(
74 -1,
75 vec2(0.0),
76 0u,
77 // We subtract 1 because the first thing `decal_iterator_next` does is
78 // add 1.
79 i32((*clusterable_object_index_ranges).first_decal_offset) - 1,
80 i32((*clusterable_object_index_ranges).last_clusterable_object_index_offset),
81 world_position,
82 );
85// Populates the `iterator.texture_index` and `iterator.uv` fields for the next
86// decal overlapping the current world position.
87//
88// Returns true if another decal was found or false if no more decals were found
89// for this position.
90fn clustered_decal_iterator_next(iterator: ptr<function, ClusteredDecalIterator>) -> bool {
91 if ((*iterator).decal_index_offset == (*iterator).end_offset) {
92 return false;
93 }
95 (*iterator).decal_index_offset += 1;
97 while ((*iterator).decal_index_offset < (*iterator).end_offset) {
98 let decal_index = i32(clustered_forward::get_clusterable_object_id(
99 u32((*iterator).decal_index_offset)
100 ));
101 let decal_space_vector =
102 (mesh_view_bindings::clustered_decals.decals[decal_index].local_from_world *
103 vec4((*iterator).world_position, 1.0)).xyz;
105 if (all(decal_space_vector >= vec3(-0.5)) && all(decal_space_vector <= vec3(0.5))) {
106 (*iterator).texture_index =
107 i32(mesh_view_bindings::clustered_decals.decals[decal_index].image_index);
108 (*iterator).uv = decal_space_vector.xy * vec2(1.0, -1.0) + vec2(0.5);
109 (*iterator).tag =
110 mesh_view_bindings::clustered_decals.decals[decal_index].tag;
111 return true;
112 }
114 (*iterator).decal_index_offset += 1;
115 }
117 return false;
120#endif // CLUSTERED_DECALS_ARE_USABLE
122// Returns the view-space Z coordinate for the given world position.
123fn get_view_z(world_position: vec3<f32>) -> f32 {
124 return dot(vec4<f32>(
125 mesh_view_bindings::view.view_from_world[0].z,
126 mesh_view_bindings::view.view_from_world[1].z,
127 mesh_view_bindings::view.view_from_world[2].z,
128 mesh_view_bindings::view.view_from_world[3].z
129 ), vec4(world_position, 1.0));
132// Returns true if the current view describes an orthographic projection or
133// false otherwise.
134fn view_is_orthographic() -> bool {
135 return mesh_view_bindings::view.clip_from_view[3].w == 1.0;
138// Modifies the base color at the given position to account for decals.
139//
140// Returns the new base color with decals taken into account. If no decals
141// overlap the current world position, returns the supplied base color
142// unmodified.
143fn apply_decal_base_color(
144 world_position: vec3<f32>,
145 frag_coord: vec2<f32>,
146 initial_base_color: vec4<f32>,
147) -> vec4<f32> {
148 var base_color = initial_base_color;
150#ifdef CLUSTERED_DECALS_ARE_USABLE
151 // Fetch the clusterable object index ranges for this world position.
153 let view_z = get_view_z(world_position);
154 let is_orthographic = view_is_orthographic();
156 let cluster_index =
157 clustered_forward::fragment_cluster_index(frag_coord, view_z, is_orthographic);
158 var clusterable_object_index_ranges =
159 clustered_forward::unpack_clusterable_object_index_ranges(cluster_index);
161 // Iterate over decals.
163 var iterator = clustered_decal_iterator_new(world_position, &clusterable_object_index_ranges);
164 while (clustered_decal_iterator_next(&iterator)) {
165 // Sample the current decal.
166 let decal_base_color = textureSampleLevel(
167 mesh_view_bindings::clustered_decal_textures[iterator.texture_index],
168 mesh_view_bindings::clustered_decal_sampler,
169 iterator.uv,
170 0.0
171 );
173 // Blend with the accumulated fragment.
174 base_color = vec4(
175 mix(base_color.rgb, decal_base_color.rgb, decal_base_color.a),
176 base_color.a + decal_base_color.a
177 );
178 }
179#endif // CLUSTERED_DECALS_ARE_USABLE
181 return base_color;
Contents