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

2024-03-07 18:36

本文主要是介绍光线追踪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
 

这篇关于光线追踪10 - Dielectrics( 电介质 )的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/784442

相关文章

SpringCloud之Sleuth(Micrometer)+ZipKin分布式链路追踪

(学习笔记) 1、分布式链路追踪概述 问题:在微服务框架中,一个由客户端发起的请求在后端系统中会经过多个不同的的服务节点调用来协同产生最后的请求结果,每一个前段请求都会形成一条复杂的分布式服务调用链路,链路中的任何一环出现高延时或错误都会引起整个请求最后的失败。 在分布式与微服务场景下,我们需要解决如下问题:   在大规模分布式与微服务集群下,如何实时观测系统的整体调用链路情况。

钓鱼邮件真相追踪:XDR见招拆招!

钓鱼陷阱,财富“蒸发” 如果一家规模5000人、业务遍布全球的企业之中有一位员工不小心点进了一个钓鱼邮件,会发生什么……?终端失陷?数据泄露?失去客户信任? 最让人破碎的当然是……核心资产泄露,钱没了!! 人有失手,"鱼"有逃命 某大型零售企业财务部门小张收到一封看似来自公司财务部的邮件,由于内容与其实际工作情况相符,小张打开了邮件中的附件,并点击了附件里的下载链接

用异步序列优雅的监听 SwiftData 2.0 中历史追踪记录(History Trace)的变化

概述 WWDC 24 一声炮响为我们送来 Swift 6.0 的同时,也颇为“低调”的推出了 SwiftData 2.0。在新版本的 SwiftData 中,苹果为其新增了多个激动人心的新特性,其中就包括历史记录追踪(History Trace)。 不过,历史记录追踪目前看起来似乎有些“白璧微瑕”,略微让人有些不爽。在这里就让我们看看如何利用 Swift 结构化并发中的异步序列(Asy

是噱头还是低成本新宠?加州大学用视觉追踪实现跨平台的机器手全掌控?

导读: 在当今科技飞速发展的时代,机器人的应用越来越广泛。从工业生产到医疗保健,从物流运输到家庭服务,机器人正在逐渐改变我们的生活方式。而机器人的有效操作和控制,离不开高效的遥操作系统。今天,我们要介绍的是UC San Diego 的新研究,一种创新的跨平台视觉外骨骼系统ACE。©️【深蓝AI】编译 1. ACE系统的背景与概述 近年来,机器人研究者们通常利用真实世界的机器人数据,来训练

GAMES202——作业5 实时光线追踪降噪(联合双边滤波、多帧的投影与积累、À-Trous Wavelet 加速单帧降噪)

任务         1.实现单帧降噪         2.实现多帧投影         3.实现多帧累积         Bonus:使用À-Trous Wavelet 加速单帧降噪 实现         单帧降噪         这里实现比较简单,直接根据给出的联合双边滤波核的公式就能实现          Buffer2D<Float3> Denoiser::Fil

追踪uboot下tftp命令的代码执行过程-Nagul

一、网卡驱动的添加  网络在uboot中的启动是在uboot的第二阶段启动代码中 /lib_arm/board.c [cpp]  view plain copy void start_armboot (void){}   里面有网络初始化函数 [cpp]  view plain copy eth_initialize(gd->bd)

SpringBoot链路追踪②:如何集成?

首先下载Zipkin的jar包:Central Repository: io/zipkin/zipkin-server (maven.org) 根据自己的项目版本。我的版本分别是: <spring-boot.version>2.7.18</spring-boot.version> <spring-cloud.version>2021.0.8</spring-cloud.version> 选

分布式系统中的Dapper与Twitter Zipkin:链路追踪技术的实现与应用

目录 一、什么是链路追踪? 二、核心思想Dapper (一)Dapper链路追踪基本概念概要 (二)Trace、Span、Annotations Trace Span Annotation 案例说明 (三)带内数据与带外数据 带外数据 带内数据 数据的传递与集中 (四)采样 采样的目的 采样率的调整 采样机制的实现 (五)存储 为什么选择 BigTable 存

windows平台调用函数堆栈的追踪方法

转自:http://blog.csdn.net/lanuage/article/details/52203447 在windows平台,有一个简单的方法来追踪调用函数的堆栈,就是利用函数CaptureStackBackTrace,但是这个函数不能得到具体调用函数的名称,只能得到地址,当然我们可以通过反汇编的方式通过地址得到函数的名称,以及具体调用的反汇编代码,但是对于有的时候我们需要直接得

旅行追踪和行程规划工具AdventureLog

什么是 AdventureLog ? AdventureLog 是一种记录您的旅行并与世界分享的简单方法。您可以在日志中添加照片、笔记等。跟踪您访问过的国家、探索去过的地区和地方。您还可以查看您的旅行统计数据和里程碑。AdventureLog 旨在成为您终极的旅行伴侣,帮助您记录您的冒险经历并轻松规划新的冒险经历。 主要功能: 使用姓名、日期、地点、描述和评级等字段记录过去的冒险经