Implement Lambertian and Metal materials.
Fixed bug with Vec3::unit_vector(). Had to remove old example programs because Sphere::new() API changed to allow materials.
This commit is contained in:
parent
73ca5c8454
commit
7c213e3a2b
@ -6,30 +6,20 @@ use rand::Rng;
|
|||||||
use rtiow::camera::Camera;
|
use rtiow::camera::Camera;
|
||||||
use rtiow::hitable::Hit;
|
use rtiow::hitable::Hit;
|
||||||
use rtiow::hitable_list::HitableList;
|
use rtiow::hitable_list::HitableList;
|
||||||
|
use rtiow::material::Lambertian;
|
||||||
|
use rtiow::material::Metal;
|
||||||
use rtiow::ray::Ray;
|
use rtiow::ray::Ray;
|
||||||
use rtiow::sphere::Sphere;
|
use rtiow::sphere::Sphere;
|
||||||
use rtiow::vec3::Vec3;
|
use rtiow::vec3::Vec3;
|
||||||
|
|
||||||
fn random_in_unit_sphere() -> Vec3 {
|
fn color(r: Ray, world: &Hit, depth: usize) -> Vec3 {
|
||||||
let mut rng = rand::thread_rng();
|
|
||||||
let v = Vec3::new(1., 1., 1.);
|
|
||||||
loop {
|
|
||||||
let p = 2. * Vec3::new(
|
|
||||||
rng.gen_range::<f32>(0., 1.),
|
|
||||||
rng.gen_range::<f32>(0., 1.),
|
|
||||||
rng.gen_range::<f32>(0., 1.),
|
|
||||||
) - v;
|
|
||||||
if p.squared_length() < 1. {
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn color(r: Ray, world: &Hit) -> Vec3 {
|
|
||||||
if let Some(rec) = world.hit(r, 0.001, std::f32::MAX) {
|
if let Some(rec) = world.hit(r, 0.001, std::f32::MAX) {
|
||||||
let target = rec.p + rec.normal + random_in_unit_sphere();
|
let scatter_response = rec.material.scatter(&r, &rec);
|
||||||
// 50% grey material
|
if depth < 50 && scatter_response.reflected {
|
||||||
return 0.5 * color(Ray::new(rec.p, target - rec.p), world);
|
return scatter_response.attenutation
|
||||||
|
* color(scatter_response.scattered, world, depth + 1);
|
||||||
|
}
|
||||||
|
return Default::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
// No hit, choose color from background.
|
// No hit, choose color from background.
|
||||||
@ -45,8 +35,26 @@ fn main() -> Result<(), std::io::Error> {
|
|||||||
let ns = 100;
|
let ns = 100;
|
||||||
println!("P3\n{} {}\n255", nx, ny);
|
println!("P3\n{} {}\n255", nx, ny);
|
||||||
let objects = vec![
|
let objects = vec![
|
||||||
Sphere::new(Vec3::new(0., 0., -1.), 0.5),
|
Sphere::new(
|
||||||
Sphere::new(Vec3::new(0., -100.5, -1.), 100.),
|
Vec3::new(0., 0., -1.),
|
||||||
|
0.5,
|
||||||
|
Box::new(Lambertian::new(Vec3::new(0.8, 0.3, 0.3))),
|
||||||
|
),
|
||||||
|
Sphere::new(
|
||||||
|
Vec3::new(0., -100.5, -1.),
|
||||||
|
100.,
|
||||||
|
Box::new(Lambertian::new(Vec3::new(0.8, 0.8, 0.))),
|
||||||
|
),
|
||||||
|
Sphere::new(
|
||||||
|
Vec3::new(1., 0., -1.),
|
||||||
|
0.5,
|
||||||
|
Box::new(Metal::new(Vec3::new(0.8, 0.6, 0.2), 0.2)),
|
||||||
|
),
|
||||||
|
Sphere::new(
|
||||||
|
Vec3::new(-1., 0., -1.),
|
||||||
|
0.5,
|
||||||
|
Box::new(Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.8)),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
let cam = Camera::new2x1();
|
let cam = Camera::new2x1();
|
||||||
let world = HitableList::new(objects.iter().map(|o| o).collect());
|
let world = HitableList::new(objects.iter().map(|o| o).collect());
|
||||||
@ -57,7 +65,7 @@ fn main() -> Result<(), std::io::Error> {
|
|||||||
let u = (rng.gen_range::<f32>(0., 1.) + i as f32) / nx as f32;
|
let u = (rng.gen_range::<f32>(0., 1.) + i as f32) / nx as f32;
|
||||||
let v = (rng.gen_range::<f32>(0., 1.) + j as f32) / ny as f32;
|
let v = (rng.gen_range::<f32>(0., 1.) + j as f32) / ny as f32;
|
||||||
let r = cam.get_ray(u, v);
|
let r = cam.get_ray(u, v);
|
||||||
col = col + color(r, &world);
|
col = col + color(r, &world, 0);
|
||||||
}
|
}
|
||||||
col = col / ns as f32;
|
col = col / ns as f32;
|
||||||
// Gamma correct, use gamma 2 correction, which is 1/gamma where gamma=2 which is 1/2
|
// Gamma correct, use gamma 2 correction, which is 1/gamma where gamma=2 which is 1/2
|
||||||
|
|||||||
@ -1,53 +0,0 @@
|
|||||||
extern crate rand;
|
|
||||||
extern crate rtiow;
|
|
||||||
|
|
||||||
use rand::Rng;
|
|
||||||
|
|
||||||
use rtiow::camera::Camera;
|
|
||||||
use rtiow::hitable::Hit;
|
|
||||||
use rtiow::hitable_list::HitableList;
|
|
||||||
use rtiow::ray::Ray;
|
|
||||||
use rtiow::sphere::Sphere;
|
|
||||||
use rtiow::vec3::Vec3;
|
|
||||||
|
|
||||||
fn color(r: Ray, world: &Hit) -> Vec3 {
|
|
||||||
if let Some(rec) = world.hit(r, 0., std::f32::MAX) {
|
|
||||||
return (rec.normal + 1.) * 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No hit, choose color from background.
|
|
||||||
let unit_direction = r.direction().unit_vector();
|
|
||||||
let t = 0.5 * (unit_direction.y + 1.);
|
|
||||||
Vec3::new(1., 1., 1.) * (1. - t) + Vec3::new(0.5, 0.7, 1.) * t
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<(), std::io::Error> {
|
|
||||||
let mut rng = rand::thread_rng();
|
|
||||||
let nx = 200;
|
|
||||||
let ny = 100;
|
|
||||||
let ns = 100;
|
|
||||||
println!("P3\n{} {}\n255", nx, ny);
|
|
||||||
let objects = vec![
|
|
||||||
Sphere::new(Vec3::new(0., 0., -1.), 0.5),
|
|
||||||
Sphere::new(Vec3::new(0., -100.5, -1.), 100.),
|
|
||||||
];
|
|
||||||
let cam = Camera::new2x1();
|
|
||||||
let world = HitableList::new(objects.iter().map(|o| o).collect());
|
|
||||||
for j in (0..ny).rev() {
|
|
||||||
for i in 0..nx {
|
|
||||||
let mut col: Vec3 = Default::default();
|
|
||||||
for _ in 0..ns {
|
|
||||||
let u = (rng.gen_range::<f32>(0., 1.) + i as f32) / nx as f32;
|
|
||||||
let v = (rng.gen_range::<f32>(0., 1.) + j as f32) / ny as f32;
|
|
||||||
let r = cam.get_ray(u, v);
|
|
||||||
col = col + color(r, &world);
|
|
||||||
}
|
|
||||||
col = col / ns as f32;
|
|
||||||
let ir = (255.99 * col[0]) as u32;
|
|
||||||
let ig = (255.99 * col[1]) as u32;
|
|
||||||
let ib = (255.99 * col[2]) as u32;
|
|
||||||
println!("{} {} {}", ir, ig, ib);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@ -6,30 +6,20 @@ use rand::Rng;
|
|||||||
use rtiow::camera::Camera;
|
use rtiow::camera::Camera;
|
||||||
use rtiow::hitable::Hit;
|
use rtiow::hitable::Hit;
|
||||||
use rtiow::hitable_list::HitableList;
|
use rtiow::hitable_list::HitableList;
|
||||||
|
use rtiow::material::Lambertian;
|
||||||
|
use rtiow::material::Metal;
|
||||||
use rtiow::ray::Ray;
|
use rtiow::ray::Ray;
|
||||||
use rtiow::sphere::Sphere;
|
use rtiow::sphere::Sphere;
|
||||||
use rtiow::vec3::Vec3;
|
use rtiow::vec3::Vec3;
|
||||||
|
|
||||||
fn random_in_unit_sphere() -> Vec3 {
|
fn color(r: Ray, world: &Hit, depth: usize) -> Vec3 {
|
||||||
let mut rng = rand::thread_rng();
|
|
||||||
let v = Vec3::new(1., 1., 1.);
|
|
||||||
loop {
|
|
||||||
let p = 2. * Vec3::new(
|
|
||||||
rng.gen_range::<f32>(0., 1.),
|
|
||||||
rng.gen_range::<f32>(0., 1.),
|
|
||||||
rng.gen_range::<f32>(0., 1.),
|
|
||||||
) - v;
|
|
||||||
if p.squared_length() < 1. {
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn color(r: Ray, world: &Hit) -> Vec3 {
|
|
||||||
if let Some(rec) = world.hit(r, 0.001, std::f32::MAX) {
|
if let Some(rec) = world.hit(r, 0.001, std::f32::MAX) {
|
||||||
let target = rec.p + rec.normal + random_in_unit_sphere();
|
let scatter_response = rec.material.scatter(&r, &rec);
|
||||||
// 50% grey material
|
if depth < 50 && scatter_response.reflected {
|
||||||
return 0.5 * color(Ray::new(rec.p, target - rec.p), world);
|
return scatter_response.attenutation
|
||||||
|
* color(scatter_response.scattered, world, depth + 1);
|
||||||
|
}
|
||||||
|
return Default::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
// No hit, choose color from background.
|
// No hit, choose color from background.
|
||||||
@ -45,8 +35,26 @@ fn main() -> Result<(), std::io::Error> {
|
|||||||
let ns = 100;
|
let ns = 100;
|
||||||
println!("P3\n{} {}\n255", nx, ny);
|
println!("P3\n{} {}\n255", nx, ny);
|
||||||
let objects = vec![
|
let objects = vec![
|
||||||
Sphere::new(Vec3::new(0., 0., -1.), 0.5),
|
Sphere::new(
|
||||||
Sphere::new(Vec3::new(0., -100.5, -1.), 100.),
|
Vec3::new(0., 0., -1.),
|
||||||
|
0.5,
|
||||||
|
Box::new(Lambertian::new(Vec3::new(0.8, 0.3, 0.3))),
|
||||||
|
),
|
||||||
|
Sphere::new(
|
||||||
|
Vec3::new(0., -100.5, -1.),
|
||||||
|
100.,
|
||||||
|
Box::new(Lambertian::new(Vec3::new(0.8, 0.8, 0.))),
|
||||||
|
),
|
||||||
|
Sphere::new(
|
||||||
|
Vec3::new(1., 0., -1.),
|
||||||
|
0.5,
|
||||||
|
Box::new(Metal::new(Vec3::new(0.8, 0.6, 0.2), 0.2)),
|
||||||
|
),
|
||||||
|
Sphere::new(
|
||||||
|
Vec3::new(-1., 0., -1.),
|
||||||
|
0.5,
|
||||||
|
Box::new(Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.8)),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
let cam = Camera::new2x1();
|
let cam = Camera::new2x1();
|
||||||
let world = HitableList::new(objects.iter().map(|o| o).collect());
|
let world = HitableList::new(objects.iter().map(|o| o).collect());
|
||||||
@ -57,7 +65,7 @@ fn main() -> Result<(), std::io::Error> {
|
|||||||
let u = (rng.gen_range::<f32>(0., 1.) + i as f32) / nx as f32;
|
let u = (rng.gen_range::<f32>(0., 1.) + i as f32) / nx as f32;
|
||||||
let v = (rng.gen_range::<f32>(0., 1.) + j as f32) / ny as f32;
|
let v = (rng.gen_range::<f32>(0., 1.) + j as f32) / ny as f32;
|
||||||
let r = cam.get_ray(u, v);
|
let r = cam.get_ray(u, v);
|
||||||
col = col + color(r, &world);
|
col = col + color(r, &world, 0);
|
||||||
}
|
}
|
||||||
col = col / ns as f32;
|
col = col / ns as f32;
|
||||||
// Gamma correct, use gamma 2 correction, which is 1/gamma where gamma=2 which is 1/2
|
// Gamma correct, use gamma 2 correction, which is 1/gamma where gamma=2 which is 1/2
|
||||||
@ -1,46 +0,0 @@
|
|||||||
extern crate rtiow;
|
|
||||||
|
|
||||||
use rtiow::hitable::Hit;
|
|
||||||
use rtiow::hitable_list::HitableList;
|
|
||||||
use rtiow::ray::Ray;
|
|
||||||
use rtiow::sphere::Sphere;
|
|
||||||
use rtiow::vec3::Vec3;
|
|
||||||
|
|
||||||
fn color(r: Ray, world: &Hit) -> Vec3 {
|
|
||||||
if let Some(rec) = world.hit(r, 0., std::f32::MAX) {
|
|
||||||
return (rec.normal + 1.) * 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No hit, choose color from background.
|
|
||||||
let unit_direction = r.direction().unit_vector();
|
|
||||||
let t = 0.5 * (unit_direction.y + 1.);
|
|
||||||
Vec3::new(1., 1., 1.) * (1. - t) + Vec3::new(0.5, 0.7, 1.) * t
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<(), std::io::Error> {
|
|
||||||
let nx = 200;
|
|
||||||
let ny = 100;
|
|
||||||
println!("P3\n{} {}\n255", nx, ny);
|
|
||||||
let lower_left_corner = Vec3::new(-2., -1., -1.);
|
|
||||||
let horizontal = Vec3::new(4., 0., 0.);
|
|
||||||
let vertical = Vec3::new(0., 2., 0.);
|
|
||||||
let origin: Vec3 = Default::default();
|
|
||||||
let objects = vec![
|
|
||||||
Sphere::new(Vec3::new(0., 0., -1.), 0.5),
|
|
||||||
Sphere::new(Vec3::new(0., -100.5, -1.), 100.),
|
|
||||||
];
|
|
||||||
let world = HitableList::new(objects.iter().map(|o| o).collect());
|
|
||||||
for j in (0..ny).rev() {
|
|
||||||
for i in 0..nx {
|
|
||||||
let u = i as f32 / nx as f32;
|
|
||||||
let v = j as f32 / ny as f32;
|
|
||||||
let r = Ray::new(origin, lower_left_corner + horizontal * u + vertical * v);
|
|
||||||
let col = color(r, &world);
|
|
||||||
let ir = (255.99 * col[0]) as u32;
|
|
||||||
let ig = (255.99 * col[1]) as u32;
|
|
||||||
let ib = (255.99 * col[2]) as u32;
|
|
||||||
println!("{} {} {}", ir, ig, ib);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@ -1,11 +1,12 @@
|
|||||||
|
use material::Material;
|
||||||
use ray::Ray;
|
use ray::Ray;
|
||||||
use vec3::Vec3;
|
use vec3::Vec3;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
pub struct HitRecord<'m> {
|
||||||
pub struct HitRecord {
|
|
||||||
pub t: f32,
|
pub t: f32,
|
||||||
pub p: Vec3,
|
pub p: Vec3,
|
||||||
pub normal: Vec3,
|
pub normal: Vec3,
|
||||||
|
pub material: &'m Material,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Hit {
|
pub trait Hit {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
pub mod camera;
|
pub mod camera;
|
||||||
pub mod hitable;
|
pub mod hitable;
|
||||||
pub mod hitable_list;
|
pub mod hitable_list;
|
||||||
|
pub mod material;
|
||||||
pub mod ray;
|
pub mod ray;
|
||||||
pub mod sphere;
|
pub mod sphere;
|
||||||
pub mod vec3;
|
pub mod vec3;
|
||||||
|
|||||||
81
rtiow/src/material.rs
Normal file
81
rtiow/src/material.rs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
extern crate rand;
|
||||||
|
use self::rand::Rng;
|
||||||
|
|
||||||
|
use hitable::HitRecord;
|
||||||
|
use ray::Ray;
|
||||||
|
use vec3::dot;
|
||||||
|
use vec3::Vec3;
|
||||||
|
|
||||||
|
fn random_in_unit_sphere() -> Vec3 {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let v = Vec3::new(1., 1., 1.);
|
||||||
|
loop {
|
||||||
|
let p = 2. * Vec3::new(
|
||||||
|
rng.gen_range::<f32>(0., 1.),
|
||||||
|
rng.gen_range::<f32>(0., 1.),
|
||||||
|
rng.gen_range::<f32>(0., 1.),
|
||||||
|
) - v;
|
||||||
|
if p.squared_length() < 1. {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ScatterResponse {
|
||||||
|
pub scattered: Ray,
|
||||||
|
pub attenutation: Vec3,
|
||||||
|
pub reflected: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Material {
|
||||||
|
fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> ScatterResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Lambertian {
|
||||||
|
albedo: Vec3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lambertian {
|
||||||
|
pub fn new(albedo: Vec3) -> Lambertian {
|
||||||
|
Lambertian { albedo }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Material for Lambertian {
|
||||||
|
fn scatter(&self, _r_in: &Ray, rec: &HitRecord) -> ScatterResponse {
|
||||||
|
let target = rec.p + rec.normal + random_in_unit_sphere();
|
||||||
|
ScatterResponse {
|
||||||
|
attenutation: self.albedo,
|
||||||
|
scattered: Ray::new(rec.p, target - rec.p),
|
||||||
|
reflected: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Metal {
|
||||||
|
albedo: Vec3,
|
||||||
|
fuzzy: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reflect(v: Vec3, n: Vec3) -> Vec3 {
|
||||||
|
v - 2. * dot(v, n) * n
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Metal {
|
||||||
|
pub fn new(albedo: Vec3, fuzzy: f32) -> Metal {
|
||||||
|
let fuzzy = fuzzy.min(1.);
|
||||||
|
Metal { albedo, fuzzy }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Material for Metal {
|
||||||
|
fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> ScatterResponse {
|
||||||
|
let reflected = reflect(r_in.direction().unit_vector(), rec.normal);
|
||||||
|
let scattered = Ray::new(rec.p, reflected + self.fuzzy * random_in_unit_sphere());
|
||||||
|
ScatterResponse {
|
||||||
|
scattered,
|
||||||
|
attenutation: self.albedo,
|
||||||
|
reflected: dot(scattered.direction(), rec.normal) > 0.,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,18 +1,23 @@
|
|||||||
use hitable::Hit;
|
use hitable::Hit;
|
||||||
use hitable::HitRecord;
|
use hitable::HitRecord;
|
||||||
|
use material::Material;
|
||||||
use ray::Ray;
|
use ray::Ray;
|
||||||
use vec3::dot;
|
use vec3::dot;
|
||||||
use vec3::Vec3;
|
use vec3::Vec3;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default)]
|
|
||||||
pub struct Sphere {
|
pub struct Sphere {
|
||||||
center: Vec3,
|
center: Vec3,
|
||||||
radius: f32,
|
radius: f32,
|
||||||
|
material: Box<Material>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sphere {
|
impl Sphere {
|
||||||
pub fn new(center: Vec3, radius: f32) -> Sphere {
|
pub fn new(center: Vec3, radius: f32, material: Box<Material>) -> Sphere {
|
||||||
Sphere { center, radius }
|
Sphere {
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
material,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,6 +36,7 @@ impl Hit for Sphere {
|
|||||||
t: temp,
|
t: temp,
|
||||||
p,
|
p,
|
||||||
normal: (p - self.center) / self.radius,
|
normal: (p - self.center) / self.radius,
|
||||||
|
material: &*self.material,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let temp = (-b + (b * b - a * c).sqrt()) / a;
|
let temp = (-b + (b * b - a * c).sqrt()) / a;
|
||||||
@ -40,6 +46,7 @@ impl Hit for Sphere {
|
|||||||
t: temp,
|
t: temp,
|
||||||
p,
|
p,
|
||||||
normal: (p - self.center) / self.radius,
|
normal: (p - self.center) / self.radius,
|
||||||
|
material: &*self.material,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,12 +40,7 @@ impl Vec3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn unit_vector(self) -> Vec3 {
|
pub fn unit_vector(self) -> Vec3 {
|
||||||
let k = 1. / self.length();
|
self / self.length()
|
||||||
Vec3 {
|
|
||||||
x: self.x / k,
|
|
||||||
y: self.y / k,
|
|
||||||
z: self.z / k,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_unit_vector(&mut self) {
|
pub fn make_unit_vector(&mut self) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user