blinn function

master
mitchellhansen 4 years ago
parent 1e042c6691
commit 5ef0ca76e6

@ -1,6 +1,7 @@
extern crate rand; extern crate rand;
use std::cmp::max; use std::cmp::max;
use std::cmp::min;
use std::f32::consts::PI; use std::f32::consts::PI;
use std::io::Cursor; use std::io::Cursor;
use std::ops; use std::ops;
@ -38,6 +39,28 @@ struct Vector3f { x: f32, y: f32, z: f32 }
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
struct Vector4f { x: f32, y: f32, z: f32, w: f32 } 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 { 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 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 { fn sub(vec_a: Vector3f, vec_b: Vector3f) -> Vector3f {
Vector3f { Vector3f {
x: vec_a.x - vec_b.x, 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 fn mix(a: Vector3f, b: Vector3f, mixValue: f32) -> Vector3f
{ {
add(mult(a, (1.0 - mixValue)), mult(b, mixValue)) 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 { fn normalize(ray: Vector3f) -> Vector3f {
let multiplier = (ray.x * ray.x + ray.y * ray.y + ray.z * ray.z).sqrt(); let multiplier = (ray.x * ray.x + ray.y * ray.y + ray.z * ray.z).sqrt();
Vector3f { Vector3f {
@ -115,21 +158,57 @@ 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 = sub(phit, sphere_center);
let nhit = normalize(nhit); let nhit = normalize(nhit);
// In this particular case, the normal is simular to a point on a unit sphere // 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 // centred around the origin. We can thus use the normal coordinates to compute
// the spherical coordinates of Phit. // the spherical coordinates of Phit.
// atan2 returns a value in the range [-pi, pi] and we need to remap it to range [0, 1] // 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] // acosf returns a value in the range [0, pi] and we also need to remap it to the range [0, 1]
Vector2f { (Vector2f {
x: (1.0 + (nhit.x).atan2(nhit.z) / PI) * 0.5, x: (1.0 + (nhit.x).atan2(nhit.z) / PI) * 0.5,
y: (nhit.y).acos() / PI, 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() { fn main() {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let view_res = Vector2i { x: 800, y: 800 }; let view_res = Vector2i { x: 800, y: 800 };
@ -194,10 +273,11 @@ fn main() {
match intersect(orig, dir, sphere_center, radius) { match intersect(orig, dir, sphere_center, radius) {
None => {} None => {}
Some(t) => { Some(t) => {
// The point on the circle which we intersected
let phit = add(orig, mult(dir, t)); 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; let scale = 4.0;
@ -212,6 +292,7 @@ fn main() {
z: -dir.z, z: -dir.z,
}; };
let hit_color = mult(mix(sphere_color, mult(sphere_color, 0.8), f32::max(0.0, dot(nhit, ndir))), pattern); 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]); *pixel = image::Rgb([hit_color.x as u8, hit_color.y as u8, hit_color.z as u8]);
} }

Loading…
Cancel
Save