func tracer(orig, dir vector.Vector) (st status, dist float64, bounce vector.Vector) { dist = 1e9 st = missUpward p := -orig.Z / dir.Z if 0.01 < p { dist = p bounce = vector.Vector{X: 0, Y: 0, Z: 1} st = missDownward } for i, _ := range objects { p := orig.Add(objects[i]) b := p.DotProduct(dir) c := p.DotProduct(p) - 1 b2 := b * b if b2 > c { q := b2 - c s := -b - math.Sqrt(q) if s < dist && s > 0.01 { dist = s bounce = p // We can lazy compute bounce based on value of p st = hit } } } if st == hit { bounce = bounce.Add(dir.Scale(dist)).Normalize() } return }
func main() { flag.Parse() if *cpuprofile != "" { f, err := os.Create(*cpuprofile) if err != nil { log.Fatal(err) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } g := vector.Vector{X: -5.5, Y: -16, Z: 0}.Normalize() a := vector.Vector{X: 0, Y: 0, Z: 1}.CrossProduct(g).Normalize().Scale(0.002) b := g.CrossProduct(a).Normalize().Scale(0.002) c := a.Add(b).Scale(-256).Add(g) img := image.NewRGBA(image.Rect(0, 0, *width, *height)) const ( R = iota G B A ) pos := 0 invheight := 1.0 / float32(*height-1) s := time.Now() for y := (*height - 1); y >= 0; y-- { fmt.Printf("%.2f%%\r", 100*(1-invheight*float32(y))) for x := (*width - 1); x >= 0; x-- { p := vector.Vector{X: 13, Y: 13, Z: 13} for i := 0; i < 64; i++ { t := a.Scale(Rand() - 0.5).Scale(99).Add(b.Scale(Rand() - 0.5).Scale(99)) orig := vector.Vector{X: 17, Y: 16, Z: 8}.Add(t) dir := t.Scale(-1).Add(a.Scale(Rand() + float64(x)).Add(b.Scale(float64(y) + Rand())).Add(c).Scale(16)).Normalize() p = sampler(orig, dir).Scale(3.5).Add(p) } img.Pix[pos+R] = uint8(p.X) img.Pix[pos+G] = uint8(p.Y) img.Pix[pos+B] = uint8(p.Z) img.Pix[pos+A] = 0xff pos += 4 } } e := time.Since(s) pixels := (*width) * (*height) fmt.Printf("Traced %d pixels in %s, %s/pixel\n", pixels, e, e/time.Duration(pixels)) f, err := os.Create(outimage) if err != nil { log.Fatalln(err) } defer f.Close() if err = png.Encode(f, img); err != nil { log.Fatalln(err) } }
func tracer(orig, dir vector.Vector) (st status, dist float64, bounce vector.Vector) { dist = 1e9 st = missUpward p := -orig.Z / dir.Z if 0.01 < p { dist = p bounce = vector.Vector{X: 0, Y: 0, Z: 1} st = missDownward } for _, object := range objects { k, j := object.k, object.j p := orig.Add(vector.Vector{X: float64(-k), Y: 3, Z: float64(-j - 4)}) b := p.DotProduct(dir) c := p.DotProduct(p) - 1 q := b*b - c if q > 0 { s := -b - math.Sqrt(q) if s < dist && s > 0.01 { dist = s bounce = p.Add(dir.Scale(dist)).Normalize() st = hit } } } return }
func (r row) render(a, b, c vector.Vector, bytes []byte, seed *uint32) { k := (*height - int(r) - 1) * 3 * *width for x := (*width - 1); x >= 0; x-- { p := vector.Vector{X: 13, Y: 13, Z: 13} for i := 0; i < 64; i++ { t := a.Scale(rnd(seed) - 0.5).Scale(99).Add(b.Scale(rnd(seed) - 0.5).Scale(99)) orig := vector.Vector{X: 17, Y: 16, Z: 8}.Add(t) dir := t.Scale(-1).Add(a.Scale(rnd(seed) + float64(x)).Add(b.Scale(float64(r) + rnd(seed))).Add(c).Scale(16)).Normalize() p = sampler(orig, dir, seed).Scale(3.5).Add(p) } bytes[k] = byte(p.X) bytes[k+1] = byte(p.Y) bytes[k+2] = byte(p.Z) k += 3 } }
func main() { runtime.GOMAXPROCS(runtime.NumCPU()) flag.Parse() if *cpuprofile != "" { f, err := os.Create(*cpuprofile) if err != nil { log.Fatal(err) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } if *procs < 1 { log.Fatalf("Procs (%v) needs to be >= 1", *procs) } fmt.Printf("P6 %v %v 255 ", *width, *height) bytes := make([]byte, 3**width**height) g := vector.Vector{X: -5.5, Y: -16, Z: 0}.Normalize() a := vector.Vector{X: 0, Y: 0, Z: 1}.CrossProduct(g).Normalize().Scale(0.002) b := g.CrossProduct(a).Normalize().Scale(0.002) c := a.Add(b).Scale(-256).Add(g) rows := make(chan row, *height) var wg sync.WaitGroup wg.Add(*procs) for i := 0; i < *procs; i++ { go worker(a, b, c, bytes, rows, &wg) } for y := (*height - 1); y >= 0; y-- { rows <- row(y) } close(rows) wg.Wait() if _, err := os.Stdout.Write(bytes); err != nil { log.Panic(err) } }
func sampler(orig, dir vector.Vector, seed *uint32) vector.Vector { st, dist, bounce := tracer(orig, dir) obounce := bounce if st == missUpward { p := 1 - dir.Z p = p * p p = p * p return vector.Vector{X: 0.7, Y: 0.6, Z: 1}.Scale(p) } h := orig.Add(dir.Scale(dist)) l := vector.Vector{X: 9 + rnd(seed), Y: 9 + rnd(seed), Z: 16}.Add(h.Scale(-1)).Normalize() b := l.DotProduct(bounce) sf := 1.0 if b < 0 { b = 0 sf = 0 } else { var st status if st, dist, bounce = tracer(h, l); st != missUpward { b = 0 sf = 0 } } if st == missDownward { h = h.Scale(0.2) fc := vector.Vector{X: 3, Y: 3, Z: 3} if int(math.Ceil(h.X)+math.Ceil(h.Y))&1 == 1 { fc = vector.Vector{X: 3, Y: 1, Z: 1} } return fc.Scale(b*0.2 + 0.1) } r := dir.Add(obounce.Scale(obounce.DotProduct(dir.Scale(-2)))) p := l.DotProduct(r.Scale(sf)) p33 := p * p // p ** 2 p33 = p33 * p33 // p ** 4 p33 = p33 * p33 // p ** 8 p33 = p33 * p33 // p ** 16 p33 = p33 * p33 // p ** 32 p33 = p33 * p // p ** 33 p = p33 * p33 * p33 return vector.Vector{X: p, Y: p, Z: p}.Add(sampler(h, r, seed).Scale(0.5)) }
func sampler(orig, dir vector.Vector) vector.Vector { st, dist, bounce := tracer(orig, dir) if st == missUpward { return vector.Vector{X: 0.7, Y: 0.6, Z: 1}.Scale(math.Pow(1-dir.Z, 4)) } h := orig.Add(dir.Scale(dist)) l := vector.Vector{X: 9 + Rand(), Y: 9 + Rand(), Z: 16}.Add(h.Scale(-1)).Normalize() r := dir.Add(bounce.Scale(bounce.DotProduct(dir.Scale(-2)))) b := l.DotProduct(bounce) if b < 0 { b = 0 } else { var st status if st, dist, bounce = tracer(h, l); st != missUpward { b = 0 } } var sf float64 if b > 0 { sf = 1.0 } p := math.Pow(l.DotProduct(r.Scale(sf)), 99) if st == missDownward { h = h.Scale(0.2) fc := vector.Vector{X: 3, Y: 3, Z: 3} if int(math.Ceil(h.X)+math.Ceil(h.Y))&1 == 1 { fc = vector.Vector{X: 3, Y: 1, Z: 1} } return fc.Scale(b*0.2 + 0.1) } return vector.Vector{X: p, Y: p, Z: p}.Add(sampler(h, r).Scale(0.5)) }