Skip to content

Commit

Permalink
SAH implemented.
Browse files Browse the repository at this point in the history
  • Loading branch information
tatsy committed Nov 11, 2017
1 parent 60dcf81 commit 7b981cd
Show file tree
Hide file tree
Showing 9 changed files with 421 additions and 150 deletions.
5 changes: 2 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
export GOPATH = $(PWD)


all:
go build ./...

run:
go build ./src/main.go && ./main
go build ./src/main.go && ./main ${ARGS}

test:
go test ./... --cover
go test ./... --cover -v

clean:
rm -rf main *.jpg *.png *.prof
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,21 @@ gopt

[![Build Status](https://travis-ci.org/tatsy/gopt.svg?branch=master)](https://travis-ci.org/tatsy/gopt)
[![Coverage Status](https://coveralls.io/repos/github/tatsy/gopt/badge.svg)](https://coveralls.io/github/tatsy/gopt)

> Physically based path tracer implemented with Go.
## Usage

```sh
export GOPATH=`pwd`
go build ./src/main.go
./main -i [Scene JSON file]
```

## Result

<img src="./results/gopher.jpg" alt="gopher.jpg" width="500"/>

## Copyright

MIT License 2017 (c) Tatsuya Yatagawa (tatsy)
6 changes: 3 additions & 3 deletions data/scene.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
{
"name": "image",
"parameters": [
{"name": "width", "value": "1280"},
{"name": "height", "value": "720"}
{"name": "width", "value": "640"},
{"name": "height", "value": "360"}
]
},
{
"name": "integrator",
"parameters": [
{"name": "type", "value": "path"},
{"name": "max-bounces", "value": "16"},
{"name": "num-samples", "value": "128"}
{"name": "num-samples", "value": "32"}
]
},
{
Expand Down
201 changes: 99 additions & 102 deletions src/accelerator/bvh.go
Original file line number Diff line number Diff line change
@@ -1,88 +1,10 @@
package accelerator

import (
"sort"
"math"
. "core"
)

type BvhNode struct {
left, right *BvhNode
primId int
bbox *Bounds3d
}

func NewLeafNode(primId int, b *Bounds3d) *BvhNode {
node := &BvhNode{}
node.left = nil
node.right = nil
node.primId = primId
node.bbox = b
return node
}

func NewForkNode(left *BvhNode, right *BvhNode, b *Bounds3d) *BvhNode {
node := &BvhNode{}
node.left = left
node.right = right
node.primId = -1
node.bbox = b
return node
}

func (node *BvhNode) IsLeaf() bool {
return node.left == nil && node.right == nil
}

type SortItem struct {
v *Vector3d
i int
}

func NewSortItem(v *Vector3d, i int) *SortItem {
item := &SortItem{}
item.v = v
item.i = i
return item
}

type AxisSorter struct {
Items []*SortItem
Axis int
}

func NewAxisSorter(items []*SortItem, axis int) *AxisSorter {
sorter := &AxisSorter{}
sorter.Items = items
sorter.Axis = axis
return sorter
}

func (a *AxisSorter) Len() int {
return len(a.Items)
}

func (a *AxisSorter) Swap(i, j int) {
a.Items[i], a.Items[j] = a.Items[j], a.Items[i]
}

func (a *AxisSorter) Less(i, j int) bool {
v1 := a.Items[i].v
v2 := a.Items[j].v
return v1.NthElement(a.Axis) < v2.NthElement(a.Axis)
}

type IndexedPrimitive struct {
p *Primitive
i int
}

func NewIndexedPrimitive(p *Primitive, i int) *IndexedPrimitive {
ip := &IndexedPrimitive{}
ip.p = p
ip.i = i
return ip
}

type Bvh struct {
primitives []*Primitive
nodes []*BvhNode
Expand All @@ -93,44 +15,119 @@ func NewBvh(primitives []*Primitive) *Bvh {
bvh := &Bvh{}
bvh.primitives = primitives

ips := make([]*IndexedPrimitive, len(primitives))
buildData := make([]*BvhPrimitiveInfo, len(primitives))
for i, p := range primitives {
ips[i] = NewIndexedPrimitive(p, i)
buildData[i] = NewBvhPrimitiveInfo(i, p.Bounds())
}

bvh.root = NewBvhSub(bvh, ips)
bvh.root = NewBvhSub(bvh, buildData)
return bvh
}

func NewBvhSub(bvh *Bvh, primitives []*IndexedPrimitive) *BvhNode {
if len(primitives) == 1 {
node := NewLeafNode(primitives[0].i, primitives[0].p.Bounds())
func NewBvhSub(bvh *Bvh, buildData []*BvhPrimitiveInfo) *BvhNode {
numData := len(buildData)
if numData == 1 {
node := NewLeafNode(buildData[0].primitiveId, buildData[0].bounds)
bvh.nodes = append(bvh.nodes, node)
return node
}

bbox := NewBounds3d()
items := make([]*SortItem, len(primitives))
for i := range primitives {
b := primitives[i].p.Bounds()
bbox.Merge(b)
items[i] = &SortItem{b.Center(), i}
bounds := NewBounds3d()
for i := 0; i < numData; i++ {
bounds = bounds.Merge(buildData[i].bounds)
}
axis := bbox.MaxExtent()
splitAxis := bounds.MaxExtent()

splitMethod := BVH_SPLIT_METHOD_SAH
splitPos := numData / 2

switch splitMethod {
case BVH_SPLIT_METHOD_EQUAL_COUNT:
sortable := NewBvhPrimitiveSortable(
buildData, splitAxis, nil,
)
SliceNthElement(sortable, 0, numData, splitPos)
case BVH_SPLIT_METHOD_SAH:
if numData <= 4 {
sortable := NewBvhPrimitiveSortable(
buildData, splitAxis, nil,
)
SliceNthElement(sortable, 0, numData, splitPos)
} else {
numBuckets := 12
centroidBounds := NewBounds3d()
for i := 0; i < numData; i++ {
centroidBounds = centroidBounds.MergePoint(buildData[i].centroid)
}

axisSorter := &AxisSorter{items, axis}
sort.Sort(axisSorter)
buckets := make([]*BucketInfo, numBuckets)
for i := 0; i < numBuckets; i++ {
buckets[i] = NewBucketInfo(0, NewBounds3d())
}

newPrimitives := make([]*IndexedPrimitive, len(primitives))
for i := range items {
newPrimitives[i] = primitives[items[i].i]
}
cMin := centroidBounds.MinPos.NthElement(splitAxis)
cMax := centroidBounds.MaxPos.NthElement(splitAxis)
invDenom := 1.0 / (math.Abs(cMax - cMin) + Eps)
for i := 0; i < numData; i++ {
c0 := buildData[i].centroid.NthElement(splitAxis)
c1 := centroidBounds.MinPos.NthElement(splitAxis)
numer := c0 - c1
b := int(Float(numBuckets) * math.Abs(numer) * invDenom)
if b == numBuckets {
b = numBuckets - 1
}

buckets[b].count += 1
buckets[b].bounds = buckets[b].bounds.Merge(buildData[i].bounds)
}

iHalf := len(newPrimitives) / 2
leftNode := NewBvhSub(bvh, newPrimitives[:iHalf])
rightNode := NewBvhSub(bvh, newPrimitives[iHalf:])
bucketCost := make([]Float, numBuckets - 1)
for i := 0; i < numBuckets - 1; i++ {
b0 := NewBounds3d()
b1 := NewBounds3d()
cnt0, cnt1 := 0, 0
for j := 0; j <= i; j++ {
b0 = b0.Merge(buckets[j].bounds)
cnt0 += buckets[j].count
}
for j := i + 1; j < numBuckets; j++ {
b1 = b1.Merge(buckets[j].bounds)
cnt1 += buckets[j].count
}
bucketCost[i] += 0.125 + (Float(cnt0) * b0.Area() + Float(cnt1) * b1.Area()) / bounds.Area()
}

minCost := bucketCost[0]
minCostSplit := 0
for i := 1; i < numBuckets - 1; i++ {
if minCost > bucketCost[i] {
minCost = bucketCost[i]
minCostSplit = i
}
}

if minCost < Float(numData) {
comparator := func(info *BvhPrimitiveInfo) bool {
cMin := centroidBounds.MinPos.NthElement(splitAxis)
cMax := centroidBounds.MaxPos.NthElement(splitAxis)
inv := 1.0 / (math.Abs(cMax - cMin) + Eps)
diff := math.Abs(info.centroid.NthElement(splitAxis) - cMin)
b := int(Float(numBuckets) * diff * inv)
if b >= numBuckets {
b = numBuckets - 1
}
return b <= minCostSplit
}
splitPos = SlicePartition(NewBvhPrimitiveSortable(
buildData, splitAxis, comparator,
))
}
}
}

node := NewForkNode(leftNode, rightNode, bbox)
leftNode := NewBvhSub(bvh, buildData[:splitPos])
rightNode := NewBvhSub(bvh, buildData[splitPos:])
node := NewForkNode(leftNode, rightNode, bounds)
bvh.nodes = append(bvh.nodes, node)
return node
}
Expand Down

0 comments on commit 7b981cd

Please sign in to comment.