Compare commits

...

3 Commits

Author SHA1 Message Date
cd0c264c6e basic two-way tracing 2025-11-16 15:25:08 +03:00
cf65929b99 show current values in the ui 2025-11-16 15:15:42 +03:00
8bed95f872 use mutable vectors, to collect into several at once 2025-11-16 14:43:31 +03:00
4 changed files with 189 additions and 36 deletions

View File

@ -2,10 +2,12 @@
use std::{convert::identity, error::Error, f32::consts::PI}; use std::{convert::identity, error::Error, f32::consts::PI};
use glam::{Mat4, UVec2, Vec3, vec3}; use glam::{Mat4, UVec2, Vec2, Vec3, vec3};
use rand_distr::Distribution as _;
use crate::{ use crate::{
camera::OrbitalCamera, camera::OrbitalCamera,
ray::Ray,
render::lines::{LookParams, Mesh, Pipeline, Vertex}, render::lines::{LookParams, Mesh, Pipeline, Vertex},
trace::{Scene, Source, Sphere}, trace::{Scene, Source, Sphere},
}; };
@ -32,6 +34,8 @@ pub struct RedrawArgs {
pub light_position: SphericalPosition, pub light_position: SphericalPosition,
pub light_radius: f32, pub light_radius: f32,
pub light_spread: f32, pub light_spread: f32,
pub accum_sigma: f32,
pub accum_scale: f32,
} }
pub struct Gpu { pub struct Gpu {
@ -84,6 +88,22 @@ fn loop_list_ex<T: Clone, U>(
} }
} }
impl OrbitalCamera {
fn make_ray(&self, rng: &mut impl rand::Rng) -> Ray {
let off: f32 = rand_distr::StandardUniform.sample(rng);
let side: Vec2 = rand_distr::UnitCircle.sample(rng).into();
let m = self.transform().inverse();
let fwd = 1. - 0.1 * off;
let side_scale = (1. - fwd.powi(2)).sqrt();
let dir = Vec3::from((side_scale * side, fwd));
Ray {
base: self.position(),
dir: m.transform_vector3(dir),
}
}
}
impl Core { impl Core {
pub fn new(gpu: Gpu) -> Self { pub fn new(gpu: Gpu) -> Self {
let Gpu { let Gpu {
@ -189,36 +209,77 @@ impl Core {
}; };
let mut prng = rand_pcg::Pcg64::new(42, 0); let mut prng = rand_pcg::Pcg64::new(42, 0);
let rays: Vec<Vertex> = (0..10000) let source_rays: Vec<Ray> = (0..10240).map(|_| source.make_ray(&mut prng)).collect();
.flat_map(|_| { let camera_rays: Vec<Ray> = (0..1024).map(|_| camera.make_ray(&mut prng)).collect();
let ray = source.make_ray(&mut prng); let mut source_ray_display: Vec<Vertex> = Vec::with_capacity(source_rays.len());
if let Some(ray) = scene.trace_ray(ray) { let mut hits: Vec<Ray> = Vec::with_capacity(source_rays.len());
[ for ray in source_rays {
Vertex { if let Some(hit) = scene.trace_ray(ray) {
pos: ray.base - 0.02 * ray.dir, hits.push(hit);
color: vec3(1., 1., 1.), source_ray_display.extend([
}, Vertex {
Vertex { pos: ray.base,
pos: ray.base, color: vec3(1., 1., 1.),
color: vec3(0., 1., 0.), },
}, Vertex {
] pos: ray.base + 0.1 * ray.dir,
} else { color: vec3(0., 1., 0.),
[ },
Vertex { Vertex {
pos: ray.base, pos: hit.base - 0.02 * hit.dir,
color: vec3(1., 1., 1.), color: vec3(0., 0., 1.),
}, },
Vertex { Vertex {
pos: ray.base + 0.1 * ray.dir, pos: hit.base,
color: vec3(1., 0., 0.), color: vec3(1., 1., 1.),
}, },
] ]);
} } else {
}) source_ray_display.extend([
.collect(); Vertex {
pos: ray.base,
color: vec3(1., 1., 1.),
},
Vertex {
pos: ray.base + 0.1 * ray.dir,
color: vec3(1., 0., 0.),
},
]);
}
}
let mut camera_ray_display: Vec<Vertex> = Vec::with_capacity(camera_rays.len());
let sigma2 = args.accum_sigma.powi(2);
let weight = (2. * PI * sigma2).sqrt().recip() * args.accum_scale;
for ray in camera_rays {
let Some(hit) = scene.trace_ray(ray) else {
continue;
};
let mut value = 0.0f32;
for light_hit in &hits {
let d2 = hit.base.distance_squared(light_hit.base);
let w = (-0.5 * d2 / sigma2).exp();
value += w;
}
value *= weight;
let r = 0.01;
let color = vec3(value, value - 1., value - 2.).clamp(Vec3::splat(0.), Vec3::splat(1.));
let vertex = |off: Vec3| Vertex {
pos: hit.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 self.pipeline
.render(&mut pass, [&Mesh::new(&self.device, &rays)]); .render(&mut pass, [&Mesh::new(&self.device, &source_ray_display)]);
self.pipeline
.render(&mut pass, [&Mesh::new(&self.device, &camera_ray_display)]);
drop(pass); drop(pass);
self.queue.submit(std::iter::once(encoder.finish())); self.queue.submit(std::iter::once(encoder.finish()));

View File

@ -18,6 +18,8 @@ struct RedrawArgs {
SphericalPosition light_position; SphericalPosition light_position;
float light_radius; float light_radius;
float light_spread; float light_spread;
float accum_sigma;
float accum_scale;
}; };
} // namespace ffi } // namespace ffi

View File

@ -16,7 +16,7 @@ float deg_to_rad(float val) {
} }
void PhotonLight::updateView() { void PhotonLight::updateView() {
m_ui->viewport->setView(RedrawArgs{ RedrawArgs args{
.camera_position = SphericalPosition{ .camera_position = SphericalPosition{
.yaw = deg_to_rad(m_ui->cameraYaw->value()), .yaw = deg_to_rad(m_ui->cameraYaw->value()),
.pitch = deg_to_rad(m_ui->cameraPitch->value()), .pitch = deg_to_rad(m_ui->cameraPitch->value()),
@ -29,7 +29,16 @@ void PhotonLight::updateView() {
}, },
.light_radius = 0.125, .light_radius = 0.125,
.light_spread = 0.125, .light_spread = 0.125,
}); .accum_sigma = exp10f(m_ui->accumSigma->value() / 25.0),
.accum_scale = exp10f(m_ui->accumScale->value() / 25.0),
};
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->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)));
m_ui->accumScaleLabel->setText(tr("Scale: %1").arg(QString::number(args.accum_scale)));
m_ui->viewport->setView(args);
} }
#include "moc_main_window.cpp" #include "moc_main_window.cpp"

View File

@ -47,7 +47,7 @@
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QLabel" name="label"> <widget class="QLabel" name="cameraYawLabel">
<property name="text"> <property name="text">
<string>Yaw</string> <string>Yaw</string>
</property> </property>
@ -73,7 +73,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="cameraPitchLabel">
<property name="text"> <property name="text">
<string>Pitch</string> <string>Pitch</string>
</property> </property>
@ -108,7 +108,7 @@
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_3">
<item> <item>
<widget class="QLabel" name="label_3"> <widget class="QLabel" name="lightYawLabel">
<property name="text"> <property name="text">
<string>Yaw</string> <string>Yaw</string>
</property> </property>
@ -131,7 +131,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="lightPitchLabel">
<property name="text"> <property name="text">
<string>Pitch</string> <string>Pitch</string>
</property> </property>
@ -162,6 +162,55 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Accumulating</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="accumSigmaLabel">
<property name="text">
<string>Sigma</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="accumSigma">
<property name="minimum">
<number>-100</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="accumScaleLabel">
<property name="text">
<string>Scale</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="accumScale">
<property name="minimum">
<number>-100</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item> <item>
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
@ -253,6 +302,38 @@
</hint> </hint>
</hints> </hints>
</connection> </connection>
<connection>
<sender>accumSigma</sender>
<signal>valueChanged(int)</signal>
<receiver>MainWindow</receiver>
<slot>updateView()</slot>
<hints>
<hint type="sourcelabel">
<x>729</x>
<y>479</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>299</y>
</hint>
</hints>
</connection>
<connection>
<sender>accumScale</sender>
<signal>valueChanged(int)</signal>
<receiver>MainWindow</receiver>
<slot>updateView()</slot>
<hints>
<hint type="sourcelabel">
<x>729</x>
<y>535</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>299</y>
</hint>
</hints>
</connection>
</connections> </connections>
<slots> <slots>
<slot>updateView()</slot> <slot>updateView()</slot>