如果我们希望不同的物体使用不同的材料,则需要进行设计决策。我们可以使用具有许多参数的通用材料,而将不同的材料类型仅将其中一些参数归零。这不是一个坏方法。或者我们可以有一个抽象的材料类来封装行为。我是后一种方法的粉丝。对于我们的程序,材料需要做两件事:

  • 产生散射射线(或说它吸收了入射射线)。
  • 如果散射,说出应将射线衰减多少。

这建议了抽象类:

9.1. An Abstract Class for Materials( 材料抽象类)

#ifndef MATERIAL_H
#define MATERIAL_H #include "rtweekend.h"
#include "hittable.h" struct hit_record; // abstract class
class material {
public:
virtual bool scatter(
const ray &r_in, const hit_record &rec, color &attenuation, ray &scattered
) const = 0;
}; #endif
material.h The material class

9.2. A Data Structure to Describe Ray-Object Intersections(描述射线对象相交的数据结构)

hit_record是为了避免使用一堆参数,因此我们可以在其中填充所需的任何信息。 您可以改为使用参数。 这是一个品味问题。 hittablesmaterial需要彼此了解,因此参考文献有一定的循环性。 在C ++中,您只需要警告编译器该指针是一个类,下面的hittable类中的“类材料”就是这样做的:

/*该结构体记录“撞点”处的信息:离光线起点的距离t、撞点的坐标向量p、撞点出的法向量normal.*/
struct hit_record {
point3 p;
vec3 normal;
// new variables
shared_ptr<material> mat_ptr;
double t;
bool front_face;
}
hittable.h Hit record with added material pointer

我们在这里设置的是材料将告诉我们射线如何与表面相互作用。 hit_record只是将一堆参数填充到结构中的一种方法,因此我们可以将它们作为一组发送。 当光线撞击表面(例如特定的球体)时,hit_record中的材质指针将被设置为指向当我们开始时在main()中设置该球体时所给定的材质指针。 当ray_color()例程获取hit_record时,它可以调用材质指针的成员函数来找出散射的光线(如果有)。

class sphere : public hittable {
public:
sphere() {}
sphere(point3 cen, double r, shared_ptr<material> m)
: center(cen), radius(r), mat_ptr(m) {}; virtual bool hit(
const ray& r, double t_min, double t_max, hit_record& rec) const override; public:
point3 center;
double radius;
shared_ptr<material> mat_ptr;
}; bool sphere::hit(const ray& r, double t_min, double t_max, hit_record& rec) const {
... rec.t = root;
rec.p = r.at(rec.t);
vec3 outward_normal = (rec.p - center) / radius;
rec.set_face_normal(r, outward_normal);
rec.mat_ptr = mat_ptr; return true;
}
sphere.h Ray-sphere intersection with added material information

9.3. Modeling Light Scatter and Reflectance(模拟光散射和反射率)

如果您仔细阅读上面的代码,您会发现一小撮恶作剧的机会。如果我们生成的随机单位向量与法向向量完全相反,则两者之和将为零,这将导致散射方向向量为零。这会在以后导致不良情况(无穷大和NaN),因此我们需要在传递条件之前先对其进行拦截。

为此,我们将创建一个新的vector方法—vec3::near_zero()如果矢量在所有维度上都非常接近零,则返回true。

class vec3 {
...
bool near_zero() const {
// Return true if the vector is close to zero in all dimensions.
const auto s = 1e-8;
return (fabs(e[0]) < s) && (fabs(e[1]) < s) && (fabs(e[2]) < s);
}
...
};
vec3 The vec3::near_zero() method
// 兰伯特模型类
class lambertian : public material {
public:
lambertian(const color& a) : albedo(a) {} virtual bool scatter(
const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered
) const override {
auto scatter_direction = rec.normal + random_unit_vector(); // Catch degenerate scatter direction
// 此时随机的向量近似为与normal法线向量相反方向的向量
if (scatter_direction.near_zero())
scatter_direction = rec.normal; scattered = ray(rec.p, scatter_direction);
attenuation = albedo;
return true;
} public:
color albedo;
};
[material.h]The lambertian material class

9.4. Mirrored Light Reflection(镜面光反射)

对于光滑的金属,射线不会被随机散射。关键数学是:射线如何从金属镜反射回来? 向量数学是我们的朋友在这里:

Figure 11: Ray reflection

红色的反射射线方向为 v+2b。在我们的设计中n是单位向量,但是 v未必。长度b 应该 v*n。因为v 点,我们将需要一个减号,产生:

// 反射
// 法线是单位向量 所以点积结果就是向量v在n上投影的长度
vec3 reflect(const vec3 &v, const vec3 &n) {
return v - 2 * dot(v, n) * n;
}
[vec3.h]vec3 reflection function

金属材料只是使用该公式反射光线:

class metal : public material {
public:
metal(const color &a) : albedo(a) {} bool scatter(const ray &r_in, const hit_record &rec, color &attenuation, ray &scattered) const override {
vec3 reflected = reflect(unit_vector(r_in.direction()), rec.normal);
scattered = ray(rec.p, reflected);
attenuation = albedo;
return (dot(scattered.direction(), rec.normal) > 0);
} public:
color albedo;
};

我们要修改ray_color()函数以使用此函数:

color ray_color(const ray& r, const hittable& world, int depth) {
hit_record rec; // If we've exceeded the ray bounce limit, no more light is gathered.
if (depth <= 0)
return color(0,0,0); if (world.hit(r, 0.001, infinity, rec)) {
ray scattered;
color attenuation;
if (rec.mat_ptr->scatter(r, rec, attenuation, scattered))
return attenuation * ray_color(scattered, world, depth-1);
return color(0,0,0);
} vec3 unit_direction = unit_vector(r.direction());
auto t = 0.5*(unit_direction.y() + 1.0);
return (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0);
}

9.5. A Scene with Metal Spheres(金属球的场景)

在场景中添加一些金属球:

    auto material_ground = make_shared<lambertian>(color(0.8, 0.8, 0.0));
auto material_center = make_shared<lambertian>(color(0.7, 0.3, 0.3));
auto material_left = make_shared<metal>(color(0.8, 0.8, 0.8));
auto material_right = make_shared<metal>(color(0.8, 0.6, 0.2)); 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.5, material_right));

大功告成

效果

9.6. Fuzzy Reflection(模糊反射)

我们还可以通过使用小球体并为射线选择新的端点来随机化反射方向:

Figure 12: Generating fuzzed reflection rays

球体越大,反射将变得越模糊。 这建议添加一个模糊度参数,该参数仅是球体的半径(因此零是没有扰动)。 要注意的是,对于大球体或掠食性射线,我们可能会散射到水面以下。 我们可以让表面吸收那些

// 金属类
class metal : public material {
public:
metal(const color &a, double f) : albedo(a), fuzz(f < 1 ? f : 1) {} bool scatter(const ray &r_in, const hit_record &rec, color &attenuation, ray &scattered) const override {
vec3 reflected = reflect(unit_vector(r_in.direction()), rec.normal);
scattered = ray(rec.p, reflected + fuzz * random_in_unit_sphere());
attenuation = albedo;
return (dot(scattered.direction(), rec.normal) > 0);
} public:
color albedo;
double fuzz;
};
Listing 51: [material.h] Metal material fuzziness
    shared_ptr<metal> material_left = make_shared<metal>(color(0.8, 0.8, 0.8), 0.3);
shared_ptr<metal> material_right = make_shared<metal>(color(0.8, 0.6, 0.2), 1.0);
Listing 52: [main.cpp] Metal spheres with fuzziness

效果

这图真的太漂亮了QAQ

《Ray Tracing in One Weekend》阅读笔记 - 9、Metal(金属)的更多相关文章

  1. Ray Tracing in one Weekend 阅读笔记

    目录 一.创建Ray类,实现背景 二.加入一个球 三.让球的颜色和其法线信息相关 四.多种形状,多个碰撞体 五.封装相机类 六.抗锯齿 七.漫发射 八.抽象出材料类(编写metal类) 九.介质材料( ...

  2. 【Ray Tracing The Next Week 超详解】 光线追踪2-1

     Preface 博主刚放假回家就进了医院,今天刚完事儿,来续写第二本书  Ready 我们来总结一下上一本书的笔记中我们的一些规定 1. 数学表达式 我们采用小写粗黑体代表向量,大写粗黑体代表矩阵, ...

  3. Three.js源码阅读笔记-5

    Core::Ray 该类用来表示空间中的“射线”,主要用来进行碰撞检测. THREE.Ray = function ( origin, direction ) { this.origin = ( or ...

  4. 《Ray Tracing in One Weekend》、《Ray Tracing from the Ground Up》读后感以及光线追踪学习推荐

    <Ray Tracing in One Weekend> 优点: 相对简单易懂 渲染效果相当好 代码简短,只看书上的代码就可以写出完整的程序,而且Github上的代码是将基类与之类写在一起 ...

  5. 【Ray Tracing The Next Week 超详解】 光线追踪2-7 任意长方体 && 场景案例

    上一篇比较简单,很久才发是因为做了一些好玩的场景,后来发现这一章是专门写场景例子的,所以就安排到了这一篇 Preface 这一篇要介绍的内容有: 1. 自己做的光照例子 2. Cornell box画 ...

  6. 【RAY TRACING THE REST OF YOUR LIFE 超详解】 光线追踪 3-7 混合概率密度

     Preface 注:鉴于很多网站随意爬取数据,可能导致内容残缺以及引用失效等问题,影响阅读,请认准原创网址: https://www.cnblogs.com/lv-anchoret/category ...

  7. 【RAY TRACING THE REST OF YOUR LIFE 超详解】 光线追踪 3-5 random direction & ONB

     Preface 往后看了几章,对这本书有了新的理解 上一篇,我们第一次尝试把MC积分运用到了Lambertian材质中,当然,第一次尝试是失败的,作者发现它的渲染效果和现实有些出入,所以结尾处声明要 ...

  8. 【RAY TRACING THE REST OF YOUR LIFE 超详解】 光线追踪 3-4 基于重要性采样的材质初探

     Preface 我们今天来把第三本书从开局到现在讲的一大堆理论运用到我们的框架中,那么今天我们首先将原始的材质改为基于重要性采样原理的材质 这一篇是代码工程中进行MC理论应用的初步尝试篇  Read ...

  9. 【Ray Tracing The Next Week 超详解】 光线追踪2-8 Volume

     Preface 今天有两个东东,一个是体积烟雾,一个是封面图 下一篇我们总结项目代码 Chapter 8:Volumes 我们需要为我们的光线追踪器添加新的物体——烟.雾,也称为participat ...

  10. 【Ray Tracing The Next Week 超详解】 光线追踪2-6 Cornell box

    Chapter 6:Rectangles and Lights 今天,我们来学习长方形区域光照  先看效果 light 首先我们需要设计一个发光的材质 /// light.hpp // ------- ...

随机推荐

  1. go的令牌桶实现库 go-rate

    关于我 我的博客|文章首发 go-rate是速率限制器库,基于 Token Bucket(令牌桶)算法实现. go-rate被用在LangTrend的生产中 用于遵守GitHub API速率限制. 速 ...

  2. MzzTxx——团队介绍

    项目 内容 这个作业属于哪个课程 2021春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 团队项目-团队介绍 我在这个课程的目标是 提升工程能力和团队意识,熟悉软件开发的流程 这个作业在哪 ...

  3. Python基础之:struct和格式化字符

    目录 简介 struct中的方法 格式字符串 字节顺序,大小和对齐方式 格式字符 格式数字 格式字符 格式字符串 填充的影响 复杂应用 简介 文件的存储内容有两种方式,一种是二进制,一种是文本的形式. ...

  4. Oracle-buffer cache过小导致SQL执行时间长

    一.问题:客户反馈在生产库和测试库执行相同SQL,测试库执行比生产库慢一倍 问题摆在这里,需要进行分析? 啥??? 版本11.2.0.4,都是单实例,主机系统硬件配置差不多. 二.对比SQL的执行效率 ...

  5. 4. Linux-startx命令

    Linux系统startx命令的功能和使用方法 Linux系统命令startx的功能很简单,就是启动X Window的服务这一项,没有其他的了.其实startx命令启动的是xinit,然后再由xini ...

  6. python进阶(15)多线程与多进程效率测试

    前言 在Python中,计算密集型任务适用于多进程,IO密集型任务适用于多线程   正常来讲,多线程要比多进程效率更高,因为进程间的切换需要的资源和开销更大,而线程相对更小,但是我们使用的Python ...

  7. WERTYU UVA - 10082

    ​ A common typing error is to place the hands on the keyboard one row to the right of the correct po ...

  8. Redis持久化——AOF日志

    最新:Redis内存--内存消耗(内存都去哪了?) 最新:Redis持久化--如何选择合适的持久化方式 最新:Redis持久化--AOF日志 更多文章... 上一篇文章Redis持久化--内存快照(R ...

  9. 在kubernetes上部署zookeeper,kafka集群

    本文采用网上镜像:mirrorgooglecontainers/kubernetes-zookeeper:1.0-3.4.10 准备共享存储:nfs,glusterfs,seaweed或其他,并在no ...

  10. 模拟退火算法(1)Python 实现

    1.模拟退火算法 模拟退火算法借鉴了统计物理学的思想,是一种简单.通用的启发式优化算法,并在理论上具有概率性全局优化性能,因而在科研和工程中得到了广泛的应用. 退火是金属从熔融状态缓慢冷却.最终达到能 ...