mandelbulb

This commit is contained in:
TÁNCZOS Vilmos Zsombor 2025-05-31 20:18:50 +02:00
parent 27d27feba7
commit d1ed3662bc
2 changed files with 66 additions and 27 deletions

View file

@ -9,13 +9,51 @@ struct VoluMaterial {
mesh_translation: vec3<f32>, mesh_translation: vec3<f32>,
sphere_radius: f32, sphere_radius: f32,
color: vec4<f32>, color: vec4<f32>,
model_inverse: mat4x4<f32>,
power: f32,
iterations: i32,
} }
@group(2) @binding(100) @group(2) @binding(100)
var<storage, read> volu_material: VoluMaterial; var<storage, read> volu_material: VoluMaterial;
fn sphere_sdf(p: vec3<f32>, center: vec3<f32>, radius: f32) -> f32 { fn mandelbulb_sdf(p: vec3<f32>) -> f32 {
return length(p - center) - radius; var z = p;
var dr = 1.0;
var r = 0.0;
let power = volu_material.power;
let iterations = volu_material.iterations;
for (var i = 0; i < iterations; i++) {
r = length(z);
if r > 2.0 {
break;
}
let theta = acos(clamp(z.z / r, -1.0, 1.0));
let phi = atan2(z.y, z.x);
dr = pow(r, power - 1.0) * power * dr + 1.0;
let zr = pow(r, power);
let new_theta = theta * power;
let new_phi = phi * power;
z = zr * vec3<f32>(
sin(new_theta) * cos(new_phi),
sin(new_theta) * sin(new_phi),
cos(new_theta)
) + p;
}
return 0.5 * log(max(r, 0.001)) * r / dr;
}
fn mandelbulb_normal(p: vec3<f32>) -> vec3<f32> {
let e = 0.001;
let dx = mandelbulb_sdf(p + vec3<f32>(e, 0.0, 0.0)) - mandelbulb_sdf(p - vec3<f32>(e, 0.0, 0.0));
let dy = mandelbulb_sdf(p + vec3<f32>(0.0, e, 0.0)) - mandelbulb_sdf(p - vec3<f32>(0.0, e, 0.0));
let dz = mandelbulb_sdf(p + vec3<f32>(0.0, 0.0, e)) - mandelbulb_sdf(p - vec3<f32>(0.0, 0.0, e));
return normalize(vec3<f32>(dx, dy, dz));
} }
fn raymarch(ray_origin: vec3<f32>, ray_direction: vec3<f32>) -> f32 { fn raymarch(ray_origin: vec3<f32>, ray_direction: vec3<f32>) -> f32 {
@ -26,7 +64,7 @@ fn raymarch(ray_origin: vec3<f32>, ray_direction: vec3<f32>) -> f32 {
for (var i = 0; i < max_steps; i++) { for (var i = 0; i < max_steps; i++) {
let current_pos = ray_origin + t * ray_direction; let current_pos = ray_origin + t * ray_direction;
let distance = sphere_sdf(current_pos, volu_material.mesh_translation, volu_material.sphere_radius); let distance = mandelbulb_sdf(current_pos);
if distance < epsilon { if distance < epsilon {
return t; return t;
@ -42,20 +80,6 @@ fn raymarch(ray_origin: vec3<f32>, ray_direction: vec3<f32>) -> f32 {
return -1.0; return -1.0;
} }
fn sphere_normal(p: vec3<f32>) -> vec3<f32> {
let epsilon = 0.001;
let center = volu_material.mesh_translation;
let radius = volu_material.sphere_radius;
let gradient = vec3<f32>(
sphere_sdf(p + vec3<f32>(epsilon, 0.0, 0.0), center, radius) - sphere_sdf(p - vec3<f32>(epsilon, 0.0, 0.0), center, radius),
sphere_sdf(p + vec3<f32>(0.0, epsilon, 0.0), center, radius) - sphere_sdf(p - vec3<f32>(0.0, epsilon, 0.0), center, radius),
sphere_sdf(p + vec3<f32>(0.0, 0.0, epsilon), center, radius) - sphere_sdf(p - vec3<f32>(0.0, 0.0, epsilon), center, radius)
);
return normalize(gradient);
}
@fragment @fragment
fn fragment( fn fragment(
vertex_output: VertexOutput, vertex_output: VertexOutput,
@ -66,20 +90,27 @@ fn fragment(
let ray_origin = view.world_position; let ray_origin = view.world_position;
let ray_direction = normalize(in.world_position.xyz - view.world_position); let ray_direction = normalize(in.world_position.xyz - view.world_position);
let t = raymarch(ray_origin, ray_direction); let local_origin = (volu_material.model_inverse * vec4<f32>(ray_origin, 1.0)).xyz;
let local_direction = normalize((volu_material.model_inverse * vec4<f32>(ray_direction, 0.0)).xyz);
let t = raymarch(local_origin, local_direction);
var out: FragmentOutput; var out: FragmentOutput;
if t > 0.0 { if t > 0.0 {
let hit_point = ray_origin + t * ray_direction; let local_hit_point = ray_origin + t * ray_direction;
let normal = sphere_normal(hit_point); let local_normal = mandelbulb_normal(local_hit_point);
in.world_position = vec4<f32>(hit_point, 1.0); let world_hit_point = (transpose(volu_material.model_inverse) * vec4<f32>(local_hit_point, 1.0)).xyz;
in.world_normal = normal; let world_normal = normalize((transpose(volu_material.model_inverse) * vec4<f32>(local_normal, 0.0)).xyz);
in.world_position = vec4<f32>(world_hit_point, 1.0);
in.world_normal = world_normal;
var pbr_input = pbr_input_from_standard_material(in, is_front); var pbr_input = pbr_input_from_standard_material(in, is_front);
pbr_input.material.base_color = alpha_discard(pbr_input.material, pbr_input.material.base_color); let n = world_normal * 0.5 + 0.5;
pbr_input.material.base_color = alpha_discard(pbr_input.material, vec4<f32>(n, 1.0));
out.color = apply_pbr_lighting(pbr_input); out.color = apply_pbr_lighting(pbr_input);

View file

@ -30,6 +30,9 @@ fn setup(
mesh_translation: transform.translation, mesh_translation: transform.translation,
sphere_radius: 1.0, sphere_radius: 1.0,
color: Vec4::default(), color: Vec4::default(),
model_inverse: transform.compute_matrix().inverse(),
power: 8.0,
iterations: 32,
})); }));
commands.spawn(( commands.spawn((
@ -49,6 +52,7 @@ fn setup(
commands.spawn(( commands.spawn((
DirectionalLight::default(), DirectionalLight::default(),
Transform::from_xyz(1.0, 1.0, 1.0).looking_at(Vec3::ZERO, Vec3::Y), Transform::from_xyz(1.0, 1.0, 1.0).looking_at(Vec3::ZERO, Vec3::Y),
Rotate,
)); ));
commands.spawn(( commands.spawn((
@ -67,14 +71,12 @@ fn rotate_things(mut q: Query<&mut Transform, With<Rotate>>, time: Res<Time>) {
} }
fn update_volu_material( fn update_volu_material(
mut q: Query<(&mut Transform, &MeshMaterial3d<VoluMaterial>), Changed<Transform>>, q: Query<(&Transform, &MeshMaterial3d<VoluMaterial>), Changed<Transform>>,
mut volu_materials: ResMut<Assets<VoluMaterial>>, mut volu_materials: ResMut<Assets<VoluMaterial>>,
mut buffers: ResMut<Assets<ShaderStorageBuffer>>, mut buffers: ResMut<Assets<ShaderStorageBuffer>>,
time: Res<Time>, time: Res<Time>,
) { ) {
for (mut transform, material) in q.iter_mut() { for (transform, material) in q.iter() {
transform.translation.y = time.elapsed_secs().sin();
let volu_material = volu_materials.get_mut(material.0.id()).unwrap(); let volu_material = volu_materials.get_mut(material.0.id()).unwrap();
let buffer = buffers let buffer = buffers
@ -87,6 +89,9 @@ fn update_volu_material(
mesh_translation: transform.translation, mesh_translation: transform.translation,
sphere_radius: (elapsed.sin() + 3.) / 4., sphere_radius: (elapsed.sin() + 3.) / 4.,
color: vec4((elapsed.sin() + 1.) / 2., 0.0, 0.0, 1.0), color: vec4((elapsed.sin() + 1.) / 2., 0.0, 0.0, 1.0),
model_inverse: transform.compute_matrix(),
power: 8.0,
iterations: 32,
}; };
buffer.set_data(volu_storage); buffer.set_data(volu_storage);
@ -110,4 +115,7 @@ pub struct VoluStorage {
mesh_translation: Vec3, mesh_translation: Vec3,
sphere_radius: f32, sphere_radius: f32,
color: Vec4, color: Vec4,
model_inverse: Mat4,
power: f32,
iterations: i32,
} }