Created
February 19, 2024 21:19
-
-
Save ethereumdegen/cd7da0e1c7d227864b45ff2b028fac2b to your computer and use it in GitHub Desktop.
Bevy 0.12 example for an animated material shader
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
use bevy::prelude::*; | |
use bevy::reflect::{TypePath, TypeUuid}; | |
use bevy::render::render_resource::*; | |
use bevy::pbr::MaterialExtension; | |
#[derive(Clone, ShaderType ,Debug)] | |
pub struct CustomMaterialUniforms { | |
pub distortion_speed_x: f32, | |
pub distortion_speed_y: f32, | |
pub scroll_repeats_x: f32 , | |
pub scroll_repeats_y: f32 , | |
pub scroll_speed_x: f32, | |
pub scroll_speed_y: f32, | |
pub distortion_amount: f32 , | |
pub distortion_cutoff: f32 | |
} | |
impl Default for CustomMaterialUniforms{ | |
fn default() -> Self{ | |
Self{ | |
scroll_speed_x : 0.1, | |
scroll_speed_y : 1.0, | |
distortion_speed_x: 3.0, | |
distortion_speed_y: 1.0, | |
distortion_amount: 0.03, | |
distortion_cutoff: 1.0, | |
scroll_repeats_x: 12.0, | |
scroll_repeats_y: 3.0, | |
} | |
} | |
} | |
#[derive(Asset, AsBindGroup, TypePath, Debug, Clone,Default )] | |
pub struct ScrollingMaterial { | |
// We need to ensure that the bindings of the base material and the extension do not conflict, | |
// so we start from binding slot 100, leaving slots 0-99 for the base material. | |
#[uniform(20)] | |
pub custom_uniforms: CustomMaterialUniforms, | |
#[texture(21)] | |
#[sampler(22)] | |
pub base_color_texture: Option<Handle<Image>>, | |
} | |
impl MaterialExtension for ScrollingMaterial { | |
fn fragment_shader() -> ShaderRef { | |
"shaders/scrolling.wgsl".into() | |
} | |
fn deferred_fragment_shader() -> ShaderRef { | |
"shaders/scrolling.wgsl".into() | |
} | |
} | |
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
//! This example demonstrates the built-in 3d shapes in Bevy. | |
//! The scene includes a patterned texture and a rotation for visualizing the normals and UVs. | |
use std::f32::consts::PI; | |
use bevy::{ | |
prelude::*, | |
render::render_resource::{Extent3d, TextureDimension, TextureFormat}, gltf::GltfMesh, | |
}; | |
use bevy::gltf::Gltf; | |
use bevy::core_pipeline::bloom::BloomSettings; | |
use crate::custom_material::{ CustomMaterialUniforms}; | |
use bevy::core_pipeline::tonemapping::Tonemapping; | |
use bevy::pbr::ExtendedMaterial; | |
use bevy::pbr::OpaqueRendererMethod; | |
pub type AnimatedMaterial = ExtendedMaterial<StandardMaterial,custom_material::ScrollingMaterial>; | |
pub type CustomPbrBundle = MaterialMeshBundle<AnimatedMaterial>; | |
mod custom_material; | |
fn main() { | |
App::new() | |
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) | |
.add_plugins(bevy_obj::ObjPlugin) | |
.add_plugins(MaterialPlugin::<AnimatedMaterial>::default()) | |
.add_systems(Startup, setup) | |
.add_systems(Update, rotate) | |
.insert_resource( AssetHandlesResource::default() ) | |
.run(); | |
} | |
#[derive(Resource,Default)] | |
pub struct AssetHandlesResource { | |
bullet_mesh: Handle<Mesh>, | |
anim_material: Handle<AnimatedMaterial> | |
} | |
fn setup( | |
mut commands: Commands, | |
mut asset_server: ResMut< AssetServer>, | |
mut asset_handles_resource: ResMut<AssetHandlesResource>, | |
mut meshes: ResMut<Assets<Mesh>>, | |
mut images: ResMut<Assets<Image>>, | |
mut materials: ResMut<Assets<StandardMaterial>>, | |
mut custom_materials: ResMut<Assets<AnimatedMaterial>>, | |
) { | |
let magic_texture = asset_server.load("textures/fire_01.png"); | |
asset_handles_resource.bullet_mesh = asset_server.load("meshes/projectile.obj"); | |
let base_color = Color::PURPLE.set_a(0.4).clone(); | |
asset_handles_resource.anim_material = custom_materials.add(ExtendedMaterial { | |
base: StandardMaterial { | |
base_color , | |
emissive: Color::rgb_linear(50.2, 1.2, 0.8), | |
// can be used in forward or deferred mode. | |
opaque_render_method: OpaqueRendererMethod::Auto, | |
alpha_mode: AlphaMode::Multiply, | |
..Default::default() | |
}, | |
extension:custom_material::ScrollingMaterial { | |
base_color_texture: Some( magic_texture ), | |
custom_uniforms: CustomMaterialUniforms{ | |
scroll_speed_x : 0.1, | |
scroll_speed_y : 1.0, | |
distortion_speed_x: 3.0, | |
distortion_speed_y: 1.0, | |
distortion_amount: 0.03, | |
distortion_cutoff: 1.0, | |
scroll_repeats_x: 12.0, | |
scroll_repeats_y: 3.0, | |
..default() | |
}, | |
..default() | |
}, | |
}); | |
let bullet_mesh_handle = &asset_handles_resource.bullet_mesh; | |
let anim_mat_handle = &asset_handles_resource.anim_material; | |
commands.spawn(( | |
CustomPbrBundle { | |
mesh: bullet_mesh_handle.clone(), | |
material: anim_mat_handle.clone(), | |
transform: Transform::from_xyz( | |
3.0, | |
2.0, | |
0.0, | |
) | |
.with_rotation(Quat::from_rotation_x(-PI / 5.)), | |
..default() | |
}, | |
bevy::pbr::NotShadowCaster | |
)); | |
//custom_materials.add(); | |
commands.spawn(PointLightBundle { | |
point_light: PointLight { | |
intensity: 9000.0, | |
range: 100., | |
shadows_enabled: true, | |
..default() | |
}, | |
transform: Transform::from_xyz(8.0, 16.0, 8.0), | |
..default() | |
}); | |
// ground plane | |
commands.spawn(PbrBundle { | |
mesh: meshes.add(shape::Plane::from_size(50.0).into()), | |
material: materials.add(Color::SILVER.into()), | |
..default() | |
}); | |
commands.spawn(( | |
Camera3dBundle { | |
camera: Camera { | |
hdr: true, // 1. HDR must be enabled on the camera | |
..default() | |
}, | |
tonemapping: Tonemapping::TonyMcMapface, | |
transform: Transform::from_xyz(0.0, 6., 12.0).looking_at(Vec3::new(0., 1., 0.), Vec3::Y), | |
..default() | |
}, | |
BloomSettings::default(), // 2. Enable bloom for the camera | |
)); | |
} | |
fn rotate(mut query: Query<&mut Transform , With<Handle<Mesh>>>, time: Res<Time>) { | |
for mut transform in &mut query { | |
transform.rotate_y(time.delta_seconds() / 2.); | |
} | |
} | |
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
//see bindings in terrain_material.rs | |
#import bevy_pbr::{ | |
mesh_view_bindings::globals, | |
forward_io::{VertexOutput, FragmentOutput}, | |
pbr_fragment::pbr_input_from_standard_material, | |
pbr_functions::{alpha_discard, apply_pbr_lighting, main_pass_post_lighting_processing}, | |
pbr_types::STANDARD_MATERIAL_FLAGS_UNLIT_BIT, | |
pbr_deferred_functions::deferred_output, | |
} | |
struct StandardMaterial { | |
time: f32, | |
base_color: vec4<f32>, | |
emissive: vec4<f32>, | |
perceptual_roughness: f32, | |
metallic: f32, | |
reflectance: f32, | |
// 'flags' is a bit field indicating various options. u32 is 32 bits so we have up to 32 options. | |
flags: u32, | |
alpha_cutoff: f32, | |
}; | |
struct CustomMaterialUniforms { | |
distortion_speed_x: f32 , | |
distortion_speed_y: f32 , | |
scroll_repeats_x: f32 , | |
scroll_repeats_y: f32 , | |
scroll_speed_x: f32, | |
scroll_speed_y: f32, | |
distortion_amount: f32 , | |
distortion_cutoff: f32 , | |
}; | |
@group(1) @binding(20) | |
var<uniform> custom_uniforms: CustomMaterialUniforms; | |
@group(1) @binding(21) | |
var base_color_texture: texture_2d<f32>; | |
@group(1) @binding(22) | |
var base_color_sampler: sampler; | |
fn get_repeated_uv_coords(coords: vec2<f32>) -> vec2<f32> { | |
let repeated_coords = vec2<f32>( | |
(coords.x % (1. / f32(custom_uniforms.scroll_repeats_x))) * f32(custom_uniforms.scroll_repeats_x), | |
(coords.y % (1. / f32(custom_uniforms.scroll_repeats_y))) * f32(custom_uniforms.scroll_repeats_y) | |
); | |
return repeated_coords; | |
} | |
//should consider adding vertex painting to this .. need another binding of course.. performs a color shift | |
@fragment | |
fn fragment( | |
mesh: VertexOutput, | |
@builtin(front_facing) is_front: bool, | |
) -> FragmentOutput { | |
let scroll_amount_x = (globals.time * custom_uniforms.scroll_speed_x) ; | |
let scroll_amount_y = (globals.time * custom_uniforms.scroll_speed_y) ; | |
//make the cutoff big and it wont have any effect | |
let distortion_radians_x = 6.28 * (globals.time * custom_uniforms.distortion_speed_x % mesh.uv[0]) ; | |
let distortion_amount_x = ( sin(distortion_radians_x) * custom_uniforms.distortion_amount ) % custom_uniforms.distortion_cutoff ; | |
let distortion_radians_y = 6.28 * (globals.time * custom_uniforms.distortion_speed_y % mesh.uv[1]) ; | |
let distortion_amount_y = ( cos(distortion_radians_y) * custom_uniforms.distortion_amount ) % custom_uniforms.distortion_cutoff ; | |
let tiled_uv = get_repeated_uv_coords (mesh.uv + vec2(scroll_amount_x,scroll_amount_y) ) | |
+ vec2( distortion_amount_x, distortion_amount_y ) ; | |
//this technique lets us use 255 total textures BUT we can only layer 2 at a time. | |
let color_from_texture_0 = textureSample(base_color_texture, base_color_sampler, tiled_uv ); | |
let blended_color = color_from_texture_0 ; | |
// generate a PbrInput struct from the StandardMaterial bindings | |
var pbr_input = pbr_input_from_standard_material(mesh, is_front); | |
//hack the material (StandardMaterialUniform) so the color is from the terrain splat | |
// alpha discard | |
pbr_input.material.base_color = pbr_input.material.base_color * blended_color ; | |
var final_color = alpha_discard(pbr_input.material, pbr_input.material.base_color ) ; | |
var pbr_out: FragmentOutput; | |
//only apply lighting if bit is set | |
if ((pbr_input.material.flags & STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) { | |
// pbr_input.material.base_color = blended_color; | |
pbr_out.color = apply_pbr_lighting(pbr_input); | |
pbr_out.color = main_pass_post_lighting_processing(pbr_input, pbr_out.color); | |
final_color = pbr_out.color; | |
} | |
// ----- | |
pbr_out.color = final_color; | |
// pbr_out.emissive = pbr_input.material.emissive; | |
// if (final_color.a < 0.1) { // Use your threshold value here | |
// discard; | |
// } | |
return pbr_out; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment