Dielectric bug

This commit is contained in:
dengqn 2025-08-12 23:52:23 +08:00
parent b28a6cc7f5
commit 37c4cbb666
8 changed files with 164 additions and 42 deletions

View File

@ -2,7 +2,7 @@ use std::fs::File;
use std::io::{BufWriter}; use std::io::{BufWriter};
use crate::hittable::HittableList; use crate::hittable::HittableList;
use crate::math_utils::clamp; use crate::math_utils::{clamp, near_zero};
use crate::ppm_writer::PPMWriter; use crate::ppm_writer::PPMWriter;
use crate::types_defined::{Camera, Color, HitRecord, Point, Ray, Vec3}; use crate::types_defined::{Camera, Color, HitRecord, Point, Ray, Vec3};
use rand::rngs::ThreadRng; use rand::rngs::ThreadRng;
@ -125,12 +125,12 @@ impl<'a> Camera<'a> {
}; };
let hit_c = match hit_m { let hit_c = match hit_m {
Some(mk) => { Some(mk) => {
let mc = match mk { match mk {
// MaterialKind::Lambertian(l) => l.albedo, // MaterialKind::Lambertian(l) => l.albedo,
MaterialKind::Lambertian(l) => self.scatter_color(ray, hc, l, depth, world), MaterialKind::Lambertian(l) => self.scatter_color(ray, hc, l, depth, world),
MaterialKind::Metal(m) => self.scatter_color(ray, hc, m, depth, world), MaterialKind::Metal(m) => self.scatter_color(ray, hc, m, depth, world),
}; MaterialKind::Dielectric(d) => self.scatter_color(ray, hc, d, depth, world)
mc }
}, },
None => { None => {
if hit_record.t >= 0.0 { if hit_record.t >= 0.0 {
@ -175,7 +175,11 @@ impl<'a> Camera<'a> {
attenuation.y * r_c.y, attenuation.y * r_c.y,
attenuation.z * r_c.z, attenuation.z * r_c.z,
); );
Color::new(sc_color.x, sc_color.y, sc_color.z) let color = Color::new(sc_color.x, sc_color.y, sc_color.z);
// if near_zero(color) {
// println!("near zero: {:?}", scatted.direction);
// }
color
} else { } else {
Color::new(0.0, 0.0, 0.0) Color::new(0.0, 0.0, 0.0)
} }

View File

@ -1,4 +1,5 @@
use crate::material::MaterialKind; use crate::material::MaterialKind;
use crate::math_utils::{front_face_normal, is_front_face};
use crate::types_defined::{HitRecord, Ray, Vec3}; use crate::types_defined::{HitRecord, Ray, Vec3};
pub trait Hittable { pub trait Hittable {
@ -37,20 +38,17 @@ impl HittableList {
closest_so_far = temp_hit_record.t; closest_so_far = temp_hit_record.t;
hit_record.t = temp_hit_record.t; hit_record.t = temp_hit_record.t;
hit_record.p = temp_hit_record.p; hit_record.p = temp_hit_record.p;
hit_record.front_face = temp_hit_record.front_face; hit_record.front_face = is_front_face(r, temp_hit_record.normal);
hit_record.normal = temp_hit_record.normal; hit_record.normal = front_face_normal(r, temp_hit_record.normal);
hit_record.material = if let Some(ref m) = temp_hit_record.material { hit_record.material = if let Some(ref m) = temp_hit_record.material {
match m { match m {
MaterialKind::Lambertian(l) => Some(MaterialKind::Lambertian(l.clone())), MaterialKind::Lambertian(l) => Some(MaterialKind::Lambertian(l.clone())),
MaterialKind::Metal(m) => Some(MaterialKind::Metal(m.clone())), MaterialKind::Metal(m) => Some(MaterialKind::Metal(m.clone())),
MaterialKind::Dielectric(d) => Some(MaterialKind::Dielectric(d.clone()))
} }
} else { } else {
None None
}; };
// if temp_hit_record.t >= 0.0 {
// return true;
// }
} }
} }

View File

@ -2,7 +2,7 @@ use std::fs::File;
use std::io::BufWriter; use std::io::BufWriter;
use crate::hittable::HittableList; use crate::hittable::HittableList;
use crate::material::{Lambertian, MaterialKind, Metal}; use crate::material::{Dielectric, Lambertian, MaterialKind, Metal};
use crate::ppm_writer::PPMWriter; use crate::ppm_writer::PPMWriter;
use crate::types_defined::{Camera, Color, Point, Sphere, Vec3}; use crate::types_defined::{Camera, Color, Point, Sphere, Vec3};
mod camera; mod camera;
@ -22,8 +22,13 @@ fn main() {
fn camera_render() { fn camera_render() {
let width: i32 = 800/2; let scale = 2;
let height: i32 = 600/2; let width: i32 = 800/scale;
let height: i32 = 600/scale;
let sample_times = 50;
let reflect_depth = 100;
let pw_r = PPMWriter::new( let pw_r = PPMWriter::new(
BufWriter::new(File::create("./target/ray_sphere_normal_scene_render.ppm").unwrap()), BufWriter::new(File::create("./target/ray_sphere_normal_scene_render.ppm").unwrap()),
width, height); width, height);
@ -36,12 +41,12 @@ fn camera_render() {
let mut camera: Camera = Camera::new( let mut camera: Camera = Camera::new(
width, width,
4.0 / 3.0, width as f32 / height as f32,
2.0, 2.0,
Point::new(0.0, -0.0, 0.0), Point::new(0.0, -0.0, 0.0),
Vec3::new(0.0, 0.0, 1.0), Vec3::new(0.0, 0.0, 1.0),
127, sample_times,
127, reflect_depth,
pw pw
); );
// world // world
@ -50,20 +55,18 @@ fn camera_render() {
let plane_m = Some(MaterialKind::Lambertian(Lambertian{albedo: Color::new(0.899, 0.899, 0.999)})); let plane_m = Some(MaterialKind::Lambertian(Lambertian{albedo: Color::new(0.899, 0.899, 0.999)}));
// let plane2_m = Some(MaterialKind::Metal(Metal{albedo: Color::new(0.784,0.784,0.784)})); // let plane2_m = Some(MaterialKind::Metal(Metal{albedo: Color::new(0.784,0.784,0.784)}));
let center_m = Some(MaterialKind::Lambertian(Lambertian{albedo: Color::new(0.5, 0.5, 0.5)})); let center_m = Some(MaterialKind::Lambertian(Lambertian{albedo: Color::new(0.2, 0.5, 0.5)}));
let left_m = Some(MaterialKind::Metal(Metal{albedo: Color::new(0.799, 0.599, 0.799), fuzz: 0.0005})); let left_m = Some(MaterialKind::Metal(Metal{albedo: Color::new(0.799, 0.599, 0.799), fuzz: 0.0005}));
let left_behind_m = Some(MaterialKind::Lambertian(Lambertian{albedo: Color::new(0.799, 0.599, 0.599)})); let left_behind_m = Some(MaterialKind::Lambertian(Lambertian{albedo: Color::new(0.799, 0.599, 0.599)}));
let right_m = Some(MaterialKind::Metal(Metal{albedo: Color::new(0.8, 0.6, 0.2), fuzz: 0.3})); let right_m = Some(MaterialKind::Metal(Metal{albedo: Color::new(0.8, 0.6, 0.2), fuzz: 0.003}));
let left_dia_m = Some(MaterialKind::Dielectric(Dielectric{albedo: Color::new(0.8, 0.6, 0.2), refraction_index: 1.5}));
world.put(Box::new(Sphere::new(Point::new(0.0, 0.0, -1.0), 0.5, center_m))); // world.put(Box::new(Sphere::new(Point::new(0.0, 0.0, -1.0), 0.5, center_m)));
world.put(Box::new(Sphere::new(Point::new(-1.0, 0.0, -1.0), 0.5, left_m))); world.put(Box::new(Sphere::new(Point::new(-1.0, 0.0, -1.0), 0.5, left_dia_m)));
world.put(Box::new(Sphere::new(Point::new(-2.5, 1.5, -3.5), 1.5, left_behind_m))); // world.put(Box::new(Sphere::new(Point::new(-1.0, 0.0, -1.0), 0.5, left_m)));
world.put(Box::new(Sphere::new(Point::new(-3.5, 1.5, -5.5), 1.5, left_behind_m)));
world.put(Box::new(Sphere::new(Point::new(1.0, 0.0, -1.0), 0.5, right_m))); world.put(Box::new(Sphere::new(Point::new(1.0, 0.0, -1.0), 0.5, right_m)));
world.put(Box::new(Sphere::new(Point::new(0.0, -25.5, -1.0), 25.0, plane_m))); world.put(Box::new(Sphere::new(Point::new(0.0, -500.5, -1.0), 500.0, plane_m)));
camera.render(&world); camera.render(&world);
// write_image(
// ppm_content,
// "".to_string(),
// )
} }

View File

@ -1,4 +1,4 @@
use crate::math_utils::{reflect}; use crate::math_utils::{near_zero, reflect, refract};
use crate::types_defined::{Color, HitRecord, Ray, Vec3}; use crate::types_defined::{Color, HitRecord, Ray, Vec3};
@ -6,6 +6,7 @@ use crate::types_defined::{Color, HitRecord, Ray, Vec3};
pub enum MaterialKind { pub enum MaterialKind {
Lambertian(Lambertian), Lambertian(Lambertian),
Metal(Metal), Metal(Metal),
Dielectric (Dielectric),
} }
pub trait Material { pub trait Material {
@ -28,13 +29,12 @@ impl Clone for Lambertian {
impl Material for Lambertian { impl Material for Lambertian {
fn scatter(&self, r_in: &Ray, hit_record: &mut HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool { fn scatter(&self, r_in: &Ray, hit_record: &mut HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool {
let scatter_direction = r_in.direction + Vec3::random_unit_on_hemisphere(hit_record.normal); let mut scatter_direction = r_in.direction + Vec3::random_unit_on_hemisphere(hit_record.normal);
// hit_record.normal + Vec3::random_unit();
// let scatter_direction = reflect(r_in.direction, hit_record.normal);
// if near_zero(scatter_direction) { if (near_zero(scatter_direction)) {
// scattered.direction = r_in.direction; scatter_direction = hit_record.normal;
// } }
*scattered = Ray::new(hit_record.p, scatter_direction); *scattered = Ray::new(hit_record.p, scatter_direction);
*attenuation = self.albedo.clone(); *attenuation = self.albedo.clone();
@ -66,4 +66,63 @@ impl Material for Metal {
*attenuation = self.albedo.clone(); *attenuation = self.albedo.clone();
true true
} }
}
impl Clone for Dielectric {
fn clone(&self) -> Self {
Dielectric {
albedo: self.albedo,
refraction_index: self.refraction_index
}
}
}
#[derive(Debug)]
pub struct Dielectric {
pub albedo: Color,
pub refraction_index: f32
}
impl Material for Dielectric {
fn scatter(&self, r_in: &Ray, hit_record: &mut HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool {
// attenuation = color(1.0, 1.0, 1.0);
// double ri = rec.front_face ? (1.0/refraction_index) : refraction_index;
// vec3 unit_direction = unit_vector(r_in.direction());
// vec3 refracted = refract(unit_direction, rec.normal, ri);
// scattered = ray(rec.p, refracted);
// return true;
*attenuation = Color::new(1.0, 1.0, 1.0);
let ri = if hit_record.front_face {
1.0 / self.refraction_index
} else {
self.refraction_index
};
let unit_direction = r_in.direction.normalize();
let refracted = refract(unit_direction, hit_record.normal, ri);
*scattered = Ray::new(hit_record.p, refracted);
true
// *attenuation = Color::new(1.0, 1.0, 1.0);
// let ri = if hit_record.front_face {
// 1.0/self.refraction_index
// } else {
// self.refraction_index
// };
// let normalized = r_in.direction;
// let refracted = refract(normalized, hit_record.normal, ri);
// *scattered = Ray::new(hit_record.p, refracted);
// true
}
} }

View File

@ -1,4 +1,4 @@
use crate::types_defined::Vec3; use crate::types_defined::{Ray, Vec3};
pub fn clamp(value: f32, low: f32, high: f32) -> f32 { pub fn clamp(value: f32, low: f32, high: f32) -> f32 {
if value < low { if value < low {
@ -10,12 +10,55 @@ pub fn clamp(value: f32, low: f32, high: f32) -> f32 {
return value; return value;
} }
// pub fn near_zero(v: Vec3) -> bool { pub fn near_zero(v: Vec3) -> bool {
// const EPSILON: f32 = 1e-4; const EPSILON: f32 = 1e-4;
// v.x.abs() < EPSILON && v.y.abs() < EPSILON && v.z.abs() < EPSILON v.x.abs() < EPSILON && v.y.abs() < EPSILON && v.z.abs() < EPSILON
// } }
pub fn reflect(v: Vec3, n: Vec3) -> Vec3 { pub fn reflect(v: Vec3, n: Vec3) -> Vec3 {
v - ((2.0 * v.dot(n)) * n) v - ((2.0 * v.dot(n)) * n)
}
// v, normal, 折射率
pub fn refract(uv: Vec3, n: Vec3, etai_over_etat: f32) -> Vec3 {
// auto cos_theta = std::fmin(dot(-uv, n), 1.0);
// vec3 r_out_perp = etai_over_etat * (uv + cos_theta*n);
// vec3 r_out_parallel = -std::sqrt(std::fabs(1.0 - r_out_perp.length_squared())) * n;
// return r_out_perp + r_out_parallel;
let cos_theta = (-uv.dot(n)).min(1.0);
let r_out_perp = etai_over_etat * (uv + cos_theta * n);
let r_out_parallel = -(f32::sqrt(f32::abs(1.0 - r_out_perp.length()))) * n;
r_out_perp + r_out_parallel
// let v_normalized = v;
// let n_normalized = n.normalize();
// let cos_theta = f32::min(-v_normalized.dot(n_normalized), 1.0);
// let sin_theta = (1.0 - cos_theta * cos_theta).sqrt();
// // 检查是否全内反射(无法折射)
// if etai_over_etat * sin_theta > 1.0 {
// return reflect(v_normalized, n)
// }
// let r_out_perp = etai_over_etat * (v_normalized + cos_theta * n_normalized);
// let discriminant = 1.0 - r_out_perp.length_squared();
// // if discriminant < 0.0 {
// // return -1.0 * v; // 全内反射,返回零向量(或改为反射)
// // }
// let r_out_parallel = -discriminant * n_normalized;
// r_out_perp + r_out_parallel
}
pub fn is_front_face(r: &Ray, outward_normal: Vec3) -> bool {
r.direction.dot(outward_normal) < 0.0
}
pub fn front_face_normal(r: &Ray, outward_normal: Vec3) -> Vec3 {
if is_front_face(r, outward_normal) {
outward_normal
} else {
-1. * outward_normal
}
} }

View File

@ -1,5 +1,6 @@
use crate::hittable; use crate::hittable;
use crate::material::{MaterialKind}; use crate::material::{MaterialKind};
use crate::math_utils::{front_face_normal, is_front_face};
use crate::types_defined::{HitRecord, Point, Ray, Sphere}; use crate::types_defined::{HitRecord, Point, Ray, Sphere};
impl Sphere { impl Sphere {
@ -44,12 +45,13 @@ impl hittable::Hittable for Sphere {
hit_record.t = root; hit_record.t = root;
hit_record.p = p; hit_record.p = p;
hit_record.normal = normal; hit_record.normal = front_face_normal(r, normal);
hit_record.front_face = r.direction.dot(normal) < 0.0; hit_record.front_face = is_front_face(r, normal);
hit_record.material = if let Some(ref m) = self.material { hit_record.material = if let Some(ref m) = self.material {
match m { match m {
MaterialKind::Lambertian(l) => Some(MaterialKind::Lambertian(l.clone())), MaterialKind::Lambertian(l) => Some(MaterialKind::Lambertian(l.clone())),
MaterialKind::Metal(m) => Some(MaterialKind::Metal(m.clone())), MaterialKind::Metal(m) => Some(MaterialKind::Metal(m.clone())),
MaterialKind::Dielectric(d) => Some(MaterialKind::Dielectric(d.clone())),
} }
} else { } else {
None None

View File

@ -53,6 +53,7 @@ pub struct Camera<'a> {
pub ppm_file_writer: &'a mut PPMWriter<BufWriter<File>> pub ppm_file_writer: &'a mut PPMWriter<BufWriter<File>>
} }
/* /*
///////////// /////////////
// for test // for test
@ -62,4 +63,4 @@ pub struct Sphere {
pub center: Point, pub center: Point,
pub radius: f32, pub radius: f32,
pub material: Option<MaterialKind>, pub material: Option<MaterialKind>,
} }

View File

@ -37,6 +37,18 @@ impl Vec3 {
return self.length_squared().sqrt(); return self.length_squared().sqrt();
} }
pub fn normalize(self) -> Vec3 {
let length = self.length_squared().sqrt();
if length != 0. {
let new_x = self.x / length;
let new_y = self.y / length;
let new_z = self.z / length;
Vec3 { x: new_x, y: new_y, z: new_z }
} else {
Vec3 { x: 0., y: 0., z: 0. }
}
}
pub fn random_range(min: f32, max: f32) -> Self { pub fn random_range(min: f32, max: f32) -> Self {
let rng = &mut thread_rng(); let rng = &mut thread_rng();
Vec3::new(rng.gen_range(min..max), rng.gen_range(min..max), rng.gen_range(min..max)) Vec3::new(rng.gen_range(min..max), rng.gen_range(min..max), rng.gen_range(min..max))