middle of attempting to fix rotations

This commit is contained in:
CDaut 2023-11-22 11:52:04 +01:00
parent 6cbd93aad0
commit 8657806aba
10 changed files with 692 additions and 78 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Before After
Before After

BIN
scenes/cube_on_plane.bin Normal file

Binary file not shown.

Binary file not shown.

BIN
scenes/cube_on_plane.blend1 Normal file

Binary file not shown.

Binary file not shown.

538
scenes/cube_on_plane.gltf Normal file
View file

@ -0,0 +1,538 @@
{
"asset":{
"generator":"Khronos glTF Blender I/O v4.1.34",
"version":"2.0"
},
"extensionsUsed":[
"KHR_lights_punctual"
],
"extensionsRequired":[
"KHR_lights_punctual"
],
"extensions":{
"KHR_lights_punctual":{
"lights":[
{
"color":[
1,
1,
1
],
"intensity":54351.41306588226,
"type":"point",
"name":"Light"
}
]
}
},
"scene":0,
"scenes":[
{
"name":"Scene",
"nodes":[
0,
1,
2,
3,
4,
5,
6
]
}
],
"nodes":[
{
"mesh":0,
"name":"Cube"
},
{
"extensions":{
"KHR_lights_punctual":{
"light":0
}
},
"name":"Light",
"rotation":[
-0.28416627645492554,
0.7269423007965088,
0.34203392267227173,
0.5232754945755005
],
"translation":[
4.076245307922363,
5.903861999511719,
-1.0054539442062378
]
},
{
"camera":0,
"name":"Camera",
"rotation":[
0,
1,
0,
0
],
"translation":[
2,
10,
-1.9999992847442627
]
},
{
"mesh":1,
"name":"Cube.001"
},
{
"mesh":2,
"name":"Cube.002",
"translation":[
2,
10,
-10
]
},
{
"mesh":3,
"name":"Cube.003",
"rotation":[
0,
0.3826834559440613,
0,
0.9238795638084412
],
"translation":[
2,
10,
10
]
},
{
"mesh":4,
"name":"Suzanne",
"translation":[
-10,
10,
0
]
}
],
"cameras":[
{
"name":"Camera",
"perspective":{
"aspectRatio":1,
"yfov":0.7853981852531433,
"zfar":100,
"znear":0.0010000000474974513
},
"type":"perspective"
}
],
"materials":[
{
"doubleSided":true,
"name":"Material",
"pbrMetallicRoughness":{
"baseColorFactor":[
0.800000011920929,
0.800000011920929,
0.800000011920929,
1
],
"metallicFactor":0,
"roughnessFactor":0.5
}
},
{
"doubleSided":true,
"name":"Material.001",
"pbrMetallicRoughness":{
"baseColorFactor":[
0.8002041578292847,
0,
0,
1
],
"metallicFactor":0,
"roughnessFactor":0.5
}
},
{
"doubleSided":true,
"name":"Material.003",
"pbrMetallicRoughness":{
"baseColorFactor":[
0.019036345183849335,
0,
0.8002663850784302,
1
],
"metallicFactor":0,
"roughnessFactor":0.5
}
},
{
"doubleSided":true,
"name":"Material.002",
"pbrMetallicRoughness":{
"baseColorFactor":[
0.008347976952791214,
0.8001724481582642,
0,
1
],
"metallicFactor":0,
"roughnessFactor":0.5
}
}
],
"meshes":[
{
"name":"Cube",
"primitives":[
{
"attributes":{
"POSITION":0,
"NORMAL":1,
"TEXCOORD_0":2
},
"indices":3,
"material":0
}
]
},
{
"name":"Cube.001",
"primitives":[
{
"attributes":{
"POSITION":4,
"NORMAL":5,
"TEXCOORD_0":6
},
"indices":3,
"material":0
}
]
},
{
"name":"Cube.002",
"primitives":[
{
"attributes":{
"POSITION":7,
"NORMAL":8,
"TEXCOORD_0":9
},
"indices":10,
"material":1
}
]
},
{
"name":"Cube.003",
"primitives":[
{
"attributes":{
"POSITION":11,
"NORMAL":12,
"TEXCOORD_0":13
},
"indices":10,
"material":2
}
]
},
{
"name":"Suzanne",
"primitives":[
{
"attributes":{
"POSITION":14,
"NORMAL":15,
"TEXCOORD_0":16
},
"indices":17,
"material":3
}
]
}
],
"accessors":[
{
"bufferView":0,
"componentType":5126,
"count":14,
"max":[
1.4142135381698608,
2,
1.4142135381698608
],
"min":[
-1.4142135381698608,
0,
-1.4142135381698608
],
"type":"VEC3"
},
{
"bufferView":1,
"componentType":5126,
"count":14,
"type":"VEC3"
},
{
"bufferView":2,
"componentType":5126,
"count":14,
"type":"VEC2"
},
{
"bufferView":3,
"componentType":5123,
"count":36,
"type":"SCALAR"
},
{
"bufferView":4,
"componentType":5126,
"count":14,
"max":[
-0.5857864618301392,
2,
-0.5857864618301392
],
"min":[
-3.4142136573791504,
0,
-3.4142136573791504
],
"type":"VEC3"
},
{
"bufferView":5,
"componentType":5126,
"count":14,
"type":"VEC3"
},
{
"bufferView":6,
"componentType":5126,
"count":14,
"type":"VEC2"
},
{
"bufferView":7,
"componentType":5126,
"count":14,
"max":[
1,
1,
1
],
"min":[
-1,
-1,
-1
],
"type":"VEC3"
},
{
"bufferView":8,
"componentType":5126,
"count":14,
"type":"VEC3"
},
{
"bufferView":9,
"componentType":5126,
"count":14,
"type":"VEC2"
},
{
"bufferView":10,
"componentType":5123,
"count":36,
"type":"SCALAR"
},
{
"bufferView":11,
"componentType":5126,
"count":14,
"max":[
1,
1,
1
],
"min":[
-1,
-1,
-1
],
"type":"VEC3"
},
{
"bufferView":12,
"componentType":5126,
"count":14,
"type":"VEC3"
},
{
"bufferView":13,
"componentType":5126,
"count":14,
"type":"VEC2"
},
{
"bufferView":14,
"componentType":5126,
"count":555,
"max":[
1.3671875,
0.984375,
0.8515625
],
"min":[
-1.3671875,
-0.984375,
-0.8515625
],
"type":"VEC3"
},
{
"bufferView":15,
"componentType":5126,
"count":555,
"type":"VEC3"
},
{
"bufferView":16,
"componentType":5126,
"count":555,
"type":"VEC2"
},
{
"bufferView":17,
"componentType":5123,
"count":2904,
"type":"SCALAR"
}
],
"bufferViews":[
{
"buffer":0,
"byteLength":168,
"byteOffset":0,
"target":34962
},
{
"buffer":0,
"byteLength":168,
"byteOffset":168,
"target":34962
},
{
"buffer":0,
"byteLength":112,
"byteOffset":336,
"target":34962
},
{
"buffer":0,
"byteLength":72,
"byteOffset":448,
"target":34963
},
{
"buffer":0,
"byteLength":168,
"byteOffset":520,
"target":34962
},
{
"buffer":0,
"byteLength":168,
"byteOffset":688,
"target":34962
},
{
"buffer":0,
"byteLength":112,
"byteOffset":856,
"target":34962
},
{
"buffer":0,
"byteLength":168,
"byteOffset":968,
"target":34962
},
{
"buffer":0,
"byteLength":168,
"byteOffset":1136,
"target":34962
},
{
"buffer":0,
"byteLength":112,
"byteOffset":1304,
"target":34962
},
{
"buffer":0,
"byteLength":72,
"byteOffset":1416,
"target":34963
},
{
"buffer":0,
"byteLength":168,
"byteOffset":1488,
"target":34962
},
{
"buffer":0,
"byteLength":168,
"byteOffset":1656,
"target":34962
},
{
"buffer":0,
"byteLength":112,
"byteOffset":1824,
"target":34962
},
{
"buffer":0,
"byteLength":6660,
"byteOffset":1936,
"target":34962
},
{
"buffer":0,
"byteLength":6660,
"byteOffset":8596,
"target":34962
},
{
"buffer":0,
"byteLength":4440,
"byteOffset":15256,
"target":34962
},
{
"buffer":0,
"byteLength":5808,
"byteOffset":19696,
"target":34963
}
],
"buffers":[
{
"byteLength":25504,
"uri":"cube_on_plane.bin"
}
]
}

View file

@ -1,87 +1,69 @@
use cgmath::{Angle, InnerSpace, Matrix4, SquareMatrix, Vector3, Vector4};
use easy_gltf::{Camera, Projection};
use cgmath::{InnerSpace, Vector3};
use cgmath::num_traits::abs;
use easy_gltf::model::Triangle;
use crate::ray::Ray;
pub struct Ray {
source: Vector3<f32>,
direction: Vector3<f32>,
}
const EPSILON: f32 = 0.000001;
pub trait Intersectable {
/*
tests whether the ray intersects the Intersectable.
returns: The intersection point or empty if there is none
*/
fn test_isec(&self, ray: &Ray) -> Option<Vector3<f32>>;
fn test_isec(&self, ray: &Ray) -> Option<(f32, Vector3<f32>)>;
}
impl Intersectable for Triangle {
//perform muller trumbore intersection
fn test_isec(&self, ray: &Ray) -> Option<Vector3<f32>> {
//TODO: implement correct intersection here
if ray.direction.x > 0.5 &&
ray.direction.x < 0.6 &&
ray.direction.y > 0.1 &&
ray.direction.y < 0.6 {
return Some(Vector3::new(1.0, 1.0, 1.0));
fn test_isec(&self, ray: &Ray) -> Option<(f32, Vector3<f32>)> {
assert!(
abs(ray.direction.dot(ray.direction) - 1.0) < EPSILON,
"Ray direction not normalized"
);
//get triangle vertices
let p0 = self[0].position;
let p1 = self[1].position;
let p2 = self[2].position;
let edge1 = p1 - p0;
let edge2 = p2 - p0;
let ray_cross_e2 = ray.direction.cross(edge2);
let determinant = edge1.dot(ray_cross_e2);
//ray is parallel to triangle
if determinant > -EPSILON && determinant < EPSILON {
return None;
}
let inverse_determinant = 1.0 / determinant;
let s = ray.source - p0;
let u = s.dot(ray_cross_e2) * inverse_determinant;
//early out 2
if u < 0.0 || u > 1.0 {
return None;
}
let s_cross_e1 = s.cross(edge1);
let v = ray.direction.dot(s_cross_e1) * inverse_determinant;
//early out 3
if v < 0.0 || u + v > 1.0 {
return None;
}
//compute ray parameter t
let t = edge2.dot(s_cross_e1) * inverse_determinant;
if t > EPSILON {
return Option::from((t, ray.source + ray.direction * t));
}
// This means that there is a line intersection but not a ray intersection.
return None;
}
}
pub fn construct_primary_rays(camera: &Camera,
(width, height): (usize, usize),
(pixel_x_coord, pixel_y_coord): (usize, usize),
) -> Vec<Ray> {
//only allow perspective rendering
//TODO: ignoring aspect ratio here
let fovy = match camera.projection {
Projection::Perspective { yfov, aspect_ratio: _aspect_ratio } => { yfov }
Projection::Orthographic { .. } => { panic!("Orthographic rendering not supported.") }
};
//ray origin in world space
let origin_world_space = camera.position();
//seems to be the distance from the camera origin to the view plane
let z: f32 = height as f32 / fovy.tan();
//obtain the inverse transformation Matrix
let inverse_transform = camera.transform.invert().unwrap_or_else(||
panic!("Non invertible transform Matrix. giving up.")
);
//TODO: take ray multiplier per pixel into account here
let mut rays: Vec<Ray> = Vec::with_capacity(height * width);
//generate all rays for this pixel and add them to the rays vector
//TODO: use blue noise here to generate multiple rays per pixel
rays.push(generate_ray(
width,
height,
&inverse_transform,
z,
pixel_x_coord,
pixel_y_coord,
origin_world_space));
rays
}
fn generate_ray(image_width: usize,
image_height: usize,
inverse_transform: &Matrix4<f32>,
focal_length: f32,
u: usize,
v: usize,
ray_origin: Vector3<f32>) -> Ray {
//calculate the ray direction and translate it to world space
let direction_view_space: Vector4<f32> =
Vector4::new(u as f32 - (image_width as f32 / 2.0),
v as f32 - (image_height as f32 / 2.0),
-focal_length,
0.0);
let direction_world_space = inverse_transform * direction_view_space;
Ray { source: ray_origin, direction: direction_world_space.normalize().truncate() }
}

View file

@ -1,5 +1,6 @@
mod renderer;
mod geometry;
mod ray;
use std::string::String;
use std::sync::{Arc, Mutex};

69
src/ray.rs Normal file
View file

@ -0,0 +1,69 @@
use std::ops::Mul;
use cgmath::{Angle, InnerSpace, Matrix4, SquareMatrix, Vector3, Vector4};
use easy_gltf::{Camera, Projection};
pub struct Ray {
pub(crate) source: Vector3<f32>,
pub(crate) direction: Vector3<f32>,
}
pub fn construct_primary_rays(camera: &Camera,
(width, height): (usize, usize),
(pixel_x_coord, pixel_y_coord): (usize, usize),
) -> Vec<Ray> {
//only allow perspective rendering
//TODO: ignoring aspect ratio here
let (fovy, aspect_ratio) = match camera.projection {
Projection::Perspective { yfov, aspect_ratio } => {
(yfov, aspect_ratio) }
Projection::Orthographic { .. } => { panic!("Orthographic rendering not supported.") }
};
//ray origin in world space
let origin_world_space = camera.position();
//dbg!(camera.transform);
// the distance from the camera origin to the view plane
let z: f32 = height as f32 / (2.0 * fovy.mul(0.5).tan());
//obtain the inverse transformation Matrix
let inverse_transform = camera.transform.invert().unwrap_or_else(||
panic!("Non invertible transform Matrix. giving up.")
);
//TODO: take ray multiplier per pixel into account here
let mut rays: Vec<Ray> = Vec::with_capacity(height * width);
//generate all rays for this pixel and add them to the rays vector
//TODO: use blue noise here to generate multiple rays per pixel
rays.push(generate_single_primary_ray(
width,
height,
&inverse_transform,
z,
pixel_x_coord,
pixel_y_coord,
origin_world_space));
rays
}
fn generate_single_primary_ray(image_width: usize,
image_height: usize,
inverse_transform: &Matrix4<f32>,
focal_length: f32,
u: usize,
v: usize,
ray_origin: Vector3<f32>) -> Ray {
//calculate the ray direction and translate it to world space
let direction_view_space: Vector4<f32> =
Vector4::new(u as f32 - (image_width as f32 / 2.0),
v as f32 - (image_height as f32 / 2.0),
-focal_length,
0.0);
//TODO: Rotation is fucked
//x rotation has sign wrong, y and z are flipped
let direction_world_space = inverse_transform * direction_view_space.normalize();
Ray { source: ray_origin, direction: direction_world_space.truncate().normalize() }
}

View file

@ -1,11 +1,13 @@
use std::cmp::max;
use std::sync::{Arc, Mutex};
use cgmath::{Vector2, Vector3, Vector4, Zero};
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use easy_gltf::model::{Mode};
use easy_gltf::{Camera, Scene};
use image::{DynamicImage, GenericImage, Rgba};
use crate::Args;
use crate::geometry::{construct_primary_rays, Intersectable, Ray};
use crate::geometry::{Intersectable};
use crate::ray::{construct_primary_rays, Ray};
pub fn render(scenes: &Vec<Scene>,
cl_args: &Args,
@ -23,8 +25,8 @@ pub fn render(scenes: &Vec<Scene>,
(px, py),
);
//let the initial pixel color be white and opaque
let mut pixel_color: Rgba<u8> = Rgba([0, 0, 0, 255]);
//let the initial pixel color be black and transparent
let mut pixel_color: Rgba<u8> = Rgba::from([0, 0, 0, 255]);
//cast each ray and get the output color
//TODO: in the end we will want to average the colors out here
@ -45,7 +47,12 @@ pub fn render(scenes: &Vec<Scene>,
fn raytrace(ray: &Ray, scene: &Scene) -> Rgba<u8> {
let mut pixel_color: Rgba<u8> = Rgba([0, 0, 0, 255]);
let mut pixel_color: Rgba<u8> = Rgba::from([0, 0, 0, 255]);
let mut smallest_t: f32 = f32::MAX;
let mut clostest_intersection_point: Option<Vector3<f32>> = None;
let mut color_at_isec = [0, 0, 0, 0];
//test intersection with all models in the scene
//TODO: Improve, to avoid iterating all models
@ -64,12 +71,29 @@ fn raytrace(ray: &Ray, scene: &Scene) -> Rgba<u8> {
match triangle.test_isec(&ray) {
//boilerplate implementation to set pixel to red if any intersection happened
None => {}
Some(point) => {
pixel_color.0 = [255, 255, 255, 255];
return
Some((t, isec)) => {
// a new closer Point is found
if t < smallest_t {
smallest_t = t;
clostest_intersection_point = Option::from(isec);
let color_vec = model.material().get_base_color_alpha(
Vector2::new(0.0, 0.0)
).map(|comp| comp * 255.0);
color_at_isec = [color_vec[0] as u8, color_vec[1] as u8, color_vec[2] as u8, color_vec[3] as u8]
}
}
};
});
});
//make pixel opaque if intersection is found
match clostest_intersection_point {
Some(_) => {
pixel_color = Rgba::from(color_at_isec);
}
None {} => {}
}
pixel_color
}