首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >一个周末的光线追踪-折射问题

一个周末的光线追踪-折射问题
EN

Stack Overflow用户
提问于 2022-06-30 07:16:14
回答 1查看 262关注 0票数 0

我,目前正在通过射线追踪一个周末来熟悉锈病。添加介电材料(玻璃)会让我头疼:我的折射没有倒转!

下面是用于向量结构的代码:

代码语言:javascript
复制
impl Vec3 {
    pub fn new(x: f64, y: f64, z: f64) -> Vec3 { Vec3 {x, y, z} }
    pub fn x(&self) -> f64 { self.x }
    pub fn y(&self) -> f64 { self.y }
    pub fn z(&self) -> f64 { self.z }
    pub fn length_squared(&self) -> f64 {
        self.x * self.x + self.y * self.y + self.z * self.z
    }
    pub fn length(&self) -> f64 { self.distance(&Vec3::default()) }
    pub fn unit_vector(&self) -> Vec3 {
        let length = self.length();
        Vec3::new(self.x / length, self.y / length, self.z / length)
    }
    pub fn dot(&self, v:&Vec3) -> f64 {
        self.x * v.x + self.y * v.y + self.z * v.z
    }
    pub fn cross(&self, v:&Vec3) -> Vec3 {
        Vec3::new(
            self.y * v.z - self.z * v.y,
            self.z * v.x - self.x * v.z,
            self.x * v.y - self.y * v.x
        )
    }
    pub fn distance(&self, other: &Vec3) -> f64 {
        let dx = self.x - other.x();
        let dy = self.y - other.y();
        let dz = self.z - other.z();
        (dx * dx + dy * dy + dz * dz).sqrt()
    }

    pub fn random(min: f64, max:f64) -> Self {
        let between = Uniform::from(min..max);
        let mut rng = rand::thread_rng();
        Vec3::new(
            between.sample(&mut rng),
            between.sample(&mut rng),
            between.sample(&mut rng))
    }
    pub fn random_in_unit_sphere() -> Self {
        loop {
            let v = Vec3::random(-1.0, 1.0);
            if v.length_squared() < 1.0 {
                return v;
            }
        }
    }
    pub fn random_in_hemisphere(normal: &Vec3) -> Self {
        let vec = Vec3::random_in_unit_sphere();
        if vec.dot(normal) > 0.0 {
            vec
        } else {
            -vec
        }
    }
    pub fn random_unit_vector() -> Self { Vec3::random_in_unit_sphere().unit_vector() }
    pub fn near_zero(&self) -> bool {
        const MAXIMUM_DISTANCE_FROM_ZERO:f64 = 1e-8;
        self.x.abs() < MAXIMUM_DISTANCE_FROM_ZERO &&
            self.y.abs() < MAXIMUM_DISTANCE_FROM_ZERO &&
            self.z.abs() < MAXIMUM_DISTANCE_FROM_ZERO
    }
    pub fn reflected(&self, normal: &Vec3) -> Vec3 {
        let dp = self.dot(normal);
        let dp = dp * 2.0 * (*normal);
        *self - dp
    }
    pub fn refract(&self, normal: &Vec3, etai_over_etat: f64) -> Vec3 {
        let dot = (-(*self)).dot(normal);
        let cos_theta = dot.min(1.0);
        let out_perp = etai_over_etat * ((*self) + cos_theta * (*normal));
        let inner =  1.0 - out_perp.length_squared();
        let abs = inner.abs();
        let r = -(abs.sqrt());
        let out_parallel = r * (*normal);
        out_perp + out_paralle
    }
}

这是我对材料的散射函数:

代码语言:javascript
复制
fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option<Ray>, Color)> {
        let refraction_ratio = if hit_record.front_face {
            1.0/self.index_of_refraction
        } else {
            self.index_of_refraction
        };
        let unit_direction = ray.direction().unit_vector();
        let cos_theta = ((-unit_direction).dot(&hit_record.normal)).min(1.0);
        let sin_theta = (1.0 - cos_theta*cos_theta).sqrt();
        let cannot_refract = refraction_ratio * sin_theta > 1.0;
        let reflectance = Dielectric::reflectance(cos_theta, refraction_ratio);
        let mut rng = rand::thread_rng();
        let color = Color::new(1.0, 1.0, 1.0);
        if cannot_refract || reflectance > rng.gen::<f64>() {
            let reflected = unit_direction.reflected(&hit_record.normal);
            let scattered = Ray::new(hit_record.point, reflected);
            Some((Some(scattered), color))
        } else {
            let direction = unit_direction.refract(&hit_record.normal, refraction_ratio);
            let scattered = Ray::new(hit_record.point, direction);
            Some((Some(scattered), color))
        }
    }

如果我否定了折射结果的xy,但看上去还是明显错误的话,这是可行的。另外,如果我在书中倒退几步,实现100%的折射玻璃,我的球体是纯黑的,我必须否定z轴才能看到任何东西。所以我的折射码出了点问题,但我搞不懂

完整代码:https://phlaym.net/git/phlaym/rustracer/src/commit/89a2333644a82f2645e4ad554eadf7d4f142f2c0/src

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-06-30 13:02:21

在检查球是否被击中的方法src/hittable.rs中,c代码如下所示。

代码语言:javascript
复制
// Find the nearest root that lies in the acceptable range.
auto root = (-half_b - sqrtd) / a;
if (root < t_min || t_max < root) {
    root = (-half_b + sqrtd) / a;
    if (root < t_min || t_max < root)
        return false;
}

您已经使用以下清单将其移植为生锈代码:

代码语言:javascript
复制
let root = (-half_b - sqrtd) / a;
if root < t_min || t_max < root {
    let root = (-half_b + sqrtd) / a;
    if root < t_min || t_max < root {
       return None;
    }
}

这里的问题是第二个let root。您已经为内部括号创建了一个具有自己范围的新变量,但没有更改前面定义的已经创建的变量。要做到这一点,就必须使其成为mutable

代码语言:javascript
复制
let mut root = (-half_b - sqrtd) / a;
if root < t_min || t_max < root {
    root = (-half_b + sqrtd) / a;
    if root < t_min || t_max < root {
       return None;
    }
}

此外,我还在src/ray.rs中更改了以下内容

代码语言:javascript
复制
return match scattered {
    Some((scattered_ray, albedo)) => {
        match scattered_ray {
            Some(sr) => {
                albedo * sr.pixel_color(world, depth-1)
            },
            None => albedo
        }
    },
    None => { return Color::default() }
};

匹配相应的C代码。注意使用的unwrap

代码语言:javascript
复制
let scattered = rect.material.scatter(self, &rect);
if let Some((scattered_ray, albedo)) = scattered {
    return albedo * scattered_ray.unwrap().pixel_color(world, depth - 1)
}
return Color::default()

并移除试图纠正这些反射的尝试:

代码语言:javascript
复制
let reflected = Vec3::new(-reflected.x(), reflected.y(), -reflected.z());

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72811706

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档