-
Notifications
You must be signed in to change notification settings - Fork 6
/
materials.go
112 lines (92 loc) · 2.72 KB
/
materials.go
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
package main
import (
"math"
)
/***********************
* Material
************************/
// Material defines how a material scatter light
type Material interface {
scatter(r *Ray, rec *HitRecord) (wasScattered bool, attenuation *Color, scattered *Ray)
}
/***********************
* Lambertian material (diffuse only)
************************/
type Lambertian struct {
albedo Color
}
func (mat Lambertian) scatter(r *Ray, rec *HitRecord) (bool, *Color, *Ray) {
target := rec.p.Translate(rec.normal).Translate(randomInUnitSphere(r.rnd))
scattered := &Ray{rec.p, target.Sub(rec.p), r.rnd}
attenuation := &mat.albedo
return true, attenuation, scattered
}
/***********************
* Metal material
************************/
type Metal struct {
albedo Color
fuzz float64
}
func (mat Metal) scatter(r *Ray, rec *HitRecord) (bool, *Color, *Ray) {
reflected := r.Direction.Unit().Reflect(rec.normal)
if mat.fuzz < 1 {
reflected = reflected.Add(randomInUnitSphere(r.rnd).Scale(mat.fuzz))
}
scattered := &Ray{rec.p, reflected, r.rnd}
attenuation := &mat.albedo
if Dot(scattered.Direction, rec.normal) > 0 {
return true, attenuation, scattered
}
return false, nil, nil
}
/***********************
* Dielectric material (glass)
************************/
type Dielectric struct {
refIdx float64
}
// Refract returns a refracted vector (or not if there is no refraction possible)
func (v Vec3) Refract(n Vec3, niOverNt float64) (bool, *Vec3) {
uv := v.Unit()
un := n.Unit()
dt := Dot(uv, un)
discriminant := 1.0 - niOverNt*niOverNt*(1-dt*dt)
if discriminant > 0 {
refracted := uv.Sub(un.Scale(dt)).Scale(niOverNt).Sub(un.Scale(math.Sqrt(discriminant)))
return true, &refracted
}
return false, nil
}
func schlick(cosine float64, iRefIdx float64) float64 {
r0 := (1.0 - iRefIdx) / (1.0 + iRefIdx)
r0 = r0 * r0
return r0 + (1.0-r0)*math.Pow(1.0-cosine, 5)
}
func (die Dielectric) scatter(r *Ray, rec *HitRecord) (bool, *Color, *Ray) {
var (
outwardNormal Vec3
niOverNt float64
cosine float64
)
dotRayNormal := Dot(r.Direction, rec.normal);
if dotRayNormal > 0 {
outwardNormal = rec.normal.Negate()
niOverNt = die.refIdx;
cosine = dotRayNormal / r.Direction.Length()
cosine = math.Sqrt(1.0 - die.refIdx*die.refIdx*(1.0-cosine*cosine));
} else {
outwardNormal = rec.normal;
niOverNt = 1.0 / die.refIdx
cosine = -dotRayNormal / r.Direction.Length()
}
wasRefracted, refracted := r.Direction.Refract(outwardNormal, niOverNt)
var direction Vec3
// refract only with some probability
if wasRefracted && r.rnd.Float64() >= schlick(cosine, die.refIdx) {
direction = *refracted
} else {
direction = r.Direction.Unit().Reflect(rec.normal)
}
return true, &White, &Ray{rec.p, direction, r.rnd}
}