Path: blob/main/examples/shader/extended_material_bindless.rs
9350 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 101 and that the index referencing it will be stored in slot 50 of the `ExampleBindlessExtendedMaterialIndices` structure. (See below or lookup the shader for the27/// definition of that structure.) That corresponds to the following shader28/// declaration:29///30/// ```wgsl31/// @group(#{MATERIAL_BIND_GROUP}) @binding(101)32/// var<storage> example_extended_material: array<ExampleBindlessExtendedMaterial>;33/// ```34///35/// The `#[bindless(index_table(range(50..53), binding(100)))]` attribute36/// specifies that this material extension should be bindless. The `range`37/// subattribute specifies that this material extension should have its own38/// index table covering bindings 50, 51, and 52. The `binding` subattribute39/// specifies that the extended material index table should be bound to binding40/// 100. This corresponds to the following shader declarations:41///42/// ```wgsl43/// struct ExampleBindlessExtendedMaterialIndices {44/// material: u32, // 5045/// modulate_texture: u32, // 5146/// modulate_texture_sampler: u32, // 5247/// }48///49/// @group(#{MATERIAL_BIND_GROUP}) @binding(100)50/// var<storage> example_extended_material_indices: array<ExampleBindlessExtendedMaterialIndices>;51/// ```52///53/// We need to use the `index_table` subattribute because the54/// [`StandardMaterial`] bindless index table is bound to binding 0 by default.55/// Thus we need to specify a different binding so that our extended bindless56/// index table doesn't conflict.57#[derive(Asset, Clone, Reflect, AsBindGroup)]58#[data(50, ExampleBindlessExtensionUniform, binding_array(101))]59#[bindless(index_table(range(50..53), binding(100)))]60struct ExampleBindlessExtension {61/// The color we're going to multiply the base color with.62modulate_color: Color,63/// The image we're going to multiply the base color with.64#[texture(51)]65#[sampler(52)]66modulate_texture: Option<Handle<Image>>,67}6869/// The GPU-side data structure specifying plain old data for the material70/// extension.71#[derive(Clone, Default, ShaderType)]72struct ExampleBindlessExtensionUniform {73/// The GPU representation of the color we're going to multiply the base74/// color with.75modulate_color: Vec4,76}7778impl MaterialExtension for ExampleBindlessExtension {79fn fragment_shader() -> ShaderRef {80SHADER_ASSET_PATH.into()81}82}8384impl<'a> From<&'a ExampleBindlessExtension> for ExampleBindlessExtensionUniform {85fn from(material_extension: &'a ExampleBindlessExtension) -> Self {86// Convert the CPU `ExampleBindlessExtension` structure to its GPU87// format.88ExampleBindlessExtensionUniform {89modulate_color: LinearRgba::from(material_extension.modulate_color).to_vec4(),90}91}92}9394/// The entry point.95fn main() {96App::new()97.add_plugins(DefaultPlugins)98.add_plugins(MaterialPlugin::<99ExtendedMaterial<StandardMaterial, ExampleBindlessExtension>,100>::default())101.add_systems(Startup, setup)102.add_systems(Update, rotate_sphere)103.run();104}105106/// Creates the scene.107fn setup(108mut commands: Commands,109asset_server: Res<AssetServer>,110mut meshes: ResMut<Assets<Mesh>>,111mut materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, ExampleBindlessExtension>>>,112) {113// Create a gray sphere, modulated with a red-tinted checkerboard pattern.114commands.spawn((115Mesh3d(meshes.add(SphereMeshBuilder::new(1161.0,117SphereKind::Uv {118sectors: 20,119stacks: 20,120},121))),122MeshMaterial3d(materials.add(ExtendedMaterial {123base: StandardMaterial {124base_color: GRAY_600.into(),125..default()126},127extension: ExampleBindlessExtension {128modulate_color: RED.into(),129modulate_texture: Some(asset_server.load("textures/uv_checker_bw.png")),130},131})),132Transform::from_xyz(0.0, 0.5, 0.0),133));134135// Create a light.136commands.spawn((137DirectionalLight::default(),138Transform::from_xyz(1.0, 1.0, 1.0).looking_at(Vec3::ZERO, Vec3::Y),139));140141// Create a camera.142commands.spawn((143Camera3d::default(),144Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),145));146}147148fn rotate_sphere(mut meshes: Query<&mut Transform, With<Mesh3d>>, time: Res<Time>) {149for mut transform in &mut meshes {150transform.rotation =151Quat::from_euler(EulerRot::YXZ, -time.elapsed_secs(), FRAC_PI_2 * 3.0, 0.0);152}153}154155156