Path: blob/main/examples/shader/automatic_instancing.rs
6595 views
//! Shows that multiple instances of a cube are automatically instanced in one draw call1//! Try running this example in a graphics profiler and all the cubes should be only a single draw call.2//! Also demonstrates how to use `MeshTag` to use external data in a custom material.34use bevy::{5mesh::MeshTag, prelude::*, reflect::TypePath, render::render_resource::AsBindGroup,6shader::ShaderRef,7};89const SHADER_ASSET_PATH: &str = "shaders/automatic_instancing.wgsl";1011fn main() {12App::new()13.add_plugins((DefaultPlugins, MaterialPlugin::<CustomMaterial>::default()))14.add_systems(Startup, setup)15.add_systems(Update, update)16.run();17}1819/// Sets up an instanced grid of cubes, where each cube is colored based on an image that is20/// sampled in the vertex shader. The cubes are then animated in a spiral pattern.21///22/// This example demonstrates one use of automatic instancing and how to use `MeshTag` to use23/// external data in a custom material. For example, here we use the "index" of each cube to24/// determine the texel coordinate to sample from the image in the shader.25fn setup(26mut commands: Commands,27assets: Res<AssetServer>,28mut meshes: ResMut<Assets<Mesh>>,29mut materials: ResMut<Assets<CustomMaterial>>,30) {31// We will use this image as our external data for our material to sample from in the vertex shader32let image = assets.load("branding/icon.png");3334// Our single mesh handle that will be instanced35let mesh_handle = meshes.add(Cuboid::from_size(Vec3::splat(0.01)));3637// Create the custom material with a reference to our texture38// Automatic instancing works with any Material, including the `StandardMaterial`.39// This custom material is used to demonstrate the optional `MeshTag` feature.40let material_handle = materials.add(CustomMaterial {41image: image.clone(),42});4344// We're hardcoding the image dimensions for simplicity45let image_dims = UVec2::new(256, 256);46let total_pixels = image_dims.x * image_dims.y;4748for index in 0..total_pixels {49// Get x,y from index - x goes left to right, y goes top to bottom50let x = index % image_dims.x;51let y = index / image_dims.x;5253// Convert to centered world coordinates54let world_x = (x as f32 - image_dims.x as f32 / 2.0) / 50.0;55let world_y = -((y as f32 - image_dims.y as f32 / 2.0) / 50.0); // Still need negative for world space5657commands.spawn((58// For automatic instancing to take effect you need to59// use the same mesh handle and material handle for each instance60Mesh3d(mesh_handle.clone()),61MeshMaterial3d(material_handle.clone()),62// This is an optional component that can be used to help tie external data to a mesh instance63MeshTag(index),64Transform::from_xyz(world_x, world_y, 0.0),65));66}6768// Camera69commands.spawn((70Camera3d::default(),71Transform::from_xyz(0.0, 0.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),72));73}7475// Animate the transform76fn update(time: Res<Time>, mut transforms: Query<(&mut Transform, &MeshTag)>) {77for (mut transform, index) in transforms.iter_mut() {78// Animate the z position based on time using the index to create a spiral79transform.translation.z = ops::sin(time.elapsed_secs() + index.0 as f32 * 0.01);80}81}8283// This struct defines the data that will be passed to your shader84#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]85struct CustomMaterial {86#[texture(0)]87#[sampler(1)]88image: Handle<Image>,89}9091impl Material for CustomMaterial {92fn vertex_shader() -> ShaderRef {93SHADER_ASSET_PATH.into()94}9596fn fragment_shader() -> ShaderRef {97SHADER_ASSET_PATH.into()98}99}100101102