Add generic function for creating grid

This commit is contained in:
hodasemi 2019-04-13 18:19:10 +02:00
parent 5db23fca50
commit d732ff8a0f
5 changed files with 108 additions and 260 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

View file

@ -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;
}

View file

@ -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,
};
}

View file

@ -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;
}

View file

@ -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()
}
}