//! A shader that uses "shaders defs", which selectively toggle parts of a shader. use bevy::{ pbr::{MaterialPipeline, MaterialPipelineKey}, prelude::*, reflect::TypePath, render::{ mesh::MeshVertexBufferLayoutRef, render_resource::{ AsBindGroup, RenderPipelineDescriptor, ShaderRef, SpecializedMeshPipelineError, }, }, }; /// This example uses a shader source file from the assets subdirectory const SHADER_ASSET_PATH: &str = "shaders/shader_defs.wgsl"; fn main() { App::new() .add_plugins((DefaultPlugins, MaterialPlugin::::default())) .add_systems(Startup, setup) .run(); } /// set up a simple 3D scene fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, ) { // blue cube commands.spawn(( Mesh3d(meshes.add(Cuboid::default())), MeshMaterial3d(materials.add(CustomMaterial { color: LinearRgba::BLUE, is_red: false, })), Transform::from_xyz(-1.0, 0.5, 0.0), )); // red cube (with green color overridden by the IS_RED "shader def") commands.spawn(( Mesh3d(meshes.add(Cuboid::default())), MeshMaterial3d(materials.add(CustomMaterial { color: LinearRgba::GREEN, is_red: true, })), Transform::from_xyz(1.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), )); } impl Material for CustomMaterial { fn fragment_shader() -> ShaderRef { SHADER_ASSET_PATH.into() } fn specialize( _pipeline: &MaterialPipeline, descriptor: &mut RenderPipelineDescriptor, _layout: &MeshVertexBufferLayoutRef, key: MaterialPipelineKey, ) -> Result<(), SpecializedMeshPipelineError> { if key.bind_group_data.is_red == 1 { let fragment = descriptor.fragment.as_mut().unwrap(); fragment.shader_defs.push("IS_RED".into()); } Ok(()) } } // This is the struct that will be passed to your shader #[derive(Asset, TypePath, AsBindGroup, Debug, Clone)] #[bind_group_data(CustomMaterialKey)] struct CustomMaterial { #[uniform(0)] color: LinearRgba, is_red: bool, } // This key is used to identify a specific permutation of this material pipeline. // In this case, we specialize on whether or not to configure the "IS_RED" shader def. // Specialization keys should be kept as small / cheap to hash as possible, // as they will be used to look up the pipeline for each drawn entity with this material type, // Which is why they are required to be `bytemuck::Pod` and `bytemuck::Zeroable` for materials // that use the `AsBindGroup` derive macro. #[repr(C)] #[derive(Eq, PartialEq, Hash, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] struct CustomMaterialKey { is_red: u32, } impl From<&CustomMaterial> for CustomMaterialKey { fn from(material: &CustomMaterial) -> Self { Self { is_red: material.is_red as u32, } } }