This repository has been archived by the owner on May 18, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tracer.go
143 lines (120 loc) · 3.09 KB
/
tracer.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
package main
// Returns the perceived color by a ray shot in the scene
func trace(r Ray, scene *Scene) RGB {
impact := raycast(r, scene)
if impact == nil {
return scene.backgroundColor
}
// return debug_distanceToColor(impact.dist).MixSub(debug_normToColor(impact.n))
// return debug_distanceToColor(impact.dist)
// return debug_normToColor(impact.n)
lightcol := getLight(impact, scene)
color := impact.mat.color.MixSub(lightcol)
if impact.mat.reflection > 0.01 {
reflectRay := Ray{
ori: impact.p,
dir: r.dir.Sub(impact.n.Scaled(2 * r.dir.Dot(impact.n))),
}
reflectCol := trace(reflectRay, scene)
color = color.Scaled(1 - impact.mat.reflection)
color = color.MixAdd(reflectCol.Scaled(impact.mat.reflection))
}
return color
}
// The closer, the whiter. The further, the darker
func debug_distanceToColor(d float64) RGB {
f := d * 32
if f > 200 {
return RGB{200, 200, 200}
}
u := uint8(f)
return RGB{u, u, u}
}
func debug_normToColor(n Vec3) RGB {
return RGB{
uint8(127 + n.x*127),
uint8(127 + n.y*127),
uint8(127 + n.z*127),
}
}
// Returns the color of the light on the impact
func getLight(impact *Impact, scene *Scene) RGB {
dirLightCol := getDirLight(impact, scene)
res := scene.ambientLight.MixAdd(dirLightCol)
return res
}
// Returns the color of the global directional light on the impact
func getDirLight(impact *Impact, scene *Scene) RGB {
if impact.n.Dot(scene.dirLight.dir) >= 0 {
return RGB{}
}
lightray := Ray{
ori: impact.p,
dir: scene.dirLight.dir.Scaled(-1),
}
if raycast(lightray, scene) != nil {
return RGB{}
}
return scene.dirLight.col.Scaled(impact.n.Dot(lightray.dir))
}
func raycast(r Ray, scene *Scene) *Impact {
var res *Impact
for _, sphere := range scene.spheres {
r := RaycastSphere(r, sphere)
if r != nil && (res == nil || r.dist < res.dist) {
res = r
}
}
for _, plane := range scene.planes {
r := RaycastPlane(r, plane)
if r != nil && (res == nil || r.dist < res.dist) {
res = r
}
}
return res
}
func renderScene(r *Render, scene *Scene, cam Camera) {
img := r.img
height := img.height
width := img.width
right := cam.up.Cross(cam.fw)
// We assume each cell is twice as high as it is wide
termCellAspectRatio := float64(2)
invAspectRatio := float64(height) / float64(width) * termCellAspectRatio
topleft := cam.origin.
Add(cam.fw).
Add(cam.up.Scaled(invAspectRatio * 0.5)).
Add(right.Scaled(-0.5))
dx := right.Scaled(1 / float64(width))
dy := cam.up.Scaled(-invAspectRatio / float64(height))
doneChan := make(chan bool, 1000)
for x := 0; x < width; x++ {
go func() {
for y := 0; y < height; y++ {
target := topleft.
Add(dx.Scaled(float64(x) + 0.5)).
Add(dy.Scaled(float64(y) + 0.5))
ray := Ray{
ori: cam.origin,
dir: target.Sub(cam.origin).Normalized(),
}
color := trace(ray, scene)
if r.stop {
doneChan <- true
return
}
img.Set(x, y, color)
doneChan <- true
}
}()
}
totalCount := width * height
doneCount := 0
for <-doneChan {
if r.stop {
return
}
doneCount++
r.progress = float32(doneCount) / float32(totalCount)
}
}