Path: blob/main/examples/shader/extended_material_bindless.rs
6595 views
//! Demonstrates bindless `ExtendedMaterial`.12use std::f32::consts::FRAC_PI_2;34use bevy::{5color::palettes::{css::RED, tailwind::GRAY_600},6mesh::{SphereKind, SphereMeshBuilder},7pbr::{ExtendedMaterial, MaterialExtension, MeshMaterial3d},8prelude::*,9render::render_resource::{AsBindGroup, ShaderType},10shader::ShaderRef,11utils::default,12};1314/// The path to the example material shader.15static SHADER_ASSET_PATH: &str = "shaders/extended_material_bindless.wgsl";1617/// The example bindless material extension.18///19/// As usual for material extensions, we need to avoid conflicting with both the20/// binding numbers and bindless indices of the [`StandardMaterial`], so we21/// start both values at 100 and 50 respectively.22///23/// The `#[data(50, ExampleBindlessExtensionUniform, binding_array(101))]`24/// attribute specifies that the plain old data25/// [`ExampleBindlessExtensionUniform`] will be placed into an array with26/// binding 100 and will occupy index 50 in the27/// `ExampleBindlessExtendedMaterialIndices` structure. (See the shader for the28/// definition of that structure.) That corresponds to the following shader29/// declaration:30///31/// ```wgsl32/// @group(2) @binding(100) var<storage> example_extended_material_indices:33/// array<ExampleBindlessExtendedMaterialIndices>;34/// ```35///36/// The `#[bindless(index_table(range(50..53), binding(100)))]` attribute37/// specifies that this material extension should be bindless. The `range`38/// subattribute specifies that this material extension should have its own39/// index table covering bindings 50, 51, and 52. The `binding` subattribute40/// specifies that the extended material index table should be bound to binding41/// 100. This corresponds to the following shader declarations:42///43/// ```wgsl44/// struct ExampleBindlessExtendedMaterialIndices {45/// material: u32, // 5046/// modulate_texture: u32, // 5147/// modulate_texture_sampler: u32, // 5248/// }49///50/// @group(2) @binding(100) var<storage> example_extended_material_indices:51/// array<ExampleBindlessExtendedMaterialIndices>;52/// ```53///54/// We need to use the `index_table` subattribute because the55/// [`StandardMaterial`] bindless index table is bound to binding 0 by default.56/// Thus we need to specify a different binding so that our extended bindless57/// index table doesn't conflict.58#[derive(Asset, Clone, Reflect, AsBindGroup)]59#[data(50, ExampleBindlessExtensionUniform, binding_array(101))]60#[bindless(index_table(range(50..53), binding(100)))]61struct ExampleBindlessExtension {62/// The color we're going to multiply the base color with.63modulate_color: Color,64/// The image we're going to multiply the base color with.65#[texture(51)]66#[sampler(52)]67modulate_texture: Option<Handle<Image>>,68}6970/// The GPU-side data structure specifying plain old data for the material71/// extension.72#[derive(Clone, Default, ShaderType)]73struct ExampleBindlessExtensionUniform {74/// The GPU representation of the color we're going to multiply the base75/// color with.76modulate_color: Vec4,77}7879impl MaterialExtension for ExampleBindlessExtension {80fn fragment_shader() -> ShaderRef {81SHADER_ASSET_PATH.into()82}83}8485impl<'a> From<&'a ExampleBindlessExtension> for ExampleBindlessExtensionUniform {86fn from(material_extension: &'a ExampleBindlessExtension) -> Self {87// Convert the CPU `ExampleBindlessExtension` structure to its GPU88// format.89ExampleBindlessExtensionUniform {90modulate_color: LinearRgba::from(material_extension.modulate_color).to_vec4(),91}92}93}9495/// The entry point.96fn main() {97App::new()98.add_plugins(DefaultPlugins)99.add_plugins(MaterialPlugin::<100ExtendedMaterial<StandardMaterial, ExampleBindlessExtension>,101>::default())102.add_systems(Startup, setup)103.add_systems(Update, rotate_sphere)104.run();105}106107/// Creates the scene.108fn setup(109mut commands: Commands,110asset_server: Res<AssetServer>,111mut meshes: ResMut<Assets<Mesh>>,112mut materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, ExampleBindlessExtension>>>,113) {114// Create a gray sphere, modulated with a red-tinted Bevy logo.115commands.spawn((116Mesh3d(meshes.add(SphereMeshBuilder::new(1171.0,118SphereKind::Uv {119sectors: 20,120stacks: 20,121},122))),123MeshMaterial3d(materials.add(ExtendedMaterial {124base: StandardMaterial {125base_color: GRAY_600.into(),126..default()127},128extension: ExampleBindlessExtension {129modulate_color: RED.into(),130modulate_texture: Some(asset_server.load("textures/uv_checker_bw.png")),131},132})),133Transform::from_xyz(0.0, 0.5, 0.0),134));135136// Create a light.137commands.spawn((138DirectionalLight::default(),139Transform::from_xyz(1.0, 1.0, 1.0).looking_at(Vec3::ZERO, Vec3::Y),140));141142// Create a camera.143commands.spawn((144Camera3d::default(),145Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),146));147}148149fn rotate_sphere(mut meshes: Query<&mut Transform, With<Mesh3d>>, time: Res<Time>) {150for mut transform in &mut meshes {151transform.rotation =152Quat::from_euler(EulerRot::YXZ, -time.elapsed_secs(), FRAC_PI_2 * 3.0, 0.0);153}154}155156157