Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion assets/shaders/custom_material.wesl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import super::shaders::util::make_polka_dots;
import super::util::make_polka_dots;

struct VertexOutput {
@builtin(position) position: vec4<f32>,
Expand Down
43 changes: 29 additions & 14 deletions assets/shaders/util.wesl
Original file line number Diff line number Diff line change
@@ -1,29 +1,44 @@
fn make_polka_dots(pos: vec2<f32>, time: f32) -> vec4<f32> {
// Create repeating circles
let scaled_pos = pos * 6.0;
let cell = vec2<f32>(fract(scaled_pos.x), fract(scaled_pos.y));
let dist_from_center = distance(cell, vec2<f32>(0.5));

// Make dots alternate between pink and purple
var dist_from_center = distance(cell, vec2<f32>(0.5));

let is_even = (floor(scaled_pos.x) + floor(scaled_pos.y)) % 2.0;

var dot_color = vec3<f32>(0.0);
var is_dot = 0.0;

@if(!PARTY_MODE) {
let color1 = vec3<f32>(1.0, 0.4, 0.8); // pink
let color2 = vec3<f32>(0.6, 0.2, 1.0); // purple
dot_color = mix(color1, color2, is_even);
}
// Animate the colors in party mode
@if(PARTY_MODE) {
let color1 = vec3<f32>(1.0, 0.2, 0.2); // red
let color2 = vec3<f32>(0.2, 0.2, 1.0); // blue
let oscillation = (sin(time * 10.0) + 1.0) * 0.5;
let animated_color1 = mix(color1, color2, oscillation);
let animated_color2 = mix(color2, color1, oscillation);
is_dot = step(dist_from_center, 0.3);
} @else {
let grid_x = floor(scaled_pos.x);
let grid_y = floor(scaled_pos.y);
let wave_speed = 3.0;
let wave_phase = time * wave_speed;

let diagonal_pos = (grid_x + grid_y) * 0.5;
let wave_value = sin(diagonal_pos + wave_phase);

let wave_normalized = (wave_value + 1.0) * 0.5;

let color1 = vec3<f32>(1.0, 0.3, 0.7);
let color2 = vec3<f32>(0.5, 0.1, 1.0);
let intense_color1 = vec3<f32>(1.0, 0.1, 0.9);
let intense_color2 = vec3<f32>(0.8, 0.0, 1.0);

let animated_color1 = mix(color1, intense_color1, wave_normalized);
let animated_color2 = mix(color2, intense_color2, wave_normalized);

dot_color = mix(animated_color1, animated_color2, is_even);

let size_mod = 0.15 * wave_value;
dist_from_center = dist_from_center * (1.0 - size_mod);
// Animate whether something is a dot by position but also time
is_dot = step(dist_from_center, 0.3 + wave_normalized * 0.2);
}

// Draw the dot
let is_dot = step(dist_from_center, 0.3);
return vec4<f32>(dot_color * is_dot, is_dot);
}
2 changes: 1 addition & 1 deletion crates/bevy_render/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ tracing = { version = "0.1", default-features = false, features = ["std"] }
indexmap = { version = "2" }
fixedbitset = { version = "0.5" }
bitflags = "2"
wesl = { version = "0.1.0", optional = true }
wesl = { version = "0.1.2", optional = true }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
# Omit the `glsl` feature in non-WebAssembly by default.
Expand Down
41 changes: 26 additions & 15 deletions crates/bevy_render/src/render_resource/shader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,23 +164,34 @@ impl Shader {
match import_path {
ShaderImport::AssetPath(asset_path) => {
let asset_path = std::path::PathBuf::from(&asset_path);
// Resolve and normalize the path
let asset_path = asset_path.canonicalize().unwrap_or(asset_path);
// Strip the asset root

// Get the base path and canonicalize it to match the format of the asset path
let mut base_path = bevy_asset::io::file::FileAssetReader::get_base_path();
// TODO: integrate better with the asset system rather than hard coding this
base_path.push("assets");
let asset_path = asset_path
.strip_prefix(&base_path)
.unwrap_or_else(|_| &asset_path);
// Wesl paths are provided as absolute relative to the asset root
let asset_path = std::path::Path::new("/").join(asset_path);
// And with a striped file name
let asset_path = asset_path.with_extension("");
let asset_path = asset_path.to_str().unwrap_or_else(|| {
panic!("Failed to convert path to string: {:?}", asset_path)
});
let import_path = ShaderImport::AssetPath(asset_path.to_string());
let base_path = base_path.canonicalize().unwrap_or(base_path);

// Try to make the path relative to the base path
let relative_path = match asset_path.canonicalize() {
Ok(canonical_asset_path) => {
match canonical_asset_path.strip_prefix(&base_path) {
Ok(rel_path) => rel_path.to_path_buf(),
Err(_) => canonical_asset_path,
}
}
Err(_) => asset_path,
};

// Create the shader import path - always starting with "/"
let shader_path = std::path::Path::new("/").join(&relative_path);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks a bit odd... why starting with / if you're going to join it with a relative path?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just saw: // Wesl paths are provided as absolute relative to the asset root that was removed above. That was useful information I guess. Interesting.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, yeah, I shouldn't have removed that. This in general is kinda messy path mangling atm, it needs to be improved more comprehensively as I mentioned on discord.


// Convert to a string with forward slashes and without extension
let import_path_str = shader_path
.with_extension("")
.to_string_lossy()
.replace('\\', "/");

let import_path = ShaderImport::AssetPath(import_path_str.to_string());

Shader {
path,
imports,
Expand Down
52 changes: 44 additions & 8 deletions examples/shader/shader_material_wesl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fn main() {
CustomMaterialPlugin,
))
.add_systems(Startup, setup)
.add_systems(Update, update)
.add_systems(Update, (update, update_ui))
.run();
}

Expand All @@ -50,7 +50,14 @@ fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<CustomMaterial>>,
mut standard_materials: ResMut<Assets<StandardMaterial>>,
) {
// circular base
commands.spawn((
Mesh3d(meshes.add(Circle::new(4.0))),
MeshMaterial3d(standard_materials.add(Color::WHITE)),
Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
));
// cube
commands.spawn((
Mesh3d(meshes.add(Cuboid::default())),
Expand All @@ -66,23 +73,52 @@ fn setup(
Camera3d::default(),
Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
));

// Add instruction text
commands.spawn((
Text::default(),
Node {
position_type: PositionType::Absolute,
top: Val::Px(12.0),
left: Val::Px(12.0),
..default()
},
));
}

fn update(
time: Res<Time>,
mut query: Query<&MeshMaterial3d<CustomMaterial>>,
cube: Single<(&MeshMaterial3d<CustomMaterial>, &mut Transform)>,
mut materials: ResMut<Assets<CustomMaterial>>,
keys: Res<ButtonInput<KeyCode>>,
) {
for material in query.iter_mut() {
let material = materials.get_mut(material).unwrap();
material.time = time.elapsed_secs();
if keys.just_pressed(KeyCode::Space) {
material.party_mode = !material.party_mode;
}
let (material, mut transform) = cube.into_inner();
let material = materials.get_mut(material).unwrap();
material.time = time.elapsed_secs();
if keys.just_pressed(KeyCode::Space) {
material.party_mode = !material.party_mode;
}

if material.party_mode {
transform.rotate(Quat::from_rotation_y(0.005));
}
}

fn update_ui(
cube: Single<&MeshMaterial3d<CustomMaterial>>,
mut ui: Single<&mut Text>,
materials: Res<Assets<CustomMaterial>>,
) {
let material = materials.get(*cube).unwrap();
let text = if material.party_mode {
"Press space to disable party mode"
} else {
"Press space to enable party mode"
};
let ui = &mut ui.0;
*ui = text.to_string();
}

// This is the struct that will be passed to your shader
#[derive(Asset, TypePath, AsBindGroup, Clone)]
#[bind_group_data(CustomMaterialKey)]
Expand Down