Add generic function for creating grid
This commit is contained in:
parent
5db23fca50
commit
d732ff8a0f
5 changed files with 108 additions and 260 deletions
BIN
raytrace.png
BIN
raytrace.png
Binary file not shown.
Before Width: | Height: | Size: 5.1 KiB |
207
src/main.rs
207
src/main.rs
|
@ -1,56 +1,50 @@
|
|||
use cgmath::{vec3, Vector3};
|
||||
use cgmath::{vec2, vec3, Vector2, Vector3, Zero};
|
||||
|
||||
use image::ImageBuffer;
|
||||
use utilities::prelude::*;
|
||||
|
||||
use std::f32::{MAX, MIN};
|
||||
|
||||
mod triangle;
|
||||
use triangle::Triangle;
|
||||
|
||||
mod aabb;
|
||||
use aabb::AABB;
|
||||
|
||||
mod ray;
|
||||
use ray::Ray;
|
||||
|
||||
mod view;
|
||||
use view::View;
|
||||
|
||||
mod camera;
|
||||
mod ray;
|
||||
mod triangle;
|
||||
|
||||
use aabb::AABB;
|
||||
use camera::Camera;
|
||||
use ray::Ray;
|
||||
use triangle::{Triangle, Vertex};
|
||||
|
||||
fn generate_grid(width: u32, height: u32) -> Vec<Triangle> {
|
||||
let mut grid = Vec::new();
|
||||
|
||||
for y in 0..height {
|
||||
for x in 0..width {
|
||||
let color = vec3(1.0, 0.0, 0.0);
|
||||
|
||||
grid.push(Triangle::new(
|
||||
Vertex::new(vec3(x as f32, y as f32, 0.0), vec2(0.0, 0.0)),
|
||||
Vertex::new(vec3(x as f32 + 1.0, y as f32, 0.0), vec2(1.0, 0.0)),
|
||||
Vertex::new(vec3(x as f32 + 1.0, y as f32 + 1.0, 0.0), vec2(1.0, 1.0)),
|
||||
color,
|
||||
));
|
||||
|
||||
grid.push(Triangle::new(
|
||||
Vertex::new(vec3(x as f32 + 1.0, y as f32 + 1.0, 0.0), vec2(1.0, 1.0)),
|
||||
Vertex::new(vec3(x as f32, y as f32 + 1.0, 0.0), vec2(0.0, 1.0)),
|
||||
Vertex::new(vec3(x as f32, y as f32, 0.0), vec2(0.0, 0.0)),
|
||||
color,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
grid
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let input_data = [
|
||||
Triangle::new(
|
||||
vec3(0.0, 0.0, 0.0),
|
||||
vec3(1.0, 0.0, 0.0),
|
||||
vec3(1.0, 1.0, 0.0),
|
||||
),
|
||||
Triangle::new(
|
||||
vec3(1.0, 1.0, 0.0),
|
||||
vec3(0.0, 1.0, 0.0),
|
||||
vec3(0.0, 0.0, 0.0),
|
||||
),
|
||||
Triangle::new(
|
||||
vec3(1.0, 1.0, 0.0),
|
||||
vec3(2.0, 1.0, 0.0),
|
||||
vec3(2.0, 2.0, 0.0),
|
||||
),
|
||||
Triangle::new(
|
||||
vec3(2.0, 2.0, 0.0),
|
||||
vec3(1.0, 2.0, 0.0),
|
||||
vec3(1.0, 1.0, 0.0),
|
||||
),
|
||||
];
|
||||
|
||||
let view = View {
|
||||
position: vec3(0.0, -4.0, 4.0),
|
||||
look_at: vec3(0.0, 0.0, 0.0),
|
||||
up: vec3(0.0, 0.0, 1.0),
|
||||
fov: 45.0,
|
||||
};
|
||||
let input_data = generate_grid(3, 3);
|
||||
|
||||
/*
|
||||
let camera = Camera::new(
|
||||
vec3(0.0, -4.0, 4.0),
|
||||
vec3(0.0, 0.0, 0.0),
|
||||
|
@ -58,10 +52,16 @@ fn main() {
|
|||
45.0,
|
||||
1280.0 / 720.0,
|
||||
);
|
||||
*/
|
||||
|
||||
let _debug_ray = camera.primary_ray(640, 360, 1280, 720);
|
||||
let camera = Camera::new(
|
||||
vec3(0.0, 0.0, 3.0),
|
||||
vec3(0.0, 0.0, 0.0),
|
||||
vec3(0.0, 1.0, 1.0),
|
||||
45.0,
|
||||
1280.0 / 720.0,
|
||||
);
|
||||
|
||||
//debug_raytracer_view(1280, 720, &view, &input_data);
|
||||
debug_raytracer_camera(1280, 720, &camera, &input_data);
|
||||
}
|
||||
|
||||
|
@ -69,67 +69,13 @@ fn f_to_u(color: f32) -> u8 {
|
|||
((color * 255.0) / 1.0) as u8
|
||||
}
|
||||
|
||||
/*
|
||||
fn calculate_ray(x: u32, y: u32, dim_x: u32, dim_y: u32, view: &View) -> Ray {
|
||||
let aspect_ratio = dim_x as f32 / dim_y as f32;
|
||||
|
||||
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;
|
||||
|
||||
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 test = look_at_test(view.position, 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(p_x, p_y, -1.0, 1.0);
|
||||
|
||||
Ray::new(origin.truncate(), -direction.truncate().normalize())
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
fn look_at_test(eye: Vector3<f32>, center: Vector3<f32>, up: Vector3<f32>) -> Matrix4<f32> {
|
||||
let mut look_at_matrix = Matrix4::identity();
|
||||
|
||||
let forward = (eye - center).normalize();
|
||||
let right = up.cross(forward).normalize();
|
||||
let up = forward.cross(right);
|
||||
|
||||
look_at_matrix[0][0] = right.x;
|
||||
look_at_matrix[0][1] = right.y;
|
||||
look_at_matrix[0][2] = right.z;
|
||||
|
||||
look_at_matrix[1][0] = up.x;
|
||||
look_at_matrix[1][1] = up.y;
|
||||
look_at_matrix[1][2] = up.z;
|
||||
|
||||
look_at_matrix[2][0] = forward.x;
|
||||
look_at_matrix[2][1] = forward.y;
|
||||
look_at_matrix[2][2] = forward.z;
|
||||
|
||||
look_at_matrix[3][0] = eye.x;
|
||||
look_at_matrix[3][1] = eye.y;
|
||||
look_at_matrix[3][2] = eye.z;
|
||||
|
||||
look_at_matrix
|
||||
}
|
||||
*/
|
||||
|
||||
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);
|
||||
let acceleration_data = create_acceleration_data(data, 8);
|
||||
|
||||
for (x, y, pixel) in imgbuf.enumerate_pixels_mut() {
|
||||
let ray = camera.primary_ray(x, y, dim_x, dim_y);
|
||||
let ray = camera.primary_ray(x, dim_y - y, dim_x, dim_y);
|
||||
|
||||
let color = pixel_color(&ray, data, &acceleration_data);
|
||||
|
||||
|
@ -139,26 +85,10 @@ fn debug_raytracer_camera(dim_x: u32, dim_y: u32, camera: &Camera, data: &[Trian
|
|||
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);
|
||||
|
||||
for (x, y, pixel) in imgbuf.enumerate_pixels_mut() {
|
||||
let ray = Ray::primary_ray(x as f32, y as f32, dim_x as f32, dim_y as f32, view);
|
||||
|
||||
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 create_acceleration_data(input_data: &[Triangle]) -> Vec<AABB> {
|
||||
fn create_acceleration_data(input_data: &[Triangle], triangles_per_as: u32) -> Vec<AABB> {
|
||||
let mut acceleration_data = Vec::new();
|
||||
|
||||
let accel_count = (input_data.len() as f32 / 24.0).ceil() as usize;
|
||||
let accel_count = (input_data.len() as f32 / triangles_per_as as f32).ceil() as usize;
|
||||
|
||||
for i in 0..accel_count {
|
||||
let mut accel = AABB::new();
|
||||
|
@ -168,8 +98,8 @@ fn create_acceleration_data(input_data: &[Triangle]) -> Vec<AABB> {
|
|||
|
||||
// 3 vertices per triangles
|
||||
// 8 triangles per acceleration structure
|
||||
let start_index = 8 * i;
|
||||
let mut end_index = 8 * (i + 1);
|
||||
let start_index = triangles_per_as as usize * i;
|
||||
let mut end_index = triangles_per_as as usize * (i + 1);
|
||||
|
||||
// check if end_index exceeds input data length
|
||||
if end_index > input_data.len() {
|
||||
|
@ -182,16 +112,16 @@ fn create_acceleration_data(input_data: &[Triangle]) -> Vec<AABB> {
|
|||
|
||||
// create bounding box
|
||||
for k in start_index..end_index {
|
||||
for vertex in input_data[k].points.iter() {
|
||||
for vertex in input_data[k].vertices.iter() {
|
||||
// check minimum values
|
||||
bbmin.x = bbmin.x.min(vertex.x);
|
||||
bbmin.y = bbmin.y.min(vertex.y);
|
||||
bbmin.z = bbmin.z.min(vertex.z);
|
||||
bbmin.x = bbmin.x.min(vertex.position.x);
|
||||
bbmin.y = bbmin.y.min(vertex.position.y);
|
||||
bbmin.z = bbmin.z.min(vertex.position.z);
|
||||
|
||||
// check maximum values
|
||||
bbmax.x = bbmax.x.max(vertex.x);
|
||||
bbmax.y = bbmax.y.max(vertex.y);
|
||||
bbmax.z = bbmax.z.max(vertex.z);
|
||||
bbmax.x = bbmax.x.max(vertex.position.x);
|
||||
bbmax.y = bbmax.y.max(vertex.position.y);
|
||||
bbmax.z = bbmax.z.max(vertex.position.z);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,16 +138,19 @@ fn pixel_color(ray: &Ray, data: &[Triangle], acceleration_data: &Vec<AABB>) -> V
|
|||
let mut final_color = vec3(0.0, 1.0, 0.0);
|
||||
let mut closest_value = MAX;
|
||||
let mut closest_index = -1;
|
||||
let mut barycentric = Vector2::zero();
|
||||
|
||||
for accel in acceleration_data.iter() {
|
||||
if accel.intersect(ray) {
|
||||
for i in accel.start_index..accel.end_index {
|
||||
let mut t = 0.0;
|
||||
let mut tmp_bary = Vector2::zero();
|
||||
|
||||
if data[i].intersect_mt(ray, &mut t) {
|
||||
if data[i].intersect_mt(ray, &mut t, &mut tmp_bary) {
|
||||
if t < closest_value {
|
||||
closest_index = i as i32;
|
||||
closest_value = t;
|
||||
barycentric = tmp_bary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -225,8 +158,26 @@ fn pixel_color(ray: &Ray, data: &[Triangle], acceleration_data: &Vec<AABB>) -> V
|
|||
}
|
||||
|
||||
if closest_index != -1 {
|
||||
final_color = vec3(1.0, 0.0, 0.0);
|
||||
let i = closest_index;
|
||||
|
||||
let triangle = &data[i as usize];
|
||||
|
||||
let wuv = vec3(
|
||||
1.0 - barycentric.x - barycentric.y,
|
||||
barycentric.x,
|
||||
barycentric.y,
|
||||
);
|
||||
|
||||
let st = point_st(&triangle, &wuv);
|
||||
|
||||
final_color = triangle.color;
|
||||
}
|
||||
|
||||
return final_color;
|
||||
}
|
||||
|
||||
fn point_st(triangle: &Triangle, wuv: &Vector3<f32>) -> Vector2<f32> {
|
||||
return wuv.x * triangle.vertices[0].texture_coordinate
|
||||
+ wuv.y * triangle.vertices[1].texture_coordinate
|
||||
+ wuv.z * triangle.vertices[2].texture_coordinate;
|
||||
}
|
||||
|
|
93
src/ray.rs
93
src/ray.rs
|
@ -1,10 +1,4 @@
|
|||
use cgmath::{
|
||||
vec2, vec3, vec4, EuclideanSpace, InnerSpace, Matrix4, Point3, SquareMatrix, Vector2, Vector3,
|
||||
};
|
||||
|
||||
use super::view::View;
|
||||
|
||||
use std::f32::consts::PI as M_PI;
|
||||
use cgmath::Vector3;
|
||||
|
||||
pub struct Ray {
|
||||
pub origin: Vector3<f32>,
|
||||
|
@ -28,89 +22,4 @@ impl Ray {
|
|||
signs: Vector3::new(x, y, z),
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
let (up, right) = view.axises();
|
||||
|
||||
let trans = 2.0 * uv - vec2(1.0, 1.0);
|
||||
let raw_dir = view.look_at + right * trans.x + up * trans.y;
|
||||
|
||||
let dir = vec3(
|
||||
raw_dir.x * aspect_ratio,
|
||||
raw_dir.y,
|
||||
raw_dir.z * aspect_ratio,
|
||||
)
|
||||
.normalize();
|
||||
*/
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,22 +1,38 @@
|
|||
use cgmath::{InnerSpace, Vector3};
|
||||
use cgmath::{InnerSpace, Vector2, Vector3};
|
||||
|
||||
use super::ray::Ray;
|
||||
|
||||
pub struct Vertex {
|
||||
pub position: Vector3<f32>,
|
||||
pub texture_coordinate: Vector2<f32>,
|
||||
}
|
||||
|
||||
impl Vertex {
|
||||
pub fn new(position: Vector3<f32>, texture_coordinate: Vector2<f32>) -> Vertex {
|
||||
Vertex {
|
||||
position,
|
||||
texture_coordinate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Triangle {
|
||||
pub points: [Vector3<f32>; 3],
|
||||
pub vertices: [Vertex; 3],
|
||||
pub color: Vector3<f32>,
|
||||
}
|
||||
|
||||
impl Triangle {
|
||||
pub fn new(v0: Vector3<f32>, v1: Vector3<f32>, v2: Vector3<f32>) -> Triangle {
|
||||
pub fn new(v0: Vertex, v1: Vertex, v2: Vertex, color: Vector3<f32>) -> Triangle {
|
||||
Triangle {
|
||||
points: [v0, v1, v2],
|
||||
vertices: [v0, v1, v2],
|
||||
color,
|
||||
}
|
||||
}
|
||||
|
||||
// source: https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/moller-trumbore-ray-triangle-intersection
|
||||
pub fn intersect_mt(&self, ray: &Ray, t: &mut f32) -> bool {
|
||||
let v0v1 = self.points[1] - self.points[0];
|
||||
let v0v2 = self.points[2] - self.points[0];
|
||||
pub fn intersect_mt(&self, ray: &Ray, t: &mut f32, barycentric: &mut Vector2<f32>) -> bool {
|
||||
let v0v1 = self.vertices[1].position - self.vertices[0].position;
|
||||
let v0v2 = self.vertices[2].position - self.vertices[0].position;
|
||||
let pvec = ray.direction.cross(v0v2);
|
||||
let det = v0v1.dot(pvec);
|
||||
|
||||
|
@ -27,17 +43,17 @@ impl Triangle {
|
|||
|
||||
let inv_det = 1.0 / det;
|
||||
|
||||
let tvec = ray.origin - self.points[0];
|
||||
let u = tvec.dot(pvec) * inv_det;
|
||||
let tvec = ray.origin - self.vertices[0].position;
|
||||
barycentric.x = tvec.dot(pvec) * inv_det;
|
||||
|
||||
if u < 0.0 || u > 1.0 {
|
||||
if barycentric.x < 0.0 || barycentric.x > 1.0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let qvec = tvec.cross(v0v1);
|
||||
let v = ray.direction.dot(qvec) * inv_det;
|
||||
barycentric.y = ray.direction.dot(qvec) * inv_det;
|
||||
|
||||
if v < 0.0 || u + v > 1.0 {
|
||||
if barycentric.y < 0.0 || barycentric.x + barycentric.y > 1.0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
28
src/view.rs
28
src/view.rs
|
@ -1,28 +0,0 @@
|
|||
use cgmath::{InnerSpace, Vector3};
|
||||
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct View {
|
||||
pub position: Vector3<f32>,
|
||||
pub look_at: Vector3<f32>,
|
||||
pub up: Vector3<f32>,
|
||||
pub fov: f32,
|
||||
}
|
||||
|
||||
impl View {
|
||||
/// returns `up` and `right`
|
||||
pub fn axises(&self) -> (Vector3<f32>, Vector3<f32>) {
|
||||
let view_dir = self.look_at - self.position;
|
||||
|
||||
let horiz_axis = view_dir.cross(self.up).normalize();
|
||||
let vert_axis = horiz_axis.cross(view_dir).normalize();
|
||||
let right = horiz_axis * Self::scale(self.fov);
|
||||
let up = vert_axis * Self::scale(-self.fov);
|
||||
|
||||
(up, right)
|
||||
}
|
||||
|
||||
fn scale(fov: f32) -> f32 {
|
||||
(0.5 * fov * PI / 180.0).tan()
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue