光线追踪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);
        else
            direction = 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);
        else
            direction = refract(unit_direction, rec.normal, refraction_ratio);

        scattered = ray(rec.p, direction);
        return true;
    }
  private:    double ir; // Index of Refraction

    static 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
 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot 热部署。

SpringBoot 热部署。 文章目录 SpringBoot 热部署。 pom.xml。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional…

.Net利用Microsoft.Extensions.DependencyInjection配置依赖注入

一、概述 为了让接口程序更加模块化和可测试&#xff0c;采用依赖注入的方式调用接口方法。 二、安装Microsoft.Extensions.DependencyInjection 在NuGet里面搜索Microsoft.Extensions.DependencyInjection&#xff0c;并进行安装。 三、代码编写 3.1 创建Service 实现类…

SpringMVC-异步调用,拦截器与异常处理

1.异步调用 1.发送异步请求 <a href"javascript:void(0);" id"testAjax">访问controller</a> <script type"text/javascript" src"js/jquery-3.7.1.js"></script> <script type"text/javascript&qu…

mysql 数据库查询 查询字段用逗号隔开 关联另一个表并显示

文章目录 问题描述解决方案 问题描述 如下如所示&#xff1a; 表一&#xff1a;wechat_dynamically_config表&#xff0c;重点字段&#xff1a;wechat_object 表二&#xff1a;wechat_object表&#xff0c;重点字段&#xff1a;wxid 需求&#xff1a;根据wechat_dynamically_…

你不知道的Postman的Mock接口测试,看这一篇就够了

前言 创建Mock服务 你可以从Postman已有的测试集(Collection)中创建Mock Server或者直接创建Mock Server&#xff08;我们这里选择从已有的测试集中创建Mock Server&#xff09; Mock server详细配置页面&#xff0c;在此页面中我们可以设置&#xff1a; Name the mock serv…

JOSEF约瑟 同步检查继电器 BT-1B/R200 额定电压100/100V, 直流电压220V

BT-1B/R型同步检查继电器型号&#xff1a; BT-1B型同步检查继电器&#xff1b; BT-1B/R200同步检查继电器; BT-1B/R160同步检查继电器; BT-1B/R130同步检查继电器; BT-1B/R120同步检查继电器; BT -1B/R90同步检查继电器; 用途 BT-1B/R型同步检查继电器用于两端供电系统…

小程序应用为亲子陪伴趣味赋能

导言&#xff1a; 在现代社会&#xff0c;随着工作压力的增加和生活节奏的加快&#xff0c;家长们往往面临着亲子陪伴不足的困扰。而小程序应用的普及&#xff0c;为家庭提供了更多的亲子活动选择和参与方式。虎克技术公司成功帮助客户通过开发小程序实现亲子陪伴的赋能&#x…

北斗卫星助力无人机在沙漠播种,促进沙漠治理

北斗卫星助力无人机在沙漠播种&#xff0c;促进沙漠治理 近年来&#xff0c;随着科技的不断发展&#xff0c;北斗卫星和无人机技术的结合被广泛应用于沙漠治理领域&#xff0c;为解决沙漠化问题提供了全新的思路和解决方案。 近日&#xff0c;黄河“几字弯”北岸的内蒙古自治…

016集——n等分cad多段线、弧、圆等——vba实现

cad命令行输入“div”选择图元后可n等分图元&#xff0c;若图中有大量图元需要n等分&#xff0c;这时可借助vba一键实现。 代码逻辑框架为&#xff1a;通过创建句柄函数来选择实体&#xff0c;通过sendcommand函数向命令行输入命令。 先来个小程序练练手&#xff1a;在屏幕上指…

qt练习案例

记录一下qt练习案例&#xff0c;方便学习qt知识点 基本部件 案例1 需求&#xff0c;做一个标签&#xff0c;显示"你好"知识点&#xff0c;QLabel画面 4. 参考&#xff0c;Qt 之 QLabel 案例2 需求&#xff0c;做一个标签&#xff0c;显示图片 知识点&#xff0c;…

R语言lavaan结构方程模型(SEM)

结构方程模型&#xff08;Sructural Equation Modeling&#xff0c;SEM&#xff09;是分析系统内变量间的相互关系的利器&#xff0c;可通过图形化方式清晰展示系统中多变量因果关系网&#xff0c;具有强大的数据分析功能和广泛的适用性&#xff0c;是近年来生态、进化、环境、…

基于dashscope在线调用千问大模型

前言 dashscope是阿里云大模型服务平台——灵积提供的在线API组件。基于它&#xff0c;无需本地加载大模型&#xff0c;通过在线方式访问云端大模型来完成对话。 申请API key 老规矩&#xff1a;要想访问各家云端大模型&#xff0c;需要先申请API key。 对于阿里云&#x…

【开源】SpringBoot框架开发陕西非物质文化遗产网站

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 设计目标2.2 研究内容2.3 研究方法与过程2.3.1 系统设计2.3.2 查阅文献2.3.3 网站分析2.3.4 网站设计2.3.5 网站实现2.3.6 系统测试与效果分析 三、系统展示四、核心代码4.1 查询民间文学4.2 查询传统音乐4.3 增改传统舞…

有趣的CSS - 圆点交互按钮

大家好&#xff0c;我是 Just&#xff0c;这里是「设计师工作日常」&#xff0c;今天分享的是一个有意思的圆点交互文字按钮效果。 《有趣的css》系列最新实例通过公众号「设计师工作日常」发布。 目录 整体效果核心代码html 代码css 部分代码 完整代码如下html 页面css 样式页…

关于汽车E\E架构演进的思考(1)

目录 1.电子电气架构概述 2.下一代架构面临的挑战 2.1 如何实现功能融合 2.2 整车通信的限制 2.3 如何保证融合ECU的功能安全和信息安全 3.小结 最近这段时间&#xff0c;汽车电子电气架构迭代的风吹得很猛烈。其中就包括国际MCU大厂纷纷对自家新推出的高性能MCU打得广告…

QEMU调试——通过获取设备树(dtb文件)查询开发板的外设地址信息

1、适用场景 使用qemu时&#xff0c;想快速知道开发板的地址空间映射情况&#xff0c;特别是某些外设控制器的寄存器基地址 2、查询QEMU支持的开发板 qemu-system-riscv32.exe -M ? 3、获取开发板对应的dtb文件 1、qemu-system-riscv32.exe -M nuclei_evalsoc 2、dumpdtb nucl…

HelpLook VS GitBook:知识库优劣详解

在信息爆炸的时代&#xff0c;企业要保持竞争优势&#xff0c;就必须善于管理和利用内部的知识资产。企业知识库作为一种集中存储和共享知识的工具&#xff0c;正在成为现代企业不可或缺的一部分。 HelpLook和Gitbook是提供专业知识库的两个平台&#xff0c;也被大众熟知。它们…

@ResponseStatus

目录 概述&#xff1a; 用途&#xff1a; 参数&#xff1a; 注意事项&#xff1a; 自定义异常类&#xff1a; 底层原理&#xff1a; 概述&#xff1a; 在 Spring MVC 中&#xff0c;我们有很多方法来设置 HTTP 响应的状态码其中最直接的方法&#xff1a;使用 ResponseSt…

教程篇:Groq API+沉浸式翻译插件 体验最快AI翻译

1、进入https://console.groq.com/keys 申请一个API&#xff08;目前免费&#xff01;抓紧白嫖&#xff09; 2、安装Chrome插件&#xff1a;沉浸式翻译。 https://immersivetranslate.com/ 3、照着抄&#xff08;注意将apikey&#xff0c;换成自己申请的groq的api-key&…

中间件 | Redis - [基本信息]

INDEX 1 常规用法2 QPS3 pipeline 1 常规用法 分布式锁 最常见用法&#xff0c;需要注意分布式锁的redis需要单点 分布式事务 分布式事务中&#xff0c;核心的技术难点其实是分布式事务这个事本身作为数据的持久化 2PC&#xff0c;比如 seata 的 AT 模式下&#xff0c;将 un…