-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathrender.go
109 lines (101 loc) · 2.76 KB
/
render.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
package main
import (
"sync"
)
type Rendering struct {
*Scene
HPixels int
}
// TODO: Also return a progress chan
func (r *Rendering) Render(parallelism int) *Image {
w := r.HPixels
h := int(float64(w) * r.Camera.Aspect)
img := NewImage(w, h)
scanner := NewLineScanner(r.Camera, r.HPixels)
var wg sync.WaitGroup
wg.Add(parallelism)
lines := scanner.Scan()
for i := 0; i < parallelism; i++ {
go func() {
result := make([]Color, scanner.hPixels)
for line := range lines {
for x := line.xMin; x < line.xMax; x++ {
result[x] = r.Trace(line.rays[x])
}
img.SetLine(line.y, result)
}
wg.Done()
}()
}
wg.Wait()
return img
}
// A LineScanner yields each line of pixels in the rendered image along with the
// vectors to represent their position on the image plane.
type LineScanner struct {
hPixels, vPixels int
camera *Camera
pixelSize float64
height float64
// across and down are unit vectors parallel to the x and y axes,
// respectively, of the rendered image. They are both perpendicular to
// the camera ray. Across is parallel to the xz plane.
across, down Vec3
origin Vec3
vantage Vec3
}
func NewLineScanner(camera *Camera, hPixels int) *LineScanner {
// Compute vertical pixels
vPixels := int(float64(hPixels) * camera.Aspect)
// Compute the size of a pixel.
pixelSize := camera.Width / float64(hPixels)
// Compute useful vectors.
// a is the perpendicular to the camera ray and parallel to the xz
// plane => it is the cross product of the camera ray and the y axis.
yAxis := Vec3{0, 1, 0}
cam := camera.Loc.D
a := cam.Cross(yAxis).Normalize()
// d is perpendicular to the camera ray and a.
d := cam.Cross(a).Normalize()
// Now we can easily compute the location of the image origin.
height := camera.Width * camera.Aspect
return &LineScanner{
hPixels: hPixels,
vPixels: vPixels,
camera: camera,
pixelSize: pixelSize,
height: height,
across: a,
down: d,
origin: camera.Loc.V.Add(d.Mul(-0.5 * height)).Add(a.Mul(-0.5 * camera.Width)),
vantage: camera.Vantage(),
}
}
type scanLine struct {
y int
xMin, xMax int
rays []Ray
}
func (s *LineScanner) Scan() <-chan scanLine {
ch := make(chan scanLine)
go func() {
for y := 0; y < s.vPixels; y++ {
line := scanLine{
y: y,
xMin: 0,
xMax: s.hPixels,
rays: make([]Ray, s.hPixels),
}
for x := range line.rays {
// The ray goes through the *center* of the pixel.
xDist := s.camera.Width*(float64(x)/float64(s.hPixels)) + 0.5*s.pixelSize
yDist := s.height*(float64(y)/float64(s.vPixels)) + 0.5*s.pixelSize
v := s.origin.Add(s.across.Mul(xDist)).Add(s.down.Mul(yDist))
line.rays[x] = Ray{V: s.vantage, D: v.Sub(s.vantage)}
}
ch <- line
}
close(ch)
}()
return ch
}