Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/shader/extended_material_bindless.rs
9350 views
1
//! Demonstrates bindless `ExtendedMaterial`.
2
3
use std::f32::consts::FRAC_PI_2;
4
5
use bevy::{
6
color::palettes::{css::RED, tailwind::GRAY_600},
7
mesh::{SphereKind, SphereMeshBuilder},
8
pbr::{ExtendedMaterial, MaterialExtension, MeshMaterial3d},
9
prelude::*,
10
render::render_resource::{AsBindGroup, ShaderType},
11
shader::ShaderRef,
12
utils::default,
13
};
14
15
/// The path to the example material shader.
16
static SHADER_ASSET_PATH: &str = "shaders/extended_material_bindless.wgsl";
17
18
/// The example bindless material extension.
19
///
20
/// As usual for material extensions, we need to avoid conflicting with both the
21
/// binding numbers and bindless indices of the [`StandardMaterial`], so we
22
/// start both values at 100 and 50 respectively.
23
///
24
/// The `#[data(50, ExampleBindlessExtensionUniform, binding_array(101))]`
25
/// attribute specifies that the plain old data
26
/// [`ExampleBindlessExtensionUniform`] will be placed into an array with
27
/// 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 the
28
/// definition of that structure.) That corresponds to the following shader
29
/// declaration:
30
///
31
/// ```wgsl
32
/// @group(#{MATERIAL_BIND_GROUP}) @binding(101)
33
/// var<storage> example_extended_material: array<ExampleBindlessExtendedMaterial>;
34
/// ```
35
///
36
/// The `#[bindless(index_table(range(50..53), binding(100)))]` attribute
37
/// specifies that this material extension should be bindless. The `range`
38
/// subattribute specifies that this material extension should have its own
39
/// index table covering bindings 50, 51, and 52. The `binding` subattribute
40
/// specifies that the extended material index table should be bound to binding
41
/// 100. This corresponds to the following shader declarations:
42
///
43
/// ```wgsl
44
/// struct ExampleBindlessExtendedMaterialIndices {
45
/// material: u32, // 50
46
/// modulate_texture: u32, // 51
47
/// modulate_texture_sampler: u32, // 52
48
/// }
49
///
50
/// @group(#{MATERIAL_BIND_GROUP}) @binding(100)
51
/// var<storage> example_extended_material_indices: array<ExampleBindlessExtendedMaterialIndices>;
52
/// ```
53
///
54
/// We need to use the `index_table` subattribute because the
55
/// [`StandardMaterial`] bindless index table is bound to binding 0 by default.
56
/// Thus we need to specify a different binding so that our extended bindless
57
/// 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)))]
61
struct ExampleBindlessExtension {
62
/// The color we're going to multiply the base color with.
63
modulate_color: Color,
64
/// The image we're going to multiply the base color with.
65
#[texture(51)]
66
#[sampler(52)]
67
modulate_texture: Option<Handle<Image>>,
68
}
69
70
/// The GPU-side data structure specifying plain old data for the material
71
/// extension.
72
#[derive(Clone, Default, ShaderType)]
73
struct ExampleBindlessExtensionUniform {
74
/// The GPU representation of the color we're going to multiply the base
75
/// color with.
76
modulate_color: Vec4,
77
}
78
79
impl MaterialExtension for ExampleBindlessExtension {
80
fn fragment_shader() -> ShaderRef {
81
SHADER_ASSET_PATH.into()
82
}
83
}
84
85
impl<'a> From<&'a ExampleBindlessExtension> for ExampleBindlessExtensionUniform {
86
fn from(material_extension: &'a ExampleBindlessExtension) -> Self {
87
// Convert the CPU `ExampleBindlessExtension` structure to its GPU
88
// format.
89
ExampleBindlessExtensionUniform {
90
modulate_color: LinearRgba::from(material_extension.modulate_color).to_vec4(),
91
}
92
}
93
}
94
95
/// The entry point.
96
fn main() {
97
App::new()
98
.add_plugins(DefaultPlugins)
99
.add_plugins(MaterialPlugin::<
100
ExtendedMaterial<StandardMaterial, ExampleBindlessExtension>,
101
>::default())
102
.add_systems(Startup, setup)
103
.add_systems(Update, rotate_sphere)
104
.run();
105
}
106
107
/// Creates the scene.
108
fn setup(
109
mut commands: Commands,
110
asset_server: Res<AssetServer>,
111
mut meshes: ResMut<Assets<Mesh>>,
112
mut materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, ExampleBindlessExtension>>>,
113
) {
114
// Create a gray sphere, modulated with a red-tinted checkerboard pattern.
115
commands.spawn((
116
Mesh3d(meshes.add(SphereMeshBuilder::new(
117
1.0,
118
SphereKind::Uv {
119
sectors: 20,
120
stacks: 20,
121
},
122
))),
123
MeshMaterial3d(materials.add(ExtendedMaterial {
124
base: StandardMaterial {
125
base_color: GRAY_600.into(),
126
..default()
127
},
128
extension: ExampleBindlessExtension {
129
modulate_color: RED.into(),
130
modulate_texture: Some(asset_server.load("textures/uv_checker_bw.png")),
131
},
132
})),
133
Transform::from_xyz(0.0, 0.5, 0.0),
134
));
135
136
// Create a light.
137
commands.spawn((
138
DirectionalLight::default(),
139
Transform::from_xyz(1.0, 1.0, 1.0).looking_at(Vec3::ZERO, Vec3::Y),
140
));
141
142
// Create a camera.
143
commands.spawn((
144
Camera3d::default(),
145
Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
146
));
147
}
148
149
fn rotate_sphere(mut meshes: Query<&mut Transform, With<Mesh3d>>, time: Res<Time>) {
150
for mut transform in &mut meshes {
151
transform.rotation =
152
Quat::from_euler(EulerRot::YXZ, -time.elapsed_secs(), FRAC_PI_2 * 3.0, 0.0);
153
}
154
}
155
156