From 4587deb6335b0292f562b0eb9ef0a7b2a99ebe9b Mon Sep 17 00:00:00 2001 From: altunenes Date: Sun, 5 May 2024 18:18:03 +0300 Subject: [PATCH] add mandelbulb --- shaders/mandelbulb.wgsl | 102 ++++++++++++++++++++++------------------ src/mandelbulb.rs | 46 +++++++++--------- src/spiralimgwgpu.rs | 2 +- 3 files changed, 79 insertions(+), 71 deletions(-) diff --git a/shaders/mandelbulb.wgsl b/shaders/mandelbulb.wgsl index ab45e59..316d0ed 100644 --- a/shaders/mandelbulb.wgsl +++ b/shaders/mandelbulb.wgsl @@ -8,13 +8,14 @@ const AMBIENT: f32 = 0.2; const SPECULAR_COEFF: f32 = 0.5; const SHININESS: f32 = 1.0; const EPS: vec3 = vec3(0.001, 0.001, 0.001); - +fn applyGamma(color: vec3, gamma: f32) -> vec3 { + return pow(color, vec3(1.0 / gamma, 1.0 / gamma, 1.0 / gamma)); +} struct TimeUniform { time: f32, }; @group(1) @binding(0) var u_time: TimeUniform; - struct Params { lambda: f32, theta: f32, @@ -36,30 +37,27 @@ struct Params { }; @group(0) @binding(1) var params: Params; - fn mandelbulb(pos: vec3) -> f32 { var z: vec3 = pos; var dr: f32 = 1.0; var r: f32 = 0.0; + var zerg: f32 = POWER+params.iter; for (var i: i32 = 0; i < MAX_STEPS; i = i + 1) { r = length(z); if (r > BAILOUT) { break; } - let theta: f32 = acos(z.z / r); let phi: f32 = atan2(z.y, z.x); - dr = pow(r, POWER - 1.0) * POWER * dr + 1.0; - - let zr: f32 = pow(r, POWER); - let newTheta: f32 = theta * POWER; - let newPhi: f32 = phi * POWER; + dr = pow(r, zerg - 1.0) * zerg * dr + 1.0; + let zr: f32 = pow(r, zerg); + let newTheta: f32 = theta * zerg; + let newPhi: f32 = phi * zerg; z = zr * vec3(sin(newTheta) * cos(newPhi), sin(newPhi) * sin(newTheta), cos(newTheta)); z = z + pos; } return 0.5 * log(r) * r / dr; } - fn normal(p: vec3) -> vec3 { let d: f32 = mandelbulb(p); return normalize(vec3( @@ -68,57 +66,67 @@ fn normal(p: vec3) -> vec3 { d - mandelbulb(p - vec3(0.0, 0.0, EPS.z)) )); } -fn colorize(d: f32, n: vec3, lightDir: vec3, time: f32) -> vec3 { - let angle: f32 = dot(n, lightDir) * params.blue + params.bound; - let base: vec3 = 0.5 + params.aa * cos(params.iter * vec3(1.0, 2.0, 3.0) * d + vec3(0.0, 0.5, 1.0) + time * vec3(0.3, 0.2, 0.1)); - return mix(base, vec3(angle, angle * 0.5, angle * 0.25), 0.5); +fn colorize(pos: vec3, normal: vec3, dist: f32, time: f32) -> vec3 { + let baseColor: vec3 = vec3(0.3, 0.3, 0.3); + let iter_ratio: f32 = clamp(dist / 0.0, 1.0, 1.0); + let lenSq: f32 = length(pos); + let exteriorColor: vec3 = vec3(0.0, 0.0, 0.0); + if (iter_ratio >= 1.0) { + let col1: vec3 = 0.5 + params.sigma * sin(params.a + vec3(params.b, params.c, params.d) + PI * vec3(2.0 * lenSq) + time / 2.0); + let col2: vec3 = 0.5 + params.blue * sin(1.5 + PI * vec3(lenSq) + time / 2.0); + return baseColor + 1.0 * sqrt(col1 * col2); + } else { + return mix(baseColor, exteriorColor, params.e); + } } - fn rotateZ(p: vec3, a: f32) -> vec3 { let s: f32 = sin(a); let c: f32 = cos(a); return vec3(c * p.x - s * p.y, s * p.x + c * p.y, p.z); } - fn rotateY(p: vec3, a: f32) -> vec3 { let s: f32 = sin(a); let c: f32 = cos(a); return vec3(c * p.x + s * p.z, p.y, -s * p.x + c * p.z); } - - @fragment fn main(@builtin(position) FragCoord: vec4) -> @location(0) vec4 { + let AA_LEVEL: i32 = i32(params.aa); let resolution: vec2 = vec2(1920.0, 1080.0); - let time: f32 = u_time.time * params.gamma; + let time: f32 = u_time.time * params.bound; let camPos: vec3 = rotateY(vec3(params.theta, params.alpha, -params.lambda), time); - var uv: vec2 = (FragCoord.xy - 0.5 * resolution.xy) / resolution.y * vec2(2.0, 1.0); - var rayDir: vec3 = normalize(vec3(uv, params.sigma)); - rayDir = rotateZ(rayDir, sin(time) * 0.5); - rayDir = rotateY(rayDir, time); - - var totalDist: f32 = 0.0; - for (var i: i32 = 0; i < MAX_STEPS; i = i + 1) { - let p: vec3 = camPos + totalDist * rayDir; - let dist: f32 = mandelbulb(p); - totalDist += dist; - if (dist < SURFACE_DIST || totalDist > MAX_DIST) { - break; + var finalColor: vec3 = vec3(0.0, 0.0, 0.0); + let totalSamples: f32 = f32(AA_LEVEL * AA_LEVEL); + for (var i: i32 = 0; i < AA_LEVEL; i = i + 1) { + for (var j: i32 = 0; j < AA_LEVEL; j = j + 1) { + let sampleUV: vec2 = (FragCoord.xy - 0.5 * resolution.xy) / resolution.y + + (vec2(f32(i), f32(j)) - 0.5 * vec2(f32(AA_LEVEL) - 1.0)) / resolution.y / f32(AA_LEVEL); + var rayDir: vec3 = normalize(vec3(sampleUV, 2.0)); + rayDir = rotateZ(rayDir, sin(time) * 0.5); + rayDir = rotateY(rayDir, time); + var totalDist: f32 = 0.0; + for (var k: i32 = 0; k < MAX_STEPS; k = k + 1) { + let p: vec3 = camPos + totalDist * rayDir; + let dist: f32 = mandelbulb(p); + totalDist += dist; + if (dist < SURFACE_DIST || totalDist > MAX_DIST) { + break; + } + } + if (totalDist < MAX_DIST) { + let p: vec3 = camPos + totalDist * rayDir; + let n: vec3 = normal(p); + let lightDir: vec3 = normalize(vec3(0.5, 1.0, -0.5)); + let viewDir: vec3 = normalize(-rayDir); + let reflectDir: vec3 = reflect(-lightDir, n); + let diff: f32 = max(dot(n, lightDir), 0.0); + let spec: f32 = pow(max(dot(viewDir, reflectDir), 0.0), SHININESS); + let baseColor: vec3 = colorize(p, n, totalDist, u_time.time); + finalColor += baseColor * (AMBIENT + diff + SPECULAR_COEFF * spec); + } } } - - var color: vec3 = vec3(0.0, 0.0, 0.0); - if (totalDist < MAX_DIST) { - let p: vec3 = camPos + totalDist * rayDir; - let n: vec3 = normal(p); - let lightDir: vec3 = normalize(vec3(0.5, 1.0, -0.5)); - let viewDir: vec3 = normalize(-rayDir); - let reflectDir: vec3 = reflect(-lightDir, n); - let diff: f32 = max(dot(n, lightDir), 0.0); - let spec: f32 = pow(max(dot(viewDir, reflectDir), 0.0), SHININESS); - let baseColor: vec3 = colorize(totalDist, n, lightDir, u_time.time); - color = baseColor * (AMBIENT + diff + SPECULAR_COEFF * spec); - } - - return vec4(color, 1.0); -} \ No newline at end of file + finalColor = applyGamma(finalColor,params.gamma); + finalColor /= totalSamples; + return vec4(finalColor, 1.0); +} diff --git a/src/mandelbulb.rs b/src/mandelbulb.rs index f47c651..329851a 100644 --- a/src/mandelbulb.rs +++ b/src/mandelbulb.rs @@ -56,22 +56,22 @@ fn update(app: &App, model: &mut Model, update: Update) { model.settings.show_ui = !model.settings.show_ui; } egui::Window::new("Shader Settings").show(&ctx, |ui| { - ui.add(egui::Slider::new(&mut model.settings.lambda, -15.00001..=15.0).text("l")); - ui.add(egui::Slider::new(&mut model.settings.theta, -15.0..=15.5).text("t")); - ui.add(egui::Slider::new(&mut model.settings.alpha, -15.5..=15.5).text("a")); - ui.add(egui::Slider::new(&mut model.settings.sigma, 0.0..=20.0).text("r")); - ui.add(egui::Slider::new(&mut model.settings.gamma, -1.0..=2.0).text("g")); - ui.add(egui::Slider::new(&mut model.settings.blue, 0.0..=2.0).text("b")); - // ui.add(egui::Slider::new(&mut model.settings.a, -10.0..=10.5).text("e1")); - // ui.add(egui::Slider::new(&mut model.settings.b, -10.0..=10.5).text("e2")); - // ui.add(egui::Slider::new(&mut model.settings.c, -10.0..=10.0).text("e3")); - // ui.add(egui::Slider::new(&mut model.settings.d, -10.0..=20.0).text("e4")); + ui.add(egui::Slider::new(&mut model.settings.lambda, 0.0..=8.0).text("l")); + ui.add(egui::Slider::new(&mut model.settings.theta, -1.0..=1.0).text("t")); + ui.add(egui::Slider::new(&mut model.settings.alpha, -1.0..=1.0).text("a")); + ui.add(egui::Slider::new(&mut model.settings.sigma, 0.0..=2.0).text("r")); + ui.add(egui::Slider::new(&mut model.settings.gamma, 0.0..=2.0).text("g")); + ui.add(egui::Slider::new(&mut model.settings.blue, 0.0..=3.0).text("b")); + ui.add(egui::Slider::new(&mut model.settings.a, 0.0..=15.0).text("e1")); + ui.add(egui::Slider::new(&mut model.settings.b, 0.0..=1.0).text("r")); + ui.add(egui::Slider::new(&mut model.settings.c, 0.0..=1.0).text("g")); + ui.add(egui::Slider::new(&mut model.settings.d, 0.0..=1.0).text("b")); // ui.add(egui::Slider::new(&mut model.settings.g, 1.0..=8.00).text("e5")); - //ui.add(egui::Slider::new(&mut model.settings.e, 0.002..=3.0).text("c1")); + ui.add(egui::Slider::new(&mut model.settings.e, 0.002..=3.0).text("c1")); //ui.add(egui::Slider::new(&mut model.settings.f, 0.002..=3.0).text("c2")); - ui.add(egui::Slider::new(&mut model.settings.iter, 0.0..=5.0).text("iter")); - ui.add(egui::Slider::new(&mut model.settings.bound, 0.0..=5.0).text("bound")); - ui.add(egui::Slider::new(&mut model.settings.aa, 0.0..=10.0).text("AA")); + ui.add(egui::Slider::new(&mut model.settings.iter, -10.0..=10.0).text("iter")); + ui.add(egui::Slider::new(&mut model.settings.bound, 0.0..=5.0).text("time")); + ui.add(egui::Slider::new(&mut model.settings.aa, 1.0..=4.0).text("AA")); //ui.add(egui::Slider::new(&mut model.settings.tt, 1.0..=250.0).text("speed")); }); let params_data = [model.settings.lambda, model.settings.theta,model.settings.alpha, model.settings.sigma,model.settings.gamma,model.settings.blue,model.settings.aa,model.settings.iter,model.settings.bound,model.settings.tt,model.settings.a,model.settings.b,model.settings.c,model.settings.d,model.settings.e,model.settings.f,model.settings.g]; @@ -168,19 +168,19 @@ fn model(app: &App) -> Model { lambda: 2.0, theta:0.0, alpha:0.0, - sigma:2.0, - gamma:0.25, + sigma:0.5, + gamma:0.5, blue:0.5, show_ui:true, - aa: 0.5, - iter:6.2831, + aa: 2.0, + iter:0.0, bound:0.5, tt:18.0, - a:0.1, - b:0.5, - c:1.0, - d:8.0, - e:2.0, + a:1.0, + b:0.0, + c:0.5, + d:1.0, + e:0.75, f:2.0, g:1.0, }; diff --git a/src/spiralimgwgpu.rs b/src/spiralimgwgpu.rs index 7570fdc..376b65f 100644 --- a/src/spiralimgwgpu.rs +++ b/src/spiralimgwgpu.rs @@ -178,7 +178,7 @@ fn model(app: &App) -> Model { label: Some("time_bind_group_layout"), }); let mut dummy_img = RgbaImage::new(800, 600); - dummy_img.put_pixel(0, 0, image::Rgba([255, 255, 255, 255])); // White pixel + dummy_img.put_pixel(0, 0, image::Rgba([255, 255, 255, 255])); let texture = Texture::from_image(app, &image::DynamicImage::ImageRgba8(dummy_img)); let texture_view = texture.view().build();