-
Notifications
You must be signed in to change notification settings - Fork 0
/
camera.go
135 lines (114 loc) · 2.74 KB
/
camera.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
package raytracing
import (
"math"
"math/rand"
)
type Camera struct {
lookFrom Point
horizontal Vector
vertical Vector
toLowerLeftCorner Vector
u Vector
v Vector
lensRadius float64
}
type CameraConfig struct {
aspectRatio float64
lookFrom Point
lookAt Point
viewUp Vector
verticalFOV float64
aperture float64
focusDistance float64
}
type CameraConfigOption func(*CameraConfig)
func NewCameraConfig(aspectRatio float64, opts ...CameraConfigOption) CameraConfig {
cfg := CameraConfig{
aspectRatio: aspectRatio,
lookFrom: origin(),
lookAt: NewPoint(0, 0, -1),
viewUp: NewVector(0, 1, 0),
verticalFOV: math.Pi / 2.0,
aperture: 0.0,
focusDistance: 1.0,
}
for _, opt := range opts {
opt(&cfg)
}
return cfg
}
func WithAspectRatio(ratio float64) CameraConfigOption {
return func(cfg *CameraConfig) {
cfg.aspectRatio = ratio
}
}
func WithPointOfView(from, to Point) CameraConfigOption {
return func(cfg *CameraConfig) {
cfg.lookFrom = from
cfg.lookAt = to
}
}
func WithViewUp(vup Vector) CameraConfigOption {
return func(cfg *CameraConfig) {
cfg.viewUp = vup
}
}
func WithVerticalFOV(rad float64) CameraConfigOption {
return func(cfg *CameraConfig) {
cfg.verticalFOV = rad
}
}
func WithAperture(aperture float64) CameraConfigOption {
return func(cfg *CameraConfig) {
cfg.aperture = aperture
}
}
func WithFocusDistance(dist float64) CameraConfigOption {
return func(cfg *CameraConfig) {
cfg.focusDistance = dist
}
}
func NewCamera(cfg CameraConfig) Camera {
h := math.Tan(cfg.verticalFOV / 2)
viewportHeight := 2 * h
viewportWidth := viewportHeight * cfg.aspectRatio
w := cfg.lookAt.to(cfg.lookFrom).normalize()
u := cfg.viewUp.cross(w).normalize()
v := w.cross(u)
horizontal := u.mul(cfg.focusDistance * viewportWidth)
vertical := v.mul(cfg.focusDistance * viewportHeight)
toLowerLeftCorner := w.neg().mul(cfg.focusDistance).
sub(horizontal.div(2)).
sub(vertical.div(2))
c := Camera{
lookFrom: cfg.lookFrom,
horizontal: horizontal,
vertical: vertical,
toLowerLeftCorner: toLowerLeftCorner,
u: u,
v: v,
lensRadius: cfg.aperture / 2.0,
}
return c
}
func (c Camera) castRay(s, t float64) ray {
r := randomInUnitDisk().mul(c.lensRadius)
offset := c.u.mul(r.x).add(c.v.mul(r.y))
return newRay(
origin().to(c.lookFrom).add(offset).point(),
c.toLowerLeftCorner.
add(c.horizontal.mul(s)).add(c.vertical.mul(t)).
sub(offset),
)
}
func randomInUnitDisk() Vector {
x, y := 0.0, 0.0
for true {
x = 2*rand.Float64() - 1
y = 2*rand.Float64() - 1
if x*x+y*y < 1 {
break
}
}
return NewVector(x, y, 0.0)
}