Created
October 1, 2023 15:15
-
-
Save thebluefish/1b577e575c9fd809ca98c2eb5f98a8c8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! A shader that uses "shaders defs", which selectively toggle parts of a shader. | |
use std::time::Duration; | |
use bevy::{ | |
pbr::{MaterialPipeline, MaterialPipelineKey}, | |
prelude::*, | |
reflect::{TypePath, TypeUuid}, | |
render::{ | |
mesh::MeshVertexBufferLayout, | |
render_resource::{ | |
AsBindGroup, RenderPipelineDescriptor, ShaderRef, SpecializedMeshPipelineError, | |
}, | |
}, | |
}; | |
fn main() { | |
App::new() | |
.add_plugins((DefaultPlugins, MaterialPlugin::<CustomMaterial>::default())) | |
.add_systems(Startup, setup) | |
.add_systems(Update, swap_colors) | |
.run(); | |
} | |
#[derive(Resource, Deref, DerefMut)] | |
pub struct SwapTimer(Timer); | |
/// set up a simple 3D scene | |
fn setup( | |
mut commands: Commands, | |
mut meshes: ResMut<Assets<Mesh>>, | |
mut materials: ResMut<Assets<CustomMaterial>>, | |
) { | |
// timer to swap colors | |
commands.insert_resource(SwapTimer(Timer::new(Duration::from_secs(2), TimerMode::Repeating))); | |
// blue cube | |
commands.spawn(MaterialMeshBundle { | |
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), | |
transform: Transform::from_xyz(-1.0, 0.5, 0.0), | |
material: materials.add(CustomMaterial { | |
color: Color::BLUE, | |
is_red: false, | |
}), | |
..default() | |
}); | |
// red cube (with green color overridden by the IS_RED "shader def") | |
commands.spawn(MaterialMeshBundle { | |
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })), | |
transform: Transform::from_xyz(1.0, 0.5, 0.0), | |
material: materials.add(CustomMaterial { | |
color: Color::GREEN, | |
is_red: true, | |
}), | |
..default() | |
}); | |
// camera | |
commands.spawn(Camera3dBundle { | |
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), | |
..default() | |
}); | |
} | |
fn swap_colors( | |
time: Res<Time>, | |
mut timer: ResMut<SwapTimer>, | |
mut materials: ResMut<Assets<CustomMaterial>>, | |
mut query: Query<&Handle<CustomMaterial>>, | |
) { | |
if timer.tick(time.delta()).just_finished() { | |
for handle in query.iter_mut() { | |
let material = materials.get_mut(handle).unwrap(); | |
material.is_red = !material.is_red; | |
} | |
} | |
} | |
impl Material for CustomMaterial { | |
fn fragment_shader() -> ShaderRef { | |
"shaders/shader_defs.wgsl".into() | |
} | |
fn specialize( | |
_pipeline: &MaterialPipeline<Self>, | |
descriptor: &mut RenderPipelineDescriptor, | |
_layout: &MeshVertexBufferLayout, | |
key: MaterialPipelineKey<Self>, | |
) -> 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(AsBindGroup, TypeUuid, TypePath, Debug, Clone)] | |
#[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] | |
#[bind_group_data(CustomMaterialKey)] | |
pub struct CustomMaterial { | |
#[uniform(0)] | |
color: Color, | |
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)] | |
pub struct CustomMaterialKey { | |
is_red: bool, | |
} | |
impl From<&CustomMaterial> for CustomMaterialKey { | |
fn from(material: &CustomMaterial) -> Self { | |
Self { | |
is_red: material.is_red, | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment