Assignment 2 Lazy KD-Tree

Murad Akhter

Date submitted: 29th Apr 2006

Code emailed: 28th & 29th Apr 2006

Comments

I had a phenomenally late start on the assignment since I was off campus most of this week. However, once I had read section 4.4 of PBRT and gone through the source code, the assignment went fairly smoothly. Unfortunately I emailed the wrong source file when I submitted so please refer to my second email for grading.

Implementation

Const issues & Data structures

To get around const issues, I moved all the KdAccelTree members I needed to modify into a mutable struct. I also made the curMailboxId a static class variable to maintain the spirit of assigning monotically increasing ray id values in KdTree intersection routines as I had chosen to implement a KdTree of KdTrees.

To keep nodes 8 bytes I used several simple tricks. I stored child nodes adjancent to one another so I would only need to keep track of one index. A lazy node was a leaf node with no mailboxes and a pointer to a dynamically assigned data structure storing its bounds, an array of primitive indices, and parameters for node number, depth and bad refines. The pointer was part of the aboveChild/primitives union and I used its least significant bit to differentiate between standard leaf nodes and lazy nodes taking advantage of the fact that pointers are byte aligned. The only complexity here was that I had to mask this bit whenever I used the pointer but that was straightforward to do.

Tree traversal and lazy evaluation

I grew the tree one level at a time, starting with 1 root node and two lazy children. The lazy nodes were expanded in the Intersect/IntersectP routines. I expanded a lazy node if a ray intersected its bounding box. First I reset the lazy bit and then I called a modified version of the buildTree function which converted the node into a leaf or intermediate node (depending on the normal factors such as depth, number of primitives and splitting cost), freeing its associated lazy info data structure. For each intermediate node, two lazy nodes were automatically generated. I wanted to use my own memory allocation scheme for this to avoid reallocating large chunks of memory with malloc and free but did not have time in the end to do so

Primitive refinement

As required for the assignment I didn't refine primitives unless I had reached a leaf node and discovered a primitive that couldn't be intersected. In this case I would refine the primitive fully into a new set of primitives and replace it with a kdtree built from those primitives. I could have refined the primitive to just one level but this didn't give superior results emperically. The main purpose of lazy refinement was to simply avoid the significant refinement cost for a primitive that would never intersect a ray.

Cost function

Due to time limitation I stuck with the normal kdtree cost function. Potential improvement to this would have been to check for intersectable and non-intersectable primitives to weigh the cost in favor of the latter and to measure the distance to the camera position and weigh the cost in favor of the node further away.

Bugs & Gotchas

I ran into issues with the KdToDo pointers getting invalidated when the nodes array was reallocated and switched to using indices to the nodes array rather than actual pointer values. The most annoying bug I ran into had to do with an infinite loop while traversing the tree for intersection tests. I hadn't considered the obvious case in which the ray didn't intersect the current lazy nodes bounding box and where there were no futher children enqueued in the todos array. I found gdb and the pointers on debuging on the assignment help site very useful for figuring this out.

Final Images Rendered with my implementation of heightfield.cpp

I had mixed results with my lazy kd tree. It performed significantly better than the regular implementation for the second and third killeroos scenes and marginally better on the first one. It performed worse on the plant scenes due to the incremental cost of additional triangle ray intersections.

killeroos-view1.pbrt (Killeroos visible)

KD Tree

Lazy KD Tree

Ratio

build time (secs)

15.8

0.0

0.0%

total time (secs)

29.989

29.13

97.13%

Num of nodes made

2.752M

1.357M

49.29%

Triangle ray intersections

673.2k

827.7k

122.95%

"killeroos-view2.pbrt (Killeroos invisible)"

KD Tree

Lazy KD Tree

Ratio

build time (secs)

16.5

0.0

0.0%

total time (secs)

27.554

12.754

46.29%

Num of nodes made

2.752M

47

0.0017%

Triangle ray intersections

758.4k

776.1k

102.33%

"killeroos-view3.pbrt (close-up)"

KD Tree

Lazy KD Tree

Ratio

build time (secs)

16.7

0.0

0.0%

total time (secs)

30.943

22.7

73.36%

Num of nodes made

2.752M

373.3k

13.56%

Triangle ray intersections

644.1k

1.018M

158.05%

"plants-view1.pbrt"

KD Tree

Lazy KD Tree

Ratio

build time (secs)

41.2

0.0

0.0%

total time (secs)

721.281

1082

150%

Num of nodes made

14.884M

4.048M

27.20%

Triangle ray intersections

20.295M

27.601M

136.00%

"plants-view2.pbrt"

KD Tree

Lazy KD Tree

Ratio

build time (secs)

47.7

0.0

0.0%

total time (secs)

1192.75

1872.8

157.02%

Num of nodes made

14.884M

5.167M

34.72%

Triangle ray intersections

25.902M

31.232M

120.58%