<?xml version="1.0"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">

  <channel>
    <title>unvirtual</title>
    <link>http://unvirtual.github.com/</link>
    <atom:link href="http://unvirtual.github.com/rss.xml" rel="self" type="application/rss+xml" />
    <description>Doing stuff with computers</description>
    <language>en-us</language>
    <pubDate>Sun, 29 Apr 2012 14:36:43 PDT</pubDate>
    <lastBuildDate>Sun, 29 Apr 2012 14:36:43 PDT</lastBuildDate>

    
      <item>
        <title>Space partitioning and ray traversal with `cukd`</title>
        <link>http://unvirtual.github.com/blog/2012-03/23/cukd</link>
        <pubDate>Fri, 23 Mar 2012 00:00:00 PDT</pubDate>
        <author>unvirtual</author>
        <guid>http://unvirtual.github.com/blog/2012-03/23/cukd</guid>
        <description>&lt;p&gt;A first working version of my parallelized kd-tree implementation, &lt;code&gt;cukd&lt;/code&gt;, is available on &lt;a href='https://github.com/unvirtual/cukd'&gt;github&lt;/a&gt;. A good moment to analyze how it performs so far.&lt;/p&gt;

&lt;h2 id='what_it_does'&gt;What it does&lt;/h2&gt;

&lt;p&gt;Given a triangle soup, &lt;code&gt;cukd&lt;/code&gt; constructs a &lt;a href='http://en.wikipedia.org/wiki/Kd-tree' title='Wikipedia: kd-tree'&gt;kd-tree&lt;/a&gt; on NVidia GPUs utilizing the CUDA framework, using empty space cutting, median splitting and surface area heuristics (SAH). Once the tree is constructed, it takes a list of rays &amp;#8212; their origins and directions - and traces them through the scene, thereby searching for triangle &amp;#8211; ray intersections, returning indices to the foremost intersected triangles and traversal costs.&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s a visualisation of the ray traversal cost for the &lt;a href='http://graphics.stanford.edu/data/3Dscanrep/'&gt;Stanford Dragon&lt;/a&gt; with roughly 200k triangles.&lt;/p&gt;

&lt;p&gt;&lt;img src='/img/dragon_ray_traversal.jpg' alt='Dragon ray traversal cost visualisation' /&gt;&lt;/p&gt;

&lt;p&gt;Red and green pixels represent triangle hits/no hits, resepectively, and the brightness encodes the traversal cost.&lt;/p&gt;

&lt;h2 id='how_well_it_works'&gt;How well it works&lt;/h2&gt;

&lt;p&gt;Quite to my surprise, the first version of &lt;code&gt;cukd&lt;/code&gt; shows nice performance. So far, the main focus of development was on correctness in implementing the algorithm detailed in &lt;a href='http://www.kunzhou.net/2008/kdtree.pdf' title='Real Time KD-Tree Construction on Graphics Hardware'&gt;Real time KD-Tree Construction on Graphics Hardware&lt;/a&gt; by Zhou et al., choosing the optimal interplay of algorithms like parallel reductions and scans, reducing memory allocations on and transfers to the GPU and using structure of arrays wherever possible. Only secondary (though very important) were hardware specific considerations, like memory alignment, using of texture/shared/constant memory and so on, though these changes can be incorporated quite easily in the future. Still, the performance is quite impressive, topping almost 90 million rays/sec for a small scene:&lt;/p&gt;

&lt;p&gt;&lt;img src='/img/kd-tree-performance.png' alt='kd-tree performance' /&gt;&lt;/p&gt;

&lt;p&gt;Building the kd-tree for the Stanford dragon consisting of 300k triangles takes around 350ms on my NVidia GTX 760, not really in real time territory. 40 million rays/sec for traversal of this scene however pretty much is. Of course, there is more to raytracing than just ray-triangles intersections, but it&amp;#8217;s a nice start. Here the rays are cast line by line in the scene. Switching to &lt;a href='http://en.wikipedia.org/wiki/Morton_order'&gt;Morton order&lt;/a&gt; should improve cache coherence and therefore ray traversal time even more.&lt;/p&gt;

&lt;p&gt;Looking closer at the graph, it&amp;#8217;s obvious that there is a lot of overhead one can potentially get rid of. While the tree creation time rises linearly and rather slowly &amp;#8211; going from ~6k to 300k triangles, a factor of 50, only requires 8 times the computation time &amp;#8211;, the large ~60ms offset around ~6k triangles indidcates room for improvement.&lt;/p&gt;

&lt;p&gt;The distribution of the traversal cost per ray (i.e. the number of steps through the tree plus the number of triangle intersection tests) looks the same for every size of the model:&lt;/p&gt;

&lt;p&gt;&lt;img src='/img/kdtree_ray_cost.png' alt='Ray traversal cost' /&gt;&lt;/p&gt;

&lt;p&gt;The peak around ~40 steps for rays intersecting some triangle is exactly where one would expect it, somewhere between the average leaf depth of the tree (18 in this case) and the average number of triangles in each leaf (26 in this case). In the no-hit case, however, there is a large spread to fairly high values. I suppose this has to do with some kind of problem in empty space splitting. Indeed, the above picture looks very much different when we allow more empty space in each node.&lt;/p&gt;

&lt;p&gt;Apparently there are huge nodes containing only few triangles at their boundaries, something that shouldn&amp;#8217;t really happen.&lt;/p&gt;</description>
      </item>
    
      <item>
        <title>K-d trees with CUDA</title>
        <link>http://unvirtual.github.com/blog/2012-03/05/kd-tree-details</link>
        <pubDate>Mon, 05 Mar 2012 00:00:00 PST</pubDate>
        <author>unvirtual</author>
        <guid>http://unvirtual.github.com/blog/2012-03/05/kd-tree-details</guid>
        <description>&lt;p&gt;Spatial acceleration structures are crucial whenever relations between multi-dimensional data are to be analyzed. For instance, nearest neighbour searches in particle-based fluid simulations or ray-triangle intersections in raytracing both profit largely from space subdivisions.&lt;/p&gt;

&lt;p&gt;Generating these structures on graphics hardware in a fast parallelized way opens new opportunities for real time applications and in the following the main ingredients for the construction of one of these structures &amp;#8211; the so-called &lt;a href='http://en.wikipedia.org/wiki/K-d_tree'&gt;k-d tree&lt;/a&gt; &amp;#8211; on GPUs is discussed. &lt;a href='http://www.kunzhou.net/2008/kdtree.pdf'&gt;&amp;#8220;K-d tree construction on Graphics Hardware&amp;#8221;&lt;/a&gt; by Zhou et al. discribes such an approach to k-d tree construction, a method mainly consisting of two iterations over nodes of the tree:&lt;/p&gt;

&lt;h3 id='large_node_stage'&gt;Large Node Stage&lt;/h3&gt;

&lt;p&gt;In the first step of the k-d tree construction the top tree structure is established by cutting of empty space in node bounding boxes and by splitting nodes into children at the median of a node&amp;#8217;s tight triangle bounding box. The split axis is chosen to be the longest node edge. A node is marked as a leaf node, as soon as it contains less than a fixed number of elements [N_l], with [N_l = 64] in this case.&lt;/p&gt;

&lt;p&gt;&lt;img src='/img/large_node_stage.png' alt='Large node stage' /&gt;&lt;/p&gt;

&lt;p&gt;The resulting child nodes contain a minimal amount of empty space and cutting the tight bounding box guarantees a termination of the process by subdividing triangles into child nodes at each step. In the case of a triangle being cut in the process, the triangle is clipped and the resulting left and right triangle bounding boxes are sorted into the respective child nodes.&lt;/p&gt;

&lt;p&gt;Most of this stage is either parallelized over nodes or triangles, building the tree iteratively from top to bottom as long as nodes with [N \lt N_l] elements exist.&lt;/p&gt;

&lt;h3 id='small_node_stage'&gt;Small Node Stage&lt;/h3&gt;

&lt;p&gt;In a subsequent step, the resulting leaf nodes &amp;#8211; the small nodes &amp;#8211; are further subdivided using Surface Area Heuristics (SAH) (further detail in &lt;a href='http://dcgi.felk.cvut.cz/home/havran/phdthesis.html'&gt;Heuristic Ray Shooting Algorithms&lt;/a&gt; by Vlastimil Havran).&lt;/p&gt;

&lt;p&gt;SAH provides a model for the estimation of ray traversal cost through a spatial acceleration structure, a crucial measure for fast ray tracing. The basis for SAH cost estimation is: Given a ray that intersects a bounding box, what is the probabilty that this same ray is also intersecting a bounding volume within the same box?&lt;/p&gt;

&lt;p&gt;&lt;img src='/img/SAH_prob.png' alt='sah probability' /&gt;&lt;/p&gt;

&lt;p&gt;The answer is simple and is given by the ratio of the surface areas of node A and B. With this at hand, one can estimate the average number of internal and external nodes as well as the number of necessary ray-triangle intersection per ray. A weighted sum over these numbers (with weights being per-node and per-intersection costs) gives an approximation of the total cost.&lt;/p&gt;

&lt;p&gt;Obviously, one would like to minimize this cost already during the construction of the k-d tree and not only after the fact.&lt;/p&gt;

&lt;p&gt;As a prerequisite, given the small nodes, a list of all possible candidate splitting planes is constructed, which are taken to be boundary planes of the triangle bounding boxes in any given node.&lt;/p&gt;

&lt;p&gt;&lt;img src='/img/sah.png' alt='sah splitting planes' /&gt;&lt;/p&gt;

&lt;p&gt;At each step during the small node stage, the resulting cost for each splitting plane in every node is estimated using the SAH model and the splitting plane with the minimal SAH cost is chosen. This process terminates as soon as the estimated cost of a child becomes larger than the number of elements in this node.&lt;/p&gt;

&lt;p&gt;Here, again, both the splitting candidate determination and node splitting are performed in parallel of the small nodes.&lt;/p&gt;

&lt;h3 id='some_implementation_details'&gt;Some Implementation Details&lt;/h3&gt;

&lt;p&gt;These are mainly notes to myself, but maybe they are helpful for someone else, too.&lt;/p&gt;

&lt;h4 id='node_and_triangle_data'&gt;Node and Triangle Data&lt;/h4&gt;

&lt;p&gt;All relevant node data required for the construction phase is kept in flat lists, stored in Structs of Arrays (SoA) to allow for coalescing during access in CUDA kernels. Simplified, a list containing nodes and elements can be stored in the following way:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='cpp'&gt;&lt;span class='k'&gt;struct&lt;/span&gt; &lt;span class='n'&gt;NodeList&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='c1'&gt;// indices to the left and right nodes in this list&lt;/span&gt;
    &lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;device_vector&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='kt'&gt;int&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;left_nodes&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;right_nodes&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='c1'&gt;// split axis of current node&lt;/span&gt;
    &lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;device_vector&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='kt'&gt;int&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;split_axis&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='c1'&gt;// split position of current node&lt;/span&gt;
    &lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;device_vector&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='kt'&gt;float&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;split_position&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='c1'&gt;// indices to the first triangle contained in the current node&lt;/span&gt;
    &lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;device_vector&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='kt'&gt;int&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;first_element_idx&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='c1'&gt;// number of elements in the current node&lt;/span&gt;
    &lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;device_vector&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='kt'&gt;int&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;node_size&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='c1'&gt;// indices to triangles in the original triangle array&lt;/span&gt;
    &lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;device_vector&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='kt'&gt;int&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;element_idx&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Triangle data (vertex information) is static and stored in an array (maybe texture?). We only keep track of indices to this list in the &lt;code&gt;NodeList&lt;/code&gt;s and do not shuffle the original data around. We keep an own list of triangle bounding boxes, that is allowed to grow. Instead of storing clipped triangles after node splitting, we only use the triangle vertex information to compute left and right triangle bounding boxes after triangle splits and assign them to the corresponding nodes.&lt;/p&gt;

&lt;h4 id='using_'&gt;Using &lt;code&gt;thrust&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;The most common operations on these arrays are transformations, reductions, scans, appending of other lists or new elements, compactifications and copies. For most of these operations, the &lt;code&gt;thrust&lt;/code&gt; framework comes in handy. To copy nodes from one &lt;code&gt;NodeLists&lt;/code&gt; to another for instance, we can use &lt;code&gt;thrust::zip_iterator&amp;lt;&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='cpp'&gt;&lt;span class='k'&gt;typedef&lt;/span&gt; &lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;tuple&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='kt'&gt;int&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kt'&gt;int&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kt'&gt;int&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kt'&gt;float&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kt'&gt;int&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kt'&gt;int&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;NodeTuple&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='k'&gt;typedef&lt;/span&gt; &lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;tuple&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;device_vector&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='kt'&gt;int&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;::&lt;/span&gt;&lt;span class='n'&gt;iterator&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                      &lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;device_vector&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='kt'&gt;int&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;::&lt;/span&gt;&lt;span class='n'&gt;iterator&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                      &lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;device_vector&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='kt'&gt;int&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;::&lt;/span&gt;&lt;span class='n'&gt;iterator&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                      &lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;device_vector&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='kt'&gt;float&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;::&lt;/span&gt;&lt;span class='n'&gt;iterator&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                      &lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;device_vector&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='kt'&gt;int&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;::&lt;/span&gt;&lt;span class='n'&gt;iterator&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                      &lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;device_vector&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='kt'&gt;int&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;::&lt;/span&gt;&lt;span class='n'&gt;iterator&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;NodeTupleIterator&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;

&lt;span class='n'&gt;NodeList&lt;/span&gt; &lt;span class='n'&gt;list1&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;list2&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='n'&gt;NodeTupleIterator&lt;/span&gt; &lt;span class='n'&gt;begin&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt;
    &lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;make_tuple&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;list1&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;left_nodes&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;begin&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt;
                       &lt;span class='n'&gt;list1&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;right_nodes&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;begin&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt;
                       &lt;span class='n'&gt;list1&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;split_axis&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;begin&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt;
                       &lt;span class='n'&gt;list1&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;split_position&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;begin&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt;
                       &lt;span class='n'&gt;list1&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;first_element_idx&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;begin&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt;
                       &lt;span class='n'&gt;list1&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;node_size&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;begin&lt;/span&gt;&lt;span class='p'&gt;());&lt;/span&gt;
&lt;span class='n'&gt;NodeTupleIterator&lt;/span&gt; &lt;span class='n'&gt;end&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt;
    &lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;make_tuple&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;list1&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;left_nodes&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;end&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt;
                       &lt;span class='n'&gt;list1&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;right_nodes&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;end&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt;
                       &lt;span class='n'&gt;list1&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;split_axis&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;end&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt;
                       &lt;span class='n'&gt;list1&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;split_position&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;end&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt;
                       &lt;span class='n'&gt;list1&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;first_element_idx&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;end&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt;
                       &lt;span class='n'&gt;list1&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;node_size&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;end&lt;/span&gt;&lt;span class='p'&gt;());&lt;/span&gt;
&lt;span class='n'&gt;NodeTupleIterator&lt;/span&gt; &lt;span class='n'&gt;result&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt;
    &lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;make_tuple&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;list2&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;left_nodes&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;begin&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt;
                       &lt;span class='n'&gt;list2&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;right_nodes&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;begin&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt;
                       &lt;span class='n'&gt;list2&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;split_axis&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;begin&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt;
                       &lt;span class='n'&gt;list2&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;split_position&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;begin&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt;
                       &lt;span class='n'&gt;list2&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;first_element_idx&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;begin&lt;/span&gt;&lt;span class='p'&gt;(),&lt;/span&gt;
                       &lt;span class='n'&gt;list2&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;node_size&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;begin&lt;/span&gt;&lt;span class='p'&gt;());&lt;/span&gt;

&lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;copy&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;make_zip_iterator&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;begin&lt;/span&gt;&lt;span class='p'&gt;),&lt;/span&gt;
             &lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;make_zip_iterator&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;end&lt;/span&gt;&lt;span class='p'&gt;),&lt;/span&gt;
             &lt;span class='n'&gt;thrust&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;make_zip_iterator&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;result&lt;/span&gt;&lt;span class='p'&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h4 id='empty_space_cutting'&gt;Empty space cutting&lt;/h4&gt;

&lt;p&gt;To ensure that every split gives exactly two new nodes in the next &lt;code&gt;NodeList&lt;/code&gt; to be processed, empty space cutting has to be done recursively for each node before median splits are done. The resulting empty splits are not appended to the next &lt;code&gt;NodeList&lt;/code&gt;, but placed directly into the intermediate tree respresentation. The next nodes&amp;#8217; bounding boxes are adjusted accordingly.&lt;/p&gt;

&lt;h4 id='chunks'&gt;Chunks&lt;/h4&gt;

&lt;p&gt;To get the most out of parallelization, the elements are organized in chunks of a fixed number of elements. The idea is to do parallel operations on these independent chunks and gather the per node results later with a segmented or keyed reduction. Since chunks need not to be filled completely, we need to keep track of the chunk size, the index of the first element in &lt;code&gt;element_idx&lt;/code&gt; and the owning node index.&lt;/p&gt;

&lt;p&gt;Processing these chunks in custom kernels is easy: set the grid size to the number of chunks and the thread size to the maximal chunk size. Often, we&amp;#8217;ll need to do per-chunk reductions, to get the chunk axis aligned bounding boxes for instance. This is currently not possible with &lt;code&gt;thrust&lt;/code&gt; in a simple way. Instead, we use a single-block reduction function taken from CUDA SDK example and call it in a custom kernel.&lt;/p&gt;

&lt;p&gt;To get node bounding boxes from chunk bounding boxes in the above example, we can simply use &lt;code&gt;thrust::reduce_by_key&lt;/code&gt; instead of a custom segmented reduction implementation.&lt;/p&gt;

&lt;h4 id='surface_area_heuristics_sah'&gt;Surface Area Heuristics (SAH)&lt;/h4&gt;

&lt;p&gt;Once no large nodes are avilable to be processed, a list of candidate split planes for each small node is needed. We take the boundary planes of the triangles&amp;#8217; axis aligned bounding boxes, which for a small node containing at most 64 elements generates at most 6*64 split candidates per node.&lt;/p&gt;

&lt;p&gt;After some experimenting, the best solution to get the minimal SAH cost of each plane seems to be a somewhat convoluted kernel configuration. We compute the SAH cost for each split in an own thread, the grid is configured to be as large as the number of nodes. Each SAH cost is stored in shared memory and once the threads of a block have synced, we perform a partitioned reduction to find the minimum split cost, separated by axis. Finally, the minimum of the resulting three values is taken as minimal SAH cost for the node. To keep the number of threads a power of two, we take 128 threads and process two split candidates (along the same axis) per thread.&lt;/p&gt;

&lt;p&gt;The following device function should do the trick:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='cpp'&gt;&lt;span class='c1'&gt;// reduce a tuple to a single value&lt;/span&gt;
&lt;span class='k'&gt;template&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='kt'&gt;unsigned&lt;/span&gt; &lt;span class='kt'&gt;int&lt;/span&gt; &lt;span class='n'&gt;size&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
         &lt;span class='kt'&gt;unsigned&lt;/span&gt; &lt;span class='kt'&gt;int&lt;/span&gt; &lt;span class='n'&gt;tuple_length&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
         &lt;span class='k'&gt;typename&lt;/span&gt; &lt;span class='n'&gt;T&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Method&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&lt;/span&gt;
&lt;span class='n'&gt;__device__&lt;/span&gt;
&lt;span class='n'&gt;T&lt;/span&gt; &lt;span class='n'&gt;partition_reduction_device&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;T&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt; &lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kt'&gt;int&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt; &lt;span class='n'&gt;offset&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt;
                             &lt;span class='kt'&gt;int&lt;/span&gt; &lt;span class='n'&gt;n_elements&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kt'&gt;int&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt; &lt;span class='n'&gt;index&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='kt'&gt;int&lt;/span&gt; &lt;span class='n'&gt;tid&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;threadIdx&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='n'&gt;x&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='n'&gt;T&lt;/span&gt; &lt;span class='n'&gt;result&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='n'&gt;__shared__&lt;/span&gt; &lt;span class='n'&gt;T&lt;/span&gt; &lt;span class='n'&gt;temp&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;size&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
    &lt;span class='n'&gt;__shared__&lt;/span&gt; &lt;span class='kt'&gt;int&lt;/span&gt; &lt;span class='n'&gt;shared_index&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='k'&gt;if&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;tid&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='n'&gt;shared_index&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='o'&gt;-&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;

    &lt;span class='k'&gt;if&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;tid&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='n'&gt;size&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='n'&gt;temp&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;tid&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;Method&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;neutral_element&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;
        &lt;span class='k'&gt;if&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;tid&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='n'&gt;n_elements&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
            &lt;span class='n'&gt;temp&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;tid&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt;
                &lt;span class='n'&gt;Method&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;reduction_operator&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;tid&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='n'&gt;offset&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;]],&lt;/span&gt;
                                           &lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;tid&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='n'&gt;offset&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;]]);&lt;/span&gt;
&lt;span class='cp'&gt;#pragma unroll&lt;/span&gt;
            &lt;span class='k'&gt;for&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;int&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='n'&gt;tuple_length&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt; &lt;span class='o'&gt;++&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
                &lt;span class='n'&gt;temp&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;tid&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt;
                    &lt;span class='n'&gt;Method&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='n'&gt;reduction_operator&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;temp&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;tid&lt;/span&gt;&lt;span class='p'&gt;],&lt;/span&gt;
                                               &lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;tid&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='n'&gt;offset&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;]]);&lt;/span&gt;
        &lt;span class='p'&gt;}&lt;/span&gt;
        &lt;span class='n'&gt;__syncthreads&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;

        &lt;span class='c1'&gt;// reduction in a single block&lt;/span&gt;
        &lt;span class='n'&gt;result&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;reduction_device&lt;/span&gt;&lt;span class='o'&gt;&amp;lt;&lt;/span&gt;&lt;span class='n'&gt;size&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;T&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;Method&lt;/span&gt;&lt;span class='o'&gt;&amp;gt;&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;temp&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
        &lt;span class='n'&gt;__syncthreads&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;

&lt;span class='cp'&gt;#pragma unroll&lt;/span&gt;
        &lt;span class='k'&gt;for&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kt'&gt;int&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt; &lt;span class='n'&gt;i&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='n'&gt;tuple_length&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt; &lt;span class='o'&gt;++&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
            &lt;span class='k'&gt;if&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;result&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;tid&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='n'&gt;offset&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;]])&lt;/span&gt;
                &lt;span class='n'&gt;shared_index&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;tid&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='n'&gt;offset&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='n'&gt;i&lt;/span&gt;&lt;span class='p'&gt;];&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt;
    &lt;span class='n'&gt;__syncthreads&lt;/span&gt;&lt;span class='p'&gt;();&lt;/span&gt;

    &lt;span class='k'&gt;if&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;tid&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
        &lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;index&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;shared_index&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
    &lt;span class='p'&gt;}&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='n'&gt;result&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h4 id='final_tree_representation'&gt;Final tree representation&lt;/h4&gt;

&lt;p&gt;For the final representation of the tree in preorder sort, the paper seems to suggest a flat array (the bottom-up procedure computes sizes of the required number of elements per node). We can simply use a list of &lt;code&gt;int&lt;/code&gt;s, where each node is stored with at least two consecutive &lt;code&gt;ints&lt;/code&gt;. For non-leaf nodes, we can pack the right child index, split axis and flags indicating empty space cuts of left and right children into the first number, while the second one stores the split position cast to an &lt;code&gt;int&lt;/code&gt;. If a non-leaf node is stored in the next &lt;code&gt;int&lt;/code&gt;, it is left of the parent, so we do not need to store this information. For leaf nodes, we store the element count in the first number and the following N numbers contain indices to the N elements contained in the leaf node.&lt;/p&gt;

&lt;h4 id='ray_traversal'&gt;Ray traversal&lt;/h4&gt;

&lt;p&gt;A nice choice for a parallel ray traversal algorithm is also outlined in Appendix C of &lt;a href='http://dcgi.felk.cvut.cz/home/havran/phdthesis.html'&gt;&amp;#8220;Heuristic Ray Shooting Algorithms&amp;#8221;&lt;/a&gt;. Each ray traverses the scene within two nested while loops and no information on the node bounding boxes has to be provided, just the spliiting plane positions. The basic principle is to traverse the tree from split-plane to split plane, processing first intersected nodes first and pushing far nodes into a stack. In case of empty nodes, we should only push the non-empty node to the stack (if applicable) and jump to the next node immediately.&lt;/p&gt;</description>
      </item>
    
      <item>
        <title>Raytracing and acceleration structures</title>
        <link>http://unvirtual.github.com/blog/2012-02/19/Raytracing-and-acceleration-structures</link>
        <pubDate>Sun, 19 Feb 2012 00:00:00 PST</pubDate>
        <author>unvirtual</author>
        <guid>http://unvirtual.github.com/blog/2012-02/19/Raytracing-and-acceleration-structures</guid>
        <description>&lt;p&gt;This is going to be a series of posts about raytracing, acceleration structures and, eventually, photon mapping on the GPU - a project I&amp;#8217;ve been working on for a while in my spare time. I&amp;#8217;ve finally decided to put the current state of this project out into the wild, most of which is an implementation based on &lt;a href='http://www.kunzhou.net/2008/kdtree.pdf' title='Real Time KD-Tree Construction on Graphics Hardware'&gt;&amp;#8220;Real Time KD-Tree Construction on Graphics Hardware&amp;#8221;&lt;/a&gt; by &lt;a href='http://www.kunzhou.net/'&gt;Zhou&lt;/a&gt; et al.&lt;/p&gt;

&lt;p&gt;The following is mostly an introductory article for all those around me who are wondering what I&amp;#8217;m so excited about, for more details on the current state of the project and the github link check out &lt;a href='/blog/2012-03/23/kd-tree-details'&gt;this post&lt;/a&gt;. Further down is a nice &lt;a href='#video'&gt;video&lt;/a&gt;, though.&lt;/p&gt;

&lt;h3 id='lets_trace_some_rays_'&gt;Let&amp;#8217;s trace some rays &amp;#8230;&lt;/h3&gt;

&lt;p&gt;Raytracing is basically the process of taking data like &lt;a href='http://graphics.stanford.edu/data/3Dscanrep/'&gt;this&lt;/a&gt;:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='bash'&gt;    &lt;span class='c'&gt;# vertex coordinates&lt;/span&gt;
    0.0321981 0.0565738 -0.0503247
    0.0330387 0.056612 -0.0501226
    0.0321019 0.0575225 -0.0502063
    0.0330546 0.0575024 -0.0503454
    0.0313077 0.0556267 -0.0500367
    &lt;span class='c'&gt;# ...&lt;/span&gt;
    &lt;span class='c'&gt;# edges&lt;/span&gt;
    3 1 2 3
    3 1 0 2
    3 8 7 5
    3 7 4 5
    &lt;span class='c'&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;and transforming it into a nice looking picture (created with &lt;a href='http://www.blender.org'&gt;Blender&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;img src='/img/dragon_rendered_small.png' alt='Dragon raytraced' /&gt;&lt;/p&gt;

&lt;p&gt;The data is a list of triangles in 3D space that form a 3D model of stuff, a dragon in this case. The triangles are encoded by their vertex positions and edge information. Combined with additional information like positions, brightness and colors of light sources and materials that describe the interaction of light with the model raytracing is capable of producing photo realistic images like the one above.&lt;/p&gt;

&lt;p&gt;What we perceive with our eyes in the real world are a bunch (actually a gazillion) of photons falling onto our retina. These photons bounced around the world getting reflected or scattered on surfaces and &amp;#8220;changing&amp;#8221; their wavelength and polarization. Raytracing simulates and approximates light falling into a virtual camera (our eye) after having interacted with objects, but the process of gathering the light information is reversed. Of all those photons flying around, only a very small amount of them in fact makes it through our small pupils. So instead of simulating all possible paths of light being emitted from light sources, rays are shot out of the camera into the world and each ray is tested for an intersection with an object that might or might not be illuminated.&lt;/p&gt;

&lt;p&gt;&lt;img src='/img/raytracing_schema.png' alt='Raytracing scheme' /&gt;&lt;/p&gt;

&lt;p&gt;At each intersection, the distance and direction to all possible light sources is computed and, given the properties of the material, its translucency, reflectance, etc., the brightness and color of light along the ray back to the camera is determined. Shading can be directly obtained from the angle between the intersection - light direction and the surface normal. With this idea, a whole lot of effects can be simulated by sending out secondary rays from the light source or intersection points: mirror reflections, refraction, shadows, projections and much more. To arrive at the final image, a plane is placed in front of the camera and intersections of the light rays with this plane give pixel positions and colors, composing the projection of the 3D scene on a 2D plane.&lt;/p&gt;

&lt;p&gt;Ok, so we know what to do: take 3D triangle data, send out some rays, check for each ray if it intersects with any triangle, compute the light being bounced back, and we are done. If we want reflections and refractions, shoot some more rays from the intersection points, which can be implemented recursively. This is in fact so simple, there exists an implementation that &lt;a href='http://www.cs.cmu.edu/~ph/'&gt;fits on a business card&lt;/a&gt; (scroll down to &amp;#8220;Minimal ray tracer&amp;#8221;).&lt;/p&gt;

&lt;h3 id='not_so_fast_space_partitioning_is_faster'&gt;Not so fast, space partitioning is faster&lt;/h3&gt;

&lt;p&gt;There is one drawback however: the naive approach requires plenty of computation time, as every ray has to be tested for intersection with every single triangle in the scene. In the vast majority of cases the tested triangle is nowhere even near the ray. Even worse, testing for triangle intersection requires expensive dot and cross products, checking if the found triangle is occluded, etc., just to find 99.9% of the time: nope, no intersection, next triangle, please. Certainly, we can do better than that, right?&lt;/p&gt;

&lt;p&gt;That&amp;#8217;s where acceleration data structures come into play.&lt;/p&gt;

&lt;p&gt;The basic idea is simple: subdivide the volume containing a soup of triangles to be rendered into smaller boxes and sort triangles into them. Testing for ray-triangle intersection then is a two-step process: traverse the boxes along the ray and if the current box is not empty, check for intersection with the triangles contained in the current box only. If no triangle was hit, we&amp;#8217;ll move on to the next box. This technique is especially effective if the full bounding volume can be partitioned such that empty and non-empty space is well-separated and each box contains only a few triangles. In the former case, no ray-triangle intersection testing has to be performed at all once a hit with an empty box was determined. How&amp;#8217;s that for a speed-up?&lt;/p&gt;

&lt;p&gt;&lt;img src='/img/kdtree_schema.png' alt='kd-tree in 2D' /&gt;&lt;/p&gt;

&lt;p&gt;There are of course many ways to organize the space partitioning and different data structures have been developed for this task (&lt;a href='http://en.wikipedia.org/wiki/Octree'&gt;Octrees&lt;/a&gt;, &lt;a href='http://en.wikipedia.org/wiki/R-tree'&gt;R-Trees&lt;/a&gt;, etc.). In the following we will focus on the so-called k-d tree using axis aligned bounding boxes, a binary tree representation of the space subdivision. Each node is associated with a splitting plane defined by a normal vector along one of the main spatial axes. The left and right child nodes represent the bounding volumes to the left and right of this plane, respectively. Constructing the tree top-down, each level of the tree subdivides the initial volume into smaller chunks and keeps track of triangles contained in them.&lt;/p&gt;

&lt;p&gt;A basic k-d tree implementation is quite straight-forward and simple, just have a look at the &lt;a href='http://en.wikipedia.org/wiki/Kd-tree#Construction'&gt;minimal a dozen of lines or so Python&lt;/a&gt; required for the construction of a kd-tree storing point clouds, only a little bit more effort is required for triangle soups.&lt;/p&gt;
&lt;a name='video'&gt;&lt;iframe src='http://player.vimeo.com/video/39118386' frameborder='0' height='375' width='500'&gt;something&lt;/iframe&gt;&lt;/a&gt;
&lt;h3 id='kd_tree_construction_and_traversal_on_the_gpu'&gt;K-d tree construction and traversal on the GPU&lt;/h3&gt;

&lt;p&gt;Using a k-d tree speeds up raytracing by orders of magnitude on a single CPU system, beautiful pictures can be rendered in minutes. But what if we&amp;#8217;d like even more speed? Maybe so much speed that we could do raytracing in real time?&lt;/p&gt;

&lt;p&gt;Before even thinking of the raytracing component, one has to think about the acceleration structure, the k-d tree. If the scene we want to render is static and all triangles composing the scene are fixed in space, the tree has to be constructed only once and one doesn&amp;#8217;t have to worry about performance too much. On the other hand, if any part of the scene changes or moves, the tree has to be reconstructed. Since this could happen every frame, the tree building better be quick.&lt;/p&gt;

&lt;p&gt;Parallelization to the rescue! Not double or quad core CPUs, massive paralellization using the GPU.&lt;/p&gt;

&lt;p&gt;It turns out, however, things become not that as simple as in the dozen lines of code above anymore, if we strive for efficiency. We have to worry about using the best parallelized algorithms and respect the restrictions of the hardware. The tree construction has to be split into fragments, that can be treated independently without exchange of information between processes.&lt;/p&gt;

&lt;p&gt;Thankfully, as with most awesome things, this work has already been done. &lt;a href='http://www.kunzhou.net/2008/kdtree.pdf' title='Real Time KD-Tree Construction on Graphics Hardware'&gt;&amp;#8220;Real time KD-Tree Construction on Graphics Hardware&amp;#8221;&lt;/a&gt; describes an efficient algorithm for k-d tree construction. Following the algorithm outlined in the paper and filling in the missing pieces, I&amp;#8217;ve jumped into the implementation.&lt;/p&gt;

&lt;p&gt;So far, I have one half of the algorithm working, creating a tree containing a given number of triangles in its leaves after cutting off empty space and performing median splitting.&lt;/p&gt;

&lt;p&gt;Implementing this little beast turns out to be quite more involved than it looks like at first, more details about the approach in &lt;a href='http://unvirtual.github.com/blog/2012-03/05/kd-tree-details/'&gt;another post&lt;/a&gt;. Until then, above is the Stanford Dragon (200k triangles) rendered with the resulting node bounding boxes of the tree in &lt;a href='http://wwww.blender.org'&gt;Blender&lt;/a&gt;, terminating the construction at 2048, 1024, 512, 256, 128 and 64 triangles per node, respectively. It&amp;#8217;s very helpful to have a visual representation for debugging purposes (and there are some inconsistencies in the empty space cutting apparent).&lt;/p&gt;</description>
      </item>
    

  </channel>
</rss>
