git.haldean.org monte / f73fc9f
Add profiling, add some more rendering infra Will Brown 9 years ago
5 changed file(s) with 92 addition(s) and 68 deletion(s). Raw diff Collapse all Expand all
11
22 import (
33 "flag"
4 "fmt"
54 "image"
65 "image/png"
6 "log"
77 "monte"
88 "os"
9 "runtime/pprof"
910 )
1011
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")
1416
1517 func main() {
1618 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 }
1726
1827 println("Monte is go.")
1928
2938 Oversample: 8,
3039 }
3140
32 img := image.NewNRGBA(image.Rect(0, 0, *Width, *Height))
41 img := image.NewNRGBA(image.Rect(0, 0, *width, *height))
3342 scene.Render(img)
3443
35 out, err := os.Create(*Output)
44 out, err := os.Create(*output)
3645 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)
3847 return
3948 }
4049 png.Encode(out, img)
4150 out.Close()
51
52 pprof.StopCPUProfile()
4253 }
88 // Find the normal vector to the geometric primitive at a point on its
99 // surface. This function should only be called with points on its surface; if
1010 // not its behavior is undefined.
11 Normal(loc *Vector) *Vector
11 NormalAt(loc *Vector) *Vector
1212 }
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 }
00 package monte
11
22 import (
3 "image"
43 "image/color"
5 "math"
6 "math/rand"
74 )
8
9 func NewColor(r, g, b, a uint8) color.NRGBA {
10 return color.NRGBA{R: r, G: g, B: b, A: a}
11 }
125
136 type Scene struct {
147 Geom []Primitive
2518 u1, u2 := s.U1.ScalarMul(u), s.U2.ScalarMul(v)
2619 return s.Look.Direction.ScalarMul(s.FDist).Add(u1).Add(u2).NormalizeInPlace()
2720 }
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 }
4040 return nil, 0
4141 }
4242
43 func (s Sphere) Normal(loc *Vector) *Vector {
43 func (s Sphere) NormalAt(loc *Vector) *Vector {
4444 return loc.Sub(s.Center).NormalizeInPlace()
4545 }