volumetric changing unlighted sphere

This commit is contained in:
TÁNCZOS Vilmos Zsombor 2025-05-31 00:38:33 +02:00
parent 6856e6ac38
commit 38513627ca
2 changed files with 93 additions and 42 deletions

View file

@ -1,30 +1,46 @@
#import bevy_pbr::{ #import bevy_pbr::{
forward_io::{VertexOutput, FragmentOutput}, forward_io::{VertexOutput, FragmentOutput},
pbr_fragment::pbr_input_from_standard_material, mesh_view_bindings::view,
pbr_functions::{alpha_discard, apply_pbr_lighting, main_pass_post_lighting_processing},
} }
struct MyExtendedMaterial { struct VoluMaterial {
quantize_steps: u32, mesh_translation: vec3<f32>,
sphere_radius: f32,
color: vec4<f32>,
} }
@group(2) @binding(100) @group(2) @binding(100)
var<uniform> my_extended_material: MyExtendedMaterial; var<storage, read> volu_material: VoluMaterial;
fn raymarch_hit(position_: vec3<f32>, direction: vec3<f32>) -> bool {
var position = position_;
for (var i = 0; i < 200; i++) {
if sphere_hit(position) {
return true;
}
position += direction * 0.01;
}
return false;
}
fn sphere_hit(p: vec3<f32>) -> bool {
return distance(p, volu_material.mesh_translation) < volu_material.sphere_radius;
}
@fragment @fragment
fn fragment( fn fragment(
in: VertexOutput, in: VertexOutput,
@builtin(front_facing) is_front: bool, @builtin(front_facing) is_front: bool,
) -> FragmentOutput { ) -> FragmentOutput {
var pbr_input = pbr_input_from_standard_material(in, is_front); let view_direction = normalize(in.world_position.xyz - view.world_position);
pbr_input.material.base_color = alpha_discard(pbr_input.material, pbr_input.material.base_color);
var out: FragmentOutput; var out: FragmentOutput;
if (raymarch_hit(in.world_position.xyz, view_direction)) {
out.color = apply_pbr_lighting(pbr_input); out.color = volu_material.color;
} else {
out.color = main_pass_post_lighting_processing(pbr_input, out.color); out.color = vec4(1.0, 1.0, 1.0, 0.2);
}
return out; return out;
} }

View file

@ -1,53 +1,54 @@
//! Demonstrates using a custom extension to the `StandardMaterial` to modify the results of the builtin pbr shader.
use bevy::{ use bevy::{
color::palettes::basic::RED, pbr::{ExtendedMaterial, MaterialExtension},
pbr::{ExtendedMaterial, MaterialExtension, OpaqueRendererMethod},
prelude::*, prelude::*,
render::render_resource::*, render::{render_resource::*, storage::ShaderStorageBuffer},
}; };
/// This example uses a shader source file from the assets subdirectory
const SHADER_ASSET_PATH: &str = "shaders/material.wgsl"; const SHADER_ASSET_PATH: &str = "shaders/material.wgsl";
type VoluMaterial = ExtendedMaterial<StandardMaterial, VoluExtension>;
fn main() { fn main() {
App::new() App::new()
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_plugins(MaterialPlugin::< .add_plugins(MaterialPlugin::<VoluMaterial>::default())
ExtendedMaterial<StandardMaterial, MyExtension>,
>::default())
.add_systems(Startup, setup) .add_systems(Startup, setup)
.add_systems(Update, rotate_things) .add_systems(Update, (rotate_things, update_volu_material))
.run(); .run();
} }
fn setup( fn setup(
mut commands: Commands, mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, MyExtension>>>, mut materials: ResMut<Assets<VoluMaterial>>,
mut buffers: ResMut<Assets<ShaderStorageBuffer>>,
) { ) {
// sphere let transform = Transform::from_xyz(0.0, 0.0, 0.0);
let storage = buffers.add(ShaderStorageBuffer::from(VoluStorage {
mesh_translation: transform.translation,
sphere_radius: 1.0,
color: Vec4::default(),
}));
commands.spawn(( commands.spawn((
Mesh3d(meshes.add(Sphere::new(1.0))), Mesh3d(meshes.add(Cuboid::new(2.0, 2.0, 2.0))),
MeshMaterial3d(materials.add(ExtendedMaterial { MeshMaterial3d(materials.add(ExtendedMaterial {
base: StandardMaterial { base: StandardMaterial {
base_color: RED.into(), alpha_mode: AlphaMode::Blend,
opaque_render_method: OpaqueRendererMethod::Auto, ..default()
..Default::default()
}, },
extension: MyExtension { quantize_steps: 3 }, extension: VoluExtension { storage },
})), })),
Transform::from_xyz(0.0, 0.5, 0.0), transform,
));
// light
commands.spawn((
DirectionalLight::default(),
Transform::from_xyz(1.0, 1.0, 1.0).looking_at(Vec3::ZERO, Vec3::Y),
Rotate, Rotate,
)); ));
// camera commands.spawn((
DirectionalLight::default(),
Transform::from_xyz(1.0, 1.0, 1.0).looking_at(Vec3::ZERO, Vec3::Y),
));
commands.spawn(( commands.spawn((
Camera3d::default(), Camera3d::default(),
Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
@ -63,14 +64,48 @@ fn rotate_things(mut q: Query<&mut Transform, With<Rotate>>, time: Res<Time>) {
} }
} }
#[derive(Asset, AsBindGroup, Reflect, Debug, Clone)] fn update_volu_material(
struct MyExtension { mut q: Query<(&mut Transform, &MeshMaterial3d<VoluMaterial>), Changed<Transform>>,
#[uniform(100)] mut volu_materials: ResMut<Assets<VoluMaterial>>,
quantize_steps: u32, mut buffers: ResMut<Assets<ShaderStorageBuffer>>,
time: Res<Time>,
) {
for (mut transform, material) in q.iter_mut() {
transform.translation.y = time.elapsed_secs().sin();
let volu_material = volu_materials.get_mut(material.0.id()).unwrap();
let buffer = buffers
.get_mut(volu_material.extension.storage.id())
.unwrap();
let elapsed = time.elapsed_secs_wrapped();
let volu_storage = VoluStorage {
mesh_translation: transform.translation,
sphere_radius: (elapsed.sin() + 3.) / 4.,
color: vec4((elapsed.sin() + 1.) / 2., 0.0, 0.0, 1.0),
};
buffer.set_data(volu_storage);
}
} }
impl MaterialExtension for MyExtension { #[derive(Asset, AsBindGroup, Reflect, Debug, Clone)]
struct VoluExtension {
#[storage(100, read_only)]
storage: Handle<ShaderStorageBuffer>,
}
impl MaterialExtension for VoluExtension {
fn fragment_shader() -> ShaderRef { fn fragment_shader() -> ShaderRef {
SHADER_ASSET_PATH.into() SHADER_ASSET_PATH.into()
} }
} }
#[derive(ShaderType, Clone, Default, Debug)]
pub struct VoluStorage {
mesh_translation: Vec3,
sphere_radius: f32,
color: Vec4,
}