docs/bevy/examples/shader/array_texture.rs 3.1 K · 102 lines · raw · history

1 //! This example illustrates how to create a texture for use with a
2 //! `texture_2d_array<f32>` shader uniform variable and then how to sample from
3 //! that texture in the shader by using a `MeshTag` component on the mesh
4 //! entity.
5
6 use bevy::{
7 image::{ImageArrayLayout, ImageLoaderSettings},
8 mesh::MeshTag,
9 prelude::*,
10 reflect::TypePath,
11 render::render_resource::AsBindGroup,
12 shader::ShaderRef,
13 };
14
15 /// This example uses a shader source file from the assets subdirectory.
16 const SHADER_ASSET_PATH: &str = "shaders/array_texture.wesl";
17
18 /// Corresponds to the number of layers in the array texture.
19 const TEXTURE_COUNT: u32 = 4;
20
21 fn main() {
22 App::new()
23 .add_plugins((
24 DefaultPlugins,
25 MaterialPlugin::<ArrayTextureMaterial>::default(),
26 ))
27 .add_systems(Startup, setup)
28 .add_systems(Update, update_mesh_tags)
29 .run();
30 }
31
32 fn setup(
33 mut commands: Commands,
34 mut meshes: ResMut<Assets<Mesh>>,
35 mut materials: ResMut<Assets<ArrayTextureMaterial>>,
36 asset_server: Res<AssetServer>,
37 ) {
38 // Load the texture.
39 let array_texture = asset_server
40 .load_builder()
41 .with_settings(|settings: &mut ImageLoaderSettings| {
42 settings.array_layout = Some(ImageArrayLayout::RowCount {
43 rows: TEXTURE_COUNT,
44 });
45 })
46 .load("textures/array_texture.png");
47
48 // light
49 commands.spawn((
50 DirectionalLight::default(),
51 Transform::from_xyz(3.0, 2.0, 1.0).looking_at(Vec3::ZERO, Vec3::Y),
52 ));
53
54 // camera
55 commands.spawn((
56 Camera3d::default(),
57 Transform::from_xyz(5.0, 5.0, 5.0).looking_at(Vec3::new(1.5, 0.0, 0.0), Vec3::Y),
58 ));
59
60 // Spawn some cubes using the array texture.
61 let mesh_handle = meshes.add(Cuboid::default());
62 let material_handle = materials.add(ArrayTextureMaterial { array_texture });
63 for x in -5..=5 {
64 commands.spawn((
65 Mesh3d(mesh_handle.clone()),
66 MeshMaterial3d(material_handle.clone()),
67 // Pass a different mesh tag to allow selecting different layers of
68 // the array texture in the shader.
69 MeshTag::new(x as u32 % TEXTURE_COUNT),
70 Transform::from_xyz(x as f32 + 0.5, 0.0, 0.0),
71 ));
72 }
73 }
74
75 fn update_mesh_tags(time: Res<Time>, mut query: Query<&mut MeshTag>, mut timer: Local<Timer>) {
76 // Initialize the timer on the first run.
77 if timer.duration().is_zero() {
78 *timer = Timer::from_seconds(1.5, TimerMode::Repeating);
79 }
80
81 timer.tick(time.delta());
82 if timer.just_finished() {
83 for mut tag in query.iter_mut() {
84 // Cycle through the texture layers to demonstrate that we can
85 // select different layers of the array texture at runtime.
86 tag.value = (tag.value + 1) % TEXTURE_COUNT;
87 }
88 }
89 }
90
91 #[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
92 struct ArrayTextureMaterial {
93 #[texture(0, dimension = "2d_array")]
94 #[sampler(1)]
95 array_texture: Handle<Image>,
96 }
97
98 impl Material for ArrayTextureMaterial {
99 fn fragment_shader() -> ShaderRef {
100 SHADER_ASSET_PATH.into()
101 }
102 }