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
|
package main
import (
maths "math"
)
//Material
type Material interface {
Attenuation() Vec3
Scatter(r Ray, hit HitRecord) Ray
}
//Lambertian < Material
type Lambertian struct {
Albedo Vec3
}
func (l Lambertian) Attenuation() Vec3 {
return l.Albedo;
}
func (l Lambertian) Scatter(r Ray, hit HitRecord) Ray {
scat_dir := hit.N.Add(RandomVec3OnUnitSphere());
if scat_dir.NearZero() {
scat_dir = hit.N;
}
return Ray{hit.P, scat_dir, true};
}
//Metal < Material
type Metal struct {
Albedo Vec3
Fuzz float64
}
func (m Metal) Attenuation() Vec3 {
return m.Albedo;
}
func (m Metal) Scatter(r Ray, hit HitRecord) Ray {
refl := r.Direction.Reflect(hit.N).Unit().
Add(SplatVec3(m.Fuzz).Mul(RandomVec3OnUnitSphere()));
valid := refl.Dot(hit.N) > 0;
return Ray{hit.P, refl, valid};
}
//Dielectric < Material
type Dielectric struct {
RefractionIndex float64
}
func (d Dielectric) Attenuation() Vec3 {
return SplatVec3(1);
}
func (d Dielectric) Scatter(r Ray, hit HitRecord) Ray {
ri := d.RefractionIndex;
if hit.F {
ri = 1.0 / ri;
}
unit_dir := r.Direction.Unit();
cos_theta := maths.Min(unit_dir.Neg().Dot(hit.N), 1.0);
sin_theta := maths.Sqrt(1.0 - maths.Pow(cos_theta, 2));
cannot_refract := ri * sin_theta > 1.0;
var dir Vec3;
if cannot_refract || schlick_reflect(cos_theta, ri) {
dir = unit_dir.Reflect(hit.N);
} else {
dir = unit_dir.Refract(hit.N, ri);
}
return Ray{hit.P, dir, true};
}
func schlick_reflect(cos_theta float64, ri float64) bool {
r0 := maths.Pow((1 - ri) / (1 + ri), 2);
approx := r0 + (1 - r0) * maths.Pow(1 - cos_theta, 5);
return approx > Interval{0, 1}.Sample();
}
|