Do clipping in 3D homogeneous space

This commit is contained in:
Patrick Walton 2019-01-31 15:29:13 -08:00
parent 4801503dd8
commit 89244ba6cf
5 changed files with 54 additions and 56 deletions

View File

@ -37,7 +37,7 @@ const MAIN_FRAMEBUFFER_WIDTH: u32 = 1067;
const MAIN_FRAMEBUFFER_HEIGHT: u32 = 800;
const MOUSELOOK_ROTATION_SPEED: f32 = 0.01;
const CAMERA_VELOCITY: f32 = 0.03;
const CAMERA_VELOCITY: f32 = 60.0;
fn main() {
let options = Options::get();
@ -65,7 +65,7 @@ fn main() {
let (drawable_width, drawable_height) = window.drawable_size();
let mut renderer = Renderer::new(&Size2D::new(drawable_width, drawable_height));
let mut camera_position = Point4DF32::new(1.1, 1.0, 3.0, 1.0);
let mut camera_position = Point4DF32::new(500.0, 500.0, 3000.0, 1.0);
let mut camera_velocity = Point4DF32::new(0.0, 0.0, 0.0, 1.0);
let (mut camera_yaw, mut camera_pitch) = (0.0, 0.0);
@ -87,7 +87,11 @@ fn main() {
camera_position = camera_position + rotation.transform_point(camera_velocity);
let mut transform =
Transform3DF32::from_perspective(FRAC_PI_4, 4.0 / 3.0, 0.0001, 100.0);
Transform3DF32::from_perspective(FRAC_PI_4, 4.0 / 3.0, 0.025, 100.0);
transform = transform.post_mul(&Transform3DF32::from_scale(1.0 / 800.0,
1.0 / 800.0,
1.0 / 800.0));
transform = transform.post_mul(&Transform3DF32::from_rotation(camera_yaw,
camera_pitch,
0.0));
@ -95,8 +99,6 @@ fn main() {
transform.post_mul(&Transform3DF32::from_translation(-camera_position.x(),
-camera_position.y(),
-camera_position.z()));
transform =
transform.post_mul(&Transform3DF32::from_scale(1.0 / 800.0, 1.0 / 800.0, 1.0));
let perspective = Perspective::new(&transform, &window_size);

View File

@ -10,7 +10,7 @@
use crate::line_segment::LineSegmentF32;
use crate::outline::{Contour, PointFlags};
use crate::point::{Point2DF32, Point3DF32};
use crate::point::{Point2DF32, Point4DF32};
use crate::segment::{CubicSegment, Segment};
use crate::util::lerp;
use arrayvec::ArrayVec;
@ -268,18 +268,6 @@ impl ContourClipper {
prev = next;
}
/*
let top = Point2DF32::new(lerp(self.clip_rect.origin.x, self.clip_rect.max_x(), 0.5),
self.clip_rect.origin.y);
self.clip_against(Edge(LineSegmentF32::new(&Point2DF32::from_euclid(self.clip_rect
.bottom_left()),
&top)));
self.clip_against(Edge(LineSegmentF32::new(&top,
&Point2DF32::from_euclid(self.clip_rect
.bottom_right()))));
self.clip_against(Edge::bottom(&self.clip_rect));
*/
self.contour
}
@ -335,27 +323,12 @@ impl ContourClipper {
if let Some(last_position) = contour.last_position() {
if last_position != segment.baseline.from() {
// Add a line to join up segments.
//check_point(&segment.baseline.from(), edge);
contour.push_point(segment.baseline.from(), PointFlags::empty());
}
}
//check_point(&segment.baseline.to(), edge);
contour.push_segment(*segment);
}
/*
fn check_point(point: &Point2DF32, edge: Edge) {
match edge {
Edge::Left(x) if point.x() + 0.1 >= x => return,
Edge::Top(y) if point.y() + 0.1 >= y => return,
Edge::Right(x) if point.x() - 0.1 <= x => return,
Edge::Bottom(y) if point.y() - 0.1 <= y => return,
_ => {}
}
panic!("point {:?} outside edge {:?}", point, edge);
}
*/
}
}
@ -368,24 +341,31 @@ enum EdgeRelativeLocation {
// 3D quad clipping
pub struct PolygonClipper3D {
subject: Vec<Point3DF32>,
subject: Vec<Point4DF32>,
}
impl PolygonClipper3D {
#[inline]
pub fn new(subject: Vec<Point3DF32>) -> PolygonClipper3D {
pub fn new(subject: Vec<Point4DF32>) -> PolygonClipper3D {
PolygonClipper3D { subject }
}
pub fn clip(mut self) -> Vec<Point3DF32> {
pub fn clip(mut self) -> Vec<Point4DF32> {
// TODO(pcwalton): Fast path for completely contained polygon?
self.clip_against(Edge3D::Left);
self.clip_against(Edge3D::Right);
//println!("before clipping against bottom: {:?}", self.subject);
self.clip_against(Edge3D::Bottom);
//println!("before clipping against top: {:?}", self.subject);
self.clip_against(Edge3D::Top);
self.clip_against(Edge3D::Near);
//println!("before clipping against left: {:?}", self.subject);
self.clip_against(Edge3D::Left);
//println!("before clipping against right: {:?}", self.subject);
self.clip_against(Edge3D::Right);
//println!("before clipping against far: {:?}", self.subject);
self.clip_against(Edge3D::Far);
//println!("before clipping against near: {:?}", self.subject);
self.clip_against(Edge3D::Near);
//println!("after clipping: {:?}", self.subject);
self.subject
}
@ -422,24 +402,28 @@ enum Edge3D {
impl Edge3D {
#[inline]
fn point_is_inside(self, point: Point3DF32) -> bool {
fn point_is_inside(self, point: Point4DF32) -> bool {
let w = point.w();
match self {
Edge3D::Left => point.x() >= -1.0, Edge3D::Right => point.x() <= 1.0,
Edge3D::Bottom => point.y() >= -1.0, Edge3D::Top => point.y() <= 1.0,
Edge3D::Near => point.z() >= -1.0, Edge3D::Far => point.z() <= 1.0,
Edge3D::Left => point.x() >= -w, Edge3D::Right => point.x() <= w,
Edge3D::Bottom => point.y() >= -w, Edge3D::Top => point.y() <= w,
Edge3D::Near => point.z() >= -w, Edge3D::Far => point.z() <= w,
}
}
fn line_intersection(self, prev: Point3DF32, next: Point3DF32) -> Point3DF32 {
// Blinn & Newell, "Clipping using homogeneous coordinates", SIGGRAPH 1978.
fn line_intersection(self, prev: Point4DF32, next: Point4DF32) -> Point4DF32 {
let (x0, x1) = match self {
Edge3D::Left | Edge3D::Right => (prev.x(), next.x()),
Edge3D::Bottom | Edge3D::Top => (prev.y(), next.y()),
Edge3D::Near | Edge3D::Far => (prev.z(), next.z()),
};
let x = match self {
let (w0, w1) = (prev.w(), next.w());
let sign = match self {
Edge3D::Left | Edge3D::Bottom | Edge3D::Near => -1.0,
Edge3D::Right | Edge3D::Top | Edge3D::Far => 1.0,
};
prev.lerp(next, (x - x0) / (x1 - x0))
let alpha = ((x0 - sign * w0) as f64) / ((sign * (w1 - w0) - (x1 - x0)) as f64);
prev.lerp(next, alpha as f32)
}
}

View File

@ -179,6 +179,11 @@ impl Point4DF32 {
Point4DF32(F32x4::new(x, y, z, w))
}
#[inline]
pub fn from_euclid_2d(point: &Point2D<f32>) -> Point4DF32 {
Point4DF32::new(point.x, point.y, 0.0, 1.0)
}
#[inline]
pub fn splat(value: f32) -> Point4DF32 {
Point4DF32(F32x4::splat(value))
@ -247,6 +252,11 @@ impl Point4DF32 {
pub fn is_zero(self) -> bool {
self.x() == 0.0 && self.y() == 0.0 && self.z() == 0.0
}
#[inline]
pub fn lerp(self, other: Point4DF32, t: f32) -> Point4DF32 {
Point4DF32(self.0 + (other.0 - self.0) * F32x4::splat(t))
}
}
impl Add<Point4DF32> for Point4DF32 {

View File

@ -181,7 +181,8 @@ impl Transform3DF32 {
#[inline]
pub fn transform_point_3d(&self, point: Point3DF32) -> Point3DF32 {
self.transform_point(point.to_4d()).perspective_divide()
let point4d = self.transform_point(point.to_4d());
point4d.perspective_divide()
}
#[inline]

View File

@ -18,7 +18,7 @@ use euclid::Rect;
use hashbrown::HashMap;
use pathfinder_geometry::clip::PolygonClipper3D;
use pathfinder_geometry::outline::Outline;
use pathfinder_geometry::point::{Point2DF32, Point3DF32};
use pathfinder_geometry::point::{Point2DF32, Point3DF32, Point4DF32};
use pathfinder_geometry::transform3d::Perspective;
use pathfinder_geometry::transform::Transform2DF32;
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator};
@ -170,19 +170,20 @@ impl Scene {
fn clip_bounding_quad_with_perspective(&self, perspective: &Perspective) -> Vec<Point3DF32> {
let mut points = vec![
Point3DF32::from_euclid_2d(&self.bounds.origin),
Point3DF32::from_euclid_2d(&self.bounds.top_right()),
Point3DF32::from_euclid_2d(&self.bounds.bottom_right()),
Point3DF32::from_euclid_2d(&self.bounds.bottom_left()),
Point4DF32::from_euclid_2d(&self.bounds.origin),
Point4DF32::from_euclid_2d(&self.bounds.top_right()),
Point4DF32::from_euclid_2d(&self.bounds.bottom_right()),
Point4DF32::from_euclid_2d(&self.bounds.bottom_left()),
];
//println!("-----");
//println!("bounds={:?} ORIGINAL quad={:?}", self.bounds, points);
for point in &mut points {
*point = perspective.transform.transform_point_3d(*point);
*point = perspective.transform.transform_point(*point);
}
//println!("PERSPECTIVE quad={:?}", points);
//points
PolygonClipper3D::new(points).clip()
//println!("... PERSPECTIVE quad={:?}", points);
points = PolygonClipper3D::new(points).clip();
//println!("... CLIPPED quad={:?}", points);
points.into_iter().map(|point| point.perspective_divide()).collect()
}
}