<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2026-03-17T11:51:27+00:00</updated><id>/feed.xml</id><title type="html">aldw</title><author><name>alden wu (goodtrailer)</name><email>me@aldw.net</email></author><entry><title type="html">CSE 168 Final Project</title><link href="/work/2024/06/11/cse168-hw5/" rel="alternate" type="text/html" title="CSE 168 Final Project" /><published>2024-06-11T00:00:00+00:00</published><updated>2024-06-11T00:00:00+00:00</updated><id>/work/2024/06/11/cse168-hw5</id><content type="html" xml:base="/work/2024/06/11/cse168-hw5/"><![CDATA[<h2 id="transmission">Transmission</h2>

<p>I implemented transmission using the GGX microfacet model<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>. The relevant command is <code class="language-plaintext highlighter-rouge">transmission</code>. Note that there is no specific <code class="language-plaintext highlighter-rouge">ior</code> command because the index of refraction is actually automatically determined from the <code class="language-plaintext highlighter-rouge">specular</code> value, according to Schlick’s approximation<sup id="fnref:2"><a href="#fn:2" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>. Essentially, the value given in <code class="language-plaintext highlighter-rouge">specular</code> is the color seen when directly looking at the object, which allows us to solve for the index of refraction <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>η</mi></mrow><annotation encoding="application/x-tex">\eta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">η</span></span></span></span>. For example, glass has the index of refraction <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mn>1</mn><mn>3</mn></mfrac><msub><mi>η</mi><mtext>glass</mtext></msub><mo>=</mo><mn>1.5</mn></mrow><annotation encoding="application/x-tex">\frac13\eta_\text{glass} = 1.5</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1901em;vertical-align:-0.345em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8451em;"><span style="top:-2.655em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">3</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.394em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">η</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord text mtight"><span class="mord mtight">glass</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1.5</span></span></span></span>. Using Schlick’s approximation, this roughly translates to a specular value <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>k</mi><mi>s</mi></msub><mo>=</mo><mn>0.04</mn></mrow><annotation encoding="application/x-tex">k_s = 0.04</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.0315em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">s</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">0.04</span></span></span></span>.</p>

<p>In order to conserve energy, I have the following setup: first calculate the amount of specular reflection (e.g. the Fresnel term <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>F</mi><mi>r</mi></msub></mrow><annotation encoding="application/x-tex">F_r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.13889em;">F</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.02778em;">r</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>), and then take <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mo>−</mo><msub><mi>F</mi><mi>r</mi></msub></mrow><annotation encoding="application/x-tex">1 - F_r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em;"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.13889em;">F</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.02778em;">r</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> to be the remaining amount. This is then distributed among diffusion and transmission. Therefore <code class="language-plaintext highlighter-rouge">diffuse</code> and <code class="language-plaintext highlighter-rouge">transmittance</code> actually represent the percentage of the <em>remaining</em> energy that is diffused/transmitted. As long as <code class="language-plaintext highlighter-rouge">diffuse + transmittance &lt;= 1</code> and <code class="language-plaintext highlighter-rouge">specular &lt;= 1</code>, energy does not increase. This setup is a little awkward, but it works well enough.</p>

<p>Also, I did not use Schlick’s approximation to compute <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>F</mi><mi>r</mi></msub></mrow><annotation encoding="application/x-tex">F_r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.13889em;">F</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.02778em;">r</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>, despite using it to approximate <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>η</mi></mrow><annotation encoding="application/x-tex">\eta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">η</span></span></span></span>. When it came time to actually compute <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>F</mi><mi>r</mi></msub></mrow><annotation encoding="application/x-tex">F_r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.13889em;">F</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.02778em;">r</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>, I instead went with the full formula described in the GGX paper<sup id="fnref:1:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>. Originally I had not planned to do so, but there is a reason for this: Schlick’s approximation does not account for total internal reflection (TIR). It <em>always</em> interpolates over the full interval <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>θ</mi><mo>∈</mo><mo stretchy="false">[</mo><mn>0</mn><mo separator="true">,</mo><mfrac><mi>π</mi><mn>2</mn></mfrac><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">\theta \in [0, \frac\pi2]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7335em;vertical-align:-0.0391em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.095em;vertical-align:-0.345em;"></span><span class="mopen">[</span><span class="mord">0</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6954em;"><span style="top:-2.655em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.394em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.03588em;">π</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose">]</span></span></span></span>, rather than maxing out at an earlier <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>θ</mi></mrow><annotation encoding="application/x-tex">\theta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">θ</span></span></span></span>. However, how TIR actually works is that we should see a smooth fade between the refraction and reflection terms. At the TIR critical angle, there should not be a sharp visual effect caused by suddenly discarding invalid rays, because we should have <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>F</mi><mi>r</mi></msub><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">F_r = 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.13889em;">F</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.02778em;">r</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span></span></span></span> before that point.</p>

<hr />

<h3 id="update-6122024">Update: 6/12/2024</h3>
<p><em>I realize this comes after the due date, so feel free to ignore the following section. I just thought the issue with Schlick’s approximation deserved a better explanation.</em></p>

<p>The easiest way to see how Schlick’s approximation does not handle TIR is that it does not distinguish between which index of refraction (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>η</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">\eta_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">η</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> or <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>η</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">\eta_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">η</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>) corresponds to the entered/exited medium. Schlick’s approximation is symmetric in that respect. However, the exact formula is <em>not</em> symmetric. Let <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>η</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">\eta_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">η</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> be the entered medium and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>η</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">\eta_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">η</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> be the exited medium. Schlick’s approximation is only accurate when <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>η</mi><mn>1</mn></msub><mo>&gt;</mo><msub><mi>η</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">\eta_1 &gt; \eta_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7335em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">η</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">η</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>.</p>

<p><a name="fresnel-1.5-1"></a>
<img src="/assets/2024-06-11-cse168-hw5/fresnel-1.5-1.png" alt="" class="figure" /></p>

<p><a name="fresnel-1-1.5"></a>
<img src="/assets/2024-06-11-cse168-hw5/fresnel-1-1.5.png" alt="" class="figure" />
<em><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>F</mi><mtext>Schlick</mtext></msub></mrow><annotation encoding="application/x-tex">F_\text{Schlick}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.13889em;">F</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord text mtight"><span class="mord mtight">Schlick</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> vs. <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>F</mi><mtext>exact</mtext></msub></mrow><annotation encoding="application/x-tex">F_\text{exact}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.13889em;">F</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em;"><span style="top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord text mtight"><span class="mord mtight">exact</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>: Red is Schlick’s approximation, and black is the exact formula. <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>η</mi><mn>1</mn></msub></mrow><annotation encoding="application/x-tex">\eta_1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">η</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>η</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">\eta_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">η</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> are chosen to represent glass and air. Technically, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>η</mi><mtext>air</mtext></msub><mo>=</mo><mn>1.0003</mn></mrow><annotation encoding="application/x-tex">\eta_\text{air} = 1.0003</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">η</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3175em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord text mtight"><span class="mord mtight">air</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1.0003</span></span></span></span>, but I have chosen <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mover accent="true"><mi>η</mi><mo>^</mo></mover><mtext>air</mtext></msub><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\hat\eta_\text{air} = 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">η</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1944em;"><span class="mord">^</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3175em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord text mtight"><span class="mord mtight">air</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span></span></span></span> for my program.</em></p>

<hr />

<p>Anyway, enough of the discussion on implementation. I think the following image best illustrates my implementation of transmission. I repurposed the GGX test scene from Homework 4.</p>

<p><a name="ggx-mis-256spp"></a>
<img src="/assets/2024-06-11-cse168-hw5/ggx-mis-256spp.png" alt="" class="figure" />
<em>GGX spheres and planes: The sphere on the left has <code class="language-plaintext highlighter-rouge">specular 0.04 0.04 0.04</code>, the plane on the middle-left has <code class="language-plaintext highlighter-rouge">roughness 0.3</code>, the plane on the middle-right is experiencing TIR (the black color is due to there being no sky), and the plane on the right has <code class="language-plaintext highlighter-rouge">specular 0.00001 0.00001 0.00001</code> and is almost fully transparent. This scene was rendered with MIS at 2048spp.</em></p>

<p>Note that the planes in the above image are bending light, which doesn’t make much physical sense (refraction should be barely noticeable for thin mediums). The program checks <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi mathvariant="normal">s</mi><mi mathvariant="normal">i</mi><mi mathvariant="normal">g</mi><mi mathvariant="normal">n</mi></mrow><mo stretchy="false">(</mo><msub><mi>ω</mi><mi>i</mi></msub><mo>⋅</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\mathrm{sign}(\omega_i \cdot n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathrm">sign</span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">ω</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span> to determine if light is entering/exiting a medium. If incoming light is aligned with the normal, it is treated as entering a medium. And if incoming light is not aligned with the normal, then it is treated as exiting a medium. So in order to be physically accurate, all objects must have front and back facing geometry. For demonstration purposes, these planes do not, which is why the refraction is noticeable. The plane on the middle-left is facing towards the camera (the camera’s perspective is from the “exterior”), while the plane on the middle-right is facing away from the camera (the camera’s perspective is from the “interior,” which is why we see TIR).</p>

<h2 id="depth-of-field">Depth of Field</h2>

<p>Depth of field is quite simple to achieve. With path tracing, it is essentially a free feature. All that needs to be done is for the ray origins to be perturbed by some small amount while fixing points at a specific distance, called the “subject distance” (okay, it’s slightly more complicated than that, but it really is quite simple). The relevant commands here are <code class="language-plaintext highlighter-rouge">aperture</code> and <code class="language-plaintext highlighter-rouge">subjectdistance</code>. In photography, aperture is often described with an “f-number,” but the <code class="language-plaintext highlighter-rouge">aperture</code> command instead refers to relative aperture. Relative aperture is simply the reciprocal of the f-number (e.g. <code class="language-plaintext highlighter-rouge">aperture 0.125</code> is equivalent to <em>f</em>/8).</p>

<p><a name="ggx-mis-256spp-dof"></a>
<img src="/assets/2024-06-11-cse168-hw5/ggx-mis-256spp-dof.png" alt="" class="figure" />
<em>GGX spheres and planes: Same as <a href="#ggx-mis-256spp">above</a>, except with <code class="language-plaintext highlighter-rouge">subjectdistance 13</code> and <code class="language-plaintext highlighter-rouge">aperture 3</code>. Note that the DOF effect occurs for the “interior” of the glass ball (e.g. everywhere but the edges). This is because the light coming through the ball is coming from far away, past the range of clarity.</em></p>

<p><a name="cornell-mis-2048spp-dof"></a>
<img src="/assets/2024-06-11-cse168-hw5/cornell-mis-2048spp-dof.png" alt="" class="figure" />
<em>Cornell box: Another simple demonstration of the DOF effect. <code class="language-plaintext highlighter-rouge">subjectdistance 2.75</code>, <code class="language-plaintext highlighter-rouge">aperture 0.7</code>. I think I may have set the aperture too high, because it was a little hard to get the sphere in focus. To be fair, it’s a little difficult to edit a scene through a text file.</em></p>

<h2 id="photon-mapping">Photon Mapping</h2>

<p>Finally, I attempted to implement photon mapping<sup id="fnref:3"><a href="#fn:3" class="footnote" rel="footnote" role="doc-noteref">3</a></sup>. However, I decided against fully committing to photon mapping: instead, I only created the caustics photon map. This is because I felt that global illumination was already done quite well by NEE/MIS. On the other hand, caustics look truly horrifying without thousands of samples.</p>

<p>In my implementation, using the photon map is a little cumbersome. Since photon maps can be reused, there is a separate command <code class="language-plaintext highlighter-rouge">sampler photon</code> and <code class="language-plaintext highlighter-rouge">shader herophoton</code> (it’s called <code class="language-plaintext highlighter-rouge">herophoton</code> because the actual shader is called <code class="language-plaintext highlighter-rouge">hero</code>). This makes the next render output a photon map (<code class="language-plaintext highlighter-rouge">.photons</code> file) instead of an image. To set the number of photons, we reuse the <code class="language-plaintext highlighter-rouge">size &lt;x&gt; &lt;y&gt;</code> command, where <code class="language-plaintext highlighter-rouge">x * y</code> photons are shot out (it doesn’t really matter that it’s a 2d size parameter; it’s just a relic from reusing commands). Afterwards, we have to switch back to <code class="language-plaintext highlighter-rouge">sampler basic</code> and <code class="language-plaintext highlighter-rouge">shader hero</code> to render the scene. By default it will ignore the photon map. To make it load/use the photon map, we need to use <code class="language-plaintext highlighter-rouge">photonmap &lt;radius&gt; &lt;count&gt;</code> with nonzero values. <code class="language-plaintext highlighter-rouge">&lt;radius&gt;</code> controls the maximum search radius for photons, and <code class="language-plaintext highlighter-rouge">&lt;count&gt;</code> controls the maximum number of photons to count.</p>

<p>The photons are stored in a <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span></span></span></span>-d tree written from scratch, since it’s honestly easier to write one yourself than to take some open-source library. Most online C++ implementations seem to use a linked-node approach, which is really annoying to copy to device memory. My <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span></span></span></span>-d tree was flat, which is also more performant. Here are some preliminary results showing the locations of photons in the scene:</p>

<p><a name="cornell-photons"></a>
<img src="/assets/2024-06-11-cse168-hw5/cornell-photons.png" alt="" class="figure" />
<em>Cornell box: The locations/counts of photons in the scene, with 1,000,000 photons. Keep in mind that there are far fewer than 1,000,000 photons here because the vast majority of them were not for caustics and were therefore discarded.</em></p>

<p>Finally, here is an actual render using the photon map:</p>

<p><a name="cornell-1000000photon-mis-64spp"></a>
<img src="/assets/2024-06-11-cse168-hw5/cornell-1000000photons-mis-64spp.png" alt="" class="figure" />
<em>Cornell box: Again, 1,000,000 photons with <code class="language-plaintext highlighter-rouge">photonmap 0.1 256</code>, <code class="language-plaintext highlighter-rouge">nexteventestimation mis</code>, <code class="language-plaintext highlighter-rouge">spp 64</code>. The caustics do look quite clean despite a low number of samples.</em></p>

<p>And here is the ground truth, rendered with brute force (MIS and 2000+ samples per pixel):</p>

<p><a name="cornell-mis-2048spp"></a>
<img src="/assets/2024-06-11-cse168-hw5/cornell-mis-2048spp.png" alt="" class="figure" />
<em>Cornell box: Ground truth. <code class="language-plaintext highlighter-rouge">nexteventestimation mis</code>, <code class="language-plaintext highlighter-rouge">spp 2048</code>.</em></p>

<p>The caustics do look comparatively clean, but they are evidently wrong. I haven’t quite figured out why yet. It’s a bit sad, but at least it wasn’t a complete failure. :/</p>

<p>My best guess is that I have calculated the power of the photons incorrectly, e.g. in BSDF evaluation. I say this because the <em>shape</em> of the caustics actually match up very closely between the photon-mapped image and the ground truth. My other reason for thinking my BSDF evaluation could be wrong is that when viewing the raw photon count (e.g. in the <a href="#cornell-photons">first image</a>), we do see a higher concentration of photons at the center of the caustic. This indicates to me that my importance sampling is working correctly. I think the lack of a bright spot despite the much higher concentration of photons means that these photons are severely under-powered.</p>

<h2 id="references">References</h2>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>Bruce Walter, Stephen R Marschner, Hongsong Li, and Kenneth E Torrance. 2007. Microfacet Models for Refraction through Rough Surfaces. In <em>Proceedings of the 18th Eurographics conference on Rendering Techniques</em>. 195–206. <a href="https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.html">https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.html</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a> <a href="#fnref:1:1" class="reversefootnote" role="doc-backlink">&#8617;<sup>2</sup></a></p>
    </li>
    <li id="fn:2">
      <p><a href="https://en.wikipedia.org/wiki/Schlick's_approximation">https://en.wikipedia.org/wiki/Schlick’s_approximation</a> <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3">
      <p>Henrik Wann Jensen. 2001. Realistic Image Synthesis Using Photon Mapping. <em>A. K. Peters, Ltd., USA</em>. <a href="http://graphics.ucsd.edu/~henrik/papers/book/">http://graphics.ucsd.edu/~henrik/papers/book/</a>. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>alden wu (goodtrailer)</name><email>me@aldw.net</email></author><category term="work" /><summary type="html"><![CDATA[Transmission]]></summary></entry><entry><title type="html">CSE 168 Final Project Milestone</title><link href="/work/2024/05/29/cse168-hw5-ms/" rel="alternate" type="text/html" title="CSE 168 Final Project Milestone" /><published>2024-05-29T00:00:00+00:00</published><updated>2024-05-29T00:00:00+00:00</updated><id>/work/2024/05/29/cse168-hw5-ms</id><content type="html" xml:base="/work/2024/05/29/cse168-hw5-ms/"><![CDATA[<h2 id="transmission">Transmission</h2>

<p>I have (almost) implemented microfacet transmission using the GGX distribution model<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup> with BSDF importance sampling. The relevant commands are <code class="language-plaintext highlighter-rouge">transmittance &lt;r&gt; &lt;g&gt; &lt;b&gt;</code> and <code class="language-plaintext highlighter-rouge">ior &lt;x&gt;</code>. The transmission seems to be working correctly with roughness, and refraction <em>kind of</em> works, as illustrated by the following demo image (modified from the test GGX scene file for Homework 4).</p>

<p><a name="ggx-good"></a>
<img src="/assets/2024-05-29-cse168-hw5-ms/ggx-good.png" alt="" class="figure" />
<em>GGX spheres and planes: The sphere on the left has <code class="language-plaintext highlighter-rouge">ior 1</code> and <code class="language-plaintext highlighter-rouge">roughness 0.25</code>, and seems to be working okay (at least it passes the test by casual eye). The plane in the middle has <code class="language-plaintext highlighter-rouge">ior 1.2</code> and <code class="language-plaintext highlighter-rouge">roughness 1</code>, but it’s weirdly bright. Finally, the plane on the right has <code class="language-plaintext highlighter-rouge">ior 1</code> and <code class="language-plaintext highlighter-rouge">roughness 0.001</code>, and it seems fine.</em></p>

<p>Unforunately, “<em>kind of</em> works” is doing some heavy-lifting here.</p>

<p><img src="/assets/2024-05-29-cse168-hw5-ms/ggx-bad.png" alt="" class="figure" />
<em>GGX spheres and planes: Same as before, but all with <code class="language-plaintext highlighter-rouge">ior 1.5</code>. Evidently, not all is right. No clue what’s going on with the sphere on the left.</em></p>

<p>However, I would like to note that all of these effects were attempted <em>without</em> photon mapping. I initially wanted to experiment with extending previous techniques to transmission, just to see how feasible it would be. As such, the NEE is completely wrong, and even wholly transparent objects will cast shadows.</p>

<p><img src="/assets/2024-05-29-cse168-hw5-ms/ggx-nee-on.png" alt="" class="figure" />
<em>GGX spheres and planes: Additional plane on the right with <code class="language-plaintext highlighter-rouge">specular 0 0 0</code>, <code class="language-plaintext highlighter-rouge">transmittance 1 1 1</code>, <code class="language-plaintext highlighter-rouge">roughness 0.001</code>, and <code class="language-plaintext highlighter-rouge">ior 1</code>. Spooky shadow…</em></p>

<p>Turning off NEE and increasing the number of samples, we do get something “decent.” The spooky shadows are gone, and the purple translucent plane now casts a purple shadow. So everything seems mostly correct. Refraction still seems mostly broken, though.</p>

<p><img src="/assets/2024-05-29-cse168-hw5-ms/ggx-nee-off.png" alt="" class="figure" />
<em>GGX sphers and planes: Same as before, but with <code class="language-plaintext highlighter-rouge">nee off</code> and <code class="language-plaintext highlighter-rouge">spp 2048</code>.</em></p>

<h3 id="ℹ️-update-5312024">ℹ️ Update: 5/31/2024</h3>

<p>I got refraction working :) Again, without NEE.</p>

<p><img src="/assets/2024-05-29-cse168-hw5-ms/ggx-refraction.png" alt="" class="figure" />
<em>GGX spheres and planes: Yay! The ball on the left is supposed to replicate glass, with <code class="language-plaintext highlighter-rouge">ior 1.5</code>. It accurately flips the image behind it. Note that the refraction here doesn’t make much sense. Planes are treated as faces of some larger object. So rays hitting the planes are treated as entering some object, but since there’s no back face, they just… stay refracted. To model a thin piece of glass, we should place backwards facing planes behind each of the planes. But then the refraction effect would be almost unnoticeable, so that is not done here for the sake of demonstration.</em></p>

<!---

Unfortunately, I came to the sad realization that the Fresnel reflection term $$F_r$$ given in Homework 4 is not # adequate for transmission. $$F_r$$ is dependent on the index of refraction $$\eta$$. In Schlick's approximation[^2], $$F_r$$ # has a term $$R_0$$ that essentially represents the "specular color" (when looking head on, this is the reflected color), # and $$R_0$$ depends on $$\eta$$. But in Homework 4, we set $$R_0 \coloneqq k_s$$, the "specular color." So what's really # going on is we're implicitly setting $$\eta$$ to a large value, such that $$R_0 = k_s$$. This means our $$\eta$$ has been # directly tied to $$s$$ the whole time (without me realizing it). This is fine without transmission, since we don't care # about $$\eta$$ anyway. But $$\eta$$ does matter when implementing transmission. If we want $$k_s$$ to have color components, # then $$\eta$$ must as well. And if we want $$\eta$$ to not have color components, then $$k_s$$ must not as well (which... # would be terrible).

As for why $$F_r$$ sucks in the first place, it's because the Fresnel reflection doesn't kick in quickly enough before # hitting total internal reflection (TIR). Ideally, we'd want the reflection to slowly fade in while the transmission # slowly fades out. This is how TIR *should* behave. But since our $$F_r$$ isn't dependent on $$\eta$$, this doesn't happen # at all. Instead, we see jarring cutoffs where TIR is occurring.

![](/assets/2024-05-29-cse168-hw5-ms/ggx-bad-fresnel.png){:.figure}
*GGX spheres and planes: The plane in the center has `ior 1.5`. Observe the giant black cutoff. Worst of all, since # $$F_r$$ is too small, there's not even a reflection. It just appears black.*

So our $$F_r$$ sucks, and improving it seems to require a color dependent $$\eta$$. Hopefully I find an easy hack to model # $$F_r$$ without a color dependent $$\eta$$. Originally I wasn't planning on modeling color dependent refraction, but # dispersion is a pretty cool effect, so maybe I will. I'm not sure how feasible it is to model with just RGB, though. # PBRT uses spectral rendering[^3], but I wonder if that's too much work.

-->

<h2 id="photon-mapping">Photon Mapping</h2>

<p>Turning off NEE and relying on brute force is pretty impractical, so I imagine this is where photon mapping comes into play. I’ve only just started reading into it, though, so I’m still not totally sure. Hence I have nothing to show related to photon mapping right now.</p>

<h2 id="references">References</h2>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>Bruce Walter, Stephen R Marschner, Hongsong Li, and Kenneth E Torrance. 2007. Microfacet models for refraction through rough surfaces. In <em>Proceedings of the 18th Eurographics conference on Rendering Techniques</em>. 195–206. <a href="https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.html">https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.html</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>alden wu (goodtrailer)</name><email>me@aldw.net</email></author><category term="work" /><summary type="html"><![CDATA[Transmission]]></summary></entry><entry><title type="html">CSE 168 Homework 1</title><link href="/work/2024/04/15/cse168-hw1/" rel="alternate" type="text/html" title="CSE 168 Homework 1" /><published>2024-04-15T00:00:00+00:00</published><updated>2024-04-15T00:00:00+00:00</updated><id>/work/2024/04/15/cse168-hw1</id><content type="html" xml:base="/work/2024/04/15/cse168-hw1/"><![CDATA[<h2 id="optix-and-acceleration-structure">OptiX and Acceleration Structure</h2>

<p>I implemented OptiX support, which massively reduced render times. Instead of using the provided OptiX 6.5 template, I set it up from scratch with a newer version (OptiX 8.0) and integrated it into my existing submission for CSE 167. However, the ray tracer still uses the CPU backend by default. To enable OptiX, the command <code class="language-plaintext highlighter-rouge">backend optix</code> must be included in the scene file. The default is <code class="language-plaintext highlighter-rouge">backend cpu</code>.</p>

<p>In my existing CPU implementation, I implemented a Bounding Volume Hierarchy along the lines of Pharr, Jakob, and Humphreys’s description in <em>Physically Based Rendering 4e</em>, alongside an efficient intersection algorithm for axis-aligned bounding volumes given by Ericson’s <em>Real-Time Collision Detection</em>. To disable the BVH, the command <code class="language-plaintext highlighter-rouge">accelerator naive</code> must be included in the scene file. The default is <code class="language-plaintext highlighter-rouge">accelerator bvh</code>.</p>

<p>A comparison of render times can be found below. The program was compiled with MSVC using <code class="language-plaintext highlighter-rouge">/O2</code> optimization and <code class="language-plaintext highlighter-rouge">/openmp</code> paralellization, and it was run on my laptop’s Intel i7-12700H and NVIDIA RTX 3060.</p>

<table class="figure">
  <thead>
    <tr>
      <th style="text-align: center">Scene</th>
      <th style="text-align: center">CPU w/o BVH</th>
      <th style="text-align: center">CPU w/ BVH</th>
      <th style="text-align: center">GPU w/ OptiX</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">scene4-ambient</td>
      <td style="text-align: center">0.053s</td>
      <td style="text-align: center">0.037s</td>
      <td style="text-align: center">0.003s</td>
    </tr>
    <tr>
      <td style="text-align: center">scene4-diffuse</td>
      <td style="text-align: center">0.051s</td>
      <td style="text-align: center">0.038s</td>
      <td style="text-align: center">0.003s</td>
    </tr>
    <tr>
      <td style="text-align: center">scene4-emission</td>
      <td style="text-align: center">0.057s</td>
      <td style="text-align: center">0.041s</td>
      <td style="text-align: center">0.003s</td>
    </tr>
    <tr>
      <td style="text-align: center">secen4-specular</td>
      <td style="text-align: center">0.083s</td>
      <td style="text-align: center">0.053s</td>
      <td style="text-align: center">0.003s</td>
    </tr>
    <tr>
      <td style="text-align: center">scene5</td>
      <td style="text-align: center">1.879s</td>
      <td style="text-align: center">0.050s</td>
      <td style="text-align: center">0.004s</td>
    </tr>
    <tr>
      <td style="text-align: center">scene6</td>
      <td style="text-align: center">0.094s</td>
      <td style="text-align: center">0.163s</td>
      <td style="text-align: center">0.004s</td>
    </tr>
    <tr>
      <td style="text-align: center">scene7</td>
      <td style="text-align: center">93.41s</td>
      <td style="text-align: center">0.133s</td>
      <td style="text-align: center">0.003s</td>
    </tr>
    <tr>
      <td style="text-align: center"><a href="#scene8">scene8</a></td>
      <td style="text-align: center">N/A</td>
      <td style="text-align: center">59.27s</td>
      <td style="text-align: center">0.519s</td>
    </tr>
    <tr>
      <td style="text-align: center"><a href="#scene9">scene9</a></td>
      <td style="text-align: center">N/A</td>
      <td style="text-align: center">17.09s</td>
      <td style="text-align: center">0.143s</td>
    </tr>
    <tr>
      <td style="text-align: center"><a href="#scene10">scene10</a></td>
      <td style="text-align: center">N/A</td>
      <td style="text-align: center">104.5s</td>
      <td style="text-align: center">0.437s</td>
    </tr>
  </tbody>
</table>

<p class="figcaption"><em>Scene render time comparisons between CPU without BVH, CPU with BVH, and GPU with OptiX. Note that scenes 8–10 are too complex to render without acceleration in a reasonable amount of time, so the times are left as N/A. <a href="https://raviucsdgroup.s3.amazonaws.com/homework1/6102a1a992205cdaa41cb91a62662014/20240415122630/index.html">CSE 168 image-grader report.</a></em></p>

<p>Using the BVH and OptiX, I was able to render much more computationally taxing scenes (scenes 8–10), such as the one below of a reflective Stanford Dragon in a highly reflective box.</p>

<p><a name="scene9"></a>
<img src="/assets/2024-04-15-cse168-hw1/scene9.png" alt="" class="figure" />
<em>Scene 9, Stanford Dragon: <code class="language-plaintext highlighter-rouge">shininess 70</code> <code class="language-plaintext highlighter-rouge">specular .7 .7 .7</code>, 3840x2160</em></p>

<h2 id="transmission">Transmission</h2>
<p>I added <code class="language-plaintext highlighter-rouge">transmittance</code> and <code class="language-plaintext highlighter-rouge">ior</code> (index of refraction) commands. The effect is decently realistic. For example, the following images demonstrate how a transparent sphere warps light at different refractive indices. Notably, a glass ball (<code class="language-plaintext highlighter-rouge">ior 1.5</code>) seems to turn the image upside down, which is physically accurate.</p>

<p>As an aside, I would like to note that while my submission in CSE 167 also included transmission, I have since improved upon it by handling total internal reflection. Interestingly, this <em>significantly</em> improved render times in certain cases. Normally, one ray becomes two rays: one reflected and one transmitted. But under total internal reflection, we only need the reflected ray. So one ray becomes one ray, which lessens the exponential explosion.</p>

<p><a name="scene8"></a>
<img src="/assets/2024-04-15-cse168-hw1/scene8-ior1.015.png" alt="" class="figure" />
<em>Scene 8, Refractive Sphere: <code class="language-plaintext highlighter-rouge">ior 1.015</code>, 3840x2880</em></p>

<p><img src="/assets/2024-04-15-cse168-hw1/scene8-ior1.5.png" alt="" class="figure" />
<em>Scene 8, Refractive Sphere: <code class="language-plaintext highlighter-rouge">ior 1.5</code>, 3840x2880</em></p>

<p>Just to experiment, I also rendered a few scenes of the Stanford dragon with varying refractive indices and transmittance.</p>

<p><a name="scene10"></a>
<img src="/assets/2024-04-15-cse168-hw1/scene10-ior1.015.png" alt="" class="figure" />
<em>Scene 10, Stanford Dragon: <code class="language-plaintext highlighter-rouge">ior 1.015</code> <code class="language-plaintext highlighter-rouge">transmittance .37 .74 .47</code>, 3840x2160</em></p>

<p><img src="/assets/2024-04-15-cse168-hw1/scene10-ior1.5.png" alt="" class="figure" />
<em>Scene 10, Stanford Dragon: <code class="language-plaintext highlighter-rouge">ior 1.5</code> <code class="language-plaintext highlighter-rouge">transmittance .37 .74 .47</code>, 3840x2160</em></p>

<p><img src="/assets/2024-04-15-cse168-hw1/scene10-ior1.5-clear.png" alt="" class="figure" />
<em>Scene 10, Stanford Dragon: <code class="language-plaintext highlighter-rouge">ior 1.5</code> <code class="language-plaintext highlighter-rouge">transmittance .95 .95 .95</code>, 3840x2160</em></p>

<p>It’s interesting how chaotic/noisy the final image is, as the refractive index is decently high and the model is quite complex/layered. There are also some dark spots near the tail caused by <code class="language-plaintext highlighter-rouge">maxdepth</code> being too low, but increasing <code class="language-plaintext highlighter-rouge">maxdepth</code> exponentially increases render times. The images above are already rendered with <code class="language-plaintext highlighter-rouge">maxdepth 9</code>.</p>

<h2 id="anti-aliasing">Anti-aliasing</h2>
<p>I added a <code class="language-plaintext highlighter-rouge">sampler</code> command for scene files with two available options: <code class="language-plaintext highlighter-rouge">sampler basic</code> and <code class="language-plaintext highlighter-rouge">sampler rgss</code>. With <code class="language-plaintext highlighter-rouge">sampler rgss</code>, the program uses Rotated Grid Supersampling (RGSS), which involves shooting 4 rays per pixel in a rotated square, which (loosely) creates a rotated grid. The grid is not perfect, but that could be viewed as a positive. Perfect grids can lead to visual artifacts when certain patterns align with the grid.</p>

<p>All of the images above have been rendered with <code class="language-plaintext highlighter-rouge">sampler basic</code> (no anti-aliasing), so below is a comparison between <code class="language-plaintext highlighter-rouge">sampler basic</code> and <code class="language-plaintext highlighter-rouge">sampler rgss</code> using scene 5. The top half of the renders are excluded because they are simply black.</p>

<p><img src="/assets/2024-04-15-cse168-hw1/scene5-comparison.png" alt="" class="figure" />
<em>Scene 5: <code class="language-plaintext highlighter-rouge">sampler basic</code> above, <code class="language-plaintext highlighter-rouge">sampler rgss</code> below, 640x480</em></p>

<h2 id="gamma-correction">Gamma Correction</h2>
<p>I also added a <code class="language-plaintext highlighter-rouge">colorspace</code> command for scene files which allows input/output colors to be in either linear (default) or sRGB. The original assignment required <em>not</em> performing gamma correction. However, PNGs assume sRGB values, which leads to linear outputs looking harsh/wrong. But since the scenes used values based on those linear outputs, simply adding <code class="language-plaintext highlighter-rouge">colorspace output srgb</code> without tweaking the values  looked quite odd, as shown below. Note that scenes 8–10 were configured with <code class="language-plaintext highlighter-rouge">colorspace input srgb</code> and <code class="language-plaintext highlighter-rouge">colorspace output srgb</code>.</p>

<p><img src="/assets/2024-04-15-cse168-hw1/scene6-linear-linear.png" alt="" class="figure" />
<em>Scene 6: <code class="language-plaintext highlighter-rouge">colorspace input linear</code> <code class="language-plaintext highlighter-rouge">colorspace output linear</code>, 3840x2880</em></p>

<p><img src="/assets/2024-04-15-cse168-hw1/scene6-linear-srgb.png" alt="" class="figure" />
<em>Scene 6: <code class="language-plaintext highlighter-rouge">colorspace input linear</code> <code class="language-plaintext highlighter-rouge">colorspace output srgb</code>, 3840x2880</em></p>

<p><img src="/assets/2024-04-15-cse168-hw1/scene6-srgb-srgb.png" alt="" class="figure" />
<em>Scene 6: <code class="language-plaintext highlighter-rouge">colorspace input srgb</code> <code class="language-plaintext highlighter-rouge">colorspace output srgb</code>, 3840x2880</em></p>]]></content><author><name>alden wu (goodtrailer)</name><email>me@aldw.net</email></author><category term="work" /><summary type="html"><![CDATA[OptiX and Acceleration Structure]]></summary></entry><entry><title type="html">CSE 167 Homework 4</title><link href="/work/2024/03/19/cse167-hw4/" rel="alternate" type="text/html" title="CSE 167 Homework 4" /><published>2024-03-19T00:00:00+00:00</published><updated>2024-03-19T00:00:00+00:00</updated><id>/work/2024/03/19/cse167-hw4</id><content type="html" xml:base="/work/2024/03/19/cse167-hw4/"><![CDATA[<h2 id="acceleration-structure">Acceleration Structure</h2>
<p>I implemented a Bounding Volume Hierarchy along the lines of Pharr, Jakob, and Humphreys’s description in <em>Physically Based Rendering 4e</em>, alongside an efficient intersection algorithm for axis-aligned bounding volumes given by Ericson’s <em>Real-Time Collision Detection</em>.</p>

<p>A comparison of render times can be found below. The program was compiled with MSVC using <code class="language-plaintext highlighter-rouge">/O2</code> optimization and <code class="language-plaintext highlighter-rouge">/openmp</code> paralellization, and it was run on my laptop’s Intel i7-12700H.</p>

<table class="figure">
  <thead>
    <tr>
      <th style="text-align: center">Scene</th>
      <th style="text-align: center">With BVH</th>
      <th style="text-align: center">No BVH</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">scene4-ambient</td>
      <td style="text-align: center">0.1s</td>
      <td style="text-align: center">0.0s</td>
    </tr>
    <tr>
      <td style="text-align: center">scene4-diffuse</td>
      <td style="text-align: center">0.1s</td>
      <td style="text-align: center">0.1s</td>
    </tr>
    <tr>
      <td style="text-align: center">scene4-emission</td>
      <td style="text-align: center">0.1s</td>
      <td style="text-align: center">0.1s</td>
    </tr>
    <tr>
      <td style="text-align: center">secen4-specular</td>
      <td style="text-align: center">0.1s</td>
      <td style="text-align: center">0.1s</td>
    </tr>
    <tr>
      <td style="text-align: center">scene5</td>
      <td style="text-align: center">0.1s</td>
      <td style="text-align: center">1.8s</td>
    </tr>
    <tr>
      <td style="text-align: center">scene6</td>
      <td style="text-align: center">0.1s</td>
      <td style="text-align: center">0.1s</td>
    </tr>
    <tr>
      <td style="text-align: center">scene7</td>
      <td style="text-align: center">0.4s</td>
      <td style="text-align: center">323.3s</td>
    </tr>
  </tbody>
</table>

<p class="figcaption"><em>Scene render times with BVH vs. without BVH. Notice how using the BVH was actually slower for very simple scenes, since tree traversal is slower than naive <code class="language-plaintext highlighter-rouge">for</code>-loops over few elements.</em></p>

<p><a href="https://raviucsdgroup.s3.amazonaws.com/hw3/6102a1a992205cdaa41cb91a62662014/20240318025251/index.html">CSE 167 image-grader report.</a> (The high accuracy of 0-10 hot pixels was achieved using precise intersection algorithms described by Wachter, Binder (2019) and Haines et al. (2019), both of which are found in <em>Ray Tracing Gems</em>)</p>

<p>Using the BVH, I was able to render the following scene of a reflective Stanford dragon in a highly reflective box, just for fun.</p>

<p><img src="/assets/2024-03-19-cse167-hw4/scene9.png" alt="" class="figure" />
<em>Stanford Dragon: <code class="language-plaintext highlighter-rouge">shininess 70</code> <code class="language-plaintext highlighter-rouge">specular .7 .7 .7</code>, 3840x2160, 2m06s</em></p>

<h2 id="transmission">Transmission</h2>
<p>I added <code class="language-plaintext highlighter-rouge">transmittance</code> and <code class="language-plaintext highlighter-rouge">ior</code> (index of refraction) commands. The implementation is imperfect, as there are some effects missing (e.g. total internal reflection, intersections, shadows etc.). However, the effect is still decently realistic. For example, the following images demonstrate how a transparent sphere warps light at different refractive indices. Notably, a glass ball (<code class="language-plaintext highlighter-rouge">ior 1.5</code>) seems to turn the image upside down, which is physically accurate.</p>

<p><img src="/assets/2024-03-19-cse167-hw4/scene8-ior1.015.png" alt="" class="figure" />
<em>Sphere: <code class="language-plaintext highlighter-rouge">ior 1.015</code>, 1920x1440, 0m15s</em></p>

<p><img src="/assets/2024-03-19-cse167-hw4/scene8-ior1.5.png" alt="" class="figure" />
<em>Sphere: <code class="language-plaintext highlighter-rouge">ior 1.5</code>, 1920x1440, 0m15s</em></p>

<p>Just to experiment, I also rendered a few scenes of the Stanford dragon with varying refractive indices and transmittance. These scenes were pretty taxing, and I actually had to implement an <a href="https://www.desmos.com/calculator/yg9q8cruat">approximate squareroot formula</a> to significantly increase performance. Though <code class="language-plaintext highlighter-rouge">std::sqrt</code> out-performed my approximation in standalone tests (modern CPUs have a built-in squareroot unit), it drastically slowed down under high volume. I’m not entirely sure why, though.</p>

<p><img src="/assets/2024-03-19-cse167-hw4/scene10-ior1.015.png" alt="" class="figure" />
<em>Stanford Dragon: <code class="language-plaintext highlighter-rouge">ior 1.015</code> <code class="language-plaintext highlighter-rouge">transmittance .37 .74 .47</code>, 3840x2160, 16m32s</em></p>

<p><img src="/assets/2024-03-19-cse167-hw4/scene10-ior1.5.png" alt="" class="figure" />
<em>Stanford Dragon: <code class="language-plaintext highlighter-rouge">ior 1.5</code> <code class="language-plaintext highlighter-rouge">transmittance .37 .74 .47</code>, 3840x2160, 26m59s</em></p>

<p><img src="/assets/2024-03-19-cse167-hw4/scene10-ior1.5-clear.png" alt="" class="figure" />
<em>Stanford Dragon: <code class="language-plaintext highlighter-rouge">ior 1.5</code> <code class="language-plaintext highlighter-rouge">transmittance .95 .95 .95</code>, 3840x2160, 26m32s</em></p>

<p>It’s interesting how chaotic/noisy the final image is, as the refractive index is decently high and the model is quite complex/layered. There are also some dark spots near the tail caused by <code class="language-plaintext highlighter-rouge">maxdepth</code> being too low, but increasing <code class="language-plaintext highlighter-rouge">maxdepth</code> exponentially increases render times.</p>

<h2 id="gamma-correction">Gamma Correction</h2>
<p>I also added a <code class="language-plaintext highlighter-rouge">colorspace</code> command for scene files which allows input/output colors to be in either linear (default) or sRGB. The original assignment required <em>not</em> performing gamma correction. However, PNGs assume sRGB values, which leads to linear outputs looking harsh/wrong. But since the scenes used values based on those linear outputs, simply adding <code class="language-plaintext highlighter-rouge">colorspace output srgb</code> without tweaking the values  looked quite odd, as shown below. Note that all of the previously shown images were rendered with <code class="language-plaintext highlighter-rouge">colorspace output srgb</code>.</p>

<p><img src="/assets/2024-03-19-cse167-hw4/scene6-linear-linear.png" alt="" class="figure" />
<em>Scene 6: <code class="language-plaintext highlighter-rouge">colorspace input linear</code> <code class="language-plaintext highlighter-rouge">colorspace output linear</code>, 1920x1440, 0m01s</em></p>

<p><img src="/assets/2024-03-19-cse167-hw4/scene6-linear-srgb.png" alt="" class="figure" />
<em>Scene 6: <code class="language-plaintext highlighter-rouge">colorspace input linear</code> <code class="language-plaintext highlighter-rouge">colorspace output srgb</code>, 1920x1440, 0m01s</em></p>

<p><img src="/assets/2024-03-19-cse167-hw4/scene6-srgb-srgb.png" alt="" class="figure" />
<em>Scene 6: <code class="language-plaintext highlighter-rouge">colorspace input srgb</code> <code class="language-plaintext highlighter-rouge">colorspace output srgb</code>, 1920x1440, 0m01s</em></p>]]></content><author><name>alden wu (goodtrailer)</name><email>me@aldw.net</email></author><category term="work" /><summary type="html"><![CDATA[Acceleration Structure I implemented a Bounding Volume Hierarchy along the lines of Pharr, Jakob, and Humphreys’s description in Physically Based Rendering 4e, alongside an efficient intersection algorithm for axis-aligned bounding volumes given by Ericson’s Real-Time Collision Detection.]]></summary></entry></feed>