|
|
|
@ -1,6 +1,7 @@
|
|
|
|
|
extern crate rand;
|
|
|
|
|
|
|
|
|
|
use std::cmp::max;
|
|
|
|
|
use std::cmp::min;
|
|
|
|
|
use std::f32::consts::PI;
|
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
use std::ops;
|
|
|
|
@ -38,6 +39,28 @@ struct Vector3f { x: f32, y: f32, z: f32 }
|
|
|
|
|
#[derive(Clone, Copy, Debug, Default)]
|
|
|
|
|
struct Vector4f { x: f32, y: f32, z: f32, w: f32 }
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, Default)]
|
|
|
|
|
struct Lighting
|
|
|
|
|
{
|
|
|
|
|
diffuse: Vector3f,
|
|
|
|
|
specular: Vector3f,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, Default)]
|
|
|
|
|
struct PointLight
|
|
|
|
|
{
|
|
|
|
|
position : Vector3f,
|
|
|
|
|
diffuseColor : Vector3f,
|
|
|
|
|
diffusePower : f32,
|
|
|
|
|
specularColor : Vector3f,
|
|
|
|
|
specularPower : f32,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn saturate(v: f32) -> f32 {
|
|
|
|
|
f32::min(f32::max(v, 0.0), 1.0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn dot(vec_a: Vector3f, vec_b: Vector3f) -> f32 {
|
|
|
|
|
vec_a.x * vec_b.x + vec_a.y * vec_b.y + vec_a.z * vec_b.z
|
|
|
|
|
}
|
|
|
|
@ -50,6 +73,14 @@ fn mult(vec: Vector3f, scalar: f32) -> Vector3f {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn div(vec: Vector3f, scalar: f32) -> Vector3f {
|
|
|
|
|
Vector3f {
|
|
|
|
|
x: vec.x / scalar,
|
|
|
|
|
y: vec.y / scalar,
|
|
|
|
|
z: vec.z / scalar,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn sub(vec_a: Vector3f, vec_b: Vector3f) -> Vector3f {
|
|
|
|
|
Vector3f {
|
|
|
|
|
x: vec_a.x - vec_b.x,
|
|
|
|
@ -66,11 +97,23 @@ fn add(vec_a: Vector3f, vec_b: Vector3f) -> Vector3f {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn pow(vec: Vector3f, scalar: f32) -> Vector3f {
|
|
|
|
|
Vector3f {
|
|
|
|
|
x: f32::powf(vec.x, scalar),
|
|
|
|
|
y: f32::powf(vec.y, scalar),
|
|
|
|
|
z: f32::powf(vec.z, scalar),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn mix(a: Vector3f, b: Vector3f, mixValue: f32) -> Vector3f
|
|
|
|
|
{
|
|
|
|
|
add(mult(a, (1.0 - mixValue)), mult(b, mixValue))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn len(vec: Vector3f) -> f32 {
|
|
|
|
|
(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z).sqrt() as f32
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn normalize(ray: Vector3f) -> Vector3f {
|
|
|
|
|
let multiplier = (ray.x * ray.x + ray.y * ray.y + ray.z * ray.z).sqrt();
|
|
|
|
|
Vector3f {
|
|
|
|
@ -115,20 +158,56 @@ fn solve_quadratic(a: f32, b: f32, c: f32, mut x0: f32, mut x1: f32) -> Option<f
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_surface_data(phit: Vector3f, nhit: Vector3f, sphere_center: Vector3f) -> Vector2f
|
|
|
|
|
// Return the texture coordinates of the surface hit
|
|
|
|
|
fn get_surface_data(phit: Vector3f, sphere_center: Vector3f) -> (Vector2f, Vector3f)
|
|
|
|
|
{
|
|
|
|
|
let nhit = sub(phit, sphere_center);
|
|
|
|
|
let nhit = normalize(nhit);
|
|
|
|
|
// In this particular case, the normal is simular to a point on a unit sphere
|
|
|
|
|
// centred around the origin. We can thus use the normal coordinates to compute
|
|
|
|
|
// the spherical coordinates of Phit.
|
|
|
|
|
// atan2 returns a value in the range [-pi, pi] and we need to remap it to range [0, 1]
|
|
|
|
|
// acosf returns a value in the range [0, pi] and we also need to remap it to the range [0, 1]
|
|
|
|
|
Vector2f {
|
|
|
|
|
// In this particular case, the normal is simular to a point on a unit sphere
|
|
|
|
|
// centred around the origin. We can thus use the normal coordinates to compute
|
|
|
|
|
// the spherical coordinates of Phit.
|
|
|
|
|
// atan2 returns a value in the range [-pi, pi] and we need to remap it to range [0, 1]
|
|
|
|
|
// acosf returns a value in the range [0, pi] and we also need to remap it to the range [0, 1]
|
|
|
|
|
(Vector2f {
|
|
|
|
|
x: (1.0 + (nhit.x).atan2(nhit.z) / PI) * 0.5,
|
|
|
|
|
y: (nhit.y).acos() / PI,
|
|
|
|
|
}, nhit)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn get_point_light(light: PointLight, pos3D: Vector3f, viewDir: Vector3f, normal: Vector3f) -> Lighting
|
|
|
|
|
{
|
|
|
|
|
let mut out = Lighting::default();
|
|
|
|
|
if (light.diffusePower > 0.0)
|
|
|
|
|
{
|
|
|
|
|
let lightDir = sub(light.position, pos3D); //3D position in space of the surface
|
|
|
|
|
let distance = len(lightDir);
|
|
|
|
|
let lightDir = div(lightDir, distance); // = normalize(lightDir);
|
|
|
|
|
let distance = distance * distance; //This line may be optimised using Inverse square root
|
|
|
|
|
|
|
|
|
|
//Intensity of the diffuse light. Saturate to keep within the 0-1 range.
|
|
|
|
|
let NdotL = dot(normal, lightDir);
|
|
|
|
|
let intensity = saturate(NdotL);
|
|
|
|
|
|
|
|
|
|
// Calculate the diffuse light factoring in light color, power and the attenuation
|
|
|
|
|
out.diffuse = div(mult(mult(light.diffuseColor, intensity), light.diffusePower), distance);
|
|
|
|
|
|
|
|
|
|
//Calculate the half vector between the light vector and the view vector.
|
|
|
|
|
// This is typically slower than calculating the actual reflection vector
|
|
|
|
|
// due to the normalize function's reciprocal square root
|
|
|
|
|
let H = normalize(add(lightDir, viewDir));
|
|
|
|
|
|
|
|
|
|
//Intensity of the specular light
|
|
|
|
|
let NdotH = dot(normal, H);
|
|
|
|
|
let intensity = f32::powf(saturate(NdotH), 1.0);
|
|
|
|
|
|
|
|
|
|
//Sum up the specular light factoring
|
|
|
|
|
out.specular = div(mult(mult(light.specularColor, intensity), light.specularPower), distance);
|
|
|
|
|
}
|
|
|
|
|
out
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
@ -194,10 +273,11 @@ fn main() {
|
|
|
|
|
match intersect(orig, dir, sphere_center, radius) {
|
|
|
|
|
None => {}
|
|
|
|
|
Some(t) => {
|
|
|
|
|
// The point on the circle which we intersected
|
|
|
|
|
let phit = add(orig, mult(dir, t));
|
|
|
|
|
let nhit = Vector3f::default();
|
|
|
|
|
|
|
|
|
|
let tex = get_surface_data(phit, nhit, sphere_center);
|
|
|
|
|
// The tex coord & normal at phit
|
|
|
|
|
let (tex, nhit) = get_surface_data(phit, sphere_center);
|
|
|
|
|
|
|
|
|
|
let scale = 4.0;
|
|
|
|
|
|
|
|
|
@ -212,6 +292,7 @@ fn main() {
|
|
|
|
|
z: -dir.z,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let hit_color = mult(mix(sphere_color, mult(sphere_color, 0.8), f32::max(0.0, dot(nhit, ndir))), pattern);
|
|
|
|
|
*pixel = image::Rgb([hit_color.x as u8, hit_color.y as u8, hit_color.z as u8]);
|
|
|
|
|
}
|
|
|
|
|