shader_defs/
shader_defs.rs

1//! A shader that uses "shaders defs", which selectively toggle parts of a shader.
2
3use bevy::{
4    mesh::MeshVertexBufferLayoutRef,
5    pbr::{MaterialPipeline, MaterialPipelineKey},
6    prelude::*,
7    reflect::TypePath,
8    render::render_resource::{
9        AsBindGroup, RenderPipelineDescriptor, SpecializedMeshPipelineError,
10    },
11    shader::ShaderRef,
12};
13
14/// This example uses a shader source file from the assets subdirectory
15const SHADER_ASSET_PATH: &str = "shaders/shader_defs.wgsl";
16
17fn main() {
18    App::new()
19        .add_plugins((DefaultPlugins, MaterialPlugin::<CustomMaterial>::default()))
20        .add_systems(Startup, setup)
21        .run();
22}
23
24/// set up a simple 3D scene
25fn setup(
26    mut commands: Commands,
27    mut meshes: ResMut<Assets<Mesh>>,
28    mut materials: ResMut<Assets<CustomMaterial>>,
29) {
30    // blue cube
31    commands.spawn((
32        Mesh3d(meshes.add(Cuboid::default())),
33        MeshMaterial3d(materials.add(CustomMaterial {
34            color: LinearRgba::BLUE,
35            is_red: false,
36        })),
37        Transform::from_xyz(-1.0, 0.5, 0.0),
38    ));
39
40    // red cube (with green color overridden by the IS_RED "shader def")
41    commands.spawn((
42        Mesh3d(meshes.add(Cuboid::default())),
43        MeshMaterial3d(materials.add(CustomMaterial {
44            color: LinearRgba::GREEN,
45            is_red: true,
46        })),
47        Transform::from_xyz(1.0, 0.5, 0.0),
48    ));
49
50    // camera
51    commands.spawn((
52        Camera3d::default(),
53        Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
54    ));
55}
56
57impl Material for CustomMaterial {
58    fn fragment_shader() -> ShaderRef {
59        SHADER_ASSET_PATH.into()
60    }
61
62    fn specialize(
63        _pipeline: &MaterialPipeline,
64        descriptor: &mut RenderPipelineDescriptor,
65        _layout: &MeshVertexBufferLayoutRef,
66        key: MaterialPipelineKey<Self>,
67    ) -> Result<(), SpecializedMeshPipelineError> {
68        if key.bind_group_data.is_red {
69            let fragment = descriptor.fragment.as_mut().unwrap();
70            fragment.shader_defs.push("IS_RED".into());
71        }
72        Ok(())
73    }
74}
75
76// This is the struct that will be passed to your shader
77#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
78#[bind_group_data(CustomMaterialKey)]
79struct CustomMaterial {
80    #[uniform(0)]
81    color: LinearRgba,
82    is_red: bool,
83}
84
85// This key is used to identify a specific permutation of this material pipeline.
86// In this case, we specialize on whether or not to configure the "IS_RED" shader def.
87// Specialization keys should be kept as small / cheap to hash as possible,
88// as they will be used to look up the pipeline for each drawn entity with this material type,
89#[repr(C)]
90#[derive(Eq, PartialEq, Hash, Copy, Clone)]
91struct CustomMaterialKey {
92    is_red: bool,
93}
94
95impl From<&CustomMaterial> for CustomMaterialKey {
96    fn from(material: &CustomMaterial) -> Self {
97        Self {
98            is_red: material.is_red,
99        }
100    }
101}