git.haldean.org monte / master
Some progress but ray bouncing isn't working well. Lots of erroneous noise Will Brown 9 years ago
7 changed file(s) with 135 addition(s) and 33 deletion(s). Raw diff Collapse all Expand all
99 "runtime/pprof"
1010 )
1111
12 var width = flag.Int("width", 300, "width of output image")
12 var width = flag.Int("width", 500, "width of output image")
1313 var height = flag.Int("height", 300, "height of output image")
1414 var output = flag.String("output", "output.png", "output PNG file")
1515 var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
2626
2727 println("Monte is go.")
2828
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 }
3033
3134 scene := monte.Scene{
3235 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)),
3437 U1: monte.Vect(1, 0, 0),
3538 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),
3943 }
4044
4145 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 }
99 // surface. This function should only be called with points on its surface; if
1010 // not its behavior is undefined.
1111 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
1217 }
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 }
00 package monte
11
22 import (
3 //"fmt"
3 "flag"
44 "image"
5 "image/color"
65 "math"
76 "math/rand"
87 )
98
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))
1223 }
1324
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 }
2230
2331 minDist := math.Inf(1)
2432 var minGeom *Primitive = nil
3341 }
3442 }
3543
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)
3846 }
3947 return s.Sky
4048 }
4149
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
4459 for i := 0; i < s.Oversample; i++ {
4560 du := (rand.Float64() - 0.5) / 200
4661 dv := (rand.Float64() - 0.5) / 200
4762
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.
5166 }
52 return NewColor(uint8(r / n), uint8(g / n), uint8(b / n), 255)
67 return c.Scale(1 / n)
5368 }
5469
5570 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)
5773 }
5874
5975 func (s *Scene) Render(img *image.NRGBA) {
6581 for j := 0; j < j_max; j++ {
6682 // (h / w) term is needed to correct for nonunity aspect ratios
6783 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
6889 s.SetColor(i, j, u, v, img)
6990 }
7091 }
00 package monte
1
2 import (
3 "image/color"
4 )
51
62 type Scene struct {
73 Geom []Primitive
106 // scan to fill in the picture
117 U1, U2 *Vector
128 FDist float64
13 Sky color.NRGBA
9 Sky Colorf
1410 Oversample int
11 Ambient Colorf
1512 }
1613
1714 func (s *Scene) DirectionAt(u, v float64) *Vector {
4343 func (s Sphere) NormalAt(loc *Vector) *Vector {
4444 return loc.Sub(s.Center).NormalizeInPlace()
4545 }
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 }
00 package monte
11
2 import "math"
2 import (
3 "math"
4 "math/rand"
5 )
36
47 // Vector
58
5255 return &Vector{X: v.X * s, Y: v.Y * s, Z: v.Z * s}
5356 }
5457
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
5567 // Ray
5668
5769 type Ray struct {