Probably wrong bilinear patch intersection

This commit is contained in:
hal8174 2025-08-21 20:50:05 +02:00
parent 6d363aecd0
commit 76448ed442
Signed by: hal8174
SSH key fingerprint: SHA256:NN98ZYwnrreQLSOV/g+amY7C3yL/mS1heD7bi5t6PPw
2 changed files with 126 additions and 18 deletions

View file

@ -1,6 +1,6 @@
use crate::prelude::*; use crate::prelude::*;
use core::panic; use core::panic;
use std::ops::{Add, Index, Sub}; use std::ops::{Add, Index, Mul, Sub};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Pos3 { pub struct Pos3 {
@ -32,6 +32,14 @@ impl Pos3 {
} }
} }
pub fn as_dir(self) -> Dir3 {
Dir3 {
x: self.x,
y: self.y,
z: self.z,
}
}
pub fn from_barycentric(vertices: [Self; 3], coordinates: [Float; 3]) -> Self { pub fn from_barycentric(vertices: [Self; 3], coordinates: [Float; 3]) -> Self {
Self { Self {
x: vertices[0].x() * coordinates[0] x: vertices[0].x() * coordinates[0]

View file

@ -58,6 +58,103 @@ pub(crate) enum ShapeType {
}, },
} }
fn quadratic(a: Float, b: Float, c: Float) -> (Option<Float>, Option<Float>) {
if a == 0.0 {
if b == 0.0 {
return (None, None);
}
return (Some(-c / b), None);
}
let discrim = b * b - 4.0 * a * c;
if discrim < 0.0 {
return (None, None);
}
let root_discrim = Float::sqrt(discrim);
let q = if b < 0.0 {
-0.5 * (b - root_discrim)
} else {
-0.5 * (b + root_discrim)
};
let mut t0 = q / a;
let mut t1 = c / q;
if t1 < t0 {
std::mem::swap(&mut t1, &mut t0);
}
(Some(t0), Some(t1))
}
fn bilinear_intersection(
ray: Ray,
min: Float,
max: Float,
patch: [Pos3; 4],
) -> Option<(Float, Float, Float)> {
let a = Dir3::dot(
Dir3::cross(patch[2] - patch[0], patch[1] - patch[3]),
ray.dir(),
);
let c = Dir3::dot(
Dir3::cross(patch[0] - ray.start(), ray.dir()),
patch[1] - patch[0],
);
let b = Dir3::dot(
Dir3::cross(patch[2] - ray.start(), ray.dir()),
patch[3] - patch[2],
) - (a + c);
let (u0, u1) = quadratic(a, b, c);
[u0, u1]
.into_iter()
.flatten()
.filter(|&u| {
if 0.0 <= u && u <= 1.0 {
return true;
}
false
})
.filter_map(|u| {
let uo = patch[0].as_dir() * u + patch[2].as_dir() * (1.0 - u);
let ud = patch[1].as_dir() * u + patch[3].as_dir() * (1.0 - u) - uo;
let deltao = uo - ray.start().as_dir();
let perp = Dir3::cross(ray.dir(), ud);
let p2 = perp.length_squared();
let v1 = deltao.x() * ray.dir().y()
+ perp.z()
+ ray.dir().x() * perp.y() * deltao.z()
+ perp.x() * deltao.y() * ray.dir().z()
- deltao.x() * perp.y() * deltao.z()
- ray.dir().x() * deltao.y() * perp.z()
- perp.x() * ray.dir().y() * deltao.z();
let t1 = deltao.x() * ud.y()
+ perp.z()
+ ud.x() * perp.y() * deltao.z()
+ perp.x() * deltao.y() * ud.z()
- deltao.x() * perp.y() * deltao.z()
- ud.x() * deltao.y() * perp.z()
- perp.x() * ud.y() * deltao.z();
if t1 > 0.0 && 0.0 < v1 && v1 <= p2 {
Some((t1 / p2, u, v1 / p2))
} else {
None
}
})
.next()
}
impl Shape { impl Shape {
pub(crate) fn intersect<R: Rng>( pub(crate) fn intersect<R: Rng>(
&self, &self,
@ -100,23 +197,19 @@ impl Shape {
std::mem::swap(&mut t1, &mut t0); std::mem::swap(&mut t1, &mut t0);
} }
if let Some(t) = [t0, t1] if let Some(t) = [t0, t1].into_iter().find(|&t| {
.into_iter() if min <= t && t <= max {
.filter(|&t| { let p = ray.at(t0);
if min <= t && t <= max { let mut phi = Float::atan2(p.y(), p.x());
let p = ray.at(t0); if phi < 0.0 {
let mut phi = Float::atan2(p.y(), p.x()); phi += 2.0 * FloatConsts::PI;
if phi < 0.0 {
phi += 2.0 * FloatConsts::PI;
}
if zmin <= p.z() && p.z() <= zmax && phi <= phimax {
return true;
}
} }
false if zmin <= p.z() && p.z() <= zmax && phi <= phimax {
}) return true;
.next() }
{ }
false
}) {
return Some(Intersection::new( return Some(Intersection::new(
t, t,
(ray.at(t) - Pos3::zero()).normalize(), (ray.at(t) - Pos3::zero()).normalize(),
@ -134,7 +227,14 @@ impl Shape {
s, s,
uv, uv,
} => (), } => (),
ShapeType::BilinearMesh { indices, p, n, uv } => (), ShapeType::BilinearMesh { indices, p, n, uv } => {
if indices.is_empty()
&& let Some((t, _u, _v)) =
bilinear_intersection(ray, min, max, [p[0], p[1], p[2], p[3]])
{
return Some(Intersection::new(t, Dir3::up(), None, None, 0.0));
}
}
ShapeType::LoopSubDiv { levels, indices, p } => todo!(), ShapeType::LoopSubDiv { levels, indices, p } => todo!(),
ShapeType::Disk { ShapeType::Disk {
height, height,