Skip to content

Commit

Permalink
fixed double-bounding-box on spheres and cubes. added moses example.
Browse files Browse the repository at this point in the history
  • Loading branch information
hunterloftis committed Jan 31, 2018
1 parent c75501f commit 6f5348d
Show file tree
Hide file tree
Showing 11 changed files with 72 additions and 30 deletions.
1 change: 1 addition & 0 deletions examples/direct/direct.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/hunterloftis/pbr/surface"
)

// The pathological case for indirect lighting: a small, very bright light at a large distance
func main() {
floor := surface.UnitCube().Move(0, -1, 0).Scale(100, 1, 100)
halogen := material.Light(10000000, 10000000, 10000000)
Expand Down
37 changes: 37 additions & 0 deletions examples/moses/moses.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package main

import (
"fmt"
"os"
"os/signal"
"syscall"

"github.com/hunterloftis/pbr"
"github.com/hunterloftis/pbr/material"
"github.com/hunterloftis/pbr/obj"
"github.com/hunterloftis/pbr/surface"
)

func main() {
moses, err := obj.ReadFile("fixtures/models/moses/model.obj", false)
if err != nil {
panic(err)
}
key := surface.UnitSphere(material.Light(100000, 100000, 50000)).Move(-20, 10, 20).Scale(5, 5, 5)
fill := surface.UnitSphere(material.Light(20000, 20000, 50000)).Move(30, 10, 5).Scale(5, 5, 5)
back := surface.UnitSphere(material.Light(25000, 25000, 100000)).Move(-30, -5, -10).Scale(8, 8, 8)
scene := pbr.NewScene(moses...)
bounds, _ := scene.Info()
target := bounds.Center
scene.Add(key, fill, back)
cam := pbr.NewCamera(888, 500).MoveTo(0, -10, 50).LookAt(target, target)
render := pbr.NewRender(scene, cam)
interrupt := make(chan os.Signal, 2)

fmt.Println("rendering moses.png (press Ctrl+C to finish)...")
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)
render.Start()
<-interrupt
render.Stop()
render.WritePngs("moses.png", "moses-heat.png", "moses-noise.png", 1)
}
12 changes: 8 additions & 4 deletions examples/shapes/shapes.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package main
import (
"fmt"
"math"
"time"
"os"
"os/signal"
"syscall"

"github.com/hunterloftis/pbr"
"github.com/hunterloftis/pbr/geom"
Expand All @@ -18,7 +20,7 @@ func main() {
whitePlastic := material.Plastic(1, 1, 1, 0.2)
bluePlastic := material.Plastic(0, 0, 0.9, 0)
greenPlastic := material.Plastic(0, 0.9, 0, 0)
gold := material.Metal(1.022, 0.782, 0.344, 0.9, 0)
gold := material.Metal(1.022, 0.782, 0.344, 0.1, 1)
greenGlass := material.Glass(0.2, 1, 0.1, 0.05)

scene := pbr.NewScene()
Expand All @@ -42,9 +44,11 @@ func main() {
surface.UnitSphere(gold).Move(0.45, 0.05, -0.4).Scale(0.2, 0.2, 0.2),
)

fmt.Println("rendering shapes.png (15 mins)...")
interrupt := make(chan os.Signal, 2)
fmt.Println("rendering shapes.png (press Ctrl+C to finish)...")
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)
render.Start()
time.Sleep(time.Minute * 15)
<-interrupt
render.Stop()
render.WritePngs("shapes.png", "shapes-heat.png", "shapes-noise.png", 1)
}
1 change: 1 addition & 0 deletions geom/direction.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ func (a Direction) Scaled(n float64) Vector3 {
}

// Cross returns the cross product of unit vectors a and b.
// TODO: Has floating point error - acceptable?
func (a Direction) Cross(b Direction) Direction {
return Direction{a.Y*b.Z - a.Z*b.Y, a.Z*b.X - a.X*b.Z, a.X*b.Y - a.Y*b.X}
}
Expand Down
3 changes: 1 addition & 2 deletions material/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,8 @@ func (m *Map) At(u, v float64) *Sample {
y += height
}
r, g, b, a := m.d.Texture.At(x, y).RGBA()
// fmt.Println("rgb:", r, g, b, a)
// panic("ok")
s.Color = rgb.Energy{float64(r), float64(g), float64(b)}.Amplified(1 / float64(a))
// TODO: deal with metals; s.Fresnel = s.Fresnel.Blend(s.Color, s.Metal)?
}
return s
}
2 changes: 1 addition & 1 deletion obj/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func NewScanner(r io.Reader) *Scanner {
vn: make([]geom.Direction, 0),
vt: make([]geom.Vector3, 0),
lib: make(map[string]*material.Map),
mat: material.Plastic(1, 1, 1, 0.7),
mat: material.Default,
}
}

Expand Down
12 changes: 6 additions & 6 deletions sampler.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ func (s *sampler) start(buffer *rgb.Framebuffer, in <-chan int, done chan<- samp
}()
}

// TODO: sample Specular reflections from direct light sources and weight results by their BSDF towards the light
// Or, better, sample lights directly in general and pass that through a unified BSDF
func (s *sampler) tracePrimary(x, y int, rnd *rand.Rand) (energy rgb.Energy) {
ray := s.camera.ray(float64(x), float64(y), rnd)
hit := s.scene.Intersect(ray)
Expand Down Expand Up @@ -84,8 +86,7 @@ func (s *sampler) traceIndirect(ray *geom.Ray3, depth int, signal rgb.Energy, rn
normal, mat := hit.Surface.At(point)
energy = energy.Merged(mat.Light, signal)
dir, strength, diffused := mat.Bsdf(normal, ray.Dir, hit.Dist, rnd)
lights := s.scene.Lights()
if diffused && lights > 0 {
if lights := s.scene.Lights(); diffused && lights > 0 {
direct, coverage := s.traceDirect(lights, point, normal, rnd)
energy = energy.Merged(direct.Strength(mat.Color), signal)
signal = signal.Amplified(1 - coverage)
Expand All @@ -98,17 +99,16 @@ func (s *sampler) traceDirect(num int, point geom.Vector3, normal geom.Direction
limit := int(math.Min(float64(s.direct), float64(num)))
for i := 0; i < limit; i++ {
light := s.scene.Light(rnd)
ray, solidAngle := light.Box().ShadowRay(point, rnd)
cos := ray.Dir.Cos(normal)
if cos <= 0 {
ray, solidAngle := light.Box().ShadowRay(point, normal, rnd)
if solidAngle <= 0 {
break
}
coverage += solidAngle
hit := s.scene.Intersect(ray)
if !hit.Ok {
break
}
e := hit.Surface.Material().Emit().Amplified(solidAngle * cos / math.Pi)
e := hit.Surface.Material().Emit().Amplified(solidAngle / math.Pi)
energy = energy.Plus(e)
}
return energy, coverage
Expand Down
1 change: 0 additions & 1 deletion scene.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ func NewScene(surfaces ...surface.Surface) *Scene {
func (s *Scene) Intersect(ray *geom.Ray3) surface.Hit {
atomic.AddUint64(&s.rays, 1)
return s.tree.Intersect(ray)
// return s.tree.IntersectSurfaces(ray, math.Inf(1))
}

// Rays returns the total count of Ray/Scene intersections tested since the Scene was created.
Expand Down
20 changes: 10 additions & 10 deletions surface/box.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,15 @@ func (b *Box) Contains(p geom.Vector3) bool {
// chooses a random point within that disc,
// and returns a Ray3 from the origin to the random point.
// https://marine.rutgers.edu/dmcs/ms552/2009/solidangle.pdf
func (b *Box) ShadowRay(origin geom.Vector3, rnd *rand.Rand) (*geom.Ray3, float64) {
norm := origin.Minus(b.Center).Unit()
center2 := b.Center.Plus(norm.Scaled(-b.Radius))
x, y := geom.RandPointInCircle(b.Radius, rnd)
right := norm.Cross(geom.Up)
up := right.Cross(norm)
point := center2.Plus(right.Scaled(x)).Plus(up.Scaled(y))
func (b *Box) ShadowRay(origin geom.Vector3, normal geom.Direction, rnd *rand.Rand) (*geom.Ray3, float64) {
forward := origin.Minus(b.Center).Unit()
x, y := geom.RandPointInCircle(b.Radius, rnd) // TODO: push center back along "forward" axis, away from origin
right := forward.Cross(geom.Up)
up := right.Cross(forward)
point := b.Center.Plus(right.Scaled(x)).Plus(up.Scaled(y))
ray := geom.NewRay(origin, point.Minus(origin).Unit()) // TODO: this should be a convenience method
dist := center2.Minus(origin).Len()
weight := (b.Radius * b.Radius) / (2 * dist * dist) // cosine-weighted ratio of disc surface area to hemisphere surface area
return ray, weight
dist := b.Center.Minus(origin).Len()
cos := ray.Dir.Cos(normal)
solidAngle := cos * (b.Radius * b.Radius) / (2 * dist * dist) // cosine-weighted ratio of disc surface area to hemisphere surface area
return ray, solidAngle
}
6 changes: 3 additions & 3 deletions surface/cube.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ func (c *Cube) transform(m *geom.Matrix4) *Cube {
c.Pos = c.Pos.Mult(m)
min := c.Pos.MultPoint(geom.Vector3{})
max := c.Pos.MultPoint(geom.Vector3{})
for x := -1.0; x <= 1; x += 2 {
for y := -1.0; y <= 1; y += 2 {
for z := -1.0; z <= 1; z += 2 {
for x := -0.5; x <= 0.5; x += 1 {
for y := -0.5; y <= 0.5; y += 1 {
for z := -0.5; z <= 0.5; z += 1 {
pt := c.Pos.MultPoint(geom.Vector3{x, y, z})
min = min.Min(pt)
max = max.Max(pt)
Expand Down
7 changes: 4 additions & 3 deletions surface/sphere.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ func (s *Sphere) transform(t *geom.Matrix4) *Sphere {
s.Pos = s.Pos.Mult(t)
min := s.Pos.MultPoint(geom.Vector3{})
max := s.Pos.MultPoint(geom.Vector3{})
for x := -1.0; x <= 1; x += 2 {
for y := -1.0; y <= 1; y += 2 {
for z := -1.0; z <= 1; z += 2 {
for x := -0.5; x <= 0.5; x += 1 {
for y := -0.5; y <= 0.5; y += 1 {
for z := -0.5; z <= 0.5; z += 1 {
pt := s.Pos.MultPoint(geom.Vector3{x, y, z})
min = min.Min(pt)
max = max.Max(pt)
Expand Down Expand Up @@ -71,6 +71,7 @@ func (s *Sphere) Box() *Box {

// Intersect tests whether the sphere intersects a given ray.
// http://tfpsly.free.fr/english/index.html?url=http://tfpsly.free.fr/english/3d/Raytracing.html
// TODO: http://kylehalladay.com/blog/tutorial/math/2013/12/24/Ray-Sphere-Intersection.html
func (s *Sphere) Intersect(ray *geom.Ray3) Hit {
if ok, _, _ := s.box.Check(ray); !ok {
return Miss
Expand Down

0 comments on commit 6f5348d

Please sign in to comment.