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