From 816d6682b4302047865d4cbf39b807818a18cd1e Mon Sep 17 00:00:00 2001 From: numzero Date: Thu, 20 Nov 2025 14:12:31 +0300 Subject: [PATCH] add custom display options --- src/lib.rs | 199 +++++++++++++++++------------- ui/src/api.hxx | 7 ++ ui/src/main_window.cxx | 17 ++- ui/src/main_window.ui | 273 ++++++++++++++++++++++++++++++++++++++--- 4 files changed, 388 insertions(+), 108 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index fa70501..ba7dd7e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,6 +36,13 @@ pub struct RedrawArgs { pub light_spread: f32, pub accum_sigma: f32, pub accum_scale: f32, + pub show_axes: bool, + pub show_shapes: bool, + pub show_hit_emission: bool, + pub show_miss_emission: bool, + pub show_direct_hit: bool, + pub show_indirect_hit: bool, + pub show_light: bool, } pub struct Gpu { @@ -163,7 +170,9 @@ impl Core { depth_stencil_attachment: None, ..Default::default() }); - self.pipeline.render(&mut pass, [&self.tripod]); + if args.show_axes { + self.pipeline.render(&mut pass, [&self.tripod]); + } let source = Source { position_yaw: args.light_position.yaw, @@ -173,14 +182,16 @@ impl Core { spread: args.light_spread, }; - let contour: Vec = loop_list(source.contour(17)) - .map(|pos| Vertex { - pos, - color: vec3(1., 1., 1.), - }) - .collect(); - self.pipeline - .render(&mut pass, [&Mesh::new(&self.device, &contour)]); + if args.show_shapes { + let contour: Vec = loop_list(source.contour(17)) + .map(|pos| Vertex { + pos, + color: vec3(1., 1., 1.), + }) + .collect(); + self.pipeline + .render(&mut pass, [&Mesh::new(&self.device, &contour)]); + } const BASE_R: f32 = 2.; const BASE_POS: Vec3 = vec3(0., 0., -BASE_R); @@ -211,35 +222,43 @@ impl Core { for ray in source_rays { if let Some(hit) = scene.trace_ray(ray) { hits.push(hit); - source_ray_display.extend([ - Vertex { - pos: ray.base, - color: vec3(1., 1., 1.), - }, - Vertex { - pos: ray.base + 0.1 * ray.dir, - color: vec3(0., 1., 0.), - }, - Vertex { - pos: hit.incident.base - 0.02 * hit.incident.dir, - color: vec3(0., 0., 1.), - }, - Vertex { - pos: hit.incident.base, - color: vec3(1., 1., 1.), - }, - ]); + if args.show_hit_emission { + source_ray_display.extend([ + Vertex { + pos: ray.base, + color: vec3(1., 1., 1.), + }, + Vertex { + pos: ray.base + 0.1 * ray.dir, + color: vec3(0., 1., 0.), + }, + ]); + } + if args.show_direct_hit { + source_ray_display.extend([ + Vertex { + pos: hit.incident.base - 0.02 * hit.incident.dir, + color: vec3(0., 0., 1.), + }, + Vertex { + pos: hit.incident.base, + color: vec3(1., 1., 1.), + }, + ]); + } } else { - source_ray_display.extend([ - Vertex { - pos: ray.base, - color: vec3(1., 1., 1.), - }, - Vertex { - pos: ray.base + 0.1 * ray.dir, - color: vec3(1., 0., 0.), - }, - ]); + if args.show_miss_emission { + source_ray_display.extend([ + Vertex { + pos: ray.base, + color: vec3(1., 1., 1.), + }, + Vertex { + pos: ray.base + 0.1 * ray.dir, + color: vec3(1., 0., 0.), + }, + ]); + } } } let mut hits2: Vec = Vec::with_capacity(hits.len()); @@ -251,62 +270,70 @@ impl Core { continue; }; hits2.push(hit2); - source_ray_display.extend([ - Vertex { - pos: hit2.incident.base - 0.02 * hit2.incident.dir, - color: vec3(1., 0., 1.), - }, - Vertex { - pos: hit2.incident.base, - color: vec3(1., 1., 1.), - }, - ]); + if args.show_indirect_hit { + source_ray_display.extend([ + Vertex { + pos: hit2.incident.base - 0.02 * hit2.incident.dir, + color: vec3(1., 0., 1.), + }, + Vertex { + pos: hit2.incident.base, + color: vec3(1., 1., 1.), + }, + ]); + } } hits.extend(hits2); let mut camera_ray_display: Vec = Vec::with_capacity(camera_rays.len()); - let sigma2 = args.accum_sigma.powi(2); - let accum_normalizator = (2. * PI * sigma2).sqrt().recip(); - for ray in camera_rays { - let Some(hit) = scene.trace_ray(ray) else { - continue; - }; - let mut total_cd = 0.0f32; - for light_hit in &hits { - let d2 = hit.incident.base.distance_squared(light_hit.incident.base); - if d2 > 9. * sigma2 { + if args.show_light { + let sigma2 = args.accum_sigma.powi(2); + let accum_normalizator = (2. * PI * sigma2).sqrt().recip(); + for ray in camera_rays { + let Some(hit) = scene.trace_ray(ray) else { continue; + }; + let mut total_cd = 0.0f32; + for light_hit in &hits { + let d2 = hit.incident.base.distance_squared(light_hit.incident.base); + if d2 > 9. * sigma2 { + continue; + } + assert!(hit.normal.is_normalized()); + assert!(hit.incident.dir.is_normalized()); + let reflector = Lambertian; + let in_lm = 1.0; + let out_cd = in_lm + * hit.normal.dot(-hit.incident.dir) + * reflector.brdf(hit.normal, hit.incident.dir, -ray.dir); + let weight = accum_normalizator * (-0.5 * d2 / sigma2).exp(); + total_cd += weight * out_cd; } - assert!(hit.normal.is_normalized()); - assert!(hit.incident.dir.is_normalized()); - let reflector = Lambertian; - let in_lm = 1.0; - let out_cd = in_lm - * hit.normal.dot(-hit.incident.dir) - * reflector.brdf(hit.normal, hit.incident.dir, -ray.dir); - let weight = accum_normalizator * (-0.5 * d2 / sigma2).exp(); - total_cd += weight * out_cd; + let brightness = 3. * (1. - (1. + total_cd * args.accum_scale).recip()); + let r = args.accum_sigma; + let color = vec3(brightness, brightness - 1., brightness - 2.) + .clamp(Vec3::splat(0.), Vec3::splat(1.)); + let vertex = |off: Vec3| Vertex { + pos: hit.incident.base + r * off, + color, + }; + camera_ray_display.extend([ + vertex(-Vec3::X), + vertex(Vec3::X), + vertex(-Vec3::Y), + vertex(Vec3::Y), + vertex(-Vec3::Z), + vertex(Vec3::Z), + ]); } - let brightness = 3. * (1. - (1. + total_cd * args.accum_scale).recip()); - let r = args.accum_sigma; - let color = vec3(brightness, brightness - 1., brightness - 2.) - .clamp(Vec3::splat(0.), Vec3::splat(1.)); - let vertex = |off: Vec3| Vertex { - pos: hit.incident.base + r * off, - color, - }; - camera_ray_display.extend([ - vertex(-Vec3::X), - vertex(Vec3::X), - vertex(-Vec3::Y), - vertex(Vec3::Y), - vertex(-Vec3::Z), - vertex(Vec3::Z), - ]); } - self.pipeline - .render(&mut pass, [&Mesh::new(&self.device, &source_ray_display)]); - self.pipeline - .render(&mut pass, [&Mesh::new(&self.device, &camera_ray_display)]); + if !source_ray_display.is_empty() { + self.pipeline + .render(&mut pass, [&Mesh::new(&self.device, &source_ray_display)]); + } + if !camera_ray_display.is_empty() { + self.pipeline + .render(&mut pass, [&Mesh::new(&self.device, &camera_ray_display)]); + } drop(pass); self.queue.submit(std::iter::once(encoder.finish())); diff --git a/ui/src/api.hxx b/ui/src/api.hxx index c1f3b2a..ff7038e 100644 --- a/ui/src/api.hxx +++ b/ui/src/api.hxx @@ -20,6 +20,13 @@ struct RedrawArgs { float light_spread = 0.; float accum_sigma = 1.; float accum_scale = 1.; + bool show_axes = true; + bool show_shapes = true; + bool show_hit_emission = true; + bool show_miss_emission = true; + bool show_direct_hit = true; + bool show_indirect_hit = true; + bool show_light = true; }; } // namespace ffi diff --git a/ui/src/main_window.cxx b/ui/src/main_window.cxx index 511b984..033645f 100644 --- a/ui/src/main_window.cxx +++ b/ui/src/main_window.cxx @@ -20,24 +20,33 @@ void PhotonLight::updateView() { .camera_position = SphericalPosition{ .yaw = deg_to_rad(m_ui->cameraYaw->value()), .pitch = deg_to_rad(m_ui->cameraPitch->value()), - .distance = 3.0, + .distance = m_ui->cameraDistance->value() / 10.0f, }, .light_position = SphericalPosition{ .yaw = deg_to_rad(m_ui->lightYaw->value()), .pitch = deg_to_rad(m_ui->lightPitch->value()), - .distance = 1.0, + .distance = m_ui->lightDistance->value() / 10.0f, }, .light_radius = 0.125, .light_spread = 0.125, .accum_sigma = exp10f(m_ui->accumSigma->value() / 25.0), .accum_scale = exp10f(m_ui->accumScale->value() / 25.0), + .show_axes = m_ui->displayAxes->isChecked(), + .show_shapes = m_ui->displayShapes->isChecked(), + .show_hit_emission = m_ui->displayEmitted->isChecked(), + .show_miss_emission = m_ui->displayEmitted->isChecked(), + .show_direct_hit = m_ui->displayDirectHits->isChecked(), + .show_indirect_hit = m_ui->displayIndirectHits->isChecked(), + .show_light = m_ui->displayResult->isChecked(), }; m_ui->cameraYawLabel->setText(tr("Yaw: %1 deg").arg(QString::number(qRadiansToDegrees(args.camera_position.yaw)))); m_ui->cameraPitchLabel->setText(tr("Pitch: %1 deg").arg(QString::number(qRadiansToDegrees(args.camera_position.pitch)))); + m_ui->cameraDistanceLabel->setText(tr("Distance: %1").arg(QString::number(args.camera_position.distance))); m_ui->lightYawLabel->setText(tr("Yaw: %1 deg").arg(QString::number(qRadiansToDegrees(args.light_position.yaw)))); m_ui->lightPitchLabel->setText(tr("Pitch: %1 deg").arg(QString::number(qRadiansToDegrees(args.light_position.pitch)))); - m_ui->accumSigmaLabel->setText(tr("Sigma: %1").arg(QString::number(args.accum_sigma, 'f', 3))); - m_ui->accumScaleLabel->setText(tr("Scale: %1").arg(QString::number(args.accum_scale, 'f', 3))); + m_ui->lightDistanceLabel->setText(tr("Distance: %1").arg(QString::number(args.light_position.distance))); + m_ui->accumSigmaLabel->setText(tr("Averaging radius: %1").arg(QString::number(args.accum_sigma, 'f', 3))); + m_ui->accumScaleLabel->setText(tr("Brightness: %1").arg(QString::number(args.accum_scale, 'f', 3))); m_ui->viewport->setView(args); } diff --git a/ui/src/main_window.ui b/ui/src/main_window.ui index 9688b64..44b57c5 100644 --- a/ui/src/main_window.ui +++ b/ui/src/main_window.ui @@ -6,8 +6,8 @@ 0 0 - 800 - 600 + 1600 + 1200 @@ -25,7 +25,7 @@ 0 0 - 800 + 1600 38 @@ -98,6 +98,26 @@ + + + + Distance + + + + + + + 50 + + + 30 + + + Qt::Horizontal + + + @@ -159,19 +179,39 @@ + + + + Distance + + + + + + + 50 + + + 10 + + + Qt::Horizontal + + + - Accumulating + Lighting - Sigma + Averaging radius @@ -194,7 +234,7 @@ - Scale + Brightness @@ -217,6 +257,75 @@ + + + + Show + + + + + + Axes + + + true + + + + + + + Shapes + + + true + + + + + + + Emitted rays + + + false + + + + + + + Direct incident rays + + + false + + + + + + + Indirect incident rays + + + false + + + + + + + Average light + + + true + + + + + + @@ -251,8 +360,8 @@ updateView() - 688 - 159 + 1585 + 169 403 @@ -267,8 +376,8 @@ updateView() - 688 - 215 + 1585 + 225 403 @@ -283,8 +392,8 @@ updateView() - 688 - 319 + 1585 + 385 403 @@ -299,8 +408,8 @@ updateView() - 688 - 375 + 1585 + 441 403 @@ -315,8 +424,8 @@ updateView() - 729 - 479 + 1585 + 601 399 @@ -331,8 +440,8 @@ updateView() - 729 - 535 + 1585 + 657 399 @@ -340,6 +449,134 @@ + + cameraDistance + valueChanged(int) + MainWindow + updateView() + + + 1489 + 271 + + + 799 + 599 + + + + + lightDistance + valueChanged(int) + MainWindow + updateView() + + + 1489 + 487 + + + 799 + 599 + + + + + displayAxes + stateChanged(int) + MainWindow + updateView() + + + 1489 + 799 + + + 799 + 599 + + + + + displayDirectHits + stateChanged(int) + MainWindow + updateView() + + + 1489 + 901 + + + 799 + 599 + + + + + displayEmitted + stateChanged(int) + MainWindow + updateView() + + + 1489 + 867 + + + 799 + 599 + + + + + displayIndirectHits + stateChanged(int) + MainWindow + updateView() + + + 1489 + 935 + + + 799 + 599 + + + + + displayResult + stateChanged(int) + MainWindow + updateView() + + + 1489 + 969 + + + 799 + 599 + + + + + displayShapes + stateChanged(int) + MainWindow + updateView() + + + 1489 + 833 + + + 799 + 599 + + + updateView()