Create correct primary rays

This commit is contained in:
hodasemi 2019-03-21 07:40:30 +01:00
parent 8f74621937
commit 5db23fca50
5 changed files with 168 additions and 5 deletions

3
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"rust-analyzer.enableCargoWatchOnStartup": true
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

63
src/camera.rs Normal file
View file

@ -0,0 +1,63 @@
use cgmath::{InnerSpace, Vector3};
use super::ray::Ray;
pub struct Camera {
position: Vector3<f32>,
direction: Vector3<f32>,
up: Vector3<f32>,
right: Vector3<f32>,
_fov: f32,
tan: f32,
aspect_ratio: f32,
}
impl Camera {
pub fn new(
eye: Vector3<f32>,
center: Vector3<f32>,
up: Vector3<f32>,
fov: f32,
aspect_ratio: f32,
) -> Camera {
let mut up = up.normalize();
let view = (center - eye).normalize();
let right = view.cross(up).normalize();
up = right.cross(view);
let fovy = fov.to_radians() / 2.0;
Camera {
position: eye,
direction: view,
up,
right,
_fov: fovy,
tan: fovy.tan(),
aspect_ratio,
}
}
pub fn primary_ray(&self, x: u32, y: u32, dim_x: u32, dim_y: u32) -> Ray {
let x_scaled = Self::scale(x, dim_x);
let y_scaled = Self::scale(y, dim_y);
let mut dir = self.direction;
dir += self.right * self.tan * self.aspect_ratio * x_scaled;
dir += self.up * self.tan * y_scaled;
dir = dir.normalize();
Ray::new(self.position, dir)
}
fn scale(v: u32, s: u32) -> f32 {
((v as f32 + 0.5) / s as f32) * 2.0 - 1.0
}
}

View file

@ -17,6 +17,9 @@ use ray::Ray;
mod view;
use view::View;
mod camera;
use camera::Camera;
fn main() {
let input_data = [
Triangle::new(
@ -48,9 +51,18 @@ fn main() {
fov: 45.0,
};
let debug_ray = Ray::primary_ray(640.0, 360.0, 1280.0, 720.0, &view);
let camera = Camera::new(
vec3(0.0, -4.0, 4.0),
vec3(0.0, 0.0, 0.0),
vec3(0.0, 0.0, 1.0),
45.0,
1280.0 / 720.0,
);
debug_raytracer(1280, 720, &view, &input_data);
let _debug_ray = camera.primary_ray(640, 360, 1280, 720);
//debug_raytracer_view(1280, 720, &view, &input_data);
debug_raytracer_camera(1280, 720, &camera, &input_data);
}
fn f_to_u(color: f32) -> u8 {
@ -111,7 +123,23 @@ fn look_at_test(eye: Vector3<f32>, center: Vector3<f32>, up: Vector3<f32>) -> Ma
}
*/
fn debug_raytracer(dim_x: u32, dim_y: u32, view: &View, data: &[Triangle]) {
fn debug_raytracer_camera(dim_x: u32, dim_y: u32, camera: &Camera, data: &[Triangle]) {
let mut imgbuf = ImageBuffer::new(dim_x, dim_y);
let acceleration_data = create_acceleration_data(data);
for (x, y, pixel) in imgbuf.enumerate_pixels_mut() {
let ray = camera.primary_ray(x, y, dim_x, dim_y);
let color = pixel_color(&ray, data, &acceleration_data);
*pixel = image::Rgb([f_to_u(color.x), f_to_u(color.y), f_to_u(color.z)]);
}
imgbuf.save("raytrace.png").unwrap();
}
fn debug_raytracer_view(dim_x: u32, dim_y: u32, view: &View, data: &[Triangle]) {
let mut imgbuf = ImageBuffer::new(dim_x, dim_y);
let acceleration_data = create_acceleration_data(data);

View file

@ -1,7 +1,11 @@
use cgmath::{vec2, vec3, InnerSpace, Vector3};
use cgmath::{
vec2, vec3, vec4, EuclideanSpace, InnerSpace, Matrix4, Point3, SquareMatrix, Vector2, Vector3,
};
use super::view::View;
use std::f32::consts::PI as M_PI;
pub struct Ray {
pub origin: Vector3<f32>,
pub direction: Vector3<f32>,
@ -26,6 +30,7 @@ impl Ray {
}
pub fn primary_ray(x: f32, y: f32, dim_x: f32, dim_y: f32, view: &View) -> Ray {
/*
let aspect_ratio = dim_x / dim_y;
let uv = vec2((x + 0.5) / dim_x, (y + 0.5) / dim_y);
@ -41,7 +46,71 @@ impl Ray {
raw_dir.z * aspect_ratio,
)
.normalize();
*/
return Self::new(view.position, dir);
let aspect_ratio = dim_x / dim_y;
let fov = view.fov / M_PI * 2.0 / 180.0;
let p_x = (1.0 - 2.0 * ((x as f32 + 0.5) / dim_x as f32)) * fov * aspect_ratio;
let p_y = (2.0 * (((y) as f32 + 0.5) / dim_y as f32) - 1.0) * fov;
return Self::cast_ray(vec2(p_x, p_y), view);
}
pub fn cast_ray(uv: Vector2<f32>, view: &View) -> Ray {
let look_at = Matrix4::look_at(
Point3::from_vec(view.position),
Point3::from_vec(view.look_at),
vec3(0.0, 0.0, 1.0),
);
let camera_to_world = look_at.invert().unwrap();
let origin = camera_to_world * vec4(0.0, 0.0, 0.0, 1.0);
let direction = camera_to_world * vec4(uv.x, uv.y, -1.0, 1.0);
Ray::new(origin.truncate(), -direction.truncate().normalize())
}
#[inline]
fn perspective(fov_y: f32, aspect: f32, z_near: f32, z_far: f32) -> cgmath::Matrix4<f32> {
let zero = 0.0;
let one = 1.0;
let two = 2.0;
let q = one / (fov_y / two).tan();
let a = q / aspect;
let b = (z_near + z_far) / (z_near - z_far);
let c = (two * z_near * z_far) / (z_near - z_far);
cgmath::Matrix4::new(
a,
zero,
zero,
zero,
zero,
-q,
zero,
zero,
zero,
zero,
b,
zero - one,
zero,
zero,
c,
zero,
)
}
}
#[test]
fn text_ray_casting() {
let center = vec3(0.0, 0.0, 0.0);
let view = View {
position: vec3(0.0, -4.0, 4.0),
look_at: center,
up: vec3(0.0, 0.0, 1.0),
fov: 45.0,
};
}