Add profiling, add some more rendering infra
Will Brown
9 years ago
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "flag" |
4 | "fmt" | |
5 | 4 | "image" |
6 | 5 | "image/png" |
6 | "log" | |
7 | 7 | "monte" |
8 | 8 | "os" |
9 | "runtime/pprof" | |
9 | 10 | ) |
10 | 11 | |
11 | var Width = flag.Int("width", 300, "width of output image") | |
12 | var Height = flag.Int("height", 300, "height of output image") | |
13 | var Output = flag.String("output", "output.png", "output PNG file") | |
12 | var width = flag.Int("width", 300, "width of output image") | |
13 | var height = flag.Int("height", 300, "height of output image") | |
14 | var output = flag.String("output", "output.png", "output PNG file") | |
15 | var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") | |
14 | 16 | |
15 | 17 | func main() { |
16 | 18 | flag.Parse() |
19 | if *cpuprofile != "" { | |
20 | f, err := os.Create(*cpuprofile) | |
21 | if err != nil { | |
22 | log.Fatal(err) | |
23 | } | |
24 | pprof.StartCPUProfile(f) | |
25 | } | |
17 | 26 | |
18 | 27 | println("Monte is go.") |
19 | 28 | |
29 | 38 | Oversample: 8, |
30 | 39 | } |
31 | 40 | |
32 | img := image.NewNRGBA(image.Rect(0, 0, *Width, *Height)) | |
41 | img := image.NewNRGBA(image.Rect(0, 0, *width, *height)) | |
33 | 42 | scene.Render(img) |
34 | 43 | |
35 | out, err := os.Create(*Output) | |
44 | out, err := os.Create(*output) | |
36 | 45 | if err != nil { |
37 | fmt.Printf("Could not open output file: %v\n", err) | |
46 | log.Fatalf("Could not open output file: %v\n", err) | |
38 | 47 | return |
39 | 48 | } |
40 | 49 | png.Encode(out, img) |
41 | 50 | out.Close() |
51 | ||
52 | pprof.StopCPUProfile() | |
42 | 53 | } |
8 | 8 | // Find the normal vector to the geometric primitive at a point on its |
9 | 9 | // surface. This function should only be called with points on its surface; if |
10 | 10 | // not its behavior is undefined. |
11 | Normal(loc *Vector) *Vector | |
11 | NormalAt(loc *Vector) *Vector | |
12 | 12 | } |
0 | package monte | |
1 | ||
2 | import ( | |
3 | //"fmt" | |
4 | "image" | |
5 | "image/color" | |
6 | "math" | |
7 | "math/rand" | |
8 | ) | |
9 | ||
10 | func NewColor(r, g, b, a uint8) color.NRGBA { | |
11 | return color.NRGBA{R: r, G: g, B: b, A: a} | |
12 | } | |
13 | ||
14 | func (s *Scene) ColorAtIntersection(geom *Primitive, in *Ray) color.NRGBA { | |
15 | shade := in.Direction.ScalarMul(-1).Dot((*geom).NormalAt(in.Origin)) | |
16 | value := uint8(255.0 * shade) | |
17 | return NewColor(value, value, value, 255) | |
18 | } | |
19 | ||
20 | func (s *Scene) RayCast(u, v float64) color.NRGBA { | |
21 | ray := NewRay(s.Look.Origin, s.DirectionAt(u, v)) | |
22 | ||
23 | minDist := math.Inf(1) | |
24 | var minGeom *Primitive = nil | |
25 | var intersect *Vector = nil | |
26 | ||
27 | for _, geom := range s.Geom { | |
28 | i, d := geom.Intersect(ray) | |
29 | if i != nil && d < minDist { | |
30 | minDist = d | |
31 | minGeom = &geom | |
32 | intersect = i | |
33 | } | |
34 | } | |
35 | ||
36 | if minGeom != nil { | |
37 | return s.ColorAtIntersection(minGeom, NewRay(intersect, ray.Direction)) | |
38 | } | |
39 | return s.Sky | |
40 | } | |
41 | ||
42 | func (s *Scene) Evaluate(u, v float64) color.Color { | |
43 | var r, g, b, n uint32 = 0, 0, 0, 0 | |
44 | for i := 0; i < s.Oversample; i++ { | |
45 | du := (rand.Float64() - 0.5) / 200 | |
46 | dv := (rand.Float64() - 0.5) / 200 | |
47 | ||
48 | c := s.RayCast(u + du, v + dv) | |
49 | r += uint32(c.R); g += uint32(c.G); b += uint32(c.B) | |
50 | n++ | |
51 | } | |
52 | return NewColor(uint8(r / n), uint8(g / n), uint8(b / n), 255) | |
53 | } | |
54 | ||
55 | func (s *Scene) SetColor(i, j int, u, v float64, img *image.NRGBA) { | |
56 | img.Set(i, j, s.Evaluate(u, v)) | |
57 | } | |
58 | ||
59 | func (s *Scene) Render(img *image.NRGBA) { | |
60 | rect := img.Bounds() | |
61 | w, h := float64(rect.Dx()), float64(rect.Dy()) | |
62 | i_max, j_max := rect.Dx(), rect.Dy() | |
63 | for i := 0; i < i_max; i++ { | |
64 | u := (float64(i) - w / 2.0) / (w / 2.0) | |
65 | for j := 0; j < j_max; j++ { | |
66 | // (h / w) term is needed to correct for nonunity aspect ratios | |
67 | v := (float64(j) - h / 2.0) / (h / 2.0) * (h / w) | |
68 | s.SetColor(i, j, u, v, img) | |
69 | } | |
70 | } | |
71 | } |
0 | 0 | package monte |
1 | 1 | |
2 | 2 | import ( |
3 | "image" | |
4 | 3 | "image/color" |
5 | "math" | |
6 | "math/rand" | |
7 | 4 | ) |
8 | ||
9 | func NewColor(r, g, b, a uint8) color.NRGBA { | |
10 | return color.NRGBA{R: r, G: g, B: b, A: a} | |
11 | } | |
12 | 5 | |
13 | 6 | type Scene struct { |
14 | 7 | Geom []Primitive |
25 | 18 | u1, u2 := s.U1.ScalarMul(u), s.U2.ScalarMul(v) |
26 | 19 | return s.Look.Direction.ScalarMul(s.FDist).Add(u1).Add(u2).NormalizeInPlace() |
27 | 20 | } |
28 | ||
29 | func (s *Scene) ColorAt(u, v float64) color.Color { | |
30 | var r, g, b, n int32 = 0, 0, 0, 0 | |
31 | sr, sg, sb := s.Sky.R, s.Sky.G, s.Sky.B | |
32 | ||
33 | for i := 0; i < s.Oversample; i++ { | |
34 | du := (rand.Float64() - 0.5) / 200 | |
35 | dv := (rand.Float64() - 0.5) / 200 | |
36 | ray := NewRay(s.Look.Origin, s.DirectionAt(u+du, v+dv)) | |
37 | ||
38 | minDist := math.Inf(1) | |
39 | var minGeom *Primitive = nil | |
40 | ||
41 | for _, geom := range s.Geom { | |
42 | i, d := geom.Intersect(ray) | |
43 | if i != nil && d < minDist { | |
44 | minDist = d | |
45 | minGeom = &geom | |
46 | } | |
47 | } | |
48 | ||
49 | if minGeom != nil { | |
50 | r += 255 | |
51 | g += 255 | |
52 | b += 255 | |
53 | } else { | |
54 | r += int32(sr) | |
55 | g += int32(sg) | |
56 | b += int32(sb) | |
57 | } | |
58 | n++ | |
59 | } | |
60 | return NewColor(uint8(r / n), uint8(g / n), uint8(b / n), 255) | |
61 | } | |
62 | ||
63 | func (s *Scene) SetColor(i, j int, u, v float64, img *image.NRGBA) { | |
64 | img.Set(i, j, s.ColorAt(u, v)) | |
65 | } | |
66 | ||
67 | func (s *Scene) Render(img *image.NRGBA) { | |
68 | rect := img.Bounds() | |
69 | w, h := float64(rect.Dx()), float64(rect.Dy()) | |
70 | i_max, j_max := rect.Dx(), rect.Dy() | |
71 | for i := 0; i < i_max; i++ { | |
72 | u := (float64(i) - w / 2.0) / (w / 2.0) | |
73 | for j := 0; j < j_max; j++ { | |
74 | // (h / w) term is needed to correct for nonunity aspect ratios | |
75 | v := (float64(j) - h / 2.0) / (h / 2.0) * (h / w) | |
76 | go s.SetColor(i, j, u, v, img) | |
77 | } | |
78 | } | |
79 | } |