Some progress but ray bouncing isn't working well. Lots of erroneous noise
Will Brown
9 years ago
9 | 9 | "runtime/pprof" |
10 | 10 | ) |
11 | 11 | |
12 | var width = flag.Int("width", 300, "width of output image") | |
12 | var width = flag.Int("width", 500, "width of output image") | |
13 | 13 | var height = flag.Int("height", 300, "height of output image") |
14 | 14 | var output = flag.String("output", "output.png", "output PNG file") |
15 | 15 | var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") |
26 | 26 | |
27 | 27 | println("Monte is go.") |
28 | 28 | |
29 | geom := []monte.Primitive{monte.Sphere{Center: monte.Vect(0, 0, 4), Radius: 1.0}} | |
29 | geom := []monte.Primitive{ | |
30 | monte.Sphere{Center: monte.Vect(0, 0, 8), Radius: 1.0}, | |
31 | monte.Sphere{Center: monte.Vect(2, 2, 8), Radius: 1.0}, | |
32 | } | |
30 | 33 | |
31 | 34 | scene := monte.Scene{ |
32 | 35 | Geom: geom, |
33 | Look: monte.NewRay(monte.Vect(0, 0, 0), monte.Vect(0, 0, 1)), | |
36 | Look: monte.NewRay(monte.Vect(1, 1, 0), monte.Vect(-0.1, -0.1, 1)), | |
34 | 37 | U1: monte.Vect(1, 0, 0), |
35 | 38 | U2: monte.Vect(0, 1, 0), |
36 | FDist: 1, | |
37 | Sky: monte.NewColor(0, 200, 255, 255), | |
38 | Oversample: 8, | |
39 | FDist: 2, | |
40 | Sky: monte.NewColorf(0.6, 0.8, 1, 1), | |
41 | Oversample: 1, | |
42 | Ambient: monte.NewColorf(0.2, 0.2, 0.2, 1), | |
39 | 43 | } |
40 | 44 | |
41 | 45 | img := image.NewNRGBA(image.Rect(0, 0, *width, *height)) |
0 | package monte | |
1 | ||
2 | import ( | |
3 | "image/color" | |
4 | ) | |
5 | ||
6 | // Color type backed by a float which supports HDR | |
7 | type Colorf struct { | |
8 | R, G, B, A float64 | |
9 | } | |
10 | ||
11 | func NewColorf(r, g, b, a float64) Colorf { | |
12 | return Colorf{R: r, G: g, B: b, A: a} | |
13 | } | |
14 | ||
15 | func NewColor(r, g, b, a int32) Colorf { | |
16 | return Colorf{ | |
17 | R: float64(r), G: float64(g), B: float64(b), A: float64(a)} | |
18 | } | |
19 | ||
20 | func clamp8(x float64) uint8 { | |
21 | x *= 255 | |
22 | switch { | |
23 | case x >= 256: | |
24 | return 255 | |
25 | case x < 0: | |
26 | return 0 | |
27 | } | |
28 | return uint8(x) | |
29 | } | |
30 | ||
31 | func (c Colorf) NRGBA() color.NRGBA { | |
32 | return color.NRGBA{ | |
33 | R: clamp8(c.R), G: clamp8(c.G), B: clamp8(c.B), A: clamp8(c.A)} | |
34 | } | |
35 | ||
36 | func (c1 Colorf) Add(c2 Colorf) Colorf { | |
37 | return Colorf{ | |
38 | R: c1.R + c2.R, G: c1.G + c2.G, B: c1.B + c2.B, A: c1.A + c2.A, | |
39 | } | |
40 | } | |
41 | ||
42 | func (c Colorf) Scale(s float64) Colorf { | |
43 | return Colorf{ | |
44 | R: c.R * s, G: c.G * s, B: c.B * s, A: c.A * s, | |
45 | } | |
46 | } | |
47 | ||
48 | func (c1 Colorf) Mix(c2 Colorf) Colorf { | |
49 | return Colorf{ | |
50 | R: c1.R * c2.R, G: c1.G * c2.G, B: c1.B * c2.B, A: c1.A * c2.A, | |
51 | } | |
52 | } |
9 | 9 | // surface. This function should only be called with points on its surface; if |
10 | 10 | // not its behavior is undefined. |
11 | 11 | NormalAt(loc *Vector) *Vector |
12 | ||
13 | // The BRDF of a primitive is a function that relates an incoming ray to a | |
14 | // color. The BRDF function takes a location on the primitive and a view | |
15 | // direction and returns the value of the BRDF at that location. | |
16 | BRDF(ray *Ray) Colorf | |
12 | 17 | } |
18 | ||
19 | func LambertBrdf(r *Ray, norm *Vector, base Colorf, amb Colorf) Colorf { | |
20 | return base.Add(amb).Scale(norm.Dot(r.Direction.ScalarMul(-1))) | |
21 | } |
0 | 0 | package monte |
1 | 1 | |
2 | 2 | import ( |
3 | //"fmt" | |
3 | "flag" | |
4 | 4 | "image" |
5 | "image/color" | |
6 | 5 | "math" |
7 | 6 | "math/rand" |
8 | 7 | ) |
9 | 8 | |
10 | func NewColor(r, g, b, a uint8) color.NRGBA { | |
11 | return color.NRGBA{R: r, G: g, B: b, A: a} | |
9 | var debug = flag.Bool("debug", false, "print debugging information") | |
10 | ||
11 | func (s *Scene) ColorAtIntersection(geom *Primitive, in *Ray, d uint8) Colorf { | |
12 | norm := (*geom).NormalAt(in.Origin) | |
13 | c := NewColorf(0, 0, 0, 0) | |
14 | n := 0.0 | |
15 | ||
16 | for i := 0; i < 20; i++ { | |
17 | out := NewRay(in.Origin, RandomVectorInHemisphere(norm)) | |
18 | dc := s.RayCast(out, d + 1) | |
19 | c = c.Add(dc) | |
20 | n += 1. | |
21 | } | |
22 | return c.Scale(1 / n).Mix((*geom).BRDF(in)) | |
12 | 23 | } |
13 | 24 | |
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)) | |
25 | // d is the ray casting depth (number of reflections we've done) | |
26 | func (s *Scene) RayCast(ray *Ray, d uint8) Colorf { | |
27 | if d > 2 { | |
28 | return s.Ambient | |
29 | } | |
22 | 30 | |
23 | 31 | minDist := math.Inf(1) |
24 | 32 | var minGeom *Primitive = nil |
33 | 41 | } |
34 | 42 | } |
35 | 43 | |
36 | if minGeom != nil { | |
37 | return s.ColorAtIntersection(minGeom, NewRay(intersect, ray.Direction)) | |
44 | if minGeom != nil && minDist > 0 { | |
45 | return s.ColorAtIntersection(minGeom, NewRay(intersect, ray.Direction), d) | |
38 | 46 | } |
39 | 47 | return s.Sky |
40 | 48 | } |
41 | 49 | |
42 | func (s *Scene) Evaluate(u, v float64) color.Color { | |
43 | var r, g, b, n uint32 = 0, 0, 0, 0 | |
50 | func (s *Scene) Evaluate(u, v float64) Colorf { | |
51 | // If we're not antialiasing, this is a super simple function. | |
52 | if s.Oversample <= 1 { | |
53 | ray := NewRay(s.Look.Origin, s.DirectionAt(u, v)) | |
54 | return s.RayCast(ray, 0) | |
55 | } | |
56 | ||
57 | c := NewColorf(0, 0, 0, 0) | |
58 | n := 0.0 | |
44 | 59 | for i := 0; i < s.Oversample; i++ { |
45 | 60 | du := (rand.Float64() - 0.5) / 200 |
46 | 61 | dv := (rand.Float64() - 0.5) / 200 |
47 | 62 | |
48 | c := s.RayCast(u + du, v + dv) | |
49 | r += uint32(c.R); g += uint32(c.G); b += uint32(c.B) | |
50 | n++ | |
63 | ray := NewRay(s.Look.Origin, s.DirectionAt(u + du, v + dv)) | |
64 | c = c.Add(s.RayCast(ray, 0)) | |
65 | n += 1. | |
51 | 66 | } |
52 | return NewColor(uint8(r / n), uint8(g / n), uint8(b / n), 255) | |
67 | return c.Scale(1 / n) | |
53 | 68 | } |
54 | 69 | |
55 | 70 | func (s *Scene) SetColor(i, j int, u, v float64, img *image.NRGBA) { |
56 | img.Set(i, j, s.Evaluate(u, v)) | |
71 | c := s.Evaluate(u, v).NRGBA() | |
72 | img.Set(i, j, c) | |
57 | 73 | } |
58 | 74 | |
59 | 75 | func (s *Scene) Render(img *image.NRGBA) { |
65 | 81 | for j := 0; j < j_max; j++ { |
66 | 82 | // (h / w) term is needed to correct for nonunity aspect ratios |
67 | 83 | v := (float64(j) - h / 2.0) / (h / 2.0) * (h / w) |
84 | ||
85 | if *debug { | |
86 | //fmt.Printf("%d/%d\n", i * j_max + j, i_max * j_max) | |
87 | } | |
88 | ||
68 | 89 | s.SetColor(i, j, u, v, img) |
69 | 90 | } |
70 | 91 | } |
0 | 0 | package monte |
1 | ||
2 | import ( | |
3 | "image/color" | |
4 | ) | |
5 | 1 | |
6 | 2 | type Scene struct { |
7 | 3 | Geom []Primitive |
10 | 6 | // scan to fill in the picture |
11 | 7 | U1, U2 *Vector |
12 | 8 | FDist float64 |
13 | Sky color.NRGBA | |
9 | Sky Colorf | |
14 | 10 | Oversample int |
11 | Ambient Colorf | |
15 | 12 | } |
16 | 13 | |
17 | 14 | func (s *Scene) DirectionAt(u, v float64) *Vector { |
43 | 43 | func (s Sphere) NormalAt(loc *Vector) *Vector { |
44 | 44 | return loc.Sub(s.Center).NormalizeInPlace() |
45 | 45 | } |
46 | ||
47 | func (s Sphere) BRDF(ray *Ray) Colorf { | |
48 | return LambertBrdf( | |
49 | ray, s.NormalAt(ray.Origin), NewColorf(1., .7, 0., 1.), | |
50 | NewColorf(.2, .2, .2, 1.)) | |
51 | } |
0 | 0 | package monte |
1 | 1 | |
2 | import "math" | |
2 | import ( | |
3 | "math" | |
4 | "math/rand" | |
5 | ) | |
3 | 6 | |
4 | 7 | // Vector |
5 | 8 | |
52 | 55 | return &Vector{X: v.X * s, Y: v.Y * s, Z: v.Z * s} |
53 | 56 | } |
54 | 57 | |
58 | func RandomVectorInHemisphere(v *Vector) *Vector { | |
59 | test := Vect(rand.Float64(), rand.Float64(), rand.Float64()) | |
60 | test.NormalizeInPlace() | |
61 | if test.Dot(v) < 0 { | |
62 | return test.ScalarMul(-1) | |
63 | } | |
64 | return test | |
65 | } | |
66 | ||
55 | 67 | // Ray |
56 | 68 | |
57 | 69 | type Ray struct { |