Skip to content

Commit

Permalink
Optimized bvh creation speed
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielPettersson committed Apr 24, 2022
1 parent 90284bf commit ae53a00
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 25 deletions.
8 changes: 8 additions & 0 deletions hittable/aabb.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,11 @@ func (b aabb) hit(r geo.Ray, rayLength util.Interval) bool {

return tmax > math.Max(tmin, 0)
}

func (b aabb) center() geo.Vec3 {
return geo.NewVec3(
b.x.Min+b.x.Max*.5,
b.y.Min+b.y.Max*.5,
b.z.Min+b.z.Max*.5,
)
}
72 changes: 47 additions & 25 deletions hittable/bvh.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package hittable

import (
"math"
"sort"

"github.com/DanielPettersson/solstrale/geo"
"github.com/DanielPettersson/solstrale/internal/util"
Expand All @@ -14,7 +13,7 @@ type bvh struct {
NonPdfLightHittable
left *Hittable
right *Hittable
bBox *aabb
bBox aabb
}

// NewBoundingVolumeHierarchy creates a new hittable object from the given hittable list
Expand All @@ -27,11 +26,10 @@ func NewBoundingVolumeHierarchy(list []Hittable) Hittable {
panic("Cannot create a Bvh with empty list of objects")
}

return _createBvh(list, 0, len(list))
return createBvh(list, 0, len(list))
}

func _createBvh(list []Hittable, start, end int) *bvh {

func createBvh(list []Hittable, start, end int) *bvh {
numObjects := end - start
var left Hittable
var right Hittable
Expand All @@ -43,50 +41,70 @@ func _createBvh(list []Hittable, start, end int) *bvh {
bBox = left.BoundingBox()

} else if numObjects == 2 {
sortHittablesSliceByMostSpreadAxis(list, start, end)
left = list[start]
right = list[start+1]
bBox = combineAabbs(left.BoundingBox(), right.BoundingBox())

} else {
sortHittablesSliceByMostSpreadAxis(list, start, end)
mid := start + numObjects/2
left = _createBvh(list, start, mid)
right = _createBvh(list, mid, end)
mid := sortHittablesSliceByMostSpreadAxis(list, start, end)

// Could not split with objects on both sides. Just split up the middle index
if mid == start || mid == end {
mid = start + numObjects/2
}

left = createBvh(list, start, mid)
right = createBvh(list, mid, end)
bBox = combineAabbs(left.BoundingBox(), right.BoundingBox())
}

return &bvh{left: &left, right: &right, bBox: &bBox}
return &bvh{left: &left, right: &right, bBox: bBox}
}

func sortHittablesSliceByMostSpreadAxis(list []Hittable, start, end int) {
func sortHittablesSliceByMostSpreadAxis(list []Hittable, start, end int) int {
slice := list[start:end]

xSpread := boundingBoxSpread(slice, func(h Hittable) util.Interval { return h.BoundingBox().x })
ySpread := boundingBoxSpread(slice, func(h Hittable) util.Interval { return h.BoundingBox().y })
zSpread := boundingBoxSpread(slice, func(h Hittable) util.Interval { return h.BoundingBox().z })
xSpread, xCenter := boundingBoxSpread(slice, func(h Hittable) float64 { return h.Center().X })
ySpread, yCenter := boundingBoxSpread(slice, func(h Hittable) float64 { return h.Center().Y })
zSpread, zCenter := boundingBoxSpread(slice, func(h Hittable) float64 { return h.Center().Z })

if xSpread >= ySpread && xSpread >= zSpread {
sortHittablesByBoundingBox(slice, func(h Hittable) util.Interval { return h.BoundingBox().x })
return sortHittablesByCenter(slice, xCenter, func(h Hittable) float64 { return h.Center().X }) + start
} else if ySpread >= xSpread && ySpread >= zSpread {
sortHittablesByBoundingBox(slice, func(h Hittable) util.Interval { return h.BoundingBox().y })
return sortHittablesByCenter(slice, yCenter, func(h Hittable) float64 { return h.Center().Y }) + start
} else {
sortHittablesByBoundingBox(slice, func(h Hittable) util.Interval { return h.BoundingBox().z })
return sortHittablesByCenter(slice, zCenter, func(h Hittable) float64 { return h.Center().Z }) + start
}
}

func boundingBoxSpread(list []Hittable, boundingIntervalFunc func(h Hittable) util.Interval) float64 {
func boundingBoxSpread(list []Hittable, centerFunc func(h Hittable) float64) (float64, float64) {
min := util.Infinity
max := -util.Infinity
for _, h := range list {
min = math.Min(min, boundingIntervalFunc(h).Min)
max = math.Max(max, boundingIntervalFunc(h).Min)
min = math.Min(min, centerFunc(h))
max = math.Max(max, centerFunc(h))
}
return max - min
return max - min, (min + max) * .5
}

func sortHittablesByBoundingBox(list []Hittable, boundingIntervalFunc func(h Hittable) util.Interval) {
sort.Slice(list, func(i, j int) bool { return boundingIntervalFunc(list[i]).Min < boundingIntervalFunc(list[j]).Min })
func sortHittablesByCenter(list []Hittable, center float64, boundingCenterFunc func(h Hittable) float64) int {

i := 0
j := len(list) - 1

for i <= j {
if boundingCenterFunc(list[i]) < center {
i++
} else {
tmpI := list[i]
tmpJ := list[j]
list[i] = tmpJ
list[j] = tmpI
j--
}
}

return i
}

func (b *bvh) Hit(r geo.Ray, rayLength util.Interval) (bool, *material.HitRecord) {
Expand All @@ -108,9 +126,13 @@ func (b *bvh) Hit(r geo.Ray, rayLength util.Interval) (bool, *material.HitRecord
}

func (b *bvh) BoundingBox() aabb {
return *b.bBox
return b.bBox
}

func (b *bvh) IsLight() bool {
return false
}

func (b *bvh) Center() geo.Vec3 {
return b.bBox.center()
}
4 changes: 4 additions & 0 deletions hittable/constantMedium.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,7 @@ func (cm constantMedium) BoundingBox() aabb {
func (cm constantMedium) IsLight() bool {
return false
}

func (cm constantMedium) Center() geo.Vec3 {
return cm.Boundary.BoundingBox().center()
}
1 change: 1 addition & 0 deletions hittable/hittable.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Hittable interface {
PdfLightHittable
Hit(r geo.Ray, rayLength util.Interval) (bool, *material.HitRecord)
BoundingBox() aabb
Center() geo.Vec3
IsLight() bool
}

Expand Down
5 changes: 5 additions & 0 deletions hittable/hittableList.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ func (hl *HittableList) BoundingBox() aabb {
return hl.bBox
}

// Center returns the center of all objects in the list
func (hl *HittableList) Center() geo.Vec3 {
return hl.bBox.center()
}

// PdfValue generates a medium pdf value for all hittables in list given an origin and a direction
func (hl *HittableList) PdfValue(origin, direction geo.Vec3) float64 {
weight := 1. / float64(len(hl.list))
Expand Down
4 changes: 4 additions & 0 deletions hittable/motionBlur.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,7 @@ func (m motionBlur) BoundingBox() aabb {
func (m motionBlur) IsLight() bool {
return m.blurredHittable.IsLight()
}

func (m motionBlur) Center() geo.Vec3 {
return m.bBox.center()
}
4 changes: 4 additions & 0 deletions hittable/quad.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ func (q quad) BoundingBox() aabb {
return q.bBox
}

func (q quad) Center() geo.Vec3 {
return q.bBox.center()
}

func (q quad) PdfValue(origin, direction geo.Vec3) float64 {
ray := geo.NewRay(
origin,
Expand Down
4 changes: 4 additions & 0 deletions hittable/rotationY.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ func (ry rotationY) BoundingBox() aabb {
return ry.bBox
}

func (ry rotationY) Center() geo.Vec3 {
return ry.bBox.center()
}

func (ry rotationY) PdfValue(origin, direction geo.Vec3) float64 {
return ry.object.PdfValue(origin, direction)
}
Expand Down
4 changes: 4 additions & 0 deletions hittable/sphere.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ func (s sphere) BoundingBox() aabb {
return s.bBox
}

func (s sphere) Center() geo.Vec3 {
return s.center
}

func (s sphere) PdfValue(origin, direction geo.Vec3) float64 {
ray := geo.NewRay(
origin,
Expand Down
4 changes: 4 additions & 0 deletions hittable/translation.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ func (t translation) BoundingBox() aabb {
return t.bBox
}

func (t translation) Center() geo.Vec3 {
return t.bBox.center()
}

func (t translation) PdfValue(origin, direction geo.Vec3) float64 {
return t.object.PdfValue(origin, direction)
}
Expand Down
8 changes: 8 additions & 0 deletions hittable/triangle.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type triangle struct {
mat material.Material
bBox aabb
area float64
center geo.Vec3
}

func NewTriangle(v0, v1, v2 geo.Vec3, mat material.Material) Hittable {
Expand All @@ -39,6 +40,8 @@ func NewTriangleWithTexCoords(v0, v1, v2 geo.Vec3, tu0, tv0, tu1, tv1, tu2, tv2
normal := n.Unit()
area := n.Length() / 2

center := v0.Add(v1).Add(v2).MulS(0.33333)

return triangle{
v0,
v0v1,
Expand All @@ -53,6 +56,7 @@ func NewTriangleWithTexCoords(v0, v1, v2 geo.Vec3, tu0, tv0, tu1, tv1, tu2, tv2
mat,
bBox,
area,
center,
}
}

Expand Down Expand Up @@ -114,6 +118,10 @@ func (t triangle) BoundingBox() aabb {
return t.bBox
}

func (t triangle) Center() geo.Vec3 {
return t.center
}

func (t triangle) PdfValue(origin, direction geo.Vec3) float64 {
ray := geo.NewRay(
origin,
Expand Down

0 comments on commit ae53a00

Please sign in to comment.