//! 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 { 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. #[derive(Eq, PartialEq, Hash, Clone)] struct CustomMaterialKey { is_red: bool, } impl From<&CustomMaterial> for CustomMaterialKey { fn from(material: &CustomMaterial) -> Self { Self { is_red: material.is_red, } } }