Support Warning
WebGPU is currently only supported on Chrome starting with version 113, and only on desktop. If they don't work on
your configuration, you can check the WebGL2 examples
here.
array_texture.rs:
use bevy::{
asset::LoadState,
prelude::*,
reflect::TypePath,
render::render_resource::{AsBindGroup, ShaderRef},
};
fn main() {
App::new()
.add_plugins((
DefaultPlugins,
MaterialPlugin::<ArrayTextureMaterial>::default(),
))
.add_systems(Startup, setup)
.add_systems(Update, create_array_texture)
.run();
}
#[derive(Resource)]
struct LoadingTexture {
is_loaded: bool,
handle: Handle<Image>,
}
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.insert_resource(LoadingTexture {
is_loaded: false,
handle: asset_server.load("textures/array_texture.png"),
});
commands.spawn(DirectionalLightBundle {
transform: Transform::from_xyz(3.0, 2.0, 1.0).looking_at(Vec3::ZERO, Vec3::Y),
..Default::default()
});
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(5.0, 5.0, 5.0).looking_at(Vec3::new(1.5, 0.0, 0.0), Vec3::Y),
..Default::default()
});
}
fn create_array_texture(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut loading_texture: ResMut<LoadingTexture>,
mut images: ResMut<Assets<Image>>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ArrayTextureMaterial>>,
) {
if loading_texture.is_loaded
|| asset_server.load_state(loading_texture.handle.id()) != LoadState::Loaded
{
return;
}
loading_texture.is_loaded = true;
let image = images.get_mut(&loading_texture.handle).unwrap();
let array_layers = 4;
image.reinterpret_stacked_2d_as_array(array_layers);
let mesh_handle = meshes.add(Cuboid::default());
let material_handle = materials.add(ArrayTextureMaterial {
array_texture: loading_texture.handle.clone(),
});
for x in -5..=5 {
commands.spawn(MaterialMeshBundle {
mesh: mesh_handle.clone(),
material: material_handle.clone(),
transform: Transform::from_xyz(x as f32 + 0.5, 0.0, 0.0),
..Default::default()
});
}
}
#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
struct ArrayTextureMaterial {
#[texture(0, dimension = "2d_array")]
#[sampler(1)]
array_texture: Handle<Image>,
}
impl Material for ArrayTextureMaterial {
fn fragment_shader() -> ShaderRef {
"shaders/array_texture.wgsl".into()
}
}
shaders/array_texture.wgsl:
#import bevy_pbr::{
forward_io::VertexOutput,
mesh_view_bindings::view,
pbr_types::{STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT, PbrInput, pbr_input_new},
pbr_functions as fns,
pbr_bindings,
}
#import bevy_core_pipeline::tonemapping::tone_mapping
@group(2) @binding(0) var my_array_texture: texture_2d_array<f32>;
@group(2) @binding(1) var my_array_texture_sampler: sampler;
@fragment
fn fragment(
@builtin(front_facing) is_front: bool,
mesh: VertexOutput,
) -> @location(0) vec4<f32> {
let layer = i32(mesh.world_position.x) & 0x3;
var pbr_input: PbrInput = pbr_input_new();
pbr_input.material.base_color = textureSample(my_array_texture, my_array_texture_sampler, mesh.uv, layer);
#ifdef VERTEX_COLORS
pbr_input.material.base_color = pbr_input.material.base_color * mesh.color;
#endif
let double_sided = (pbr_input.material.flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u;
pbr_input.frag_coord = mesh.position;
pbr_input.world_position = mesh.world_position;
pbr_input.world_normal = fns::prepare_world_normal(
mesh.world_normal,
double_sided,
is_front,
);
pbr_input.is_orthographic = view.clip_from_view[3].w == 1.0;
pbr_input.N = normalize(pbr_input.world_normal);
#ifdef VERTEX_TANGENTS
let Nt = textureSampleBias(pbr_bindings::normal_map_texture, pbr_bindings::normal_map_sampler, mesh.uv, view.mip_bias).rgb;
let TBN = fns::calculate_tbn_mikktspace(mesh.world_normal, mesh.world_tangent);
pbr_input.N = fns::apply_normal_mapping(
pbr_input.material.flags,
TBN,
double_sided,
is_front,
Nt,
);
#endif
pbr_input.V = fns::calculate_view(mesh.world_position, pbr_input.is_orthographic);
return tone_mapping(fns::apply_pbr_lighting(pbr_input), view.color_grading);
}