-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmaterial.h
More file actions
155 lines (132 loc) · 5.18 KB
/
Copy pathmaterial.h
File metadata and controls
155 lines (132 loc) · 5.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#ifndef MATERIAL_H
#define MATERIAL_H
#include "hittable.h"
class material {
public:
virtual ~material() = default;
virtual bool scatter(
const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered
) const {
return false;
}
};
// 散射材料
/*
random_unit_vector()
↗
法向量 n /
↑ /
| /
| /
| / scatter_direction
| / (n + random)
| /
| /
| /
| /
| /
● ← rec.p (交点)
──────────────── (表面)
情况2:退化情况处理
↑ n
|
| scatter ≈ 0
| (当随机向量接近 -n 时)
● → 使用 n 作为散射方向
────────────────
*/
class lambertian : public material {
public:
lambertian(const color& albedo) : albedo(albedo) {}
bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered)
const override {
/*将随机单位向量与法向量相加(即 rec.normal + random_unit_vector()),
可以看作是把原本严格指向法线的方向进行随机偏移,从而使得生成的新射线既保留了主要沿法线的趋势,
又添加了随机性。这样处理后,新射线的方向大部分还是指向法线所在的半球,但由于随机性,
每次得到的方向都会有所不同
*/
auto scatter_direction = rec.normal + random_unit_vector();
// Catch degenerate scatter direction
if (scatter_direction.near_zero())
scatter_direction = rec.normal;
scattered = ray(rec.p, scatter_direction);
attenuation = albedo;
return true;
}
private:
color albedo; // 定义某种形式的反射率分数
};
// 具有反射函数的金属材料
/*
入射光线 r
\ 散射光线 scattered
\ /
\ / 衰减系数 attenuation
\ / (比如: 0.5 表示反射时损失一半能量)
\ /
\ /
\ /
\/
───────●─────── (物体表面)
hit point
*/
class metal : public material {
public:
metal(const color& albedo) : albedo(albedo) {}
metal(const color& albedo, double fuzz) : albedo(albedo), fuzz(fuzz < 1 ? fuzz : 1) {} // fuzz 被限制在 [0,1] 范围内
// attenuation: 衰减系数
bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered)
const override {
vec3 reflected = reflect(r_in.direction(), rec.normal);
// 添加随机扰动,扰动大小由 fuzz 控制
reflected = unit_vector(reflected) + (fuzz * random_unit_vector());
scattered = ray(rec.p, reflected);
attenuation = albedo;
// 确保散射方向在表面上方
return (dot(scattered.direction(), rec.normal) > 0);
}
private:
color albedo;
double fuzz;
};
class dielectric : public material {
public:
dielectric(double refraction_index) : refraction_index(refraction_index) {}
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 ri = rec.front_face ? (1.0/refraction_index) : refraction_index;
vec3 unit_direction = unit_vector(r_in.direction());
// vec3 refracted = refract(unit_direction, rec.normal, ri);
// scattered = ray(rec.p, refracted);
// $$ \cos\theta = \mathbf{R} \cdot \mathbf{n} $$
double cos_theta = std::fmin(dot(-unit_direction, rec.normal), 1.0);
// $$ \sin\theta = \sqrt{1 - \cos^2\theta} $$
double sin_theta = std::sqrt(1.0 - cos_theta*cos_theta);
// > 1.0 全反射,无法折射
bool cannot_refract = ri * sin_theta > 1.0;
vec3 direction;
// 如果由 Schlick 近似算出的反射概率高于一个随机值,那么就随机决定采取反射而不是折射。
// 这样做可以在视觉上产生更加真实的抖动效果,即模拟部分光线反射、部分光线折射的现象
if (cannot_refract || reflectance(cos_theta, ri) > random_double())
// 全反射
direction = reflect(unit_direction, rec.normal);
else
// 折射
direction = refract(unit_direction, rec.normal, ri);
scattered = ray(rec.p, direction);
return true;
}
private:
// Refractive index in vacuum or air, or the ratio of the material's refractive index over
// the refractive index of the enclosing media
double refraction_index;
static double reflectance(double cosine, double refraction_index) {
// Use Schlick's approximation for reflectance.
// 经验公式,用简单的数学表达式近似复杂的菲涅耳方程
auto r0 = (1 - refraction_index) / (1 + refraction_index);
r0 = r0*r0;
return r0 + (1-r0)*std::pow((1 - cosine),5);
}
};
#endif