-
Notifications
You must be signed in to change notification settings - Fork 0
/
material.h
102 lines (85 loc) · 3.2 KB
/
material.h
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
#ifndef MATERIAL_H
#define MATERIAL_H
#include "rtweekend.h"
#include "hittable.h"
class material {
public:
virtual bool scatter(
const ray& ray_in, const hit_record& rec, vec3& attenuation, ray& ray_out
) const = 0;
};
// Polynomial approximation by Christopher Schlick
double schlick(double cosine,double ref_idx) {
auto r0 = (1-ref_idx) / (1+ref_idx);
r0 *= r0;
return r0 + (1-r0)*pow((1 - cosine), 5);
}
class lambertian: public material {
public:
lambertian(const vec3& a) : albedo(a) {}
virtual bool scatter(
const ray& ray_in, const hit_record& rec, vec3& attenuation, ray& ray_out
) const {
vec3 scatter_dir = rec.normal + random_unit_vector();
ray_out = ray(const_cast<vec3&>(rec.point), scatter_dir);
attenuation = albedo;
return true;
};
public:
vec3 albedo;
};
class metal: public material {
public:
metal(const vec3& a, double f) : albedo(a), fuzz(clamp(f, 0, 1)) {}
virtual bool scatter(
const ray& ray_in, const hit_record& rec, vec3& attenuation, ray& ray_out
) const {
vec3 reflect_dir = reflect(unit_vector(ray_in.direction()), rec.normal);
reflect_dir += fuzz*random_in_unit_sphere();
ray_out = ray(const_cast<vec3&>(rec.point), reflect_dir);
attenuation = albedo;
return true;
};
public:
vec3 albedo;
double fuzz; // a value between 0 and 1
};
class dielectric: public material {
public:
dielectric(double ri) : ref_idx(ri) {}
virtual bool scatter(
const ray& ray_in, const hit_record& rec, vec3& attenuation, ray& ray_out
) const {
attenuation = vec3(1,1,1); // glass/dielectric surface absorbs nothing
// determine if air->sphere refraction or sphere->air refraction
double eta_over_etap;
if (rec.front_face) {
eta_over_etap = 1 / ref_idx;
} else {
eta_over_etap = ref_idx;
}
// determine if reflect or refract
vec3 unit_dir_in = unit_vector(ray_in.direction());
double cos_theta = ffmin(dot(-unit_dir_in, rec.normal), 1.0);
double sin_theta = sqrt(1.0 - cos_theta*cos_theta);
if (eta_over_etap * sin_theta > 1.0) {
// no solution to Snell's law: must reflect
vec3 reflect_dir = reflect(unit_dir_in, rec.normal);
ray_out = ray(const_cast<vec3&>(rec.point), reflect_dir);
return true;
}
// can refract
double reflect_prob = schlick(cos_theta, eta_over_etap);
if (random_double() < reflect_prob) {
vec3 reflect_dir = reflect(unit_dir_in, rec.normal);
ray_out = ray(const_cast<vec3&>(rec.point), reflect_dir);
return true;
}
vec3 refract_dir = refract(unit_dir_in, rec.normal, eta_over_etap);
ray_out = ray(const_cast<vec3&>(rec.point), refract_dir);
return true;
}
public:
double ref_idx;
};
#endif /* MATERIAL_H */