<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>TinyCranes</title>
        <link>https://www.tinycranes.com</link>
        <atom:link href="https://www.tinycranes.com/feed.xml" rel="self" type="application/rss+xml" />
        <description>Software design and development by Kevin Fung</description>
        <language>en-us</language>
        <managingEditor>kfung@tinycranes.com (Kevin Fung)</managingEditor>
        <lastBuildDate>Sat, 09 Jan 2016 05:08:16 GMT</lastBuildDate>
        <item>
            <title>Browser Wars</title>
            <link>https://www.tinycranes.com/blog/2016/01/browser-wars/</link>
            <guid isPermaLink="true">https://www.tinycranes.com/blog/2016/01/browser-wars/</guid>
            <pubDate>Sat, 09 Jan 2016 05:08:16 GMT</pubDate>
            <author>kfung@tinycranes.com (Kevin Fung)</author>
            <description>Apple advertises up to 9 hours of battery life for the MacBook Pro, yet I consistently clocked in around 5 hours. Lunchtime was determined the needs of my computer. Where I ate was dictated by…</description>
            <content:encoded><![CDATA[<p>Apple advertises up to 9 hours of battery life for the MacBook Pro, yet I consistently clocked in around 5 hours. Lunchtime was determined the needs of my computer. Where I ate was dictated by whether or not there was an outlet available nearby. For months, I went through this routine, until I reformatted my computer at the start of the semester, only this time I neglected to reinstall Google Chrome. Chalk it up to extreme laziness I guess. Little did I know ...</p>
<p>My battery life skyrocketed. I went for 11 hours straight once, no outlets to be seen. This was vastly superior than even the advertised battery life! I soon started chanting &quot;Safari on battery, Chrome at home&quot; in my mind. Soon, I was spending so much time on battery, I set Safari to my default browser. Incidentally, it is fascinating how much sticking power the &quot;Default&quot; browser has.</p>
<p>I began investigating how to switch to Safari for good. I&#39;m not a huge extensions user, so that was an easy win; all the popular extensions are available for every major browser. Beyond that, I depended on the Chrome Password Manager to sync my logins across devices. 1Password takes care of that for me now. Flash is a mostly dead medium and if I do come across a Flash video, I can just fire up Chrome.</p>
<p>The last holdout might be my heavy use of the Chrome Developer Tools, but even then, I can just fire up Chrome when testing, like I have to do for Firefox anyway. For day-to-day usage, a browser is just a browser. For the most part, Safari gets the job done, and with my recent discovery of <code>⌘⌥R</code> for responsive development, mobile development is much smoother than the equivalent interfaces in Chrome or Firefox. Now if only I could make everything play nicely under Windows, that would just about put the final nail in the coffin ...</p>
]]></content:encoded>
        </item>
        <item>
            <title>Unplugging</title>
            <link>https://www.tinycranes.com/blog/2016/01/unplugging/</link>
            <guid isPermaLink="true">https://www.tinycranes.com/blog/2016/01/unplugging/</guid>
            <pubDate>Thu, 07 Jan 2016 04:05:30 GMT</pubDate>
            <author>kfung@tinycranes.com (Kevin Fung)</author>
            <description>I quit Facebook today. More precisely: I installed a plugin to disable the news feed, deleted Facebook from my browser&amp;#39;s predictive suggestions, and cleared all related browser history. I doubt…</description>
            <content:encoded><![CDATA[<p>I quit Facebook today. More precisely: I installed a plugin to disable the news feed, deleted Facebook from my browser&#39;s predictive suggestions, and cleared all related browser history. I doubt I&#39;ll ever be totally free of Facebook&#39;s grasp -- the convenience of Facebook login and an easy way to contact the people I know via Messenger remain immensely powerful tools -- but at least this is a step in the right direction.</p>
]]></content:encoded>
        </item>
        <item>
            <title>From Armchair to Professional</title>
            <link>https://www.tinycranes.com/blog/2015/08/from-armchair-to-professional/</link>
            <guid isPermaLink="true">https://www.tinycranes.com/blog/2015/08/from-armchair-to-professional/</guid>
            <pubDate>Thu, 13 Aug 2015 08:20:35 GMT</pubDate>
            <author>kfung@tinycranes.com (Kevin Fung)</author>
            <description>It recently occurred to me that in graduating from college, I have also graduated from armchair game developer to professional game developer ... wait, what does that even mean? Not much apparently.…</description>
            <content:encoded><![CDATA[<p>It recently occurred to me that in graduating from college, I have also graduated from <em>armchair game developer</em> to <em>professional game developer</em> ... wait, what does that even mean?  Not much apparently. If anything, the path ahead is more clouded than before. As a student, I merely aspired to enter the games industry in any way, shape, or form. If you told me four years ago that I would be working for a major game studio fresh out of college, I would have laughed at you.</p>
<p>Now that I have a (very junior) seat at the table, I look up the food chain and am entirely stumped by the question &quot;so what do you want to do?&quot; What do I want to become? I&#39;m terrified because I have no idea. All I can think of is that the game industry needs more role models. Offhand, there are people like John Carmack, Ken Levine, Sid Meier, <em>legends</em> in the game industry. But where are all the people in between? Where are the people that are the lifeblood of a studio, but that the games media neglected to instead fawn over the next &quot;indie game developer superstar!&quot;?</p>
<p>Hah! And I bet you thought this would be some mindless platitudes about reinforcing my credibility as a &quot;professional&quot; game developer! Guess what? <em>I&#39;m just as clueless as you armchair game developers.</em></p>
]]></content:encoded>
        </item>
        <item>
            <title>Impractically Paperless</title>
            <link>https://www.tinycranes.com/blog/2015/07/impractically-paperless/</link>
            <guid isPermaLink="true">https://www.tinycranes.com/blog/2015/07/impractically-paperless/</guid>
            <pubDate>Thu, 30 Jul 2015 05:02:57 GMT</pubDate>
            <author>kfung@tinycranes.com (Kevin Fung)</author>
            <description>I&amp;#39;m practically paperless, but going paperless is a lot harder than I thought. First, the quirks. When setting up paperless billing for some services, I received a confirmation in the mail…</description>
            <content:encoded><![CDATA[<p>I&#39;m practically paperless, but going paperless is a lot harder than I thought. First, the quirks. When setting up paperless billing for some services, I received a confirmation <em>in the mail</em> thanking me for going paperless. Next, the inevitable deluge in which I receive three form letters in succession thanking me for such and such. The worst part is when somebody expects you to sign, stamp, and mail a form back to some P.O. box. Don&#39;t want to waste postage? No problem! Just <em>fax</em> the form instead! Please, I beg of you: help me help you! It&#39;s not as if email has been around for two decades now ...</p>
<p>To be fair, I have had some successes. Airlines have made flying a surprisingly paperless experience. In the space of four years, I&#39;ve gone from running in sub-freezing temperatures across the quad to pick up my boarding pass, to checking my email for a convenient e-boarding pass that automagically pops up on my phone as I approach the security line.</p>
<p>More and more food trucks seem to be using <a target="_blank" rel="noopener noreferrer" href="https://squareup.com">Square</a> card readers, and the software takes care of emailing me a receipt. I wish <a target="_blank" rel="noopener noreferrer" href="https://localregister.amazon.com">Amazon Register</a> did the same. Anyway, as digital payment technology improves, my hope is that in the next few years, those spools of receipt paper will become a thing of the past.</p>
<p>As much as I hate to admit it, I am also starting to rely on iCloud Reminders as my primary to-do list, which sync across all of my devices. I still miss my vibrant shades of Post-it notes though. The lone holdout? Work. It seems writing on whiteboards and snapping a picture with my phone have only partially supplanted graph pads, where hand-drawn diagrams, scribbles, and <a target="_blank" rel="noopener noreferrer" href="http://patrickrhone.com/dashplus/">Dash/Plus</a> reign supreme.</p>
]]></content:encoded>
        </item>
        <item>
            <title>Every Day is Day One</title>
            <link>https://www.tinycranes.com/blog/2015/07/every-day-is-day-one/</link>
            <guid isPermaLink="true">https://www.tinycranes.com/blog/2015/07/every-day-is-day-one/</guid>
            <pubDate>Tue, 07 Jul 2015 06:04:36 GMT</pubDate>
            <author>kfung@tinycranes.com (Kevin Fung)</author>
            <description>I started at Amazon today. The phrase &amp;quot;every day is day one&amp;quot; floated around every so often as an intern. As a full-time hire, it is time to put that to the test. This is just one step on a…</description>
            <content:encoded><![CDATA[<p>I started at Amazon today. The phrase &quot;every day is day one&quot; floated around every so often as an intern. As a full-time hire, it is time to put that to the test. This is just one step on a very long road, and I am at a crossroads. Having <a target="_blank" rel="noopener noreferrer" href="https://www.linkedin.com/pulse/taking-charge-your-career-piyush-gupta">read a bit on the subject</a>, my goal now is to meet people who can help shape the rest of my career. Placement in a satellite office complicates things, but as the saying goes: challenge accepted.</p>
]]></content:encoded>
        </item>
        <item>
            <title>Visualizing the Mandelbrot Set</title>
            <link>https://www.tinycranes.com/blog/2015/05/visualizing-the-mandelbrot-set/</link>
            <guid isPermaLink="true">https://www.tinycranes.com/blog/2015/05/visualizing-the-mandelbrot-set/</guid>
            <pubDate>Tue, 26 May 2015 22:15:27 GMT</pubDate>
            <author>kfung@tinycranes.com (Kevin Fung)</author>
            <description>In this post, I will cover how to draw the Mandelbrot Set using my dead simple OpenCL parallel programming framework: Chlorine. In doing so, I will demonstrate an 800x performance increase against a…</description>
            <content:encoded><![CDATA[<p>In this post, I will cover how to draw the <a target="_blank" rel="noopener noreferrer" href="http://en.wikipedia.org/wiki/mandelbrot_set">Mandelbrot Set</a> using my dead simple OpenCL parallel programming framework: <a target="_blank" rel="noopener noreferrer" href="https://github.com/Polytonic/Chlorine/">Chlorine</a>. In doing so, I will demonstrate an <em>800x performance increase</em> against a reference CPU implementation, changing just <em>5 lines of code</em>. If you want to follow along, go download the <a target="_blank" rel="noopener noreferrer" href="https://github.com/Polytonic/Chlorine/tree/master/examples/mandelbrot">source code on GitHub</a>.</p>
<p><strong>For the impatient, the data first:</strong></p>
<div style="overflow-x: auto; overflow-y: hidden;"><div id="mandelbrot-chart"></div></div><h5> Mandelbrot Execution Time</h5>
<table>
    <tr><td>Intel Core i5 (Reference)</td><td>4680.11ms</td></tr>
    <tr><td>Intel Core i7 (Reference)</td><td>3480.67ms</td></tr>
    <tr><td>Intel Core i5 (OpenCL)</td>   <td>2506.86ms</td></tr>
    <tr><td>Intel Core i7 (OpenCL)</td>   <td>2070.07ms</td></tr>
    <tr><td>Intel HD 5000</td>            <td>5.729ms</td></tr>
    <tr><td>Intel Iris Pro 5200</td>      <td>4.289ms</td></tr>
    <tr><td>Nvidia GT 750M</td>           <td>5.684ms</td></tr>
</table><p>I used the following devices to generate the above figures.</p>
<blockquote>
<p><strong>Apple MacBook Air (6,1)</strong></p>
<ul>
<li>Intel Core i5-4250U @ 1.30GHz</li>
<li>Intel HD 5000</li>
</ul>
<p><strong>Apple MacBook Pro (11,3)</strong></p>
<ul>
<li>Intel Core i7-4980HQ @ 2.80GHz</li>
<li>Intel Iris Pro 5200</li>
<li>Nvidia GT 750M</li>
</ul>
</blockquote>
<p>As you can see, the OpenCL implementation absolutely destroys the reference CPU implementation. Granted, not much effort was put into optimizing the reference code. The point is to demonstrate the ability to significantly accelerate your code with very little time investment and effort.</p>
<h4>Walkthrough</h4>
<p><a target="_blank" rel="noopener noreferrer" href="http://en.wikipedia.org/wiki/mandelbrot_set">The Mandelbrot Set</a> is a subset of complex numbers defined by an equation that, when plotted on the complex plane, forms a fractal. One simple way of calculating the Mandelbrot Set is to iterate the series <em>z<sub>M</sub></em> until <em>|M| &gt; 2</em> for each <em>M</em> in the complex numbers.  If the computation does not reach this point, then <em>M</em> is in the Mandelbrot Set. A set estimation can be achieved by iterating a finite, large amount of times. This type of computation is easy to run in parallel, since each candidate can be run independently.</p>
<p>To compare and contrast, we implement the Mandelbrot Set to run both on a traditional processor, as well as using Chlorine. To demonstrate, here are both prototypes for <code>solve_mandelbrot()</code>. Other than the OpenCL memory tags and use of pointers, they are otherwise very similar.</p>
<pre><code class="hljs language-c++"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve_mandelbrot</span><span class="hljs-params">(std::vector&lt;<span class="hljs-type">float</span>&gt; <span class="hljs-type">const</span> &amp; real,
                      std::vector&lt;<span class="hljs-type">float</span>&gt; <span class="hljs-type">const</span> &amp; imag,
                                  <span class="hljs-type">int</span> iterations,
                      std::vector&lt;<span class="hljs-type">int</span>&gt; &amp; result)</span></span>
</code></pre><pre><code class="hljs language-c">__kernel <span class="hljs-type">void</span> <span class="hljs-title function_">solve_mandelbrot</span><span class="hljs-params">(__global <span class="hljs-type">float</span> <span class="hljs-type">const</span> * real,
                               __global <span class="hljs-type">float</span> <span class="hljs-type">const</span> * imag,
                                        <span class="hljs-type">int</span> iterations,
                               __global <span class="hljs-type">int</span> * result)</span>
</code></pre><p>The function <code>solve_mandelbrot()</code> accepts a vector of real and imaginary points (representing <em>M<sub>i</sub> = x<sub>i</sub> + y<sub>i</sub></em> j), the number of iterations to run the series before assuming the number is in the set, and a vector to store the output. Look at both the kernel and host algorithm implementation and confirm for yourself that they are, in fact, identical.</p>
<pre><code class="hljs language-c++"><span class="hljs-type">float</span> x = real[i]; <span class="hljs-comment">// Real Component</span>
<span class="hljs-type">float</span> y = imag[i]; <span class="hljs-comment">// Imaginary Component</span>
<span class="hljs-type">int</span>   n = <span class="hljs-number">0</span>;       <span class="hljs-comment">// Tracks Color Information</span>

<span class="hljs-comment">// Compute the Mandelbrot Set</span>
<span class="hljs-keyword">while</span> ((x * x + y * y &lt;= <span class="hljs-number">2</span> * <span class="hljs-number">2</span>) &amp;&amp; n &lt; iterations)
{
    <span class="hljs-type">float</span> xtemp = x * x - y * y + real[i];
    y = <span class="hljs-number">2</span> * x * y + imag[i];
    x = xtemp;
    n++;
}

<span class="hljs-comment">// Write Results to Output Arrays</span>
result[i] = x * x + y * y &lt;= <span class="hljs-number">2</span> * <span class="hljs-number">2</span> ? <span class="hljs-number">-1</span> : n;
</code></pre><p>The key difference here is that we must iterate through the entire image using a <code>for</code> loop on the host, while in OpenCL, we can remove the <code>for</code> loop altogether, and instead retrieve the index by querying the device.</p>
<pre><code class="hljs language-c++"><span class="hljs-comment">// Host Code (Reference Implementation)</span>
<span class="hljs-keyword">for</span>(<span class="hljs-type">unsigned</span> <span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; real.<span class="hljs-built_in">size</span>(); i++)

<span class="hljs-comment">// Chlorine Kernel (OpenCL Implementation)</span>
<span class="hljs-type">unsigned</span> <span class="hljs-type">int</span> i = <span class="hljs-built_in">get_global_id</span>(<span class="hljs-number">0</span>);
</code></pre><p>Next we define some settings for the Mandelbrot Set we are going to generate:</p>
<ul>
<li>We set a maximum number of iterations at a large number to be near accurate.</li>
<li>Since the Mandelbrot Set is completely contained within the unit circle of radius 2, we calculate only for the circumscribed square.</li>
<li>The step value is set so the resulting image resolution is 1000x1000px.</li>
</ul>
<pre><code class="hljs language-c++">    <span class="hljs-comment">// Define Mandelbrot Settings</span>
    <span class="hljs-type">int</span> iterations = <span class="hljs-number">10000</span>;
    <span class="hljs-type">float</span> x_min  = <span class="hljs-number">-2</span>f;
    <span class="hljs-type">float</span> x_max  =  <span class="hljs-number">2</span>f;
    <span class="hljs-type">float</span> y_min  = <span class="hljs-number">-2</span>f;
    <span class="hljs-type">float</span> y_max  =  <span class="hljs-number">2</span>f;
    <span class="hljs-type">float</span> x_step = <span class="hljs-number">0.002f</span>;
    <span class="hljs-type">float</span> y_step = <span class="hljs-number">0.002f</span>;
</code></pre><p>Next, we turn the points in this range into a single vector of points to test, by linearizing the grid to make it easier to pass in. The stride is recorded so the result can be put back into a grid.</p>
<pre><code class="hljs language-cpp">    <span class="hljs-comment">// Create Linear Vector of Coordinates</span>
    <span class="hljs-type">unsigned</span> <span class="hljs-type">int</span> stride = (x_max - x_min) / x_step + <span class="hljs-number">2</span>;
    std::vector&lt;<span class="hljs-type">float</span>&gt; reals;
    std::vector&lt;<span class="hljs-type">float</span>&gt; imags;
    <span class="hljs-keyword">for</span>(<span class="hljs-type">float</span> y = y_min; y &lt; y_max; y += y_step)
    <span class="hljs-keyword">for</span>(<span class="hljs-type">float</span> x = x_min; x &lt; x_max; x += x_step)
    {
        reals.<span class="hljs-built_in">push_back</span>(x);
        imags.<span class="hljs-built_in">push_back</span>(y);
    }
</code></pre><p>For the CPU, we first make an output vector to pass into the function. We then record the start time, call the function, then record the end time. By recording the start and end times, we can compare the time taken between the CPU and Chlorine, for a rough performance benchmark.</p>
<pre><code class="hljs language-cpp">    <span class="hljs-comment">// Compute the Mandelbrot Set on the CPU</span>
    <span class="hljs-function">std::vector&lt;<span class="hljs-type">int</span>&gt; <span class="hljs-title">cpu_ans</span><span class="hljs-params">(reals.size())</span></span>;
    <span class="hljs-type">clock_t</span> cpu_begin = <span class="hljs-built_in">clock</span>();
    <span class="hljs-built_in">solve_mandelbrot</span>(reals, imags, iterations, cpu_ans);
    <span class="hljs-type">clock_t</span> cpu_end = <span class="hljs-built_in">clock</span>();
</code></pre><p>We first create a worker that will load the OpenCL runtime and compile the kernel code. Note that calling Chlorine is very similar to calling traditional functions: the function name is passed to the <code>call()</code> function instead of being called directly. Everything else is exactly the same.</p>
<pre><code class="hljs language-cpp">    <span class="hljs-comment">// Compute the Mandelbrot Set Using Chlorine</span>
    <span class="hljs-function">std::vector&lt;<span class="hljs-type">int</span>&gt; <span class="hljs-title">cl_ans</span><span class="hljs-params">(reals.size())</span></span>;
    <span class="hljs-function">ch::Worker <span class="hljs-title">benoit</span><span class="hljs-params">(<span class="hljs-string">&quot;mandelbrot.cl&quot;</span>)</span></span>;
    <span class="hljs-type">clock_t</span> cl_begin = <span class="hljs-built_in">clock</span>();
    benoit.<span class="hljs-built_in">call</span>(<span class="hljs-string">&quot;solve_mandelbrot&quot;</span>, reals, imags, iterations, cl_ans);
    <span class="hljs-type">clock_t</span> cl_end = <span class="hljs-built_in">clock</span>();
</code></pre><p>As a matter of good practice, we compare the output to ensure that we are really getting the same answer. Note that due to floating point precision limitations, there may be a small degree of error between the host and OpenCL outputs.</p>
<pre><code class="hljs language-cpp">    <span class="hljs-comment">// Compare the Output Arrays</span>
    <span class="hljs-type">unsigned</span> <span class="hljs-type">int</span> error_count = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">for</span>(<span class="hljs-type">unsigned</span> <span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; cpu_ans.<span class="hljs-built_in">size</span>(); i++)
        <span class="hljs-keyword">if</span> (cpu_ans[i] != cl_ans[i])
            error_count++;
</code></pre><p>From here, we write the image to the (very simple) <a target="_blank" rel="noopener noreferrer" href="http://en.wikipedia.org/wiki/Netpbm_format">PPM</a> format, which you can display using <a target="_blank" rel="noopener noreferrer" href="http://www.imagemagick.org/">ImageMagick</a>, <a href="www.adobe.com/products/photoshop.html">Adobe Photoshop</a>, or similar. If all goes well, you should see the same image as the one at the top of this document.</p>
<p>If you were wondering, the five lines of code that differ between the reference and OpenCL implementations:</p>
<pre><code class="hljs language-cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&quot;chlorine.hpp&quot;</span>             <span class="hljs-comment">// the include statement.</span></span>
<span class="hljs-function">ch::Worker <span class="hljs-title">benoit</span><span class="hljs-params">(<span class="hljs-string">&quot;mandelbrot.cl&quot;</span>)</span></span>; <span class="hljs-comment">// worker creation.</span>
<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">solve_mandelbrot</span><span class="hljs-params">(...)</span>          <span class="hljs-comment">// function prototype (moved to kernel).</span>
<span class="hljs-type">unsigned</span> <span class="hljs-type">int</span> i </span>= <span class="hljs-built_in">get_global_id</span>(<span class="hljs-number">0</span>);  <span class="hljs-comment">// indexing inside a kernel</span>
benoit.<span class="hljs-built_in">call</span>(...);                   <span class="hljs-comment">// the function call.</span>
</code></pre><p>You&#39;re probably thinking <em>&quot;can I get away with doing a mass find/replace?&quot;</em> What do you think I did? Yes, <em>it&#39;s that easy</em> to use <a target="_blank" rel="noopener noreferrer" href="https://github.com/Polytonic/Chlorine/">Chlorine</a>. Give it a try today!</p>
<p>*<sub>A significant portion of this was written in collaboration with fellow Rensselaer student <a target="_blank" rel="noopener noreferrer" href="https://github.com/breadknock">Christopher Brenon</a>.</sub></p>
]]></content:encoded>
        </item>
        <item>
            <title>Trivially Parallel</title>
            <link>https://www.tinycranes.com/blog/2015/05/trivially-parallel/</link>
            <guid isPermaLink="true">https://www.tinycranes.com/blog/2015/05/trivially-parallel/</guid>
            <pubDate>Thu, 21 May 2015 00:04:19 GMT</pubDate>
            <author>kfung@tinycranes.com (Kevin Fung)</author>
            <description>Having just finished a semester studying concurrency, I thought it would be fun to dig into the backlog and write a post on parallel computing. In the past, writing a program might have involved…</description>
            <content:encoded><![CDATA[<p>Having just finished a semester studying concurrency, I thought it would be fun to dig into the backlog and write a post on parallel computing. In the past, writing a program might have involved message passing or threading. Today, the widespread availability of graphics coprocessors effectively places immense, parallel computing power in the hands of every teenager clutching a smartphone, and more.</p>
<p>As a student with limited resources, I prefer tackling a subset of <a target="_blank" rel="noopener noreferrer" href="http://en.wikipedia.org/wiki/Embarrassingly_parallel">embarrassingly parallel</a> problems that I call <em>trivially parallel</em>. These are problems that benefit from parallelization without necessarily requiring supercomputing levels of hardware. A simple example might be matrix multiplication, or even more basic, swapping the contents of two arrays.</p>
<p>This is the point where I declare <a target="_blank" rel="noopener noreferrer" href="https://www.khronos.org/opencl/">OpenCL</a> a horrible mess. You can read more about the <a target="_blank" rel="noopener noreferrer" href="http://arrayfire.com/quest-for-the-smallest-opencl-program/">Quest for the Smallest OpenCL Program</a> to get a sense of all the hoops you have to jump through just to do basic math. You could start with the 37 line example. It is quite basic, and difficult to expand upon. Not impossible, but there is an easier way!</p>
<p>I wrote <a target="_blank" rel="noopener noreferrer" href="https://github.com/Polytonic/Chlorine">Chlorine</a> as a simpler way to interact with devices. The goal is for you to work with your data, not fight with hardware interfaces. How does it work? The following is a line-by-line explanation of the <a target="_blank" rel="noopener noreferrer" href="https://github.com/Polytonic/Chlorine/tree/master/examples/swap">swap example</a> on the <a target="_blank" rel="noopener noreferrer" href="https://github.com/Polytonic/Chlorine#getting-started">project homepage</a>.</p>
<p>Start by including the Chlorine header.</p>
<pre><code class="hljs language-c++"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&quot;chlorine.hpp&quot;</span></span>
</code></pre><p>Now we create some dummy data. While this example uses <code>std::vector</code> for brevity, you can freely mix and match containers of any type. This can be useful if you need to mix bounded and unbounded array types.</p>
<pre><code class="hljs language-c++"><span class="hljs-comment">// Create Some Data</span>
<span class="hljs-function">std::vector&lt;<span class="hljs-type">float</span>&gt; <span class="hljs-title">spam</span><span class="hljs-params">(<span class="hljs-number">10</span>, <span class="hljs-number">3.1415f</span>)</span></span>;
<span class="hljs-function">std::vector&lt;<span class="hljs-type">float</span>&gt; <span class="hljs-title">eggs</span><span class="hljs-params">(<span class="hljs-number">10</span>, <span class="hljs-number">2.7182f</span>)</span></span>;
</code></pre><p>Next, we create a Chlorine Worker, using the filename constructor, which takes a path to an OpenCL kernel file.</p>
<pre><code class="hljs language-c++"><span class="hljs-comment">// Initialize a Chlorine Worker</span>
<span class="hljs-function">ch::Worker <span class="hljs-title">worker</span><span class="hljs-params">(<span class="hljs-string">&quot;swap.cl&quot;</span>)</span></span>;
</code></pre><p>Now that our worker is aware of kernel functions, we can simply invoke <code>Worker::call(kernel_function, ... )</code> with the first argument being the name of the kernel function you wish to call, followed by the same arguments (in the same order!) as the kernel function.</p>
<pre><code class="hljs language-c++"><span class="hljs-comment">// Call the Swap Function in the Given Kernel</span>
worker.<span class="hljs-built_in">call</span>(<span class="hljs-string">&quot;swap&quot;</span>, spam, eggs);
</code></pre><p>After this completes, data is automatically written back to the same memory locations allocated by your program.</p>
<pre><code class="hljs language-c++"><span class="hljs-comment">// Host Containers Are Automatically Updated</span>
std::cout &lt;&lt; <span class="hljs-string">&quot;Spam: &quot;</span> &lt;&lt; spam[<span class="hljs-number">0</span>] &lt;&lt; <span class="hljs-string">&quot;\n&quot;</span>; <span class="hljs-comment">// 2.7182</span>
std::cout &lt;&lt; <span class="hljs-string">&quot;Eggs: &quot;</span> &lt;&lt; eggs[<span class="hljs-number">0</span>] &lt;&lt; <span class="hljs-string">&quot;\n&quot;</span>; <span class="hljs-comment">// 3.1415</span>
</code></pre><p>Don&#39;t take my word for it though! If you build and run this example, you&#39;ll see that the values in each array have been swapped. In order for this to compile, we need to link with the system installation of OpenCL. We also need to pass <code>-std=c++11</code> to the compiler to enable variadic templating in Chlorine. You should end up with something like this:</p>
<pre><code class="hljs language-bash">$ clang++ -std=c++11 swap.cpp -lOpenCL  <span class="hljs-comment"># Compile</span>
$ ./a.out                               <span class="hljs-comment"># and Run</span>
</code></pre><p>Timing is built in, so you can also effortlessly recover profiling data! Chlorine Workers return an OpenCL event associated with the kernel function call. This allows you to recover profiling data, such as how much time was spent executing the kernel function.</p>
<pre><code class="hljs language-c++"><span class="hljs-comment">// Store the Returned OpenCL Event Object</span>
<span class="hljs-keyword">auto</span> event = worker.<span class="hljs-built_in">call</span>(<span class="hljs-string">&quot;swap&quot;</span>, spam, eggs);
</code></pre><p>To make things easier, there is a helper function <code>ch::elapsed()</code> which accepts an OpenCL event and returns the elapsed time spent on your kernel function. This helper preserves the nanosecond resolution offered by the OpenCL API and is merely a convenience wrapper.</p>
<pre><code class="hljs language-c++"><span class="hljs-comment">// Print Some Profiling Data</span>
std::cout &lt;&lt; <span class="hljs-string">&quot;Elapsed Time: &quot;</span> &lt;&lt; ch::<span class="hljs-built_in">elapsed</span>(event) &lt;&lt; <span class="hljs-string">&quot;ns\n&quot;</span>;
</code></pre><p>Kernel files are written in a variant of the C programming language. While I won&#39;t go into detail about it here, I hope this serves as a valuable demonstration in how Chlorine may be used to easily port code to run in parallel. Up next: <a href="/blog/2015/05/visualizing-the-mandelbrot-set/">Visualizing the Mandelbrot Set</a>.</p>
]]></content:encoded>
        </item>
        <item>
            <title>Annotated Realtime Raytracing</title>
            <link>https://www.tinycranes.com/blog/2015/05/annotated-realtime-raytracing/</link>
            <guid isPermaLink="true">https://www.tinycranes.com/blog/2015/05/annotated-realtime-raytracing/</guid>
            <pubDate>Wed, 13 May 2015 02:36:55 GMT</pubDate>
            <author>kfung@tinycranes.com (Kevin Fung)</author>
            <description>Ever wonder how a raytracer works? No really, a line-by-line explanation, and not some academic paper filled with technical jargon? Want to see a GPU raytracer running in realtime, in your browser…</description>
            <content:encoded><![CDATA[<p>Ever wonder how a raytracer works? No really, a line-by-line explanation, and not some academic paper filled with technical jargon? Want to see a GPU raytracer running in realtime, in your browser window? Well here you go: if you&#39;re impatient, <a href="/blog/2015/05/annotated-realtime-raytracing/#live-demo">click here to go straight to the demo</a>, or read on for the more detailed walkthrough.</p>
<p>So what exactly is ray tracing? Consider a lamp hanging from the ceiling. Light is constantly being emitted from the lamp in the form of light rays, which bounce around the room until they hit your eye. Ray tracing follows a similar concept by simulating the path of light through a scene, except in reverse. There is no point in doing the math for light rays you cannot see!</p>
<p>Algorithmically, ray tracing is very elegant. For each pixel, shoot a light ray from the camera through each pixel on screen. If the ray collides with geometry in the scene, create new rays that perform the same process for both reflection, as in a mirror, and refraction, as in through water. Repeat to your satisfaction.</p>
<p>Having worked extensively with OpenCL in the past, this seemed like a good candidate to port to a parallel runtime on a GPU. Inspired by the <a target="_blank" rel="noopener noreferrer" href="http://www.kevinbeason.com/smallpt/#moreinfo">smallpt</a> line-by-line explanation, I decided to write a parallel ray tracer with extensive annotations, using only the GLSL fragment shader drawing on a rectangle (i.e. &quot;2D Quad&quot;). I start with a simple ray definition, consisting of an origin point and a direction vector. I also define a directional light to illuminate my scene.</p>
<pre><code class="hljs language-c"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Ray</span> {</span>
    vec3 origin;
    vec3 direction;
};

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Light</span> {</span>
    vec3 color;
    vec3 direction;
};
</code></pre><p>In real life, objects have many different material properties. Some objects respond very differently to light than others. For instance, a sheet of paper and a polished mirror. The former exhibits a strong <em>diffuse</em> response; incoming light is reflected at many angles. The latter is an example of a <em>specular</em> response, where incoming light is reflected in a single direction. To model this, I create a basic material definition. Objects in my scene share a single (RGB) color with diffuse and specular weights.</p>
<pre><code class="hljs language-c"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Material</span> {</span>
    vec3 color;
    <span class="hljs-type">float</span> diffuse;
    <span class="hljs-type">float</span> specular;
};
</code></pre><p>To render the scene, I need to know where a ray intersects with an object. Since rays have infinite length from an origin, I can model the point of intersection by storing the distance along the ray. I also need to store the surface normal so I know which way to bounce! Once I create a ray, it loses the concept of scene geometry, so one more thing I do is forward the surface material properties.</p>
<pre><code class="hljs language-c"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Intersect</span> {</span>
    <span class="hljs-type">float</span> len;
    vec3 normal;
    Material material;
};
</code></pre><p>The last data structures I create are for objects used to fill my scene. The most basic object I can model is a sphere, which is defined as a radius at some center position, with some material properties. To draw the floor, I also define a simple horizontal plane centered at the origin, with a normal vector pointing upwards.</p>
<pre><code class="hljs language-c"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Sphere</span> {</span>
    <span class="hljs-type">float</span> radius;
    vec3 position;
    Material material;
};

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Plane</span> {</span>
    vec3 normal;
    Material material;
};
</code></pre><p>At this point, I define some global variables. A more advanced program might pass these values in as uniforms, but for now, this is easier to tinker with. Due to floating point precision errors, when a ray intersects geometry at a surface, the point of intersection could possibly be just below the surface. The subsequent reflection ray would then bounce off the <em>inside</em> wall of the surface. This is known as self-intersection. When creating new rays, I initialize them at a slightly offset origin to help mitigate this problem.</p>
<pre><code class="hljs language-c"><span class="hljs-type">const</span> <span class="hljs-type">float</span> epsilon = <span class="hljs-number">1e-3</span>;
</code></pre><p>The classical ray tracing algorithm is recursive. However, GLSL does not support recursion, so I instead use an iterative approach to control the number of light bounces.</p>
<pre><code class="hljs language-c"><span class="hljs-type">const</span> <span class="hljs-type">int</span> iterations = <span class="hljs-number">16</span>;
</code></pre><p>Next, I define an exposure time and gamma value. At this point, I also create a basic directional light and define the ambient light color; the color here is mostly a matter of taste. Basically ... lighting controls.</p>
<pre><code class="hljs language-c"><span class="hljs-type">const</span> <span class="hljs-type">float</span> exposure = <span class="hljs-number">1e-2</span>;
<span class="hljs-type">const</span> <span class="hljs-type">float</span> gamma = <span class="hljs-number">2.2</span>;
<span class="hljs-type">const</span> <span class="hljs-type">float</span> intensity = <span class="hljs-number">100.0</span>;
<span class="hljs-type">const</span> vec3 ambient = vec3(<span class="hljs-number">0.6</span>, <span class="hljs-number">0.8</span>, <span class="hljs-number">1.0</span>) * intensity / gamma;

<span class="hljs-comment">// For a Static Light</span>
Light light = Light(vec3(<span class="hljs-number">1.0</span>) * intensity, normalize(vec3(<span class="hljs-number">-1.0</span>, <span class="hljs-number">0.75</span>, <span class="hljs-number">1.0</span>)));

<span class="hljs-comment">// For a Rotating Light</span>
Light light = Light(vec3(<span class="hljs-number">1.0</span>) * intensity, normalize(
               vec3(<span class="hljs-number">-1.0</span> + <span class="hljs-number">4.0</span> * <span class="hljs-built_in">cos</span>(iGlobalTime), <span class="hljs-number">4.75</span>,
                     <span class="hljs-number">1.0</span> + <span class="hljs-number">4.0</span> * <span class="hljs-built_in">sin</span>(iGlobalTime))));
</code></pre><p>I strongly dislike this line. I needed to know when a ray hits or misses a surface. If it hits geometry, I returned the point at the surface. Otherwise, the ray misses all geometry and instead hits the sky box. In a language that supports dynamic return values, I could <code>return false</code>, but that is not an option in GLSL. In the interests of making progress, I created an intersect of distance zero to represent a miss and moved on.</p>
<pre><code class="hljs language-c"><span class="hljs-type">const</span> Intersect miss = Intersect(<span class="hljs-number">0.0</span>, vec3(<span class="hljs-number">0.0</span>), Material(vec3(<span class="hljs-number">0.0</span>), <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>));
</code></pre><p>As indicated earlier, I implement ray tracing for spheres. I need to compute the point at which a ray intersects with a sphere. <a target="_blank" rel="noopener noreferrer" href="http://en.wikipedia.org/wiki/Line-sphere_intersection">Line-Sphere</a> intersection is relatively straightforward. For reflection purposes, a ray either hits or misses, so I need to check for no solutions, or two solutions. In the latter case, I need to determine which solution is &quot;in front&quot; so I can return an intersection of appropriate distance from the ray origin.</p>
<pre><code class="hljs language-c">Intersect <span class="hljs-title function_">intersect</span><span class="hljs-params">(Ray ray, Sphere sphere)</span> {
    <span class="hljs-comment">// Check for a Negative Square Root</span>
    vec3 oc = sphere.position - ray.origin;
    <span class="hljs-type">float</span> l = dot(ray.direction, oc);
    <span class="hljs-type">float</span> det = <span class="hljs-built_in">pow</span>(l, <span class="hljs-number">2.0</span>) - dot(oc, oc) + <span class="hljs-built_in">pow</span>(sphere.radius, <span class="hljs-number">2.0</span>);
    <span class="hljs-keyword">if</span> (det &lt; <span class="hljs-number">0.0</span>) <span class="hljs-keyword">return</span> miss;

    <span class="hljs-comment">// Find the Closer of Two Solutions</span>
             <span class="hljs-type">float</span> len = l - <span class="hljs-built_in">sqrt</span>(det);
    <span class="hljs-keyword">if</span> (len &lt; <span class="hljs-number">0.0</span>) len = l + <span class="hljs-built_in">sqrt</span>(det);
    <span class="hljs-keyword">if</span> (len &lt; <span class="hljs-number">0.0</span>) <span class="hljs-keyword">return</span> miss;
    <span class="hljs-keyword">return</span> Intersect(len, (ray.origin + len*ray.direction - sphere.position) / sphere.radius, sphere.material);
}
</code></pre><p>Since I created a floor plane, I likewise have to handle reflections for planes by implementing <a target="_blank" rel="noopener noreferrer" href="http://en.wikipedia.org/wiki/Line-plane_intersection">Line-Plane</a> intersection. I only care about the intersect for the purposes of reflection, so I only check if the quotient is non-zero.</p>
<pre><code class="hljs language-c">Intersect <span class="hljs-title function_">intersect</span><span class="hljs-params">(Ray ray, Plane plane)</span> {
    <span class="hljs-type">float</span> len = -dot(ray.origin, plane.normal) / dot(ray.direction, plane.normal);
    <span class="hljs-keyword">return</span> (len &lt; <span class="hljs-number">0.0</span>) ? miss : Intersect(len, plane.normal, plane.material);
}
</code></pre><p>In a <em>real</em> ray tracing renderer, geometry would be passed in from the host as a mesh containing vertices, normals, and texture coordinates, but for the sake of simplicity, I hand-coded the scene-graph. In this function, I take an input ray and iterate through all geometry to determine intersections.</p>
<pre><code class="hljs language-c">Intersect <span class="hljs-title function_">trace</span><span class="hljs-params">(Ray ray)</span> {
    <span class="hljs-type">const</span> <span class="hljs-type">int</span> num_spheres = <span class="hljs-number">3</span>;
    Sphere spheres[num_spheres];
    ...
</code></pre><p>I initially started with the <a href="www.kevinbeason.com/smallpt/">smallpt</a> scene definition, but soon found performance was abysmal on very large spheres. I kept the general format, modified to fit my data structures.</p>
<pre><code class="hljs language-c">...
spheres[<span class="hljs-number">0</span>] = Sphere(<span class="hljs-number">2.0</span>, vec3(<span class="hljs-number">-4.0</span>, <span class="hljs-number">3.0</span> + <span class="hljs-built_in">sin</span>(iGlobalTime), <span class="hljs-number">0</span>), Material(vec3(<span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.2</span>), <span class="hljs-number">1.0</span>, <span class="hljs-number">0.001</span>));
spheres[<span class="hljs-number">1</span>] = Sphere(<span class="hljs-number">3.0</span>, vec3( <span class="hljs-number">4.0</span> + <span class="hljs-built_in">cos</span>(iGlobalTime), <span class="hljs-number">3.0</span>, <span class="hljs-number">0</span>), Material(vec3(<span class="hljs-number">0.0</span>, <span class="hljs-number">0.2</span>, <span class="hljs-number">1.0</span>), <span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>));
spheres[<span class="hljs-number">2</span>] = Sphere(<span class="hljs-number">1.0</span>, vec3( <span class="hljs-number">0.5</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">6.0</span>),                  Material(vec3(<span class="hljs-number">1.0</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">1.0</span>), <span class="hljs-number">0.5</span>, <span class="hljs-number">0.25</span>));
...
</code></pre><p>Since my ray tracing approach involves drawing to a 2D quad, I can no longer use the OpenGL Depth and Stencil buffers to control the draw order. Drawing is therefore sensitive to z-indexing, so I first intersect with the plane, then loop through all spheres back-to-front.</p>
<pre><code class="hljs language-c">...
Intersect intersection = miss;
Intersect plane = intersect(ray, Plane(vec3(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>), Material(vec3(<span class="hljs-number">1.0</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">1.0</span>), <span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>)));
<span class="hljs-keyword">if</span> (plane.material.diffuse &gt; <span class="hljs-number">0.0</span> || plane.material.specular &gt; <span class="hljs-number">0.0</span>) { intersection = plane; }
<span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; num_spheres; i++) {
    Intersect sphere = intersect(ray, spheres[i]);
    <span class="hljs-keyword">if</span> (sphere.material.diffuse &gt; <span class="hljs-number">0.0</span> || sphere.material.specular &gt; <span class="hljs-number">0.0</span>)
        intersection = sphere;
}
<span class="hljs-keyword">return</span> intersection;
</code></pre><p>This is the critical part of writing a ray tracer. I start with some empty scratch vectors for color data and the Fresnel factor. I trace the scene with using an input ray, and continue to fire new rays until the iteration depth is reached, at which point I return the total sum of the color values from computed at each bounce.</p>
<pre><code class="hljs language-c">vec3 <span class="hljs-title function_">radiance</span><span class="hljs-params">(Ray ray)</span> {
    vec3 color, fresnel;
    vec3 mask = vec3(<span class="hljs-number">1.0</span>);
    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt;= iterations; ++i) {
        Intersect hit = trace(ray);
        ...
</code></pre><p>This goes back to the dummy &quot;miss&quot; intersect. Basically, if the scene trace returns an intersection with either a diffuse or specular coefficient, then it has encountered a surface of a sphere or plane. Otherwise, the current ray has reached the ambient-colored sky box.</p>
<pre><code class="hljs language-c"><span class="hljs-keyword">if</span> (hit.material.diffuse &gt; <span class="hljs-number">0.0</span> || hit.material.specular &gt; <span class="hljs-number">0.0</span>) {
</code></pre><p>Here I use the <a target="_blank" rel="noopener noreferrer" href="http://en.wikipedia.org/wiki/Schlick's_approximation">Schlick Approximation</a> to determine the Fresnel specular contribution factor, a measure of how much incoming light is reflected or refracted. I compute the Fresnel term and use a mask to track the fraction of reflected light in the current ray with respect to the original.</p>
<pre><code class="hljs language-c">vec3 r0 = hit.material.color.rgb * hit.material.specular;
<span class="hljs-type">float</span> hv = clamp(dot(hit.normal, -ray.direction), <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>);
fresnel = r0 + (<span class="hljs-number">1.0</span> - r0) * <span class="hljs-built_in">pow</span>(<span class="hljs-number">1.0</span> - hv, <span class="hljs-number">5.0</span>);
mask *= fresnel;
</code></pre><p>I handle shadows and diffuse colors next. I condensed this part into one conditional evaluation for brevity. Remember <code>epsilon</code>? I use it to trace a ray slightly offset from the point of intersection to the light source. If the shadow ray does not hit an object, it will be a &quot;miss&quot; as it hits the skybox. This means there are no objects between the point and the light, at which point I can add the diffuse color to the fragment color since the object is not in shadow.</p>
<pre><code class="hljs language-c"><span class="hljs-keyword">if</span> (trace(Ray(ray.origin + hit.len * ray.direction + epsilon * light.direction, light.direction)) == miss) {
color += clamp(dot(hit.normal, light.direction), <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>) * light.color
       * hit.material.color.rgb * hit.material.diffuse
       * (<span class="hljs-number">1.0</span> - fresnel) * mask / fresnel;
}
</code></pre><p>After computing diffuse colors, I then generate a new reflection ray and overwrite the original ray that was passed in as an argument to the radiance(...) function. Then I repeat until I reach the iteration depth.</p>
<pre><code class="hljs language-c">vec3 reflection = reflect(ray.direction, hit.normal);
ray = Ray(ray.origin + hit.len * ray.direction + epsilon * reflection, reflection);
</code></pre><p>This is the other half of the tracing branch. If the trace failed to return an intersection with an attached material, then it is safe to assume that the ray points at the sky, or out of bounds of the scene. At this point I realized that real objects have a small sheen to them, so I hard-coded a small spotlight pointing in the same direction as the main light for pseudo-realism.</p>
<pre><code class="hljs language-c"><span class="hljs-keyword">else</span> {
    vec3 spotlight = vec3(<span class="hljs-number">1e6</span>) * <span class="hljs-built_in">pow</span>(<span class="hljs-built_in">abs</span>(dot(ray.direction, light.direction)), <span class="hljs-number">250.0</span>);
    color += mask * (ambient + spotlight); <span class="hljs-keyword">break</span>;
}
</code></pre><p>The main function primarily deals with organizing data from OpenGL into a format that the ray tracer can use. For ray tracing, I need to fire a ray for each pixel, or more precisely, a ray for every fragment. However, pixels to fragment coordinates do not map one a one-to-one basis, so I need to divide the fragment coordinates by the viewport resolution. I then offset that by a fixed value to re-center the coordinate system.</p>
<pre><code class="hljs language-c"><span class="hljs-type">void</span> <span class="hljs-title function_">mainImage</span><span class="hljs-params">(out vec4 fragColor, in vec2 fragCoord)</span> {
    vec2 uv    = fragCoord.xy / iResolution.xy - vec2(<span class="hljs-number">0.5</span>);
         uv.x *= iResolution.x / iResolution.y;
    ...
</code></pre><p>For each fragment, create a ray at a fixed point of origin directed at the coordinates of each fragment. The last thing before writing the color to the fragment is to post-process the pixel values using tone-mapping. In this case, I adjust for exposure and perform linear gamma correction.</p>
<pre><code class="hljs language-c">...
Ray ray = Ray(vec3(<span class="hljs-number">0.0</span>, <span class="hljs-number">2.5</span>, <span class="hljs-number">12.0</span>), normalize(vec3(uv.x, uv.y, <span class="hljs-number">-1.0</span>)));
fragColor = vec4(<span class="hljs-built_in">pow</span>(radiance(ray) * exposure, vec3(<span class="hljs-number">1.0</span> / gamma)), <span class="hljs-number">1.0</span>);
</code></pre><p>If all goes well, you should see an animated scene below, assuming your computer isn&#39;t a potato! Alternately, you can check out the complete source code on <a target="_blank" rel="noopener noreferrer" href="https://www.shadertoy.com/view/4ljGRd">Shadertoy</a>.</p>
<iframe id="live-demo"
        src="https://www.shadertoy.com/embed/4ljGRd?gui=true&paused=false"
        sandbox="allow-scripts allow-same-origin"
        referrerpolicy="no-referrer"
        loading="lazy"
        width="100%" height="380px" frameborder="0" allowfullscreen></iframe><p>So, to recap, this was my first foray into ray tracing. Originally, I wanted to write this using the OpenGL Compute Shader. That was harder to setup than I originally anticipated, and I spent a fair bit of time mucking around with OpenGL and cmake before deciding to just sit down and start programming.</p>
<p>All things considered, this is a pretty limited ray tracer. Some low hanging fruit might be to add anti-aliasing and soft shadows. The former was not an issue until I ported this from a HiDPI display onto the WebGL canvas. The latter involves finding a quality random number generator.</p>
]]></content:encoded>
        </item>
        <item>
            <title>Hello World</title>
            <link>https://www.tinycranes.com/blog/2015/04/hello-world/</link>
            <guid isPermaLink="true">https://www.tinycranes.com/blog/2015/04/hello-world/</guid>
            <pubDate>Wed, 15 Apr 2015 04:23:55 GMT</pubDate>
            <author>kfung@tinycranes.com (Kevin Fung)</author>
            <description>Starting a blog was a significant goal of mine. It has been, ever since I started programming. Today, I can finally cross that goal off my list. The code is not pretty. If anything, it is some of the…</description>
            <content:encoded><![CDATA[<p>Starting a blog was a significant goal of mine. It has been, ever since I started programming. Today, I can finally cross that goal off my list. The code is not pretty. If anything, it is some of the messiest I have ever written, but it works. <em>I made this</em>, and that is something I can be proud of.</p>
]]></content:encoded>
        </item>
    </channel>
</rss>