光线追踪10 - Dielectrics( 电介质 )

水、玻璃和钻石等透明物质都属于电介质。当光线射入这些物质时,会分为反射光线和折射(透射)光线。我们将通过随机选择反射或折射来处理这一现象,每次相互作用只生成一条散射光线。

11.1 Refraction
 最难调试的部分是折射光线。通常情况下,如果存在折射光线,我会首先让所有光线都发生折射。对于这个项目,我尝试在场景中放置了两个玻璃球,结果如下(我还没有告诉你如何正确或错误地完成它,但很快会告诉你!):

Image 15: Glass first

这是正确的吗?在现实生活中,玻璃球看起来很奇怪。但不,这不正确。世界应该被颠倒过来,而且没有奇怪的黑色物体。我只是将光线直接打印在图像的中间,显然是错误的。这种方法通常能够解决问题。

11.2 Snell's Law
折射现象可由斯涅尔定律描述:
η⋅sinθ=η′⋅sinθ′
其中θ和θ′是相对于法线的角度,η和η′(pronounced “eta” and “eta prime”)是折射率(一般情况下,空气为1.0,玻璃为1.3-1.7,钻石为2.4)。几何关系如下图所示:


Figure 17: Ray refraction

为了确定折射光线的方向,我们需要解出sinθ':
sinθ
=(η/η)⋅sinθ

    在折射面的一侧,存在一条折射光线R'和一条法线n',它们之间存在一个角度θ'。我们可以将R'分为垂直于n'的部分和平行于n'的部分:R′=R′+R′||
如果我们解出 RR′||,我们得到:

    如果你愿意,你可以自己去证明这一点,但我们将把它视为事实并继续。本书的其余部分不需要你理解这个证明。我们知道右侧每一项的值,除了cosθ。众所周知,两个向量的点积可以用它们之间夹角的余弦来解释:
a⋅b=|a||b|cosθ
如果我们将向量a和b限制为单位向量:
a⋅b=cosθ

现在我们可以用已知量重写R'⊥:


当我们将它们重新组合在一起时,我们可以编写一个函数来进行计算R'

...
inline vec3 reflect(const vec3& v, const vec3& n) {
return v - 2*dot(v,n)*n;
}inline vec3 refract(const vec3& uv, const vec3& n, double etai_over_etat) {
auto cos_theta = fmin(dot(-uv, n), 1.0);
vec3 r_out_perp =  etai_over_etat * (uv + cos_theta*n);
vec3 r_out_parallel = -sqrt(fabs(1.0 - r_out_perp.length_squared())) * n;
return r_out_perp + r_out_parallel;
}

Listing 65: [vec3.h] Refraction function

而那种经常发生折射的介质是:

...
class metal : public material {
...
};class dielectric : public material {public:
dielectric(double index_of_refraction) : ir(index_of_refraction) { }bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered) const override {attenuation = color(1.0, 1.0, 1.0);double refraction_ratio = rec.front_face ? (1.0/ir) : ir;vec3 unit_direction = unit_vector(r_in.direction());vec3 refracted = refract(unit_direction, rec.normal, refraction_ratio);scattered = ray(rec.p, refracted);return true;}
private:double ir; // Index of Refraction
};

Listing 66: [material.h] Dielectric material class that always refracts

现在我们将更新场景,将左侧和中间的球体改为玻璃材质:

auto material_ground = make_shared<lambertian>(color(0.8, 0.8, 0.0));
auto material_center = make_shared<dielectric>(1.5);
auto material_left = make_shared<dielectric>(1.5);
auto material_right  = make_shared<metal>(color(0.8, 0.6, 0.2), 1.0);

Listing 67: [main.cc] Changing left and center spheres to glass

得到如下:

Image 16: Glass sphere that always refracts


11.3  Total Internal Reflection(全反射
那看起来肯定是不对的。一个麻烦的实际问题是,当光线处于折射率较高的介质中时,斯涅尔定律没有真实解,因此无法发生折射。如果我们回顾一下斯涅尔定律和sinθ′的推导过程:
sinθ′=(η/η′)⋅sinθ
    如果光线在玻璃内部,外部是空气(η=1.5,η=1.0):
sinθ
=(1.5/1.0)⋅sinθ
sin
θ的值不能大于1。因此,如果...
1.5/1.0)⋅sinθ>1.0
方程两边的等式被打破,无法存在解。如果没有解,光就无法折射,因此必须反射光线:

if (refraction_ratio * sin_theta > 1.0) {
// Must Reflect...
} 
else {
// Can Refract...
}

Listing 68: [material.h] Determining if the ray can refract

在这种情况下,所有光线都被反射回来,因为通常是在固体物体内部发生的,所以被称为“全内反射”。这就是为什么有时候当你在水下时,水和空气的边界会像一个完美的镜子一样反射光线。

我们可以使用三角函数的性质来求解sin_theta:

double cos_theta = fmin(dot(-unit_direction, rec.normal), 1.0);
double sin_theta = sqrt(1.0 - cos_theta*cos_theta);
if (refraction_ratio * sin_theta > 1.0) {
// Must Reflect
...
}
else {
// Can Refract
...
}

Listing 69: [material.h] Determining if the ray can refract


并且总是(当可能时)发生折射的介质是:

class dielectric : public material {public:
dielectric(double index_of_refraction) : ir(index_of_refraction) {}bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered)const override {attenuation = color(1.0, 1.0, 1.0);double refraction_ratio = rec.front_face ? (1.0/ir) : ir;vec3 unit_direction = unit_vector(r_in.direction());double cos_theta = fmin(dot(-unit_direction, rec.normal), 1.0);double sin_theta = sqrt(1.0 - cos_theta*cos_theta);bool cannot_refract = refraction_ratio * sin_theta > 1.0;vec3 direction;if (cannot_refract) direction = reflect(unit_direction, rec.normal);elsedirection = refract(unit_direction, rec.normal, refraction_ratio);scattered = ray(rec.p, direction);return true;
}
private:    double ir; // Index of Refraction
};

Listing 70: [material.h] Dielectric material class with reflection

衰减始终为1——玻璃表面不吸收任何东西。如果我们使用这些参数进行尝试:

auto material_ground = make_shared<lambertian>(color(0.8, 0.8, 0.0));
auto material_center = make_shared<lambertian>(color(0.1, 0.2, 0.5));
auto material_left = make_shared<dielectric>(1.5);
auto material_right = make_shared<metal>(color(0.8, 0.6, 0.2), 0.0);

Listing 71: [main.cc] Scene with dielectric and shiny sphere

从而得到

Image 17: Glass sphere that sometimes refracts

11.4 Schlick Approximation 希克近似
现实玻璃的反射率随角度变化而不同 - 当以陡峭的角度看窗户时,它会变成镜子。有一个复杂的方程可以描述这种变化,但几乎每个人都使用克里斯托夫·希克(Christophe Schlick)提供的便宜且令人惊讶地准确的多项式近似。这可以得到我们完整的玻璃材质:

class dielectric : public material {public:
dielectric(double index_of_refraction) : ir(index_of_refraction) {}bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered)const override {attenuation = color(1.0, 1.0, 1.0);double refraction_ratio = rec.front_face ? (1.0/ir) : ir;vec3 unit_direction = unit_vector(r_in.direction());double cos_theta = fmin(dot(-unit_direction, rec.normal), 1.0);double sin_theta = sqrt(1.0 - cos_theta*cos_theta);bool cannot_refract = refraction_ratio * sin_theta > 1.0;vec3 direction;if (cannot_refract || reflectance(cos_theta, refraction_ratio) > random_double())direction = reflect(unit_direction, rec.normal);elsedirection = refract(unit_direction, rec.normal, refraction_ratio);scattered = ray(rec.p, direction);return true;}private:    double ir; // Index of Refractionstatic double reflectance(double cosine, double ref_idx) {// Use Schlick's approximation for reflectance.auto r0 = (1-ref_idx) / (1+ref_idx);r0 = r0*r0;return r0 + (1-r0)*pow((1 - cosine),5);}
};

Listing 72: [material.h] Full glass material


11.5 Modeling a Hollow Glass Sphere(建模一个空心玻璃球)
使用介电球体的一个有趣且简单的技巧是注意到,如果使用负半径,几何形状不受影响,但表面法线指向内部。这可以用作一个气泡来制作空心玻璃球:

...
world.add(make_shared<sphere>(point3( 0.0, -100.5, -1.0), 100.0, material_ground));
world.add(make_shared<sphere>(point3( 0.0,    0.0, -1.0),   0.5, material_center));
world.add(make_shared<sphere>(point3(-1.0,    0.0, -1.0),   0.5, material_left));
world.add(make_shared<sphere>(point3(-1.0,    0.0, -1.0),  -0.4, material_left));
world.add(make_shared<sphere>(point3( 1.0,    0.0, -1.0),   0.5, material_right));
...

Listing 73: [main.cc] Scene with hollow glass sphere


得到效果

Image 18: A hollow glass sphere
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/726730.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

铅酸废电池回收螯合树脂CH-90除镉系统

项目名称 某再生资源公司铅酸废电池回收除镉项目 工艺选择 化学沉淀系统过滤系统螯合树脂深度除镉系统 工艺原理 镉离子沉淀后进入螯合树脂除镉树脂 项目背景 铅酸蓄电池作为广泛应用的化学电源&#xff0c;凭借其电压稳定性、优异的功率性能&#xff0c;以及高性价比等…

LVS集群(Linux Virtual server)

集群概念lvs模型lvs调度算法lvs实现lvs高可用性&#xff0c;负载均衡 1 集群和分布式 系统性能扩展方式&#xff1a; Scale UP&#xff1a;垂直扩展&#xff0c;向上扩展,增强&#xff0c;性能更强的计算机运行同样的服务 升级单机的硬件设备Scale Out&#xff1a;水平扩展…

Linux Ubuntu系统安装MySQL并实现公网连接本地数据库【内网穿透】

文章目录 前言1 .安装Docker2. 使用Docker拉取MySQL镜像3. 创建并启动MySQL容器4. 本地连接测试4.1 安装MySQL图形化界面工具4.2 使用MySQL Workbench连接测试 5. 公网远程访问本地MySQL5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定TCP地址远程访问 前言 本文主…

el-table 插入单选并进行校验

<template><div><el-form :model"list" ref"ruleForm"><el-table :data"list.tableData" style"width: 100%"><el-table-column prop"time" label"日期" width"180"><…

STM32 学习9 中断、外部中断及定时器中断

STM32 学习9 中断、外部中断及定时器中断 一、STM32中断介绍一、STM32中断介绍1. 什么是中断&#xff1f;2. 中断在嵌入式系统中的作用和重要性3. STM32中断的概述 4. 中断的优先级4.1 中断优先级级别4.2 中断优先级分类&#xff08;1&#xff09;硬件优先级&#xff08;2&…

挑战杯 基于深度学习的目标检测算法

文章目录 1 简介2 目标检测概念3 目标分类、定位、检测示例4 传统目标检测5 两类目标检测算法5.1 相关研究5.1.1 选择性搜索5.1.2 OverFeat 5.2 基于区域提名的方法5.2.1 R-CNN5.2.2 SPP-net5.2.3 Fast R-CNN 5.3 端到端的方法YOLOSSD 6 人体检测结果7 最后 1 简介 &#x1f5…

Nuxt2升级Nuxt3指南(二):nuxt.config.js配置文件

一、代码移植原则 前置说明&#xff1a;根据项目开发的实际情况&#xff0c;本次升级不采用Typescript。 升级的原则是开始尽量的简单配置&#xff0c;将代码分阶段逐步移植到新版本框架上&#xff0c;遇到问题逐一排查解决。 大致阶段&#xff0c;可以分为&#xff1a; 第一…

在idea中如何开启项目的热部署

热部署&#xff1a;就是当我们IDEA的项目在运行期间&#xff0c;我们修改代码以后&#xff0c;不需要我们自己重启项目&#xff0c;IDEA就会自动的重启项目 在idea中开启项目热部署的步骤 第一步&#xff1a;引入热部署的依赖 <dependency><groupId>org.springfr…

STP---生成树协议

STP的作用 a)Stp通过阻塞端口来消除环路&#xff0c;并能够实现链路备份目的 b)消除了广播风暴 c)物理链路冗余&#xff0c;网络变成了层次化结构的网络 STP操作 选举一个根桥每个非根交换机选举一个根端口每个网段选举一个指定端口阻塞非根&#xff0c;非指定端口 STP--生成树…

基于单片机的智能空调设计

目 录 摘 要 I Abstract II 引 言 1 1 系统整体设计 3 1.1 系统方案设计 3 1.2 系统工作原理 3 2 硬件设计 5 2.1 电源模块设计 5 2.1.1 电源模块选择 5 2.1.2 电源模块电路设计 5 2.2 单片机模块设计 5 2.2.1 单片机型号选择 5 2.2.2 单片机模块电路设计 6 2.3 按键模块设计 …

vue3中el-input输入无效的原因之一

表单的model用的是&#xff1a;reactive let updateForm reactive({ id: 0, className: "" }); reactive的数据不能这么赋值&#xff0c;会破坏响应性 错误方法&#xff08;&#xff09;{ updateForm { id: 0, className: "asdasdas" }; } 解决方法&…

1.5如何缓解图像分类任务中训练数据不足带来的问题?

1.5 图像数据不足时的处理方法 场景描述 在机器学习中&#xff0c;绝大部分模型都需要大量的数据进行训练和学习(包括有监督学习和无监督学习)&#xff0c;然而在实际应用中经常会遇到训练数据不足的问题。 比如图像分类&#xff0c;作为计算机视觉最基本的任务之一&#xff0…

高效提升控制效率 | 基于ACM32 MCU的LED灯箱控制器方案

LED灯箱上各种文字、图案有序跳跃、交替辉映&#xff0c;产生强烈的视觉冲击力&#xff0c;被广泛应用于商场、美容美发、宾馆、娱乐场所等地方。 锁存器的工作原理 在LED和数码管显示方面&#xff0c;要维持一个数据的显示&#xff0c;往往要持续的快速的刷新。尤其是在四段八…

Python算法100例-3.6 自守数

1.问题描述2.问题分析3.算法设计4.求给定数的位数5.分离给定数中的最后几位6.确定程序框架7.完整的程序 1&#xff0e;问题描述 自守数是指一个数的平方的尾数等于该数自身的自然数。例如&#xff0c; 5 2 25 &#xff0c; 2 5 2 625 &#xff0c; 7 6 2 5776 &#xff0c…

java基础-锁之volatilesynchronized

文章目录 volatilevolatile内存语义volatile的可见性volatile无法保证原子性volatile禁止重排优化硬件层的内存屏障volatile内存语义的实现下面是基于保守策略的JMM内存屏障插入策略。下面是保守策略下&#xff0c;volatile写插入内存屏障后生成的指令序列示意图下图是在保守策…

Android APP性能指标(二)

文章目录 一、响应时间1.1 数据获取1.2 响应时间指标测试点1.3 启动速度测试点1.4 响应时间测试解决方法 二、流量2.1 数据获取2.2 流量测试关注点2.3 测试标准 三、电量3.1 连接手机3.2 数据获取3.3 获取APP的UID3.3 重置电池数据收集数据3.4 电量指标测试 四、温度五、性能测…

打包系统待优化点

Base.Widget.AppCompat.ActivityChooserView中相关资源重复 D:\channelPackage\ToolConfigPath\games\dcpPro\100081\mumu\tempRes\values\attrs.xml:1171: error: duplicate value for resource attr/displayOptions with config . D:\channelPackage\ToolConfigPath\games\d…

【C++精简版回顾】19.异常处理

1.throw抛出问题 int print(int a,int b) {if (b 0)throw b;return a / b; } 2.try与catch解决问题 try {print(2, 0); } catch (int b) {cout << "竟然是&#xff1a;"<<b<<endl; } 结果&#xff1a; 补充1&#xff1a;可以抛出字符串等 1.throw…

day13_微服务监控Nginx(微服务集成SBA)

文章目录 1 微服务系统监控1.1 监控系统的意义1.2 SBA监控方案1.3 SBA实战1.3.1 创建SBA服务端1.3.2 微服务集成SBA 1.4 微服务集成logback1.5 配置邮件告警 2 Nginx2.1 Nginx简介2.2 下载和安装2.2.1 方式1&#xff1a;window本地安装2.2.1.1 下载2.2.1.2 安装2.2.1.3 目录结构…

关于 typeof 与 instanceof 区别引出的原型对象问题

一、关于 typeof 与 instanceof 区别&#xff1a; typeof 和 instanceof 是 JavaScript 中用于检查变量类型的两个不同操作符&#xff0c;它们在使用上有着明显的区别和不同的适用场景。 typeof typeof 是一个一元操作符&#xff0c;用于返回一个变量或表达式的数据类型的字符…