<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>CodeItNow &#187; Irradiance Caching</title>
	<atom:link href="http://www.rorydriscoll.com/category/graphics/global-illumination/irradiance-caching/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.rorydriscoll.com</link>
	<description></description>
	<lastBuildDate>Mon, 23 Jan 2012 01:50:36 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Irradiance Caching: Part 2</title>
		<link>http://www.rorydriscoll.com/2009/01/24/irradiance-caching-part-2/</link>
		<comments>http://www.rorydriscoll.com/2009/01/24/irradiance-caching-part-2/#comments</comments>
		<pubDate>Sat, 24 Jan 2009 22:45:17 +0000</pubDate>
		<dc:creator>Rory</dc:creator>
				<category><![CDATA[Global Illumination]]></category>
		<category><![CDATA[Graphics]]></category>
		<category><![CDATA[Irradiance Caching]]></category>

		<guid isPermaLink="false">http://www.rorydriscoll.com/?p=200</guid>
		<description><![CDATA[In my previous post, I wrote very briefly about an  important improvement to the irradiance caching algorithm &#8211; irradiance gradients &#8211; and I&#8217;m going to expand on rotational gradients this time. Gradients The gradient of a function represents both the direction and rate of change of that function as the inputs vary. For a one [...]]]></description>
			<content:encoded><![CDATA[<p>In my previous post, I wrote very briefly about an  important improvement to the irradiance caching algorithm &#8211; irradiance gradients &#8211; and I&#8217;m going to expand on rotational gradients this time.</p>
<h2>Gradients</h2>
<p>The gradient of a function represents both the direction and rate of change of that function as the inputs vary. For a one dimensional function this is simply the derivative of the function. As you move into higher dimensions, you need to consider which coordinate system the inputs for the function are specified in, as this will change how you need to calculate the gradient.</p>
<p>For now, I&#8217;m just going to focus on calculating the gradient of a function defined using normalized spherical coordinates. Unfortunately, there&#8217;s no real standard way to define spherical coordinates, and despite similar looking symbols, the values are often interchanged. I&#8217;m going to define the spherical coordinates on the unit sphere as azimuthal value φ [0, π), and polar value θ [0, 2π).</p>
<p style="text-align: center;"><img class="size-full wp-image-238 aligncenter" title="sphericalcoordinates" src="http://www.rorydriscoll.com/wp-content/uploads/2009/01/sphericalcoordinates.png" alt="sphericalcoordinates" width="290" height="264" /></p>
<p><span id="more-200"></span>When dealing with multiple dimensions, you can calculate the gradient by splitting the gradient calculation into multiple partial derivatives and summing them with appropriate vector weights. For normalized spherical coordinates the gradient is:</p>
<p><img src="http://latex.codecogs.com/gif.latex?\nabla&amp;space;f(\phi,&amp;space;\theta)&amp;space;=&amp;space;\frac{\delta&amp;space;f}{\delta&amp;space;\theta}&amp;space;\vec{v_\theta}&amp;space;+&amp;space;\frac{1}{\sin&amp;space;\theta}\frac{\delta&amp;space;f}{\delta&amp;space;\phi}\vec{v_\phi}" alt="" /></p>
<p>The function <em>f</em> represents a scalar field, so the gradient is a vector field. Each vector points in the direction of greatest increase. Much like the integrals of functions described using spherical coordinates, you have to take care to weight the azimuthal contribution by the sine of the polar angle.</p>
<p>As a real-world example of using gradients, let&#8217;s calculate the gradient for a simple function:</p>
<p><img src="http://latex.codecogs.com/gif.latex?f(\phi,&amp;space;\theta)&amp;space;=&amp;space;\cos&amp;space;\theta" border="0" alt="" /></p>
<p>The derivatives of the function for each argument are easy to calculate:</p>
<p><img src="http://latex.codecogs.com/gif.latex?\frac{\delta&amp;space;f}{\delta&amp;space;\theta}&amp;space;=&amp;space;-\sin&amp;space;\theta" border="0" alt="" /></p>
<p><img src="http://latex.codecogs.com/gif.latex?\frac{\delta&amp;space;f}{\delta&amp;space;\phi}&amp;space;=&amp;space;0" border="0" alt="" /></p>
<p>Combining these together with the previous definition, you can calculate the gradient vector at any point on the unit sphere for <em>f </em>using:</p>
<p><img src="http://latex.codecogs.com/gif.latex?\nabla&amp;space;f(\phi,&amp;space;\theta)&amp;space;=&amp;space;-\vec{v_\theta}&amp;space;\sin&amp;space;\theta" border="0" alt="" /></p>
<p>There&#8217;s lots more to be learned about gradients, and a good start would be <a href="http://en.wikipedia.org/wiki/Gradient">Wikipedia</a>, and also <a href="http://www-math.mit.edu/18.013A/HTML/chapter09/section04.html">this page</a> on the MIT website.</p>
<h2>Rotational Irradiance Gradient</h2>
<p>The irradiance contribution from a direction on the hemisphere about the surface normal, specified using spherical coordinates φ [0, 2π) and θ [0, π / 2) is:</p>
<p><img src="http://latex.codecogs.com/gif.latex?f&amp;space;(\phi,&amp;space;\theta)&amp;space;=&amp;space;L(\phi,&amp;space;\theta)&amp;space;\cos&amp;space;\theta" alt="" /></p>
<p>Where L is the incident radiance in the supplied direction. So to calculate the gradient vector at any point on the hemisphere, you just need to evaluate:</p>
<p><img src="http://latex.codecogs.com/gif.latex?\nabla&amp;space;f(\phi,&amp;space;\theta)&amp;space;=&amp;space;-\vec{v_\theta}L(\phi,&amp;space;\theta)&amp;space;\sin&amp;space;\theta" alt="" /></p>
<p>This just calculates the gradient in a specific direction, but for the irradiance gradient we need to calculate the average gradient over the entire hemisphere. We can do this at the same time as we calculate the irradiance by using a similar Monte Carlo estimator. We want to share the sampling strategy between the irradiance calculation and the rotational gradient calculation, so we&#8217;re stuck using the same pdf:</p>
<p><img src="http://latex.codecogs.com/gif.latex?p(x)&amp;space;=&amp;space;\frac&amp;space;{\cos&amp;space;\theta}{\pi}" alt="" /></p>
<p>So the estimator for the irradiance gradient becomes:</p>
<p><img src="http://latex.codecogs.com/gif.latex?\nabla&amp;space;E&amp;space;\approx&amp;space;\frac&amp;space;{1}{N}&amp;space;\sum_{i=1}^{N}{&amp;space;\frac&amp;space;{-\vec{v_\theta}L_i\sin&amp;space;\theta}{\frac{\cos&amp;space;\theta}{\pi}}&amp;space;}" alt="" /></p>
<p>Which collapses down to:</p>
<p><img src="http://latex.codecogs.com/gif.latex?\nabla&amp;space;E&amp;space;\approx&amp;space;\frac&amp;space;{\pi}{N}&amp;space;\sum_{i=1}^{N}{-\vec{v_\theta}L_i\tan&amp;space;\theta}" alt="" /></p>
<p>The vector <em>v</em> is a unit vector on the plane of the hemisphere pointing in the perpendicular direction to the angle φ.  There are two perpendicular vectors to φ, and which one you decide to use depends on the order you do the cross product on when evaluating the gradient. Using the left hand rule for rotation, I&#8217;m doing a clockwise rotation of φ by ninety degrees.</p>
<h2>Using the Rotational Irradiance Gradient</h2>
<p>The irradiance estimate at a point is defined by a weighted sum of irradiance cache entries:</p>
<p><img src="http://latex.codecogs.com/gif.latex?E&amp;space;=&amp;space;\frac{\sum_{1}^{N}{w_i&amp;space;E_i}}{\sum_{1}^{N}{w_i}}" alt="" /></p>
<p>Now we have the rotational gradient, we can use it to improve the estimate. The cross product of the surface normal and cache entry normal represents both a direction and magnitude of rotational difference. We then project this difference onto the irradiance gradient to calculate how much the irradiance is changing in that direction:</p>
<p><img src="http://latex.codecogs.com/gif.latex?E&amp;space;=&amp;space;\frac{\sum_{1}^{N}{w_i&amp;space;(E_i&amp;space;+&amp;space;\nabla&amp;space;E_i&amp;space;\cdot&amp;space;(\vec{n_i}&amp;space;\times&amp;space;\vec{n}))}}{\sum_{1}^{N}{w_i}}" alt="" /></p>
<p>Note that conceptually this calculation needs to be performed once for each color channel, since the gradient of the irradiance is really the gradient of three scalar fields &#8211; red, green and blue. In practice it&#8217;s easier to assume that the gradient is a three dimensional vector of colors, rather than scalars.</p>
<h2>Implementation</h2>
<p>The implementation is pretty straightforward, but there are a couple of optimizations that can be made because we are using a unit hemisphere for sampling. Assuming that we have the sample direction in Cartesian coordinates in local space, where the z-axis points in the polar direction, we can get the cos weighting from the z coordinate:</p>
<p><img title="z = \cos \theta" src="http://latex.codecogs.com/gif.latex?z&amp;space;=&amp;space;\cos&amp;space;\theta" alt="" /></p>
<p>Also, we can get the sine weighted projected unit vector by simply setting the z value to zero, since:</p>
<p><img src="http://latex.codecogs.com/gif.latex?\lvert&amp;space;(x,&amp;space;y,&amp;space;0)&amp;space;\rvert&amp;space;=&amp;space;\sin&amp;space;\theta" alt="" /></p>
<p>So, to get the local space tan weighted perpendicular vector, we just need to use the following:</p>
<p><img src="http://latex.codecogs.com/gif.latex?\vec{v_\theta}&amp;space;\tan&amp;space;\theta&amp;space;=&amp;space;(\frac{y}{z},&amp;space;-\frac{x}{z},&amp;space;0)" alt="" /></p>
<p>Note that the x and y values were swapped, and the x value negated to get the perpendicular vector.</p>
<h2>Results</h2>
<p>I&#8217;ve rendered out a before and after shot, showing just the indirect irradiance. There are no translational gradients being used at the moment, so there are still some artefacts. Here&#8217;s the before shot:</p>
<p><img class="aligncenter size-full wp-image-231" title="nogradient" src="http://www.rorydriscoll.com/wp-content/uploads/2009/01/nogradient.png" alt="nogradient" width="656" height="396" /></p>
<p>And here&#8217;s the same render, but with rotational gradients this time:</p>
<p><img class="aligncenter size-full wp-image-232" title="rotationalgradient" src="http://www.rorydriscoll.com/wp-content/uploads/2009/01/rotationalgradient.png" alt="rotationalgradient" width="656" height="396" /></p>
<p>Note that the time to render each frame is almost exactly the same, yet the rotational gradients provide a much smoother result. Next I&#8217;ll implement translational gradients and hopefully the image will look considerably better.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.rorydriscoll.com/2009/01/24/irradiance-caching-part-2/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Irradiance Caching: Part 1</title>
		<link>http://www.rorydriscoll.com/2009/01/18/irradiance-caching-part-1/</link>
		<comments>http://www.rorydriscoll.com/2009/01/18/irradiance-caching-part-1/#comments</comments>
		<pubDate>Mon, 19 Jan 2009 01:35:35 +0000</pubDate>
		<dc:creator>Rory</dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Global Illumination]]></category>
		<category><![CDATA[Graphics]]></category>
		<category><![CDATA[Irradiance Caching]]></category>

		<guid isPermaLink="false">http://www.rorydriscoll.com/?p=166</guid>
		<description><![CDATA[Solving the rendering equation with even just one bounce of indirect lighting can take a long time. The majority of time spent rendering a frame is in estimating the lighting integral. For example, rendering a single bounce of indirect lighting at 720p resolution with 256 sample rays for a Monte Carlo estimator requires about 237 [...]]]></description>
			<content:encoded><![CDATA[<p>Solving the rendering equation with even just one bounce of indirect lighting can take a long time. The majority of time spent rendering a frame is in estimating the lighting integral. For example, rendering a single bounce of indirect lighting at 720p resolution with 256 sample rays for a Monte Carlo estimator requires about 237 million rays to be cast. This doesn&#8217;t even include the rays needed for sampling the lights for direct lighting, so in practice, the total will be even higher.</p>
<p>One interesting observation made by Greg Ward in his <a href="http://radsite.lbl.gov/radiance/papers/sg88/paper.html">Siggraph &#8217;88 paper</a> is that contrary to direct lighting, where shadows and lights can cause harsh changes, the indirect lighting on a surface tends to vary relatively slowly. One way to picture why this is, is to imagine the computing average color from the what you can see from each of your eyes. Even though each eye has a slightly different view on the world, the images they see are nearly similar, and so the average color is also nearly the same.</p>
<p><span id="more-166"></span></p>
<p>The image below shows the same scene from my previous post with just the indirect irradiance, and it&#8217;s pretty clear that for each surface, the lighting varies in a very smooth fashion.</p>
<p><img class="aligncenter size-full wp-image-167" title="finalgatherindirectonly" src="http://www.rorydriscoll.com/wp-content/uploads/2009/01/finalgatherindirectonly.png" alt="finalgatherindirectonly" width="656" height="396" /></p>
<p>Ward proposed using this knowledge to reduce the number of times that the Monte Carlo estimator was evaluated by interpolating between nearby previously calculated values. At the time he just called it &#8216;lazy evaluation&#8217;, which I personally think is a good way to picture the idea. Later it became known as irradiance caching.</p>
<h2>Irradiance Caching</h2>
<p>The basic concept for irradiance caching is really simple: For each point on a surface at which you want to evaluate irradiance, if the cache contains any valid entries then interpolate between them. Otherwise, calculate a new irradiance entry, and add it to the cache.</p>
<p>A cache entry contains the position and normal for the point on the surface where the irradiance was evaluated as well as the irradiance value itself. One important additional piece of information that the cache requires is the range over which the entry is considered potentially valid. This range could be calculated in a number of ways, but the most common one is to use the <a href="http://en.wikipedia.org/wiki/Harmonic_mean">harmonic mean</a> of the hit distance of the rays used for the estimator. For n estimator samples, each with hit distance d, the harmonic mean is simply:</p>
<p><img src="http://latex.codecogs.com/gif.latex?H = \frac{N}{\sum_{1}^{N}{\frac{1}{d_i}}}" title="H = \frac{N}{\sum_{1}^{N}{\frac{1}{d_i}}}" /></p>
<p>Using the harmonic mean distance makes the cache entry distribution very dense in corners and crevices, and sparse in open spaces. This matches up very well with where the indirect irradiance is likely to be changing the fastest. To get an idea of how the cache entry distribution looks, here&#8217;s the scene above with the cache entry positions shown as red dots:</p>
<p><img class="aligncenter size-full wp-image-176" title="irradiancecachepoints" src="http://www.rorydriscoll.com/wp-content/uploads/2009/01/irradiancecachepoints.png" alt="irradiancecachepoints" width="656" height="396" /></p>
<p>Once you can add entries into the cache, you need to know how to find whether or not a particular cache entry can be used for interpolating the irradiance at a sample point. There are potentially quite a few ways that you can discard invalid cache entries depending on how fancy you want to get. For now, I&#8217;m using three simple tests.</p>
<p>Discard the entry if any of the following are true</p>
<ul>
<li>It is out of range of the sample point.</li>
<li>It has a normal that is too different than the sample normal.</li>
<li>It is in front of the sample point.</li>
</ul>
<p>Once you have a valid cache entry, you need to calculate a weight for that entry, then carry on looking for other entries that are potentially valid. As you come across each valid cache entry, you need to keep the sum of the weighted irradiance values, and the sum of the weights themselves. From these two sums, you can calculate the final interpolated irradiance:</p>
<p><img src="http://latex.codecogs.com/gif.latex?E = \frac{\sum_{1}^{N}{w_i E_i}}{\sum_{1}^{N}{w_i}}" title="E = \frac{\sum_{1}^{N}{w_i E_i}}{\sum_{1}^{N}{w_i}}" /></p>
<p>The weight for a particular cache entry is another part of the algorithm that can potentially be calculated in many different ways. For now, I&#8217;m using the weight that Ward proposes, but there&#8217;s some interesting information about the weights used at Dreamworks in <a href="http://www.graphics.cornell.edu/~jaroslav/papers/2008-irradiance_caching_class/10-EricSlides.pdf">this paper</a>. Here&#8217;s Ward&#8217;s initial weighting function:</p>
<p><img src="http://latex.codecogs.com/gif.latex?w_i = \frac{1}{\frac{\lvert \vec{P} - \vec{P_i} \rvert}{r_i} + \sqrt{1 + \vec{N} \cdot \vec{N_i}}}" title="w_i = \frac{1}{\frac{\lvert \vec{P} - \vec{P_i} \rvert}{r_i} + \sqrt{1 + \vec{N} \cdot \vec{N_i}}}" /></p>
<p>Note that you have to be a little bit wary of this function, since it is unbounded. When the sample point lies exactly at the same point as the cache entry then there you will get a divide by zero.</p>
<p>Typically, you would also discard cache entries that are below some weight threshold as specified by the user. This effectively scales the density of the cache entries and allows the user to make the trade off between speed and quality.</p>
<h2>Implementation</h2>
<p>I&#8217;ve made a very bare bones implementation of irradiance caching as outlined above. At the moment I&#8217;m not using a quad tree to store the cache entries, so each cache check requires iterating through an array of entries. Clearly this is a very slow way to process the cache entries, but for now it does a decent enough job to allow me to focus on the irradiance caching algorithm itself. Here are the results:</p>
<p><img class="aligncenter size-full wp-image-186" title="irradiancecacheindirectonly" src="http://www.rorydriscoll.com/wp-content/uploads/2009/01/irradiancecacheindirectonly.png" alt="irradiancecacheindirectonly" width="656" height="396" /></p>
<p>Not very impressive, or smooth, is it? I was hoping that the simple implementation I have made would provide better results than this, but apparently not. At the moment there&#8217;s one crucial improvement to the algorithm that my implementation is missing though &#8211; <a href="http://radsite.lbl.gov/radiance/papers/erw92/paper.pdf">Irradiance Gradients</a>. Irradiance Gradients basically give a better clue as to how to interpolate the irradiance cache entries, both positionally and rotationally. I&#8217;m hoping that they will significantly reduce the artefacts visible at the moment.</p>
<p>One problem that can occur when using an irradiance cache is that later cache entries don&#8217;t contribute to previously rendered pixels. When this happens, you can see blocky artefacts where the irradiance values have been interpolated differently. Something like this:</p>
<p><img class="aligncenter size-full wp-image-187" title="irradiancecachenopregather" src="http://www.rorydriscoll.com/wp-content/uploads/2009/01/irradiancecachenopregather.png" alt="irradiancecachenopregather" width="656" height="396" /></p>
<p>One thing you can do to avoid this situation is to perform an irradiance gathering pass before doing the final render. When you perform the final render, you should have no cache misses. In my case, I am using a progressive renderer, so the cache is actually fairly well primed before rendering the 1&#215;1 pixel size.</p>
<p><img class="aligncenter size-full wp-image-188" title="irradiancecacheprogressive" src="http://www.rorydriscoll.com/wp-content/uploads/2009/01/irradiancecacheprogressive.png" alt="irradiancecacheprogressive" width="656" height="396" /></p>
<h2>Improvements</h2>
<p>In addition to irradiance gradients, there have been a load of improvements made to irradiance caching since the inital paper. The <a href="http://www.graphics.cornell.edu/~jaroslav/papers/2008-irradiance_caching_class/index.htm">course notes </a>for the Siggraph 2008 course provide details of many of these. I&#8217;ll post up some screenshots when I&#8217;ve added the irradiance gradients.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.rorydriscoll.com/2009/01/18/irradiance-caching-part-1/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

