-
Notifications
You must be signed in to change notification settings - Fork 12
/
object.go
153 lines (132 loc) · 3.78 KB
/
object.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
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
package render3d
import (
"math"
"math/rand"
"sort"
"github.com/unixpickle/model3d/model3d"
)
// An Object is a renderable 3D object.
type Object interface {
model3d.Bounder
// Cast finds the first collision with ray r.
//
// It returns not only the ray collision, but also the
// material on the surface of the object at the point.
//
// The final return value indicates if there was a
// collision or not.
Cast(r *model3d.Ray) (model3d.RayCollision, Material, bool)
}
// A ColliderObject wraps a model3d.Collider in the Object
// interface, using a constant material.
type ColliderObject struct {
Collider model3d.Collider
Material Material
}
// Min gets the minimum of the bounding box.
func (c *ColliderObject) Min() model3d.Coord3D {
return c.Collider.Min()
}
// Max gets the maximum of the bounding box.
func (c *ColliderObject) Max() model3d.Coord3D {
return c.Collider.Max()
}
// Cast returns the first ray collision.
func (c *ColliderObject) Cast(r *model3d.Ray) (model3d.RayCollision, Material, bool) {
coll, ok := c.Collider.FirstRayCollision(r)
return coll, c.Material, ok
}
// ParticipatingMedium is a volume in which a ray has a
// probability of hitting a particle, in which the
// collision probability increases with distance.
//
// It is recommended that you use an HGMaterial with this
// object type.
//
// Normals reported for collisions are random and have no
// bearing on how rays are scattered, since the medium
// simulates complex particles which either reflect or
// refract light.
// Hence, materials which use normals should not be
// employed.
type ParticipatingMedium struct {
Collider model3d.Collider
Material Material
// Lambda controls how likely a collision is.
// Larger lambda means lower probability.
// Mean distance is 1 / lambda.
Lambda float64
}
// Min gets the minimum of the bounding box.
func (p *ParticipatingMedium) Min() model3d.Coord3D {
return p.Collider.Min()
}
// Max gets the maximum of the bounding box.
func (p *ParticipatingMedium) Max() model3d.Coord3D {
return p.Collider.Max()
}
// Cast returns the first probabilistic ray collision.
func (p *ParticipatingMedium) Cast(r *model3d.Ray) (model3d.RayCollision, Material, bool) {
t := -math.Log(rand.Float64()) / p.Lambda
t /= r.Direction.Norm()
var collisions []model3d.RayCollision
p.Collider.RayCollisions(r, func(rc model3d.RayCollision) {
collisions = append(collisions, rc)
})
sort.Slice(collisions, func(i, j int) bool {
return collisions[i].Scale < collisions[j].Scale
})
inside := len(collisions)%2 == 1
lastT := 0.0
for _, c := range collisions {
if inside {
passed := c.Scale - lastT
t -= passed
if t < 0 {
return model3d.RayCollision{
Scale: c.Scale + t,
// Normal could be anything, but we randomize
// it so that the normal cosine term is very
// unlikely to be 0.
Normal: model3d.NewCoord3DRandUnit(),
}, p.Material, true
}
}
inside = !inside
lastT = c.Scale
}
return model3d.RayCollision{}, nil, false
}
// A JoinedObject combines multiple Objects.
type JoinedObject []Object
// Min gets the minimum of the bounding box.
func (j JoinedObject) Min() model3d.Coord3D {
min := j[0].Min()
for _, x := range j[1:] {
min = min.Min(x.Min())
}
return min
}
// Max gets the maximum of the bounding box.
func (j JoinedObject) Max() model3d.Coord3D {
max := j[0].Max()
for _, x := range j[1:] {
max = max.Max(x.Max())
}
return max
}
// Cast casts the ray onto the objects and chooses the
// closest ray collision.
func (j JoinedObject) Cast(r *model3d.Ray) (model3d.RayCollision, Material, bool) {
var coll model3d.RayCollision
var mat Material
var found bool
for _, o := range j {
if c, m, f := o.Cast(r); f && (!found || c.Scale < coll.Scale) {
coll = c
mat = m
found = true
}
}
return coll, mat, found
}