-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
/
Copy pathshader_material_wesl.rs
135 lines (120 loc) · 3.61 KB
/
shader_material_wesl.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
//! A shader that uses the WESL shading language.
use bevy::{
pbr::{MaterialPipeline, MaterialPipelineKey},
prelude::*,
reflect::TypePath,
render::{
mesh::MeshVertexBufferLayoutRef,
render_resource::{
AsBindGroup, RenderPipelineDescriptor, ShaderDefVal, ShaderRef,
SpecializedMeshPipelineError,
},
},
};
/// This example uses shader source files from the assets subdirectory
const FRAGMENT_SHADER_ASSET_PATH: &str = "shaders/custom_material.wesl";
fn main() {
App::new()
.add_plugins((
DefaultPlugins,
MaterialPlugin::<CustomMaterial>::default(),
CustomMaterialPlugin,
))
.add_systems(Startup, setup)
.add_systems(Update, update)
.run();
}
/// A plugin that loads the custom material shader
pub struct CustomMaterialPlugin;
/// An example utility shader that is used by the custom material
#[expect(
dead_code,
reason = "used to kept a strong handle, shader is referenced by the material"
)]
#[derive(Resource)]
struct UtilityShader(Handle<Shader>);
impl Plugin for CustomMaterialPlugin {
fn build(&self, app: &mut App) {
let handle = app
.world_mut()
.resource_mut::<AssetServer>()
.load::<Shader>("shaders/util.wesl");
app.insert_resource(UtilityShader(handle));
}
}
/// set up a simple 3D scene
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<CustomMaterial>>,
) {
// cube
commands.spawn((
Mesh3d(meshes.add(Cuboid::default())),
MeshMaterial3d(materials.add(CustomMaterial {
time: Vec4::ZERO,
party_mode: false,
})),
Transform::from_xyz(0.0, 0.5, 0.0),
));
// camera
commands.spawn((
Camera3d::default(),
Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
));
}
fn update(
time: Res<Time>,
mut query: Query<(&MeshMaterial3d<CustomMaterial>, &mut Transform)>,
mut materials: ResMut<Assets<CustomMaterial>>,
keys: Res<ButtonInput<KeyCode>>,
) {
for (material, mut transform) in query.iter_mut() {
let material = materials.get_mut(material).unwrap();
material.time.x = time.elapsed_secs();
if keys.just_pressed(KeyCode::Space) {
material.party_mode = !material.party_mode;
}
if material.party_mode {
transform.rotate(Quat::from_rotation_y(0.005));
}
}
}
// This is the struct that will be passed to your shader
#[derive(Asset, TypePath, AsBindGroup, Clone)]
#[bind_group_data(CustomMaterialKey)]
struct CustomMaterial {
// Needed for 16 bit alignment in WebGL2
#[uniform(0)]
time: Vec4,
party_mode: bool,
}
#[derive(Eq, PartialEq, Hash, Clone)]
struct CustomMaterialKey {
party_mode: bool,
}
impl From<&CustomMaterial> for CustomMaterialKey {
fn from(material: &CustomMaterial) -> Self {
Self {
party_mode: material.party_mode,
}
}
}
impl Material for CustomMaterial {
fn fragment_shader() -> ShaderRef {
FRAGMENT_SHADER_ASSET_PATH.into()
}
fn specialize(
_pipeline: &MaterialPipeline<Self>,
descriptor: &mut RenderPipelineDescriptor,
_layout: &MeshVertexBufferLayoutRef,
key: MaterialPipelineKey<Self>,
) -> Result<(), SpecializedMeshPipelineError> {
let fragment = descriptor.fragment.as_mut().unwrap();
fragment.shader_defs.push(ShaderDefVal::Bool(
"PARTY_MODE".to_string(),
key.bind_group_data.party_mode,
));
Ok(())
}
}