这里的“介质”是指光可以通过的物质。比如,水,玻璃等。也就是我们常说的具有一定透明度的物质。

24.1 预备知识

24.1.1 反射和折射光线的方向向量

反射光线的方向向量:

漫反射:n + p。其中n为单位法向量,p为“起点在原点,长度小于1,方向随机”的随机向量。

镜面反射:v - 2*dot(v,n)*n。其中n为单位法向量,v为入射光线的方向向量。

折射光线的方向向量:

注意:此处的N是普通法向量(不需要是单位法向量)。

要求的的折射光线的单位方向向量T(这里设置|T|=1,方便计算。),I’和-N’都是在|T|=1的基础上添加的辅助向量。

T 的求解方式如下:

uv = unit_vector(v);

dt = dot(uv, n);

discriminat = 1.0 -ni_over_nt*ni_over_nt*(1-dt*dt);

refracted = ni_over_nt*(uv - n*dt) - n*sqrt(discriminat);

24.1.2 介质界面的反射系数

当光从一种介质进入另一种介质时,实际上,有一部分光会折射进入另一种介质,有另一部分光则会反射回来。反射系数=反射光振幅(能量)/入射光振幅(能量)。

Wiki上对“reflection coefficient”的介绍:

https://en.wikipedia.org/wiki/Reflection_coefficient

反射系数的求解是是一个非常复杂的过程,Christophe Schlick这个人提供一个逼近公式,这个公式被称为“ChristopheSchlick’s Approximation”。Wiki链接:

https://en.wikipedia.org/wiki/Schlick%27s_approximation

24.2 模拟介质的颜色(反射系数为0)

反射系数为0,只有折射,没有反射。

但是,光从光密介质进入光疏介质时可能出现的全反射(只有反射,没有折射)是需要考虑的。

又但是,此章节的的物体是均匀球体(根据球体的几何特点和光路可逆原理),从球体内进入空气的光线不会发生全反射。

所以,所以,此章节的内容不会有反射系数,也不会有全反射。

需要添加的code如下:

----------------------------------------------dielectic.h------------------------------------------

dielectic.h

#ifndef DIELECTRIC_H
#define DIELECTRIC_H#include <material.h>
#include <metal.h>
#include "log.h"class dielectric : public material
{public:dielectric(float ri) : ref_idx(ri) {}virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered) const;float ref_idx;
};#endif // DIELECTRIC_H

----------------------------------------------dielectic.cpp------------------------------------------

dielectic.cpp

#include "dielectric.h"bool refract(const vec3& v, const vec3& n, float ni_over_nt, vec3& refracted) {
/*该函数计算折射光线的方向向量。计算方法正是24.1.1中说明的。ni_over_nt为入射介质的折射指数和折射介质的折射指数的比值*/vec3 uv = unit_vector(v);float dt = dot(uv, n);float discriminat = 1.0 - ni_over_nt*ni_over_nt*(1-dt*dt);if (discriminat > 0) {refracted = ni_over_nt*(uv - n*dt) - n*sqrt(discriminat);return true;}else
/*根号里面的内容小于零,说明折射光线的方向向量无实根,即没有折射光线,即出现全反射。所以,折射光线函数return false*/return false;
}bool dielectric::scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered) const {vec3 outward_normal;vec3 reflected = reflect(r_in.direction(), rec.normal);
float ni_over_nt;
/* ni_over_nt为入射介质的折射指数和折射介质的折射指数的比值*/
attenuation = vec3(1.0, 1.0, 1.0);
/*介质的衰减向量为(1,1,1)不是光线不衰减*/vec3 refracted;
if (dot(r_in.direction(), rec.normal) > 0) {
/*光线的方向向量和球的法向量的点乘大于零,说明光线是从球体内部射入空气。所以,入射时的法向量和球的法向量方向相反;注意,ref_idx是指光密介质的折射指数和光疏介质的折射指数的比值,此时入射介质是光密介质,折射介质是光疏介质,所以ni_over_nt=ref_idx*/outward_normal = -rec.normal;ni_over_nt = ref_idx;}else {
/*光线的方向向量和球的法向量的点乘bu大于零,说明光线是从空气射入球体气。所以,入射时的法向量和球的法向量方向同向;注意,ref_idx是指光密介质的折射指数和光疏介质的折射指数的比值,此时入射介质是光疏介质,折射介质是光密介质,所以ni_over_nt=1.0/ref_idx*/outward_normal = rec.normal;ni_over_nt = 1.0 / ref_idx;}if (refract(r_in.direction(), outward_normal, ni_over_nt, refracted)) {scattered = ray(rec.p, refracted);}else {
/*计算折射光线方向向量的函数返回false,即出现全反射。但是,本章节讨论的是均匀球体,是不会有全反射的,所以,次数也返回false*/scattered = ray(rec.p, reflected);return false;
}
/*注意:这里只判断了“有没有折射光线”,除了全反射的情况,其他情况都是有折射光线的;而没有判断“有没有反射光线”,全反射是反射光线的一种,其他情况也都是有反射光线的,但是此处不考虑*/return true;
}

----------------------------------------------main.cpp------------------------------------------

main.cpp

        hitable *list[4];list[0] = new sphere(vec3(0,0,-1), 0.5, new lambertian(vec3(0.1, 0.2, 0.5)));list[1] = new sphere(vec3(0,-100.5,-1), 100, new lambertian(vec3(0.8, 0.8, 0.0)));list[2] = new sphere(vec3(1,0,-1), 0.5, new metal(vec3(0.8, 0.6, 0.2), 0.0));list[3] = new sphere(vec3(-1,0,-1), 0.5, new dielectric(1.5));hitable *world = new hitable_list(list,4);

产生的图片如下:

解释一下这个介质球的颜色:

透过介质球看到的像的位置和球背后景的位置是上下颠倒的。

24.3 模拟介质的颜色(反射系数不为0)

此章节考虑反射系数不为0,即反射是时时存在的的。反射光线的强度或者说反射光线的多少,就要用到24.1.2中提到的反射系数啦。公式如下:

看看要修改哪些代码吧(红色字体为改动的)

----------------------------------------------dielectic.cpp------------------------------------------

dielectic.cpp

float schlick(float cosine, float ref_idx) {

/*这个函数是实现Schlick's approximation。其中ref_idx=n2/n1*/

float r0= (1-ref_idx) / (1+ref_idx);

r0 =r0*r0;

returnr0 + (1-r0)*pow((1-cosine),5);

}

bool dielectric::scatter(const ray& r_in, const hit_record&rec, vec3& attenuation, ray& scattered) const {

vec3 outward_normal;

vec3 reflected =reflect(r_in.direction(), rec.normal);

float ni_over_nt;

attenuation = vec3(1.0,1.0, 1.0);

vec3 refracted;

floatreflect_prob;

floatcosine;

if (dot(r_in.direction(),rec.normal) > 0) {

outward_normal =-rec.normal;

ni_over_nt = ref_idx;

cosine = ref_idx * dot(r_in.direction(), rec.normal) /r_in.direction().length();

/*不知道这里为什么要乘以一个ref_idx,下面的cosine在计算时又没有乘。此处去掉“ref_idx*”前后的图片差异看不出来*/

}

else {

outward_normal =rec.normal;

ni_over_nt = 1.0 /ref_idx;

cosine = -dot(r_in.direction(), rec.normal) / r_in.direction().length();

}

if(refract(r_in.direction(), outward_normal, ni_over_nt, refracted)) {

reflect_prob = schlick(cosine, ref_idx);

/*如果有折射,计算反射系数。如果没有反射,即发生了全反射,反射系数为1*/

}

else {

scattered = ray(rec.p,reflected);

/*这条语句可以去掉,因为scattered的值都会被重写*/

reflect_prob = 1.0;

}

if ((rand()%(100)/(float)(100))< reflect_prob) {

/*产生一个(0,1)的随机数,如果随机数小于反射系数,则设置为反射光线,反之,设置为折射光线。也就是只有反射光线或折射光线中的一个咯,为什么?不是说好反射光线和折射光线都有吗?考虑到一个像素点被设置为采样100次,这100次中反射光线的条数基本和reflect_prob的值正相关,所以,100次的平均值也就是该像素点出反射光线和折射光线的叠加*/

scattered = ray(rec.p, reflected);

}

else {

scattered = ray(rec.p, refracted);

}

return true;

}

----------------------------------------------main.cpp------------------------------------------

main.cpp

hitable *list[4];

list[0] = newsphere(vec3(0,0,-1), 0.5, new lambertian(vec3(0.1, 0.2, 0.5)));

list[1] = newsphere(vec3(0,-100.5,-1), 100, new lambertian(vec3(0.8, 0.8, 0.0)));

list[2] = newsphere(vec3(1,0,-1), 0.5, new metal(vec3(0.8, 0.6, 0.2), 0.0));

list[3] = newsphere(vec3(-1,0,-1), 0.5, new dielectric(1.5));

hitable *world = newhitable_list(list,4);

main.cpp没有改动。

考虑反射光线前后的图片对比:

另外,书上又提到,针对介质球,如果将将球半径设置为负,则会出现一个玻璃空心球。

(都是考虑过反射系数的,如果不考虑反射系数,则是图片不同)(一下所有改动都是相对于上述main.cpp)

改动1:

----------------------------------------------main.cpp------------------------------------------

main.cpp

hitable *list[4];

list[0] = newsphere(vec3(0,0,-1), 0.5, new lambertian(vec3(0.1, 0.2, 0.5)));

list[1] = newsphere(vec3(0,-100.5,-1), 100, new lambertian(vec3(0.8, 0.8, 0.0)));

list[2] = newsphere(vec3(1,0,-1), 0.5, new metal(vec3(0.8, 0.6, 0.2), 0.0));

list[3] = newsphere(vec3(-1,0,-1), -0.5, newdielectric(1.5));

hitable *world = newhitable_list(list,4);

(左球上为什么有蓝色的东西,个人觉得是因为左球的折射光或者反射光有可能撞击到蓝色球。那之前为什么没有?我只能说空心玻璃球和实心玻璃球的光路不一样,所以之前没有。为什么书上的图上没有蓝色?我只能说,书上的改动不一样。)

改动2:(书上的改动)

hitable *list[5];

list[0] = new sphere(vec3(0,0,-1), 0.5, new lambertian(vec3(0.1, 0.2,0.5)));

list[1] = new sphere(vec3(0,-100.5,-1), 100, new lambertian(vec3(0.8,0.8, 0.0)));

list[2] = new sphere(vec3(1,0,-1), 0.5, new metal(vec3(0.8, 0.6, 0.2),0.0));

list[3] = new sphere(vec3(-1,0,-1), 0.5, new dielectric(1.5));

list[4] = new sphere(vec3(-1,0,-1),-0.45, new dielectric(1.5));

hitable *world = new hitable_list(list,5);

此次改动有点奇怪,新增的球和之前的左球放的球心在同一位置,之前的左球是一个实心的透明球,现在在同一球心位置新增一个半径为-0.45的(空心球)。这两个球的关系,搞不懂!!!看图片吧:

如果不考虑反射,是这样的:

回到考虑反射,如果-0.45改成-0.5,我X,竟然是这个图(和原图看不出差别啊。测了几遍,我确定没搞错):

如果将-0.45改成-0.3,是这个鬼(不要问为什么。不知道!!!半径为负,本来就搞不清!):

问题二十四:怎么模拟ray tracing图形中介质材料的颜色(dielectric)相关推荐

  1. 问题二十一:怎么模拟ray tracing图形中不同材料的颜色(diffuse and metal)

    在漫射材料章节,我们将多个球都模拟成漫射材料的颜色.那么问题来了,我们能不能将不同的球模拟成不同材料的颜色呢?可以哈!我们这一章节就干这事. 21.1总结一下设置颜色的几种方法 我们还是先回忆一下:r ...

  2. 问题四十五:怎么画ray tracing图形中的blending and joining surface

    当两个曲面在空间相交时,怎么连接相交处? 比如,两个相互垂直的柱面在空间相交如下: 怎么将黑色圈内的相交处画得平滑些?也就是将相交处包起来,添加一个类似水管的四通接口. 45.1 数学推导 45.2 ...

  3. 问题二十九:测试ray tracing中camera几个主要参数

    camera(vec3 lookfrom, vec3 lookat, vec3vup, float vfov, float aspect, float aperture, float focus_di ...

  4. 二十四、爬取古诗网中的100首古诗文

    @Author: Runsen 想当年,我可是背不了古诗三百首的蠢材.我国古代叙事.抒情的方式.手段,古人的情怀至今我真的搞不懂. 今天安利一个网站:古诗文网,简直就是学生党的福音,我的噩梦,尤其是对 ...

  5. tensorflow精进之路(二十四)——Object Detection API目标检测(中)(COCO数据集训练的模型—ssd_mobilenet_v1_coco模型)

    1.概述 上一讲简单的讲了目标检测的原理以及Tensorflow Object Detection API的安装,这一节继续讲Tensorflow Object Detection API怎么用. 2 ...

  6. matlab火星漫游车转向控制,OSG开发笔记(二十四):OSG漫游之平移与转向

    若该文为原创文章,未经允许不得转载 原博主博客地址:https://blog.csdn.net/qq21497936 本文章博客地址:https://blog.csdn.net/qq21497936/ ...

  7. 【二十四讲】ControllerAdvice 之 @InitBinder

    [二十四讲]ControllerAdvice 之 @InitBinder 绑定器工厂的扩展点:@InitBinder 及来源 编程技巧:缓存加速 文章目录 [二十四讲]ControllerAdvice ...

  8. Vue实战篇二十九:模拟一个简易留言板

    系列文章目录 Vue基础篇一:编写第一个Vue程序 Vue基础篇二:Vue组件的核心概念 Vue基础篇三:Vue的计算属性与侦听器 Vue基础篇四:Vue的生命周期(秒杀案例实战) Vue基础篇五:V ...

  9. 二十四、Struts2中的UI标签

    二十四.Struts2中的UI标签 Struts2中UI标签的优势: 数据回显 页面布局和排版(Freemark),struts2提供了一些常用的排版(主题:xhtml默认 simple ajax) ...

最新文章

  1. 一起学Hadoop——实现两张表之间的连接操作
  2. 对10个整数按由大到小顺序排序
  3. ecs服务器配置git_基于ECS和NAS搭建个人网盘
  4. powerdesigner 同步mysql 报错_PowerDesigner技巧小结
  5. 浅谈Android系统开发中LOG的使用
  6. OSI模型中的数据链路层和物理层的区分
  7. SQL Server2012登录记录怎么删除?
  8. java脏字过滤_脏字过滤
  9. JS-为金额添加千分位逗号分割符
  10. leetcode - 486. 预测赢家
  11. 前端判断是否为空字符窜
  12. 2019年9月全国程序员工资统计,看看你拖后腿了吗?
  13. 新款iPhone SE预约量超40万,拼多多已经安排上,击穿底价2999元?
  14. Linux shell的条件判断、循环语句及实例
  15. 水逆的 Google,或许应该向百度取个经?
  16. Linux Netcat command – The swiss army knife of net
  17. 编程基础(五)—— 虚拟内存
  18. python智能算法,人工智能算法Python案例实战
  19. php rgb转cmyk,php – 使用Imagick将图像从RGB转换为CMYK
  20. docker的容器间通信

热门文章

  1. Swift - 多线程实现方式(3) - Grand Central Dispatch(GCD)
  2. Hello Rails
  3. It seems that scikit-learn has not been built correctly.
  4. 如何使用pattern recognition letter 的word写作模板
  5. 微信扫描二维码在内置浏览器打不开文件的下载链接怎么办?哪些api接口可以解决...
  6. 三、函数的嵌套、作用域链、函数名的应用、闭包。
  7. 基于Ubuntu Server 16.04 LTS版本安装和部署Django之(一):安装Python3-pip和Django
  8. c# Winform 开发分屏显示应用程序
  9. 单反相机入门教程视频(48集)
  10. 集成计算引擎在大型企业绩效考核系统的应用方案