<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title>Tobias Alexander Franke</title>
        <description>Tobias Alexander Franke</description>
        <link>https://www.tobias-franke.eu/</link>
        <atom:link href="https://www.tobias-franke.eu/rss/index.xml" rel="self" type="application/rss+xml"/>
        
        
        <item>
            <title>STAR-NT: Spatiotemporal Acceleration of Real-Time Neural Transparency Rendering</title>
            <description><![CDATA[ <div class="publication">
    <h1>STAR-NT: Spatiotemporal Acceleration of Real-Time Neural Transparency Rendering</h1>
    <p>
        <span class="authors">Grigoris Tsopouridis et al. and Tobias Alexander Franke</span>
        <span class="journal">Proceedings of Computer Graphics International 2026</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/tsopouridis26starnt/tsopouridis26starnt.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/tsopouridis26starnt/tsopouridis26starnt.jpg" alt="Quality comparison of methods: FLIP mean error (white) and MSE * 10&lt;sup&gt;2&lt;/sup&gt; (green). The ground truth is shown on the left with the depth complexity (maximum number of transparent layers) in the parenthesis." title="Quality comparison of methods: FLIP mean error (white) and MSE * 10&lt;sup&gt;2&lt;/sup&gt; (green). The ground truth is shown on the left with the depth complexity (maximum number of transparent layers) in the parenthesis.">
    
    </a>
    
    
    <figcaption>Quality comparison of methods: FLIP mean error (white) and MSE * 10<sup>2</sup> (green). The ground truth is shown on the left with the depth complexity (maximum number of transparent layers) in the parenthesis.</figcaption>
    
</figure>

    <h2>Abstract</h2>
    <p>Neural order-independent transparency delivers high-quality rendering of overlapping transparent surfaces, but its geometry passes and network input generation remain costly, particularly on mobile and legacy hardware. We present a spatiotemporal acceleration framework that exploits spatial and temporal coherence to reduce this overhead while preserving visual quality. Spatially, we use adaptive quadtree-based screen-space subdivision to scale geometry pass resolution according to local color variance. Temporally, selected frames reuse the previous transparency result through depth-based reprojection instead of full rendering. Together, these optimizations reduce rendering cost and integrate efficiently into existing real-time rendering pipelines.</p>


    
    <h2>Preview</h2>
    <p class="images">
    
    <img src="https://www.tobias-franke.eu/publications/tsopouridis26starnt/preview/tsopouridis26starnt-0.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/tsopouridis26starnt/preview/tsopouridis26starnt-1.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/tsopouridis26starnt/preview/tsopouridis26starnt-2.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/tsopouridis26starnt/preview/tsopouridis26starnt-3.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/tsopouridis26starnt/preview/tsopouridis26starnt-4.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/tsopouridis26starnt/preview/tsopouridis26starnt-5.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/tsopouridis26starnt/preview/tsopouridis26starnt-6.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/tsopouridis26starnt/preview/tsopouridis26starnt-7.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/tsopouridis26starnt/preview/tsopouridis26starnt-8.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/tsopouridis26starnt/preview/tsopouridis26starnt-9.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/tsopouridis26starnt/preview/tsopouridis26starnt-10.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/tsopouridis26starnt/preview/tsopouridis26starnt-11.png" alt="" title="">
    
    </p>
    

    
    <h2>Supplemental Video</h2>
    
    <div class="video default-size">
        <img class="default-size" src="https://www.tobias-franke.eu/publications/tsopouridis26starnt/tsopouridis26starnt_video.jpg" alt="" title="">
        
        <a class="fa fa-5x default-size" href="https://www.tobias-franke.eu/publications/tsopouridis26starnt/tsopouridis26starnt.mp4"></a>
        
    </div>
    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://www.tobias-franke.eu/publications/tsopouridis26starnt/tsopouridis26starnt.pdf">Paper</a></li>
        
        
        
        
        <li><a href="https://arxiv.org/abs/2606.16747">Arxiv</a></li>
        
        
        
        
        <li><a href="https://arxiv.org/html/2606.16747v1">Arxiv HTML</a></li>
        
        
        
        
        <li><a href="https://cgrg.eu/publications/star-nt-spatiotemporal-acceleration-of-real-time-neural-transparency-rendering">CGRG</a></li>
        
        
        
        
        <li><a href="https://www.cgs-network.org/cgi26/programme/">CGI 2026</a></li>
        

        
        
        <li><a href="https://www.tobias-franke.eu/publications/tsopouridis26starnt/tsopouridis26starnt.mp4">Video</a></li>
        
        

        
        <li><a href="https://www.tobias-franke.eu/publications/tsopouridis26starnt/tsopouridis26starnt.bib">Bibtex</a></li>
        
    </ul>
</div> ]]></description>
            <pubDate>Mon, 15 Jun 2026 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/publications/tsopouridis26starnt/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhwcU9RRC9VTi95T3dnRUZZTTJCUW1kMkxDSAozU0gwY21hT3NBVGQ1ekhHdlR6ZXRmc0JBS2k3a2hFMzh6Z0pBYURERXJDdzRTV25naVdNV3pUdGdLeHc3K0hpCmNkVUEKPUFnT2IKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Conference</category><category>Publication</category><category>STAR</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Agent OPML</title>
            <description><![CDATA[ <figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2026_05_30_agent-opml-logo.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2026_05_30_agent-opml-logo.jpg" alt="" title="">
    
    </a>
    
    
</figure>

<h1 id="same-procedure-as-last-year">Same procedure as last year</h1>

<p>We do not need to debate the merits of AI; this has been discussed to death on the web and reminds me of one of the many programming language wars I was involved in in the early 2000s, where a single language was somehow the solution to world hunger and memory allocation bugs.</p>

<p>I wanted to talk about a part of my day to day work. I need to evaluate a lot of publications - blog posts, articles from various hardware vendors, Github releases, papers, conference programs … - to judge if they match our business use case or can be feasibly integrated into a prototype. Naturally, I have a lot of constraints around what I can and cannot do: Something must be open source and MIT or equivalently licensed, must meet certain hardware requirements, must be using a subset of GPU APIs, must be part of a certain graphics domain etc.</p>

<p>I read a lot of sources every day that publish new and interesting stuff, like Jendrik Illner's most excellent <a href="https://www.jendrikillner.com/tags/weekly/">Graphics Programming Weekly</a> blog, individual bloggers, conference programs and more.</p>

<p>As an example, a single <a href="https://www.siggraph.org/">SIGGRAPH conference</a> can sport around 100 publications that I need to review. Each publication comes with a session, title, an abstract and a full PDF. Naturally, there's a progression how I get to the juicy parts: Discard sessions I don't care for first, search for interesting sounding titles, review their abstract and eventually keep the most interesting matches to read later in full.</p>

<p>I am subscribed to several conferences, around 250 personal blogs and several newsletters of companies, all in the computer graphics domain, all of them crunching out several articles a day. This takes an <em>insane</em> amount of time off from my day, <strong>every day</strong>, and the process is almost completely monotonous. <em>Clanker-like</em> one might say.</p>

<p>So here's my AI use-case, and how my obsession with RSS <em>feeds</em> directly into all of this.</p>

<h1 id="pipeline-overview">Pipeline Overview</h1>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2026_05_30_agent-opml-diag.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2026_05_30_agent-opml-diag.png" alt="" title="">
    
    </a>
    
    
</figure>

<p><strong>Agent OPML</strong> is a local LLM-based agent (NanoClaw, Pi, OpenCode, OpenClaw, whatever) that uses a tool to fetch publications from various sources and serializes all new entries into a fresh JSON file. Each entry looks like this:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"..."</span><span class="p">,</span><span class="w">
    </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"..."</span><span class="p">,</span><span class="w">
    </span><span class="nl">"authors"</span><span class="p">:</span><span class="w"> </span><span class="s2">"..."</span><span class="p">,</span><span class="w">
    </span><span class="nl">"summary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"..."</span><span class="p">,</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Once the data is fetched, the agent's task, described in a skill, is to analyze each entry. The agent first analyzes the <code class="language-plaintext highlighter-rouge">summary</code> (which in many cases is the entire publication pulled via RSS), and if this is unsatisfactory - because it is just a shortened RSS <code class="language-plaintext highlighter-rouge">&lt;description&gt;</code>, is metadata about the publication but  <em>linking</em> to it or is a URL of a video - go to the original <code class="language-plaintext highlighter-rouge">url</code> and try to fetch more using extra tooling. For instance in case of Arxiv, there may be a HTML version of the paper, and if not there may be a PDF instead, in which case the agent proceeds to download the file and converts it to Markdown for processing.</p>

<p>The skill describes what is interesting, what sounds interesting, etc. Each entry is analyzed for its relevance and goals, and more importantly if it fits within certain constraints. If all of these criteria are met, create a mini-summary and rating why this was deemed useful. The report is produced as a Markdown document, with annotations into a JSON file. Both are combined and attached to a new feed file that is served on some httpd.</p>

<p>The outcome is this: A single feed I can subscribe to which updates me daily with those publications I truly want, and the pipeline/algorithm to judge all of it entirely under my control.</p>

<figure class="basic-figure">

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2026_05_30_agent-opml-feed.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2026_05_30_agent-opml-feed.png" alt="" title="">
    
    </a>
    
    
</figure>

<h1 id="routing-data-to-the-agent">Routing data to the agent</h1>

<p>The input for the agent is a stream or feed of new publications that is updated regularly.</p>

<p>To do this, we will use an <a href="https://opml.org/">OPML file</a>. For the uninitated: An OPML is a XML file pointing to multiple URLs of feeds. A feed in turn is a structured file containing blog posts or any other updates from a webpage, and those come in three major formats: <a href="https://validator.w3.org/feed/docs/atom.html">Atom</a>, <a href="https://www.rssboard.org/rss-specification">RSS 2.0</a> or <a href="https://www.jsonfeed.org/">JSON Feed</a>. Most importantly however is that everything in those files is <em>machine-readable</em>. Usually OPML files and feeds are used with a feed-reader such as <a href="https://netnewswire.com/">NetNewsWire</a> and are a simple way to store all your subscriptions.</p>

<p>We are going to use the OPML instead to let the agent automatically fetch new content from webpages for automagic analysis. The OPML should capture every feed of information that you'd have to wade through manually, but want the agent to do it for you.</p>

<p>To use my interests as an example, the OPML contains the following types of feed subscriptions:</p>
<ul>
  <li>
<strong>GPU Vendor</strong>: ARM, NVIDIA, Qualcomm, AMD, …</li>
  <li>
<strong>Graphics API</strong>: DirectX blog, Khronos blog, Apple Metal, SLANG, …</li>
  <li>
<strong>Game Engines</strong>: Godot, Unreal, Unity, O3DE, Unigine, OGRE, Stride, …</li>
  <li>
<strong>Tools</strong>: Renderoc, Pix, NSIGHT, …</li>
  <li>
<strong>Research Blogs</strong>: Activision, EA Seed</li>
  <li>
<strong>Paper Feeds</strong>: Arxiv CS section, The Journal of Graphics Techniques (JGCT), Siggraph Digital Library entries, …</li>
  <li>
<strong>Author Feeds</strong>: DBLP and Google Scholar, individual blogs</li>
  <li>
<strong>Github Commits</strong>: Microsoft hlsl-spec, Gigi, …</li>
  <li>
<strong>Conference Social Media</strong>: GPC, GDC, Siggraph, EGSR, EG, I3D, …</li>
  <li>
<strong>Extras</strong>: One feed set up using <a href="https://www.rsslibrarian.ch/librarian.php">RSS-Librarian</a>
</li>
</ul>

<p>The last two here are worth elaborating on:</p>
<ul>
  <li>
<em>Conference social media</em> feed might sound odd at first: These are subscriptions from Mastodon, Bluesky and Twitter (through Nitter) that will have a special rule in the skill to catch announcements of a program URL for an upcoming conference. This way, the agent can grab more publications before they might appear anywhere else.</li>
  <li>The <em>Extras</em> feed serves the outlier case: If some article source is <strong>not</strong> captured by the OPML, i.e. some interesting but completely random blog post that appears somewhere you cannot subscribe to or that is otherwise irrelevant, then this individual article can be added to the <em>Extras</em> feed manually.</li>
</ul>

<p>The OPML is used when running a script called <code class="language-plaintext highlighter-rouge">fetch_publications.py</code>, which goes through every subscription one by one, fetches the feeds and then combines all new entries it has not seen since the last run into a JSON file that is then used by the agent for analysis.</p>

<h1 id="setting-up-agent-opml">Setting up Agent OPML</h1>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2026_05_30_agent-opml-htop.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2026_05_30_agent-opml-htop.png" alt="" title="">
    
    </a>
    
    
</figure>

<h2 id="prerequisites">Prerequisites</h2>

<p>This setup includes a bunch of things:</p>

<ul>
  <li>A spare Raspberry Pi (optional)</li>
  <li>Python</li>
  <li><a href="https://opencode.ai/download">OpenCode</a></li>
  <li><a href="https://pypi.org/project/markitdown/">Markitdown</a></li>
  <li><a href="https://pandoc.org/installing.html">Pandoc</a></li>
  <li>Your OPML file</li>
  <li><a href="#code-snippets">A bunch of scripts</a></li>
</ul>

<p>Start by setting up a Raspberry Pi with Raspbian, and create a separate non-sudo account for the agent. If you want to run this on your local machine, you can skip this step. Then simply run the following steps.</p>

<p>On your sudo account:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>apt <span class="nb">install </span>pandoc cython3
</code></pre></div></div>

<p>On the agent account:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>curl <span class="nt">-fsSL</span> https://opencode.ai/install | bash
<span class="nv">$ </span>pip <span class="nt">-m</span> venv ~/.venv
<span class="nv">$ </span><span class="nb">.</span> ~/.venv/bin/activate
<span class="nv">$ </span>pip <span class="nb">install </span>markitdown feedparser
</code></pre></div></div>

<h2 id="code-snippets">Code snippets</h2>

<h3 id="fetch_publicationspy">fetch_publications.py</h3>

<p>This mini feed-aggregator <code class="language-plaintext highlighter-rouge">fetch_publications.py</code> is a simple Python script: Loop through a list of subscriptions from an OPML given via argument <code class="language-plaintext highlighter-rouge">--opml</code> and fetch the feeds, parse feed entries, reformat them into JSON dictionaries and use the URLs as IDs to remove duplicates. The outputs are written to a file given by <code class="language-plaintext highlighter-rouge">--output</code>  that contains the extracted data. A temporary file called <code class="language-plaintext highlighter-rouge">feed_state.json</code> is kept in the same output directory to mark already visited articles.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
</span>
<span class="kn">import</span> <span class="n">json</span>
<span class="kn">import</span> <span class="n">hashlib</span>
<span class="kn">import</span> <span class="n">requests</span>
<span class="kn">import</span> <span class="n">feedparser</span>
<span class="kn">import</span> <span class="n">argparse</span>
<span class="kn">import</span> <span class="n">xml.etree.ElementTree</span> <span class="k">as</span> <span class="n">ET</span>

<span class="kn">from</span> <span class="n">fake_useragent</span> <span class="kn">import</span> <span class="n">UserAgent</span>
<span class="kn">from</span> <span class="n">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Dict</span><span class="p">,</span> <span class="n">Any</span><span class="p">,</span> <span class="n">Optional</span>
<span class="kn">from</span> <span class="n">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>

<span class="k">def</span> <span class="nf">load_opml</span><span class="p">(</span><span class="n">opml_path</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
    <span class="n">tree</span> <span class="o">=</span> <span class="n">ET</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">opml_path</span><span class="p">)</span>
    <span class="n">root</span> <span class="o">=</span> <span class="n">tree</span><span class="p">.</span><span class="nf">getroot</span><span class="p">()</span>
    <span class="n">urls</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">outline</span> <span class="ow">in</span> <span class="n">root</span><span class="p">.</span><span class="nf">findall</span><span class="p">(</span><span class="sh">"</span><span class="s">.//outline</span><span class="sh">"</span><span class="p">):</span>
        <span class="n">url</span> <span class="o">=</span> <span class="n">outline</span><span class="p">.</span><span class="n">attrib</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">xmlUrl</span><span class="sh">"</span><span class="p">)</span> <span class="ow">or</span> <span class="n">outline</span><span class="p">.</span><span class="n">attrib</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">url</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">url</span><span class="p">:</span>
            <span class="n">urls</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">urls</span>

<span class="k">def</span> <span class="nf">load_state</span><span class="p">(</span><span class="n">path</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]]:</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="nc">Path</span><span class="p">(</span><span class="n">path</span><span class="p">).</span><span class="nf">exists</span><span class="p">():</span>
        <span class="k">return</span> <span class="p">{}</span>
    <span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="sh">"</span><span class="s">r</span><span class="sh">"</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="sh">"</span><span class="s">utf-8</span><span class="sh">"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">json</span><span class="p">.</span><span class="nf">load</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">save_state</span><span class="p">(</span><span class="n">path</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">state</span><span class="p">:</span> <span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]]):</span>
    <span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="sh">"</span><span class="s">w</span><span class="sh">"</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="sh">"</span><span class="s">utf-8</span><span class="sh">"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
        <span class="n">json</span><span class="p">.</span><span class="nf">dump</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="n">f</span><span class="p">,</span> <span class="n">ensure_ascii</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span> <span class="n">indent</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">st</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">entry_id</span><span class="p">(</span><span class="n">e</span><span class="p">:</span> <span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="n">candidate</span> <span class="o">=</span> <span class="n">e</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">id</span><span class="sh">"</span><span class="p">)</span> <span class="ow">or</span> <span class="n">e</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">guid</span><span class="sh">"</span><span class="p">)</span> <span class="ow">or</span> <span class="n">e</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">link</span><span class="sh">"</span><span class="p">)</span> <span class="ow">or</span> <span class="sh">""</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">candidate</span><span class="p">:</span>
        <span class="n">composite</span> <span class="o">=</span> <span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">title</span><span class="sh">"</span><span class="p">,</span><span class="sh">""</span><span class="p">)</span> <span class="o">+</span> <span class="n">e</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">summary</span><span class="sh">"</span><span class="p">,</span><span class="sh">""</span><span class="p">)</span> <span class="o">+</span> <span class="n">e</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">published</span><span class="sh">"</span><span class="p">,</span><span class="sh">""</span><span class="p">)).</span><span class="nf">encode</span><span class="p">(</span><span class="sh">"</span><span class="s">utf-8</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">hashlib</span><span class="p">.</span><span class="nf">sha256</span><span class="p">(</span><span class="n">composite</span><span class="p">).</span><span class="nf">hexdigest</span><span class="p">()</span>
    <span class="k">return</span> <span class="n">candidate</span>

<span class="k">def</span> <span class="nf">normalize_entry</span><span class="p">(</span><span class="n">e</span><span class="p">:</span> <span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">],</span> <span class="n">feed_url</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]:</span>
    <span class="k">return</span> <span class="p">{</span>
        <span class="sh">"</span><span class="s">title</span><span class="sh">"</span><span class="p">:</span> <span class="n">e</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">title</span><span class="sh">"</span><span class="p">),</span>
        <span class="sh">"</span><span class="s">url</span><span class="sh">"</span><span class="p">:</span> <span class="n">e</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">link</span><span class="sh">"</span><span class="p">),</span>
        <span class="sh">"</span><span class="s">summary</span><span class="sh">"</span><span class="p">:</span> <span class="n">e</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">summary</span><span class="sh">"</span><span class="p">),</span>
        <span class="sh">"</span><span class="s">authors</span><span class="sh">"</span><span class="p">:</span> <span class="p">[</span><span class="n">a</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">name</span><span class="sh">"</span><span class="p">)</span> <span class="k">for</span> <span class="n">a</span> <span class="ow">in</span> <span class="n">e</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">authors</span><span class="sh">"</span><span class="p">,</span> <span class="p">[])]</span> <span class="k">if</span> <span class="n">e</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">authors</span><span class="sh">"</span><span class="p">)</span> <span class="k">else</span> <span class="p">[],</span>
    <span class="p">}</span>

<span class="k">def</span> <span class="nf">fetch_feed</span><span class="p">(</span><span class="n">url</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">20</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]:</span>
    <span class="n">headers</span> <span class="o">=</span> <span class="p">{</span> <span class="sh">"</span><span class="s">User-Agent</span><span class="sh">"</span><span class="p">:</span> <span class="nc">UserAgent</span><span class="p">().</span><span class="n">random</span> <span class="p">}</span>
    <span class="n">resp</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="n">headers</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="n">timeout</span><span class="p">)</span>
    
    <span class="k">try</span><span class="p">:</span>
        <span class="n">resp</span><span class="p">.</span><span class="nf">raise_for_status</span><span class="p">()</span>
    <span class="k">except</span><span class="p">:</span>
        <span class="c1"># retry without user agent
</span>        <span class="n">requests</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="p">{},</span> <span class="n">timeout</span><span class="o">=</span><span class="n">timeout</span><span class="p">)</span>

    <span class="n">resp</span><span class="p">.</span><span class="nf">raise_for_status</span><span class="p">()</span>
    
    <span class="k">return</span> <span class="n">feedparser</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">resp</span><span class="p">.</span><span class="n">content</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">load_existing_entries</span><span class="p">(</span><span class="n">path</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]]:</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="nc">Path</span><span class="p">(</span><span class="n">path</span><span class="p">).</span><span class="nf">exists</span><span class="p">():</span>
        <span class="k">return</span> <span class="p">[]</span>
    <span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="sh">"</span><span class="s">r</span><span class="sh">"</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="sh">"</span><span class="s">utf-8</span><span class="sh">"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">json</span><span class="p">.</span><span class="nf">load</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">save_entries</span><span class="p">(</span><span class="n">path</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">entries</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]]):</span>
    <span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="sh">"</span><span class="s">w</span><span class="sh">"</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="sh">"</span><span class="s">utf-8</span><span class="sh">"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
        <span class="n">json</span><span class="p">.</span><span class="nf">dump</span><span class="p">(</span><span class="n">entries</span><span class="p">,</span> <span class="n">f</span><span class="p">,</span> <span class="n">ensure_ascii</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span> <span class="n">indent</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="nb">str</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
    <span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="p">.</span><span class="nc">ArgumentParser</span><span class="p">()</span>
    <span class="n">parser</span><span class="p">.</span><span class="nf">add_argument</span><span class="p">(</span><span class="sh">"</span><span class="s">-i</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">--opml</span><span class="sh">"</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="nf">add_argument</span><span class="p">(</span><span class="sh">"</span><span class="s">-o</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">--output</span><span class="sh">"</span><span class="p">)</span>

    <span class="n">param</span> <span class="o">=</span> <span class="n">parser</span><span class="p">.</span><span class="nf">parse_args</span><span class="p">()</span>

    <span class="nf">if </span><span class="p">(</span><span class="ow">not</span> <span class="n">param</span><span class="p">.</span><span class="n">opml</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">param</span><span class="p">.</span><span class="n">output</span><span class="p">):</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Parameters missing</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">return</span>

    <span class="n">feeds</span> <span class="o">=</span> <span class="nf">load_opml</span><span class="p">(</span><span class="n">param</span><span class="p">.</span><span class="n">opml</span><span class="p">)</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">feeds</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">No feeds found in OPML.</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">return</span>

    <span class="n">state_file</span> <span class="o">=</span> <span class="nc">Path</span><span class="p">(</span><span class="n">param</span><span class="p">.</span><span class="n">output</span><span class="p">).</span><span class="nf">with_name</span><span class="p">(</span><span class="sh">"</span><span class="s">feed_state.json</span><span class="sh">"</span><span class="p">)</span>
    <span class="n">state</span> <span class="o">=</span> <span class="nf">load_state</span><span class="p">(</span><span class="n">state_file</span><span class="p">)</span>

    <span class="n">new_entries</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]]</span> <span class="o">=</span> <span class="p">[]</span>

    <span class="k">for</span> <span class="n">feed_url</span> <span class="ow">in</span> <span class="n">feeds</span><span class="p">:</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">parsed</span> <span class="o">=</span> <span class="nf">fetch_feed</span><span class="p">(</span><span class="n">feed_url</span><span class="p">)</span>
        <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">ex</span><span class="p">:</span>
            <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Failed to fetch </span><span class="si">{</span><span class="n">feed_url</span><span class="si">}</span><span class="s">: </span><span class="si">{</span><span class="n">ex</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
            <span class="k">continue</span>

        <span class="n">seen_for_feed</span> <span class="o">=</span> <span class="nf">set</span><span class="p">(</span><span class="n">state</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="n">feed_url</span><span class="p">,</span> <span class="p">[]))</span>
        <span class="n">new_ids</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">parsed</span><span class="p">.</span><span class="n">entries</span><span class="p">:</span>
            <span class="n">eid</span> <span class="o">=</span> <span class="nf">entry_id</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">eid</span> <span class="ow">in</span> <span class="n">seen_for_feed</span><span class="p">:</span>
                <span class="k">continue</span>
            <span class="n">ne</span> <span class="o">=</span> <span class="nf">normalize_entry</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="n">feed_url</span><span class="p">)</span>
            <span class="n">new_entries</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">ne</span><span class="p">)</span>
            <span class="n">new_ids</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">eid</span><span class="p">)</span>
            <span class="n">seen_for_feed</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">eid</span><span class="p">)</span>

        <span class="n">state</span><span class="p">.</span><span class="nf">setdefault</span><span class="p">(</span><span class="n">feed_url</span><span class="p">,</span> <span class="p">[])</span>
        <span class="n">state</span><span class="p">[</span><span class="n">feed_url</span><span class="p">].</span><span class="nf">extend</span><span class="p">(</span><span class="n">new_ids</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">new_entries</span><span class="p">:</span>
        <span class="nf">save_entries</span><span class="p">(</span><span class="n">param</span><span class="p">.</span><span class="n">output</span><span class="p">,</span> <span class="n">new_entries</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">No new entries found.</span><span class="sh">"</span><span class="p">)</span>

    <span class="nf">save_state</span><span class="p">(</span><span class="n">state_file</span><span class="p">,</span> <span class="n">state</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Collected </span><span class="si">{</span><span class="nf">len</span><span class="p">(</span><span class="n">new_entries</span><span class="p">)</span><span class="si">}</span><span class="s"> new entries</span><span class="sh">"</span><span class="p">)</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">"</span><span class="s">__main__</span><span class="sh">"</span><span class="p">:</span>
    <span class="nf">main</span><span class="p">()</span>
</code></pre></div></div>

<p>Re-running the script should only produce a file if any new entries have been found since the last run.</p>

<h3 id="convert-pdf-to-mdsh">convert-pdf-to-md.sh</h3>

<p>OpenCode has a directory <code class="language-plaintext highlighter-rouge">~/.config/opencode/tools</code> which contains small scripts the agent can use to do something without requiring it to go through the process manually, thus saving tokens.</p>

<p>This tool uses <code class="language-plaintext highlighter-rouge">markitdown</code> to convert a PDF to Markdown. This is helpful when the agent downloads a PDF and needs to process/read it. The easiest way to do that is to simply convert it to a structured text file.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>

<span class="nv">INPUT</span><span class="o">=</span><span class="nv">$1</span>
<span class="nv">OUTPUT</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">INPUT</span><span class="p">%.*</span><span class="k">}</span><span class="s2">"</span>

markitdown <span class="nv">$INPUT</span> <span class="o">&gt;</span> <span class="nv">$OUTPUT</span>.md
</code></pre></div></div>

<h3 id="the-filter-skills">The Filter Skills</h3>

<p>This is the heart of the analysis and filtering process. These files describe what the agent should do, structured into these three general steps:</p>

<ol>
  <li>Read the combined entries that <a href="#fetch_publicationspy">fetch_publications.py</a> spits out</li>
  <li>Analyse each entry if it's worth your time</li>
  <li>Create a report of what passed the filter</li>
</ol>

<p>First is the <code class="language-plaintext highlighter-rouge">filter-publications</code> skill, which generically describes how to parse through the JSON file with all the new entries and how to collect them together. Save it as <code class="language-plaintext highlighter-rouge">~/.config/opencode/skills/filter-publications/SKILL.md</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>---
name: filter-publications
description: Given a list of new publications, find those relevant to our business use-case and produce a Markdown report on the agents findings.
---

# When to use this skill

Use this skill when the user wants the agent to evaluate and filter publications such as papers, blog posts, announcements, conference proceedings and so on.

Rank each publication by whether its idea, approximation, pipeline, or result could realistically transfer to the users specified use-case.

Be conservative and evidence-grounded. Clearly separate what the publication actually measured from what the agent infers as possible goals that can be reached with high relevance. Do not invent performance estimates, deployment claims, or results that are not supported by the inspected sources.

Use these three steps outlined below:
1. Read Input
2. Analyze Publications
3. Create Output

# Read Input

The agent is given one JSON file by the user (most often called `new_entries.json`). It contains a list of publications, where each entry has: a `title`, a `url`, a `summary`, and a list of `authors`. 

If the file the agent was given by the user is empty or does not exist, immediately halt and do not progress any further! Do not read any other json files or try to get data from somewhere else.

Each entry represents one new publication that was just discovered. The agent will need to analyze each one independently for its merits. Try to read the `summary` first. The summary may or may not contain the full publication. If it seems too short, try to open the URL and read the contents from there instead. If the `summary` is a short message from a social media page and contains a URL, follow that URL, especially if it contains a full program of a scientific conference.

Execute the [Analyze Publications](#analyze-publications) forstep described in the next section for all entries.

# Analyze Publications

## Required behavior

1. Read one publication entry source from the supplied JSON list.
2. Extract or read the best available information from the `summary` field.
3. Summarize the publication's core idea, evidence, and claimed results.
4. Evaluate the publication using the `review-publication` skill and produce an assessment with a clear verdict.

Report missing abstracts, inaccessible PDFs, broken links, paywalls, failed document extraction, low-quality extracted text, missing project pages, missing code, or insufficient evidence.

## Execution strategy

Do not treat a preliminary title/session-based inference as a confirmed analysis. If only title, session, program metadata, or DOI was inspected, label the evidence as **Limited** and make the score conservative.

Before finalizing top recommendations, do a detailed inspection of the strongest candidates. A publication should not appear as a final **Validated Top Recommendations** with a high score unless the agent has inspected substantive evidence such as an abstract, project page, PDF/text extraction, code repository, results, or limitations. If a publication cannot be analyzed in detail because the PDF is too large, paywalled, inaccessible, or time-limited, list it as a **Potential Top Candidates** rather than presenting it as fully validated.

These recommendations should not be homogeneous. Prefer a diverse shortlist that covers different topics that match the *Relevance* and *Goals* sections of the `review-publication` skill.

Prefer lightweight sources first: program page, DOI or publisher pages, abstracts, project pages, code repositories, videos, and supplemental descriptions. Do not download very large PDFs or supplemental files unless necessary. If a large file is skipped, report that limitation.

# Create Output

Create a subdirectory called `output`. All files the agent generates, even intermediate files, need to be written into this directory!

## Markdown report

Create a file called `report.md`. The file must start with a frontmatter. Use this exact style, with no blank line before the closing delimiter:

```yaml
---
title: Daily Report
author: Agent OPML
date: \today
documentclass: report
colorlinks: true
urlcolor: Maroon
linkcolor: Maroon
---
```

Do not modify this frontmatter. The report must only use Markdown headings, bullet points, and tables. Do not use `---` after the frontmatter, and no HTML tags. Structure the report into the following sections and no other headings:

1. **Overview**: A summary - as a list - of the overall statistics of the analysis, how many entries the agent processed, the themes the agent discovered and what the agent discarded. The list can be structured by source or category.
2. **Validated Top Recommendations**: Publications supported by substantive evidence. Use the the output of the `review-publication` skill for each publication.
3. **Potential Top Candidates**: Publications that look promising from title/session/metadata but lack enough evidence for a confident recommendation. Use the the output of the `review-publication` skill for each publication.
4. **Overall Verdict**: A summary of the agents findings and the papers that were recommended.

## JSON list

Create a file called `report.json` which follows the [Markdown report](#markdown-report), and use the JSON outputs from `review-publication` to populate the `validated_top_recommendations` and `potential_top_candidates` lists.

```json
{
  "overview": "{OVERVIEW}",
  "validated_top_recommendations": [],
  "potential_top_candidates": [],
  "overall_verdict": "{OVERALL_VERDICT}"
}
```
</code></pre></div></div>

<p>In this file you will need adjust and modify the <code class="language-plaintext highlighter-rouge"># Analyze Publications</code> section and/or provide a customized skill <code class="language-plaintext highlighter-rouge">review-publication</code> that judges and summarizes each individual entry the filtering process goes through.</p>

<p>Next is the <code class="language-plaintext highlighter-rouge">review-publication</code> skill that will guide the agent to judge an individual publication. Save it as <code class="language-plaintext highlighter-rouge">~/.config/opencode/skills/review-publication/SKILL.md</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>---
name: review-publication
description: Judge a publication based on relevance, constraints and goals, and describes how to score the publication
---

# When to use this skill

The user asks the agent to produce a summary or judgement of a publication.

# Preprocessing

- If the publication is a social media post ...
- If the publication is a video, first ...
- ...

# Relevance

[Summarize here what is considered a relevant publication to you.]

# Constraints

[Describe all constraints you have and whether they can be circumvented.]

# Goals

[Describe the goals you want to achieve by using or implementing something described in a publication.]

# Final Scores

Use a 1 to 10 relevance score. The score should reflect the how well the publications fits the items in [Relevance](#relevance), [Constraints](#constraints) and [Goals](#goals).

...

Generate the following **Scores**:

- **Overall score**: 1 to 10.
- ...

# Output

- **Summary**: Title, authors, source, and core idea.
- **Evidence**: Abstract, PDF, project page, code, video, supplemental, or pasted metadata.
- **Measurements**: 
- ...
</code></pre></div></div>

<p>Note that some fields are being referenced in the <code class="language-plaintext highlighter-rouge">filter-publications</code> skill that drives it, most importantly the <strong>Goals, Constraints and Relevance</strong> sections. Filling these sections should already give you a working set to get a summary of the inputs.</p>

<h3 id="create_feedsh">create_feed.sh</h3>

<p>This script reads the Markdown report from the agent, converts it into HTML and creates a feed file ready to deploy to a host where feed readers can subscribe to it. It only adds one entry. This could benefit from some polishing later.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>

<span class="nv">REPORT</span><span class="o">=</span><span class="si">$(</span>pandoc <span class="nt">-f</span> markdown <span class="nt">-t</span> html <span class="nv">$1</span><span class="si">)</span>
<span class="nv">DATE</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span> +%Y-%m-%d<span class="si">)</span>
<span class="nv">UPDATE</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span> <span class="nt">-R</span><span class="si">)</span>
<span class="nv">GUID</span><span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="nv">$REPORT</span> | <span class="nb">sha256sum</span> | <span class="nb">awk</span> <span class="s1">'{print $1}'</span><span class="si">)</span>

<span class="nb">cat</span> <span class="o">&gt;</span> <span class="s2">"</span><span class="nv">$2</span><span class="s2">"</span> <span class="o">&lt;&lt;</span><span class="no">EOF</span><span class="sh">
&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;rss version="2.0"&gt;
&lt;channel&gt;
  &lt;title&gt;Publication Filter Feed&lt;/title&gt;
  &lt;generator&gt;Agent OPML&lt;/generator&gt;
  &lt;lastBuildDate&gt;</span><span class="nv">$UPDATE</span><span class="sh">&lt;/lastBuildDate&gt;
  &lt;item&gt;
    &lt;author&gt;Agent OPML&lt;/author&gt;
    &lt;title&gt;Daily Report - </span><span class="nv">$DATE</span><span class="sh">&lt;/title&gt;
    &lt;description&gt;&lt;![CDATA[ </span><span class="nv">$REPORT</span><span class="sh"> ]]&gt;&lt;/description&gt;
    &lt;pubDate&gt;</span><span class="nv">$UPDATE</span><span class="sh">&lt;/pubDate&gt;
    &lt;guid&gt;</span><span class="nv">$GUID</span><span class="sh">&lt;/guid&gt;
&lt;/item&gt;
&lt;/channel&gt;
&lt;/rss&gt;
</span><span class="no">EOF
</span></code></pre></div></div>

<h3 id="the-cronjob">The Cronjob</h3>

<p>A small script <code class="language-plaintext highlighter-rouge">run.sh</code> to implement the <a href="#pipeline-overview">pipeline</a>: Fetch new publications, run OpenCode to filter those and upload the resulting feed item.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>

<span class="nv">FEED_FILE</span><span class="o">=</span><span class="s2">"feeds.opml"</span>
<span class="nv">OUTPUT_DIR</span><span class="o">=</span><span class="s2">"./output"</span>
<span class="nv">JSON_FILE</span><span class="o">=</span><span class="s2">"</span><span class="nv">$OUTPUT_DIR</span><span class="s2">/new_entries.json"</span>
<span class="nv">ARCHIVE_DIR</span><span class="o">=</span><span class="s2">"</span><span class="nv">$OUTPUT_DIR</span><span class="s2">/</span><span class="si">$(</span><span class="nb">date</span> +%s<span class="si">)</span><span class="s2">"</span>

<span class="nb">.</span> ~/.venv/bin/activate

<span class="nb">cd</span> ~/ai-paper-filter

<span class="c"># create output dir</span>
<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="nv">$OUTPUT_DIR</span>

<span class="c"># fetch publications</span>
python fetch_publications.py <span class="nt">-i</span> <span class="nv">$FEED_FILE</span> <span class="nt">-o</span> <span class="nv">$JSON_FILE</span>

<span class="c"># generate filtered report</span>
<span class="k">if</span> <span class="o">[</span> <span class="nt">-f</span> <span class="nv">$JSON_FILE</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
    ~/.opencode/bin/opencode run <span class="s2">"Use filter-publications on </span><span class="nv">$JSON_FILE</span><span class="s2">"</span>

    <span class="c"># upload new feed file</span>
    sh create_feed.sh <span class="nv">$OUTPUT_DIR</span>/report.md <span class="nv">$OUTPUT_DIR</span>/report.xml    
    curl <span class="nt">-T</span> <span class="nv">$OUTPUT_DIR</span>/report.xml <span class="s2">"ftp://USER:PASS@MYFTP/PATH/report.xml"</span>

    <span class="c"># archive everything</span>
    <span class="nb">mkdir</span> <span class="nt">-p</span> <span class="nv">$ARCHIVE_DIR</span>
    <span class="nb">mv</span> <span class="nv">$OUTPUT_DIR</span>/report<span class="k">*</span> <span class="nv">$ARCHIVE_DIR</span>

    <span class="nb">rm</span> <span class="nv">$JSON_FILE</span>
<span class="k">fi</span>
</code></pre></div></div>

<p>Put this into a Cron job by running <code class="language-plaintext highlighter-rouge">crontab -e</code> on your target machine:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0 8 * * * ~/run.sh &gt; log.txt
</code></pre></div></div>

<p>This will execute the pipeline every day at 8:00 in the morning and produce a new report. If <a href="#fetch_publicationspy">fetch_publications.py</a> does not produce any new entries, the pipeline will not run. If it does however, the findings will be uploaded as a feed file to some FTP after the agent finished execution.</p>

<h1 id="future-ideas">Future ideas</h1>

<h2 id="more-extraction-tooling">More Extraction Tooling</h2>

<p>The agent has to read or interpret content, and in some cases it boils down to the extraction mechanism behind it all: Scrape a URL and convert it to Markdown. Through my work on <a href="https://www.rsslibrarian.ch/librarian.php">RSS-Librarian</a> I've already found ways to deal with text extraction. For non-raw-text document formats Markitdown and Pandoc already do a great job. I want to find ways however to also address videos (maybe generate dubs automatically) and presentations (either XLSX or PDF), which are harder to parse because of missing structure tags.</p>

<h2 id="recommendation-system-for-rss-categories">Recommendation system for RSS categories</h2>

<p>A second thing I plan to test is my own <em>recommendation algorithm</em> for video subscriptions from Youtube/Odysee/Vimeo/PeerTube/etc. A general complaint from those who never grew up on RSS is that subscribing to video channels can quickly drown out good videos in a lot of nonsense (shorts mostly, but also irrelevant videos from channels that veer off topic), whereas the Youtube recommendation algorithm can easily get sidetracked if you click on one wrong video and suddenly everything recommended is just funny slop videos.</p>

<p>I am testing a skill that ingests various video platform feeds (I continue to be amazed that these still exist on Youtube at all), sorts out feed items based on their description and the subtitles downloaded by Markitdown, and then repackages what is left into a new RSS feed that I subscribe to. This feed has one nice property: It is not a discovery feed, meaning the algorithm can never steer from its original goal of giving me <em>only</em> that what I subscribe to. It merely removes entries rather and recommends the highlights in the data stream.</p>

<h2 id="agentic-rss">Agentic RSS</h2>

<p>Taking the same idea further I can imagine expanding this recommendation algorithm to the entire subscription library: One could subscribe - very liberally - to RSS feeds and end up with thousands of items per day, but the agent runs through them and instead of generating a report, decides whether to keep an item or not, then attaches the ones that were kept to a filtered feed. The agent can have arbitrary many input feeds, but you only ever subscribe to the output feed that has the sorted-out content based on custom rules.</p>

<p>I currently run several digests similar to the paper filter already, but I'd like them to act as pure filters on feed files by abstracting some of the scripting logic more.</p>

<h1 id="conclusion">Conclusion</h1>

<p>This pipeline is not perfect, but has reduced the time I spend on reviewing new publications (especially around SIGGRAPH each year) significantly. Instead of mindlessly eyeballing loads of paper titles and spending hours <em>discarding</em> irrelevant stuff I can concentrate on the things that matter to me the most. Sure the agent might miss something, but so do I when I stare at thousands of unread items in my feed-reader. All I keep doing now is adding publications I find on random sources to a <a href="https://www.rsslibrarian.ch/librarian.php">RSS-Librarian</a> feed and adjust the OPML every now and then.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2026_05_30_agent-opml-good-vibes.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2026_05_30_agent-opml-good-vibes.jpg" alt="" title="">
    
    </a>
    
    
</figure>

<p>The setup is minimal, and the <em>algorithm</em> behind it all is not some opaque logic from a subscription service, but something I fully control end-to-end. This particular use-case might appeal to you as well: Wading through tons of items for sorting is a common task almost everyone will run into. You may write a paper and need to find new related publications to yours in a sea of articles. You may have too many RSS items in your feed reader. You may need to setup a more in-depth filtering mechanism than a simple keyword search.</p>

<p>Whatever it is, if your task boils down to a for loop with some slightly more complex word-bingo search inside a bunch of feeds, and you've set up this process as well, I'm interested in your story. Write a blog post about it perhaps?</p> ]]></description>
            <pubDate>Sat, 30 May 2026 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/log/2026/05/30/agent-opml.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhySWN3RUFuM1RML0MvdTMxRmNaV3lPMFlSSQoweHNnT2d0QnFMTmdTOTlUUHRDSXNoc0JBTGk2RUNzMXVRT3ZSTzcwT21OMEY1dTJidDlaZU9ENFJLbkdBOUh5CnFWd1AKPVNNZVMKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>AI</category><category>RSS</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>The RSS Librarian</title>
            <description><![CDATA[ <figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2025_11_08_rsslibfavicon.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2025_11_08_rsslibfavicon.png" alt="" title="">
    
    </a>
    
    
</figure>

<h1 id="knowledge-is-power">Knowledge is power</h1>

<p>Many of you may know me as a huge Warhammer 40,000 nerd. I like the absurd and over-the-top nature of it, the grimdark setting and stagnant vision of the future where, what is left of humanity, slowly rots from within. Many small quotes encapsulate the universe very well, but the following one from <a href="https://www.dawnofwar.com/">Dawn of War</a> is one of my favorites:</p>

<blockquote>
  <p>"Knowledge is power, hide it well."</p>

  <p><em><a href="https://dow.fandom.com/wiki/Dawn_of_War/Librarian">Astartes Librarian</a></em></p>
</blockquote>

<p>Similar to a Black Mirror episode, this quote perfectly reflects our current strange reality of hiding most of the Internet's articles worth reading behind walls of ads, paywalls or questionable design ideas. Knowledge has not become entirely inaccessible, but similar to what happened in the academic community there is a cost to accessing articles: Your privacy, your sanity, or both.</p>

<p>Saving articles for later-reading has turned into a small ecosystem: Safari has a built-in method to store pages. Mozilla, until recently, ran Pocket <a href="https://support.mozilla.org/en-US/kb/future-of-pocket">and then shut it down</a>. <a href="https://instapaper.com/">Instapaper</a> remains stable and clean commercial solution I really like, and on the open-source front <a href="https://wallabag.org/">Wallabag</a> is a viable alternative. There are great offline apps such as <a href="https://apps.apple.com/ch/app/later-save-links-read-later/id1507396839">Later</a>. Saving articles can also take an archival character, which <a href="https://archivebox.io/">ArchiveBox</a> supports nicely.</p>

<p>I already have <a href="https://netnewswire.com/">my feed-reader</a> where I read everything else. All of these services are extra layers and apps that are too heavy for someone who just wants to read articles, offline, in a clean layout, without ads, without intrusive layouts or pop-up videos or spyware. This particular problem has already been solved many years ago, so rather than re-inventing the wheel I can instead leverage existing, well-established web-standards to let me read arbitrary articles from the web in the comfy environment of my feed-reader, next to my subscriptions.</p>

<p>If you subscribe via RSS/Atom to blogs, podcasts or Youtube channels, then RSS-Librarian is one more way for you to tunnel the Internet through your feed-reader.</p>

<h1 id="version-one">Version One</h1>

<blockquote>
  <p><a href="https://github.com/thefranke/rss-librarian">RSS-Librarian</a> is a read-it-later service for RSS purists. You can store articles from the web in your own <em>personal RSS/Atom feed</em> and use your favorite feed-reader software to read your stored articles later. RSS-Librarian uses no database and works without accounts.</p>
</blockquote>

<p>I am happy to announce that <a href="https://www.github.com/thefranke/rss-librarian">RSS-Librarian</a> has now arrived at <em>Version One</em> and is ready for production. Since <a href="/log/2024/04/28/a-reader-service-for-rss-purists.html">my first article</a> a lot has happened.</p>

<p>The <a href="https://github.com/thefranke/rss-librarian/commit/48ebf96f3099b4426b67fbad3b96e48e357c187c#diff-ee8c822add044a72b0a9d632d12b30bf1d329400b1e6d369e930741d94ee6ca2">original RSS-Librarian</a> was kept extremely simple at just 211 lines of code in a single file. RSS-Librarian is written in PHP to make it as easy as possible to drop it on a VPS (virtually zero hosters come without PHP support these days). The script receives a URL as input, runs it through <a href="https://www.fivefilters.org/">a readability service</a> to extract the raw content, and then attaches it to a randomly generated feed file, no database required. The random feed can be revisited to add more articles to it. Once subscribed to it via a feed-reader, the reader software does the rest. I personally am using readers like <a href="https://github.com/seazon/FeedMe">FeedMe</a> which download the full content of a feed, images included, to my phone, making all stored articles available offline for later-reading during for instance flights.</p>

<p>Since then, I have addressed several things:</p>

<ol>
  <li>
<strong>Bugs</strong>: The code structure was mixed up with HTML quite badly. After some iterations it got better and easier to maintain. Still not perfect, but much easier to read. Separating code from structure also got rid of a lot of state-tracking bugs.</li>
  <li>
<strong>External Configuration</strong>: RSS-Librarian now reads its configuration from a JSON file rather than needing source modifications. The <strong>Instance Info</strong> section at the footer of the webpage provides an overview of the current configuration.</li>
  <li>
<strong>Feed Validation</strong>: The original RSS 2.0 output created invalid feeds. This has been fixed.</li>
  <li>
<strong>Local Readability</strong>: Rather than relying on a centralized external readability service, one can now download an extra library and run the extraction of article content locally.</li>
  <li>
<strong>Disabling Content Extraction</strong>: Content extraction can now be turned <strong>off</strong>. Whilst this seems strange, some feed-readers like <a href="https://www.feedflow.dev/">FeedFlow</a> do not read RSS item content anymore and instead rely on a built-in reader-view <a href="https://support.mozilla.org/en-US/kb/firefox-reader-view-clutter-free-web-pages">similar to the ones in browsers</a> to pull content from the URL directly. To save redundant space in these cases this option will skip adding any content to the feed.</li>
  <li>
<strong>Atom Support</strong>: The <a href="https://www.ietf.org/rfc/rfc4287.txt">Atom feed format</a> is slightly more convenient when it comes to storing an author without an email address, and some other small things. I decided to leave the format RSS-Librarian uses up to the user. One can easily switch between both and RSS-Librarian will automatically convert between formats for existing feeds once a user adds a new article.</li>
  <li>
<strong>User Onboarding</strong>: On the original test-instance I noticed many feeds were repeatedly created with just one article in them, suggesting users forgot to bookmark their randomly created feed ID. Now users are onboarded with a warning before adding their first article.</li>
  <li>
<strong>Mobile CSS</strong>: RSS-Librarian now has a beautiful and easy to tap-on user interface for mobile.</li>
  <li>
<strong>iOS Share integration</strong>: With iOS Shortcuts I was able to <a href="https://www.icloud.com/shortcuts/d047b96550114317beb45bb57466a88f">create a share-integration</a> that allows any app to share a URL with your bookmarked RSS-Librarian feed.</li>
  <li>
<strong>Stylization</strong>: Both custom CSS for RSS-Librarian and XSLT for the feeds can now be configured. When opening the feed's XML file one now gets a full preview of all stored articles in a nicely formatted webpage rather than just raw XML. If no XSLT is present the code will default to <a href="https://feedreader.xyz">feedreader.xyz</a> for a quick preview of the feed. Both the logo and the FavIcon can be configured as well.</li>
  <li>
<strong>Administration</strong>: All users of an RSS-Librarian instance are pseudonymous, meaning they have an ID but no other identifier. To inform users of the instance about downtimes, changes or any other admin related notices, a special ID is pre-generated on first start up which lets an admin attach a message to all existing user feeds. Furthermore, the admin interface allows cleaning up abandoned feed files. The configuration also contains a field to add contact information for the instance administrator, which defaults to the <a href="https://github.com/thefranke/rss-librarian/issues">Github issues of RSS-Librarian</a>. The administrator ID has its own feed that keeps track of any administrator operations and URLs added by any feed managed on the instance.</li>
</ol>

<p>There are also two contributions I want to mention:</p>
<ol>
  <li>
<a href="https://github.com/thefranke/rss-librarian/pull/2">Alex Faxå cleaned up the code</a> and added more robust cURL parameters to make RSS-Librarian appear more like a regular browser.</li>
  <li>
<a href="https://github.com/thefranke/rss-librarian/pull/3">Abe Ramseyer added</a> the ability to see the list of stored items and optionally remove them again.</li>
</ol>

<p>To summarize, the code is now much easier to deploy, much easier to modify, has less bugs and creates valid RSS and Atom feeds. The user interface is visually better and easier to use on a phone.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2025_11_08_rsslib_fennec.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2025_11_08_rsslib_fennec.png" alt="RSS-Librarian running via Fennec on a LineageOS phone" title="RSS-Librarian running via Fennec on a LineageOS phone">
    
    </a>
    
    
    <figcaption>RSS-Librarian running via Fennec on a LineageOS phone</figcaption>
    
</figure>

<p>A full documentation on how to use RSS-Librarian, how to self-host and configure it and an FAQ is now <a href="https://github.com/thefranke/rss-librarian/wiki">available on the Github Wiki</a> of the project.</p>

<h1 id="store-it-well">Store it well</h1>

<p>Most importantly though, I want to announce the official RSS-Librarian instance hosted at</p>

<p><a href="https://www.rsslibrarian.ch/librarian.php">https://www.rsslibrarian.ch/librarian.php</a></p>

<p>The old test instance hosted on hstn.me had several issues that caused subscription errors, particularly with online readers like <a href="https://freshrss.org/">FreshRSS</a>, since it's HTTPS certificate did not work correctly and regularly rerouted linked XML files to error pages. Additionally hstn.me would often attach extra URL parameters that needed to be filtered out from that particular instance. W3C feed validation for the same reasons would regularly fail. For <em>Version One</em> therefore I wanted to unveil a stable main instance everyone can use for free. If you have any bugs to report, feature requests or PRs, do not hesitate to <a href="https://github.com/thefranke/rss-librarian/issues">open a Github issue</a>.</p>

<p>Join the great crusade against the enshitification of the web. We do not need to burn it all down and re-create it from the ashes, the foundational standards for a better web are already here.</p> ]]></description>
            <pubDate>Sat, 08 Nov 2025 00:00:00 +0100</pubDate>
            <link>https://www.tobias-franke.eu/log/2025/11/08/the-rss-librarian.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhvcy9BRUFyRUtjcWFDbnJCamI2MVRnTmVpSgo0Q0VYeWFyYTNMVEkvckk0bW01Nkwzd0EvMkNoK2NsMm9aZTZ0YkhmS1JQNm5YSGNiN3VtejZjVEQ0Yk1rWWYvCmR2d0cKPUovRHgKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>RSS</category><category>Deshitifaction</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Next-gen challenges for mobile game rendering</title>
            <description><![CDATA[ <div class="publication">
    <h1>Next-gen challenges for mobile game rendering</h1>
    <p>
        <span class="authors">Tobias Alexander Franke</span>
        <span class="journal">Machine Learning Meetup Dublin</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/franke25dublinml/franke25dublinml.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/franke25dublinml/franke25dublinml.jpg" alt="" title="">
    
    </a>
    
    
</figure>

    <h2>Abstract</h2>
    <p>Mobile hardware vendors are facing a growing number of game studios that seek to push AAA content on mobile platforms.</p>

<p>In this talk I present an overview of the requirements for next-gen games on mobile: AAA asset sizes, raytracing, volumetric lighting, transparency for foliage, global illumination, high-detail characters, elaborate postprocessing and supersampling, leveraging on-chip CPUs, GPUs and NPUs.</p>

<p>Each one of these categories needs to overcome certain hardware limitations before being feasible on mobile platforms. The goal is to focus on research of highly adaptive and scalable alternatives to algorithms currently in use in AAA games.</p>


    

    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://www.adaptcentre.ie/news-and-events/machine-learning-meetup-at-sandbox-vr-for-dublin-tech-week/">ADAPT Centere article</a></li>
        
        
        
        
        <li><a href="https://web.archive.org/web/20250609091021/https://www.adaptcentre.ie/news-and-events/machine-learning-meetup-at-sandbox-vr-for-dublin-tech-week/">Wayback Machine Archive</a></li>
        

        

        
    </ul>
</div> ]]></description>
            <pubDate>Mon, 09 Jun 2025 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/publications/franke25dublinml/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhyRVJRRC9RbGVFZ04xT3hIbW9kdTdTZzczZAorUFFuTWM1TWNFa0xnS2xTcWlZNk1OZ0JBTXhreGRIY3B3d01BRDBFMnhwM242MHA4VWlSTXNiN0FvalZ2N3BUClY5SUYKPW9YOVkKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Talk</category><category>Publication</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Traditional and Neural Order-Independent Transparency</title>
            <description><![CDATA[ <div class="publication">
    <h1>Traditional and Neural Order-Independent Transparency</h1>
    <p>
        <span class="authors">Grigoris Tsopouridis et al. and Tobias Alexander Franke</span>
        <span class="journal">Proceedings of Eurographics 2025</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/tsopouridis25tnoit/tsopouridis25tnoit.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/tsopouridis25tnoit/tsopouridis25tnoit.jpg" alt="" title="">
    
    </a>
    
    
</figure>

    <h2>Abstract</h2>
    <p>Order independent transparency (OIT) is a technique in computer graphics that allows for accurate rendering of transparent objects without the need to sort them in a specific order based on their depth. Traditional transparency methods often suffer from artifacts and inaccuracies due to this sorting process, especially in complex scenes with many overlapping transparent surfaces. OIT is important because it provides a more visually correct representation of transparent materials, ensuring that colors mix accurately and that all elements are rendered consistently, regardless of their draw order. This enhances realism in applications such as video games, simulations, and visual effects in films. The tutorial will provide an overview of traditional (exact, approximate and hybrid) and deep learning approaches to OIT and examine their scope, performance and accuracy.</p>


    

    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://diglib.eg.org/bitstream/handle/10.2312/egt20251001/tut1002.pdf">Content</a></li>
        
        
        
        
        <li><a href="https://diglib.eg.org/bitstream/handle/10.2312/egt20251001/transparency_tutorial_eg2025.pptx">Slides</a></li>
        
        
        
        
        <li><a href="https://cgrg.eu/publications/traditional-and-neural-order-independent-transparency">CGRG</a></li>
        
        
        
        
        <li><a href="https://diglib.eg.org/handle/10.2312/egt20251001">EG DigLib</a></li>
        
        
        
        
        <li><a href="https://eg25.cs.ucl.ac.uk">EG 2025</a></li>
        

        

        
        <li><a href="https://www.tobias-franke.eu/publications/tsopouridis25tnoit/tsopouridis25tnoit.bib">Bibtex</a></li>
        
    </ul>
</div> ]]></description>
            <pubDate>Mon, 12 May 2025 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/publications/tsopouridis25tnoit/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhwaHdRRC9TaXZhVnpCSVFWYXpyeGhVTHJSMgoxNnA1eHVrRGoxQW9QQWJQUW1UZzU1c0EvMlN1UUQ5dVJoYnJNQ2p4NnNndFRFYWM5SE82aS8vYll4akY1R2RNCmNBSU0KPVRSeUgKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Conference</category><category>Publication</category><category>Tutorial</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>A read-it-later service for RSS purists</title>
            <description><![CDATA[ <figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2024_05_robo-readable.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2024_05_robo-readable.png" alt="" title="">
    
    </a>
    
    
</figure>

<h1 id="leave-no-stylesheet-alive">Leave no stylesheet alive</h1>

<p>I read a lot. I read a lot of papers for instance. They are easy to read, because they have a common format, by which I mean their appearance. This is achieved in the academic community largely by separating markup, the language that describes what - in an article - constitutes a headline, a paragraph and so on, and the formatting of thosed marked up properties, through <a href="https://www.siggraph.org/preparing-your-content/author-instructions/">a common Latex template such as the SIGGRAPH acmtog</a> or using common formatters for simpler markup languages such as Markdown. This has one tremendous benefit: Reading a lot of articles is easy, because they all look the same.</p>

<p>I have rambled about <a href="/log/2019/08/07/in-praise-of-syndication.html">the terrible state of the web</a> before, <a href="/log/2024/04/13/redirect-everything.html">twice actually</a>. Most webpages have become <strong>unreadable</strong>, which describes a state where it is hard or impossible to find the actual content of a webpage amongst ads, banners, popups or other annoyances. This is especially disappointing if one opened said webpage with the expectation to just read some piece of text matching a headline.</p>

<p>It is ironic that HTML - being <strong>the</strong> markup language to differentiate content from style - has been abused by designers so much that a bunch of tools emerged to extract the content and throw away the style. Over a decade ago, when this problem became apparent, it was addressed with <a href="https://en.wikipedia.org/wiki/Readability_(service)">a bookmarklet called Readability</a> reformatting webpages to reduce clutter, followed by browser implementations which added so called <strong>reader views</strong>, for instance in <a href="https://support.mozilla.org/en-US/kb/firefox-reader-view-clutter-free-web-pages">Firefox</a> and <a href="https://support.brave.com/hc/en-us/articles/360045031392-What-is-Speedreader">Brave</a>.</p>

<p>Now <strong>Read-it-later</strong> services solve two problems at once and make articles offline-readable: <a href="https://www.instapaper.com/">Instapaper</a>, <a href="https://getpocket.com">Pocket</a> and the FOSS self-hostable <a href="https://wallabag.org/">Wallabag</a> download and store bookmarked articles after running them through a readability tool, so that eventually all articles have the same layout and same formatting.</p>

<h1 id="motivation">Motivation</h1>

<p>As someone who reads a lot for research (papers, blog posts, news articles etc.) I wanted to have the simplest possible version of such a read-it-later service: All my bookmarked articles look the same and are readable offline  .</p>

<h3 id="failure-1">Failure 1</h3>

<p>My first experiment was to manually store PDFs of the reader view in Firefox. That's a lot of manual labor though, and syncing across devices requires to store all PDFs on some cloud service. It's also not easy to use on the go.</p>

<h3 id="failure-2">Failure 2</h3>

<p>My second experiment was trying out <a href="https://archivebox.io/">Archivebox</a>, a neat FOSS project that streamlines archiving. Archivebox will not just download a copy of a webpage, but run it through several polishing tools. One of them is a readability library, which stores a PDF and HTML version alongside the original article. However, I found Archivebox hard to deploy on a webhost with just PHP on it (basically the things you get for free) and it produces quite bit of extra data I don't need, but otherwise ticks almost all the boxes.</p>

<h3 id="failure-3">Failure 3</h3>

<p>My third experiment ended in <a href="https://github.com/Ranchero-Software/NetNewsWire/issues/3023">a personal frustration of mine</a>: My workflow for reading anything I am interested in is by adding a star in my RSS reader to an article, which necessitates that anything I want to read is somehow a subscription or feed I can add to my RSS reader, and that isn't true for individual articles I stumble across. Sometimes this isn't even true for articles on blogs, where the blog is either too huge to subscribe to for just this one article, or (a sad occurunce these days) it doesn't have RSS at all, even though that is trivial to add.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2024_05_reeder-instpaper.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2024_05_reeder-instpaper.jpg" alt="" title="">
    
    </a>
    
    
</figure>

<p>Before switching to the most excellent <a href="https://netnewswire.com/">NetNewsWire</a> reader, I was fond of <a href="https://reederapp.com/">Reeder</a>, which includes the functionality to add read-it-later services like Instapaper as accounts, showing the same interface for both RSS and read-it-later subscriptions. This, essentially, made Reeder my one-stop reading application, indifferent to whether the article came from a single URL I wanted to read or from any of my RSS feeds. Whenever there was an article I wanted to read, I'd simply push it to an Instapaper or Pocket account and then fetch it via Reeder. However, this had one major downside: I'd use a centralized service that gradually became a huge data dump, as more and more articles ended up in those accounts.</p>

<h3 id="failure-4">Failure 4</h3>

<p>My fourth experiment involved RSS itself. NetNewsWire, like most RSS readers, is a minimalist, pure RSS reader, which means anything you want to <strong>star</strong> inside the application has to come from an RSS feed you can subscribe to.</p>

<p>Both <a href="https://getpocket.com">Pocket</a> and <a href="https://wallabag.org/">Wallabag</a> allow you to subscribe to your personal collection of articles via RSS, essentially treating your article collection as some kind of blog with new posts appearing whenever you add an article. However, this approach had the same downside as when I was using Reeder: The read-it-later accounts become a huge dump after a while, because I was not <em>really</em> interested in either Pocket or Wallabag, only in redirecting saved articles to my RSS reader. This solution however was extremely close to optimal, with just the exception of me logging into my accounts in regular intervals to delete everything.</p>

<h1 id="requirements">Requirements</h1>

<p>From the four failures I learned that what I wanted was actually this:</p>

<blockquote>
  <p>Send a URL to a script, which runs it through readability and attaches the resulting content directly to an RSS feed. Ideally, I can create different feeds for different collections, for example one for work and for personal interests.</p>
</blockquote>

<p><strong>Specifically, I want to:</strong></p>
<ul>
  <li>Store single articles in a RSS reader application</li>
  <li>Avoid third-party read-it-later services such as <a href="https://getpocket.com">Pocket</a>, <a href="https://www.instapaper.com">Instapaper</a> or <a href="https://wallabag.org/">Wallabag</a>
</li>
  <li>Minimize the amount of necessary apps for reading articles</li>
  <li>Get rid of accounts and not sign up to anything</li>
  <li>Read articles (offline) in a readable format, but not categorize or store them indefinitely</li>
  <li>Synchronize stored articles to multiple devices</li>
  <li>Optionally be able to self-host the whole architecture</li>
</ul>

<p>The outcome of this is a project I call <a href="https://github.com/thefranke/rss-librarian">RSS-Librarian</a>, a read-it-later service for RSS purists. RSS-Librarian solves all of these bullet points with a single, self-hostable PHP file that extracts content from URLs using <a href="https://www.fivefilters.org/">a readability service</a> and adds what remains as new entries into a personal RSS feed, without requiring special libraries, a database or user accounts.</p>

<h1 id="how-it-works">How it works</h1>

<p>You can drop <code class="language-plaintext highlighter-rouge">librarian.php</code> onto any host that supports PHP with no other requirements. RSS-Librarian has two parameters: <code class="language-plaintext highlighter-rouge">librarian.php?id=HASH&amp;url=SOMEPAGE</code>.</p>

<ul>
  <li>
<code class="language-plaintext highlighter-rouge">id</code> is a random ID for a personal feed. If this parameter is not supplied, RSS-Librarian will generate a new one and add a feed file corresponding to <code class="language-plaintext highlighter-rouge">id</code> into the subfolder <code class="language-plaintext highlighter-rouge">feeds/</code>.</li>
  <li>
<code class="language-plaintext highlighter-rouge">url</code> is a URL you submit to RSS-Librarian, whose content will be extracted and added to the feed corresponding to <code class="language-plaintext highlighter-rouge">id</code>.</li>
</ul>

<p>For each <code class="language-plaintext highlighter-rouge">url</code> posted to RSS-Librarian, the extracted content will be added to a RSS file derived from <code class="language-plaintext highlighter-rouge">id</code> - if it exists in the <code class="language-plaintext highlighter-rouge">feeds/</code> folder. If it does not exist, it will be generated and written. The RSS file will store a maximum of 100 entries before removing the oldest one and adding the new one.</p>

<h1 id="an-example-use-case">An example use-case</h1>

<p>I will be using the <a href="http://alternator.hstn.me/librarian.php">this demo instance of RSS-Librarian</a> in combination with <a href="https://netnewswire.com/">NetNewsWire</a> on MacOS (<a href="https://netnewswire.com/NetNewsWire.zip">MacOS App</a>).</p>

<p>I can recommend <a href="https://netnewswire.com/">NetNewsWire</a> on iOS (<a href="https://apps.apple.com/us/app/netnewswire-rss-reader/id1480640210">App Store Link</a>), <a href="https://github.com/seazon/FeedMe">FeedMe</a> on Android (<a href="https://play.google.com/store/apps/details?id=com.seazon.feedme">Play Store Link</a>, <a href="https://github.com/seazon/FeedMe/releases">APK</a>) and <a href="https://nodetics.com/feedbro/">FeedBro</a> for various Browsers (<a href="https://addons.mozilla.org/firefox/addon/feedbroreader/">Firefox Addon</a>, <a href="https://chrome.google.com/webstore/detail/feedbro/mefgmmbdailogpfhfblcnnjfmnpnmdfa">Brave/Chromium Addon</a>). They all download offline copies of RSS feeds to your device ready for access even when disconnected completely.</p>

<h3 id="step-1-create-your-personal-feed">Step 1: Create your personal feed</h3>

<p>Assume we have <a href="https://ohshitgit.com/">the following article</a> we want to store for later reading. First go to <a href="http://alternator.hstn.me/librarian.php">the RSS-Librarian instance</a>. You'll be greeted by the following interface.</p>

<figure class="basic-figure">

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2024_05_rss-librarian-step-1.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2024_05_rss-librarian-step-1.png" alt="The view of RSS-Librarian on first visit" title="The view of RSS-Librarian on first visit">
    
    </a>
    
    
    <figcaption>The view of RSS-Librarian on first visit</figcaption>
    
</figure>

<p>This instance is currently hosting 8 other feeds. Paste the article URL <code class="language-plaintext highlighter-rouge">https://ohshitgit.com/</code> into the empty field and press <em>Add to feed</em>. Because you are a new user, RSS-Librarian will now generate a random, unique user ID for you.</p>

<figure class="basic-figure">

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2024_05_rss-librarian-step-2.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2024_05_rss-librarian-step-2.png" alt="RSS-Librarian after adding the first link" title="RSS-Librarian after adding the first link">
    
    </a>
    
    
    <figcaption>RSS-Librarian after adding the first link</figcaption>
    
</figure>

<p>On the next page you will see <strong>two important things</strong>:</p>
<ol>
  <li>Your <em>personal URL</em>: Store this URL in your bookmarks! This allows you to add more articles to your <em>personal RSS feed</em>.</li>
  <li>Your <em>personal RSS feed</em>: This is your feed that you can subscribe to with your RSS reader application. It is unique and can only be managed with the <em>personal URL</em> above.</li>
</ol>

<p><strong>It is important</strong> - after adding your first link - to bookmark your <em>personal URL</em> somewhere so you can keep adding links to your feed (instead of creating a new one accidentally)!</p>

<h3 id="step-2-open-your-personal-url">Step 2: Open your personal URL</h3>

<p>For this demo, the <em>personal URL</em> now links to <code class="language-plaintext highlighter-rouge">http://alternator.hstn.me/librarian.php?id=ff4d8b605c2cffacd19639af2a1d3ff8712021d66431de86f425e0279a1e768a</code>. RSS-Librarian, on first use, will create a random hash (in our case <code class="language-plaintext highlighter-rouge">ff4d...768a</code>). This hash will be used to store new URLs to your <em>personal RSS feed</em>, which is derived from that hash. You can think of this as your user ID.</p>

<p>Open the <em>personal URL</em> with a browser again.</p>

<figure class="basic-figure">

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2024_05_rss-librarian-step-3.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2024_05_rss-librarian-step-3.png" alt="RSS-Librarian personal URL" title="RSS-Librarian personal URL">
    
    </a>
    
    
    <figcaption>RSS-Librarian personal URL</figcaption>
    
</figure>

<p>With this page you can add more articles to your <em>personal RSS feed</em>. Let's <a href="https://plus.maths.org/content/godel-and-limits-logic">add another article</a> to the feed we just created.</p>

<figure class="basic-figure">

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2024_05_rss-librarian-step-4.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2024_05_rss-librarian-step-4.png" alt="Adding another article to the personal RSS feed" title="Adding another article to the personal RSS feed">
    
    </a>
    
    
    <figcaption>Adding another article to the personal RSS feed</figcaption>
    
</figure>

<p>You can now point your RSS reader to your <em>personal RSS feed</em> and check out the result. At the bottom of the page you can find additional tools:</p>

<ul>
  <li>
<em>Feed bookmarklet</em>: A bookmarklet you can use instead of the <em>personal URL</em>. This will add the currently open page to your <em>personal RSS feed</em>.</li>
  <li>
<em>Feed preview</em>: If you have no RSS viewer at hand you can preview your <em>personal RSS feed</em> with this page.</li>
</ul>

<h3 id="step-3-add-it-to-an-rss-reader-of-your-choice">Step 3: Add it to an RSS reader of your choice</h3>

<p>Let's try this out with NetNewsWire on MacOS. Copy the link <em>personal RSS feed</em>, press the + symbol in NetNewsWire and add the URL.</p>

<figure class="basic-figure">

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2024_05_rss-librarian-step-5.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2024_05_rss-librarian-step-5.png" alt="Adding the personal RSS feed to NetNewsWire" title="Adding the personal RSS feed to NetNewsWire">
    
    </a>
    
    
    <figcaption>Adding the personal RSS feed to NetNewsWire</figcaption>
    
</figure>

<p>After adding the feed, your stored articles appear in your RSS reader. Most RSS readers will automatically download all articles in a feed, meaning you also have a stored copy offline on the go, for instance when reading on a plane without WiFi.</p>

<figure class="basic-figure">

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2024_05_rss-librarian-step-6.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2024_05_rss-librarian-step-6.png" alt="A view of the stored articles" title="A view of the stored articles">
    
    </a>
    
    
    <figcaption>A view of the stored articles</figcaption>
    
</figure>

<p>If you click the title of the article, you will end up on the original URL (in the above sample our <a href="https://ohshitgit.com/">first article</a>). If you open the feed itself - <em>RSS-Librarian (ff4d)</em> - you will jump back to your <em>personal URL</em> where you can add more articles to the feed.</p>

<p>If you do not have an RSS reader around, you can also click on <em>Feed preview</em> in the tools section of your <em>personal URL</em> to get a quick web-based view of the feed.</p>

<figure class="basic-figure">

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2024_05_rss-librarian-step-7.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2024_05_rss-librarian-step-7.png" alt="Viewing a feed preview using feedreader.xyz" title="Viewing a feed preview using feedreader.xyz">
    
    </a>
    
    
    <figcaption>Viewing a feed preview using feedreader.xyz</figcaption>
    
</figure>

<p>And that is pretty much it. By visiting your <em>personal URL</em> on a RSS-Librarian instance, you can add more  articles to your <em>personal RSS feed</em> and they will become easy to read and storable offline by your reader software. You can subscribe to your <em>personal RSS feed</em> with as many readers as you want and even share that feed with others (which however will let them add articles to it too).</p>

<h1 id="deploying-your-own-instance">Deploying your own instance</h1>

<p>Simply get a copy of <code class="language-plaintext highlighter-rouge">librarian.php</code> from the <a href="https://github.com/thefranke/rss-librarian">Github repository</a> and put it on any VPS, Raspberry Pi or other webserver that has PHP. There are no other requirements. Create a directory called <code class="language-plaintext highlighter-rouge">feeds</code> right next to the file and give the webserver write access to that folder.</p>

<p>In <code class="language-plaintext highlighter-rouge">librarian.php</code> you can configure some globals:</p>
<ul>
  <li>
<code class="language-plaintext highlighter-rouge">$g_max_items</code> is the number of articles stored per feed. Adding a new article beyond that number will remove the last one in the feed. Reduce this number if you're tight on space.</li>
  <li>
<code class="language-plaintext highlighter-rouge">$g_dir_feeds</code> is the directory where user feeds are stored. Rename this if you want to use a different directory on your instance.</li>
</ul>

<h1 id="future-work">Future work</h1>

<p>There are a bunch of things I want to implement.</p>

<h3 id="protected-shareable-feeds">Protected shareable feeds</h3>

<p>Currently the <code class="language-plaintext highlighter-rouge">id</code> given to RSS-Librarian is used to create a feed in the <code class="language-plaintext highlighter-rouge">feeds/</code> directory that has the same name. This means that sharing this feed with others will allow them to access the <em>personal URL</em> as well and add articles. I'd like to change this behavior so that the feed's file name is generated by hashing the <code class="language-plaintext highlighter-rouge">id</code> again. This way, the user id and the feed id are separated but correlated in one direction: The <code class="language-plaintext highlighter-rouge">id</code> can be used to generate the corresponding feed file name, but not vice versa. This comes at the cost of usability: The feed currently links back to it's <em>personal URL</em>, which means you can quickly jump to the place where you can add more articles. This would need to be removed to not give away that page.</p>

<h3 id="remove-fivefilters-dependency">Remove FiveFilters dependency</h3>

<p>RSS Librarian sends articles to FiveFilters, which extracts them into single RSS entries and then appends that entry to the locally hosted feed. This is not optimal, because the feed generation is essentially centralized and not independent. Instead, RSS-Librarian needs to do the extraction locally via the FiveFilters <a href="https://github.com/fivefilters/readability.php">Readability library for PHP</a>.</p>

<h3 id="maintenance">Maintenance</h3>

<p>Every user can add a new feed at any time and as many as they want. An instance can quickly create a huge dump inside the feeds folder full of garbage feeds that are abandoned because users forgot to bookmark their <em>personal URL</em>, or because they lost the link somehow later.</p>

<p>On every new article added RSS-Librarian could run through the <code class="language-plaintext highlighter-rouge">feeds/</code> folder and simply delete feeds that are too old, for instance those that had no new items added to them for more than 6 months. This is not an issue if the <code class="language-plaintext highlighter-rouge">id</code> gets re-used again: RSS-Librarian recreates files for <code class="language-plaintext highlighter-rouge">id</code>s when another article URL is added.</p>

<h3 id="share-feature-for-mobile">Share-feature for mobile</h3>

<p>App based read-it-later services tie into the OS and let you share an article with the app, which is the default way to add an article quickly to the read-it-later list. RSS-Librarian however has no app and uses a webpage instead, which makes quickly adding an article to a <em>personal RSS feed</em> tedious.</p>

<p>Currently, one can get to the <em>personal URL</em> by simply opening the <em>personal RSS feed</em> URL. Ideally however I'd like a simple share button that adds some URL being shared from any app.</p>

<h3 id="main-instance">Main instance</h3>

<p>An RSS-Librarian demo instance for testing <a href="https://alternator.hstn.me/library.php">is currently hosted here</a>. The instance has a self-signed certificate and therefore many RSS readers will run into issues. Using HTTP instead is a no-go though. I want to eventually host a reliable main instance.</p>

<h1 id="the-great-crusade-against-web-enshitification">The Great Crusade against Web-Enshitification</h1>

<p>I've been using RSS-Librarian just for myself for about half a year now and it's been a delight so far, given how compact the code is. I created two feeds for myself: One for articles I'm interested in personally, and one for stuff I need for work. I subscribe to my work feed using my personal devices and with <a href="https://nodetics.com/feedbro/">FeedBro</a> on my work machine, which means I can easily add and read articles I find for work everywhere, whereas the feed containing articles I'm just interested in personally is only in my feed readers on my own devices. It's all neatly separated and easy to manage.</p>

<p>Of course I wrote this for myself initially, so if you find it weird, confusing, chaotic, or you'd like to see a feature, please <a href="https://github.com/thefranke/rss-librarian/issues">open an issue</a> or let me know otherwise.</p> ]]></description>
            <pubDate>Sun, 28 Apr 2024 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/log/2024/04/28/a-reader-service-for-rss-purists.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhyREh3RCtLOEFsbFhucFJHbUFNTVMxTTFRSQpRTjJRNlBGVzZUYVlHMmpFTjhOR0ZNSUEvMEJpMHRQY2RLdVcxcmZ6L0VOOWs3dnJuVDh3WDJCY3ozMDBseXhtCkxkVUcKPVdpNlQKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>RSS</category><category>Notes</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Redirect everything</title>
            <description><![CDATA[ <figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2024_04_web-intro-lol.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2024_04_web-intro-lol.png" alt="" title="">
    
    </a>
    
    
</figure>

<h1 id="the-web-is-terrible-redux">The web is terrible: Redux</h1>

<p>The screams are getting louder, but the solutions keep getting dumber.</p>

<p>I know that my background as a graphics programmer is adding heavy bias to my point of view, but I really do not send data-oriented design lectures from Mike Acton to random Electron-app developers because I think their code is slow. Nevertheless, corporate web programmers have collectively given up on the notion that there is something called <em>efficiency</em>. (No time for a good rant? Go to the <a href="#tldr">TL;DR</a>!)</p>

<p>Case in point: Most news websites have tiny irrelevant content that hides between full-screen ads, newsletter popups, cookie warnings, autoplaying videos, and Javascript yanking mouse control away from you to add custom scrolling behavior. Look, for instance, at the following <a href="https://twitter.com/karpathy/status/1435827240286109702">capture from Andrej Karpathy</a>.</p>

<figure>
    
    <video class="video" style="width:720px;" controls="">
      <source style="width:720px;" src="https://www.tobias-franke.eu/layout/logcache/2024_04_web-nonsense-sample-1.mp4" type="video/mp4">
      Your browser does not support the video tag.
    </source></video>
    
    
    <figcaption>Browsing the web, 2021</figcaption>
    
</figure>

<p>Of course this browser-violation is just the tip of the iceberg, as there is more code that is running in the background, streaming ever more useless stuff to the machine. Here is a sample <a href="https://twitter.com/lunasorcery/status/1277690244725460994">captured by Lunasorcery</a>.</p>

<figure>
    
    <video class="video" style="width:720px;" controls="">
      <source style="width:720px;" src="https://www.tobias-franke.eu/layout/logcache/2024_04_web-nonsense-sample-2.mp4" type="video/mp4">
      Your browser does not support the video tag.
    </source></video>
    
    
    <figcaption>Imagine getting paid for writing software like this!</figcaption>
    
</figure>

<p>Think about just how much all of this, per tab, eats into your CPU time for no good reason, heats up the machine and drains battery of your mobile device, even if you do absolutely nothing on that page. Soon mobile 4G won't provide adequate bandwidth to load a simple news article anymore. Part of the problem here is that modern web design is a lazy pieced together conglomerate of hilarious libraries such as the <a href="https://www.npmjs.com/package/is-odd?activeTab=code">is-odd node module</a>.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2024_04_web-is-odd-node.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2024_04_web-is-odd-node.png" alt="A modulo operation, web-style!" title="A modulo operation, web-style!">
    
    </a>
    
    
    <figcaption>A modulo operation, web-style!</figcaption>
    
</figure>

<p>The pyramid-scheme continues with an even greater absurdity, also known as the <a href="https://www.npmjs.com/package/is-even?activeTab=code">is-even node module</a>, which relies on - you guessed it - is-odd. To put that into perspective: is-even loads 254 bytes in addition to is-odd loading another 543 bytes for essentially this operation <code class="language-plaintext highlighter-rouge">(n % 2) == 0</code> (12 bytes), or <strong>6641% of the necessary bytes to load</strong>. You can imagine how the rest of your average page looks like. If you believe this is just an elaborate prank or a contrived example, I present to you a sample of this <a href="https://github.com/micromatch/nanomatch/pull/7/commits/dba3131bcafbfc6c6d009e7d1591427ef7d71d82">being used in a glob matcher/parser</a> before someone had the decency to remove it, make a mistake in the process and <a href="https://github.com/micromatch/nanomatch/pull/7/commits/573338f1f118775f0c16370d989e6079aa2a6c68">summon the comment from hell</a>.</p>

<p>So here we are, with your browser wading through mountains of garbage, to display a simple piece of text to you. One way to deal with this is to fully embrace Stockholm-syndrome and <a href="https://noyb.eu/en/pay-or-okay-explained-why-more-and-more-websites-make-you-pay-your-privacy">just pay the ransom</a> some of these sites demand from you to make the pain go away. Then there are a variety of tools that have been introduced to combat these issues, ranging from <a href="https://support.mozilla.org/en-US/kb/firefox-reader-view-clutter-free-web-pages">reader-views built into browsers</a>, using <strong>Readability services</strong> (just read that out loud) such as <a href="https://wallabag.org/">Wallabag</a>, <a href="https://getpocket.com/saves">Pocket</a> or <a href="https://www.instapaper.com/">Instapaper</a>, or simply adding dozens of plugins into your browser to make pages look sane such as <a href="https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/">user scripts like GreaseMonkey</a> and <a href="https://addons.mozilla.org/en-US/firefox/addon/styl-us/">user stylesheets like Stylus</a> to, and I quote:</p>

<blockquote>
  <p>Redesign your favorite websites</p>
</blockquote>

<p>However, all of this trickery will still not get the major problem under control: Browsers need ever more powerful machinery to display the most mundane content to you.</p>

<p>Never fear, the age of cloud is here! So why not put the browser on <em>a different machine</em> that is 10x more powerful and <strong>STREAM</strong> what you see to your machine, that extra-layer will solve it!</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2024_04_mighty-browser-alive.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2024_04_mighty-browser-alive.png" alt="Architecture Astronaut hard at work" title="Architecture Astronaut hard at work">
    
    </a>
    
    
    <figcaption>Architecture Astronaut hard at work</figcaption>
    
</figure>

<p>Turns out that this idea is still a tad bit too ludicrous to be implemented these days, so lucky for us this project is dead.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2024_04_mighty-browser-dead.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2024_04_mighty-browser-dead.png" alt="RIP ... and may you stay dead forever" title="RIP ... and may you stay dead forever">
    
    </a>
    
    
    <figcaption>RIP ... and may you stay dead forever</figcaption>
    
</figure>

<p><a id="tldr"></a>
Let's recap where we are right now:</p>
<ul>
  <li>Your browser is as complex as an operating system</li>
  <li>Browser complexity is the source of many security issues</li>
  <li>(Corporate) websites abuse browser complexity at every opportunity</li>
  <li>Even if you counter the abuse with layers and layers of tools, the issue is getting worse over time</li>
  <li>There is no incentive for cooperations to reverse course</li>
  <li>New programmers are born into habits of being as inefficient as possible</li>
</ul>

<h1 id="the-alternative">The alternative</h1>

<p>My number one suggestion is to leave most of the web entirely and <a href="/log/2019/08/07/in-praise-of-syndication.html">pipe as much content as you can through RSS</a>.</p>

<p><em>However</em>, that doesn't take care of individual URLs and sites that simply don't have an RSS feed. Every day someone sends me a link to one of the various horrible social media sites that I need to then open in a browser.</p>

<p>This is where a new open-source trend comes to the rescue: The <a href="https://github.com/topics/alternative-frontends">Alternative Frontend</a> movement. Alternative frontends, as the name suggest, are websites that provide a different frontend to an existing webpage.</p>

<p>For instance, <a href="https://invidio.us/">Invidious</a> is a popular alternative frontend for Youtube. It provides access to anything on Youtube, however in a much cleaner interface with much less clutter, no ads, zero tracking and directly downloadable MP4s in place of the video streaming.</p>

<p>Right now there are many frontends to popular websites that extract their content and present it in a much saner way. These include Youtube, Twitter, Reddit, Tiktok, Imgur, Reuters, Quora, IMDB, Medium, Google, Stack Overflow, Fandom Wiki, Snopes… the list keeps growing, because most of the original pages are terrible.</p>

<p>A lot of these alternatives will use either public APIs or scraping to fetch the content of the original page and display it to you, but those details are not important.</p>

<h1 id="this-way-please">This way please</h1>

<p>One key question for me is how to use these frontends in a manner that is not intrusive, easy to use and allows me to open random links and get the best possible result.</p>

<p>The easiest way I have found is to use a <strong>redirector plugin</strong> for your browser. These plugins will detect a URL you enter into the browser, match it with a regular expression, and if a match has been found, replace it with another.</p>

<p>For instance, you might detect the pattern <code class="language-plaintext highlighter-rouge">https://www.youtube.com/*</code> and replace it with <code class="language-plaintext highlighter-rouge">https://invidious.fdn.fr/$1</code>.</p>

<p>A URL to <code class="language-plaintext highlighter-rouge">https://www.youtube.com/channel/UCYO_jab_esuFRV4b17AJtAw</code> (the 3Blue1Brown channel) will therefore be redirected to <code class="language-plaintext highlighter-rouge">https://invidious.fdn.fr/channel/UCYO_jab_esuFRV4b17AJtAw</code>.</p>

<p>These plugins allow you to add multiple patterns for multiple webpages. I have created a configuration you can simply import without the need to add all these patterns by hand, <a href="https://gist.github.com/thefranke/d6a8137e7bf8c837b64621a3a1f9b9b9">which is available here</a>.</p>

<h3 id="redirector-plugins-for-firefoxlibrewolftorbrowser">Redirector plugins for Firefox/Librewolf/Torbrowser</h3>
<ul>
  <li>
<a href="https://addons.mozilla.org/en-US/firefox/addon/redirector">Redirector</a> - General purpose redirector you can customize yourself</li>
  <li>
<a href="https://addons.mozilla.org/en-US/firefox/addon/libredirect">LibRedirect</a> - Redirector to popular alternative frontends, highly configurable</li>
  <li>
<a href="https://addons.mozilla.org/en-US/firefox/addon/privacy-redirect">Privacy Redirect</a> - Redirector to popular alternative frontends, but limited in scope</li>
</ul>

<h3 id="redirector-plugins-for-safari">Redirector plugins for Safari</h3>
<ul>
  <li>
<a href="https://apps.apple.com/de/app/redirect-web-for-safari/id1571283503">Redirect Web for Safari</a> - General purpose redirector you can customize yourself (can load exported Firefox Redirector JSON)</li>
  <li>
<a href="https://apps.apple.com/de/app/privacy-redirect/id1578144015">Privacy Redirect</a> - Redirector to popular alternative frontends, but limited in scope</li>
</ul>

<h1 id="load-balancing">Load-Balancing</h1>

<p>Many of the alternatives are hosted privately by volunteers and are called <em>instances</em>, which means there isn't one URL for one alternative, but many. This decentralization acts as a safety net: Banning one doesn't bring down the service itself and distributes the load.</p>

<p>As an example, there are many <a href="https://api.invidious.io/">Invidious instances available</a>. However, using one of them exclusively has two downsides:</p>
<ol>
  <li>It might stop working, get banned, rate-limited or shut down</li>
  <li>If many users pile on one instance, it will be overloaded and most likely rate-limited more quickly</li>
</ol>

<p>To solve this issue, ideally recognizing a pattern should redirect to a <em>random instance</em> instead of a fixed one, so that the load distributes equally across all hosted instances and prevents cases of rate-limiting or banning of an instance. This would need two things: For each alternative frontend a list of all available instances that is up to date, and the ability to forward to  randomly selected URLs. The forwarding though is the problem.</p>

<p>A solution to this comes in the form of redirecting-gateways such as <a href="https://github.com/benbusby/farside">Farside</a>. It's <a href="https://farside.link/">a simple page</a> that lists frontends and their instances. For each frontend, there is one link at the top of each section which will forward you randomly to one of the instances. You can configure your redirector to forward your URL to use those as targets instead.</p>

<p>As an example, instead of redirecting <code class="language-plaintext highlighter-rouge">https://www.youtube.com/*</code> to <code class="language-plaintext highlighter-rouge">https://invidious.fdn.fr/$1</code>, you may want to redirect it to <code class="language-plaintext highlighter-rouge">https://farside.link/invidious/$1</code>, which will select a random Invidious instance and attach your $1 parameters to it.</p>

<p>I provide a simplified, self-hostable redirector-gateway <a href="https://github.com/thefranke/tzeentch">called Tzeentch</a> which is heavily inspired by Farside, but simpler to deploy. You can <a href="https://alternator.hstn.me">check out a demo instance here</a>. It also adds a feature absent in Farside, which is <a href="https://alternator.hstn.me/?_redirector_config">to create a ready-made configuration for your redirector</a> for all the frontends you select.</p>

<p><a href="https://addons.mozilla.org/en-US/firefox/addon/libredirect">LibRedirect</a> on the other hand maintains an internal list of instances where you can select one or more favorite ones that it chooses from randomly, but this is connected to a bit of manual labor every now and then.</p>

<h1 id="conclusion">Conclusion</h1>

<p>The battle for a cleaner, saner and less intrusive web is lost.</p>

<p>Alternative frontends remove the need for a barrage of plugins to sanitize the web, are privacy-friendly and just generally provide a better experience. Redirector plugins using gateways which distribute the requests among frontend-instances will ensure decentralization and reduce the likelihood of instances getting rate-limited or banned.</p>

<p>I highly encourage you to <strong>redirect everything</strong> and check out this way of browsing! Just take a look at <a href="https://libreddit.eu.org/">LibReddit</a> and compare it with Reddit itself, for instance on a phone. Ask yourself: Is this better than the original?</p> ]]></description>
            <pubDate>Sat, 13 Apr 2024 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/log/2024/04/13/redirect-everything.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhwbDdRRCtNSWVKSy9WWnd2ZlRVWXd5UHNaWQptbzA0ZWlWMVp0UTNlcDJ4aFhtcUljd0JBS3Roc0c0R2pSbFRkUVRFZXRzeVptbFloT3lGaU1jVDRhQTZSMzBrCjQ1VUQKPUZlaXYKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Commentary</category><category>RSS</category><category>Notes</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Engine Design as a Holistic Adventure</title>
            <description><![CDATA[ <div class="publication">
    <h1>Engine Design as a Holistic Adventure</h1>
    <p>
        <span class="authors">Tobias Alexander Franke and Royal O'Brien</span>
        <span class="journal">Tales from the 3rd Dimension Podcast</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/franke22edaaha/franke22edaaha.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/franke22edaaha/franke22edaaha.jpg" alt="" title="">
    
    </a>
    
    
</figure>

    <h2>Abstract</h2>
    <p>Most listeners know that Huawei isn’t just a mobile phone company. But many might not realize that it runs a global network of research institutes that are working on optimizing engine development. Tobias Alexander Franke, principal game engine architect at Huawei, talks with Royal about why the company prefers working with open source 3D engines over commercial 3D engines. Highlighting the benefits of different perspectives, he believes open source collaboration results in a more well-rounded engine. Tobias and Royal reflect on other hot topics like the early days of the Open 3D Engine (O3DE) in GitHub, the need for standardization for greater interoperability and the development of the Metaverse.</p>

<p>Tobias Alexander Franke, principal game engine architect at Huawei, is an experienced graphics engineer with a background in both graphics research and the gaming industry. He holds a PhD from TU-Darmstadt focused on augmented reality, relighting and global illumination.</p>


    

    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://podcasts.apple.com/us/podcast/engine-design-as-a-holistic-adventure/id1646520722?i=1000583075509">Apple Podcasts</a></li>
        
        
        
        
        <li><a href="https://open.spotify.com/show/1zUZ2crUiWSm7J2P88QMw1">Spotify Podcasts</a></li>
        
        
        
        
        <li><a href="https://www.deezer.com/us/show/5213737">Deezer Podcast</a></li>
        

        

        
    </ul>
</div> ]]></description>
            <pubDate>Tue, 18 Oct 2022 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/publications/franke22edaaha/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhxYWNBRUFydzNtOHZRb3pyTm9OdHNldjVPcAplL2drZGVCV1FuekhoZEtuc1NYZUxxRUEvUnppeS9tdzhyVUNZQnRCVzk2TzRONm9td3Q4aXBkSzdHKzdZT1dGCnpMTUwKPXltY1MKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>O3DE</category><category>Architecture</category><category>Open Source</category><category>Publication</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Life in the metaverse will be revolutionary</title>
            <description><![CDATA[ <div class="publication">
    <h1>Life in the metaverse will be revolutionary</h1>
    <p>
        <span class="authors">Tobias Alexander Franke and Sandra O'Connell</span>
        <span class="journal">Irish Times</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/franke22irishtimes/franke22irishtimes.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/franke22irishtimes/franke22irishtimes.jpg" alt="" title="">
    
    </a>
    
    
</figure>

    <h2>Abstract</h2>
    <p>I have been interviewed by the Irish Times on the topic of the Metaverse, what it is, why one would be interested in it, the big problem it is supposed to solve and how it will affect our lives. In this interview, I give a sober analysis of the Metaverse, and my goal is to cut out the hype.</p>


    

    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://www.irishtimes.com/special-reports/2022/07/21/life-in-the-metaverse-will-be-revolutionary/">Irish Times Article</a></li>
        
        
        
        
        <li><a href="https://web.archive.org/web/20220724204315/https://www.irishtimes.com/special-reports/2022/07/21/life-in-the-metaverse-will-be-revolutionary/">Wayback Machine Archive</a></li>
        

        

        
    </ul>
</div> ]]></description>
            <pubDate>Thu, 21 Jul 2022 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/publications/franke22irishtimes/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhvVTVBRCtPbmViOC81L1lGS0c0aGFyZWVDbAp1VGRITDFkNWR6NDh6d1lCWEV1bXhXb0JBTE1nbWNiUHBETi9lcFJDRUh2cU1QYUVKNnplUWlLN3laYldVN2dxCjNwZ0QKPWxkb2wKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Interview</category><category>Metaverse</category><category>Cryptocurrency</category><category>Publication</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>A New Gaming Eco-system for Huawei</title>
            <description><![CDATA[ <div class="publication">
    <h1>A New Gaming Eco-system for Huawei</h1>
    <p>
        <span class="authors">Tobias Alexander Franke</span>
        <span class="journal">Huawei GSTS 2022</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/franke22angefh/franke22angefh.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/franke22angefh/franke22angefh.jpg" alt="" title="">
    
    </a>
    
    
</figure>

    <h2>Abstract</h2>
    <p>Cloud-based gaming services have seen varying success in practice. Where-as multiplayer games feature a wide variety of aspects which are hosted remotely, most games today run exclusively on one device and require the customer to adapt to new hardware requirements in regular cycles, usually tied to the current console generation. Streaming-based gaming services have in contrast failed so far, underestimating technological challenges as well as selling to the wrong market. Edge computing architectures are designed to distribute computational tasks to other nodes in a network or cloud. Huawei's involvement in the Open 3D Engine presents a unique opportunity to build a more resilient architecture for game engines that scale from one device to an arbitrary number. In doing so, a new gaming eco-system evolves as a business opportunity for Huawei.</p>


    

    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://huawei-events.de/en/gsts22-tobias-alexander-franke.htm">Huawei GSTS 2022</a></li>
        

        

        
    </ul>
</div> ]]></description>
            <pubDate>Thu, 07 Jul 2022 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/publications/franke22angefh/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhwNk9nRDlHYmFrYkZZWTFOUzFqenBFVG1XVwpkYlZkUHRiZGNjYWdFeURYNnJvRTlSa0EvMkhnZzEwZDd4cDNLUHlBRmRmalY4VlZtbGFZc1B0N0ZldGxYY0xWCmRQVU0KPTVxQ3UKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Conference</category><category>Publication</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Scalable Global Illumination for O3DE</title>
            <description><![CDATA[ <div class="publication">
    <h1>Scalable Global Illumination for O3DE</h1>
    <p>
        <span class="authors">Tobias Alexander Franke</span>
        <span class="journal">O3DECon 2021</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/franke21sgifo/franke21sgifo.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/franke21sgifo/franke21sgifo.jpg" alt="" title="">
    
    </a>
    
    
</figure>

    <h2>Abstract</h2>
    <p>Implementing global illumination for games at scale (on mobile devices and up to high end PCs) is a hard problem. O3DE currently uses DDGI as a solution to provide both baked and real-time global illumination. However several key issues need to be addressed before this system is ready for production. In this talk I want to address those issues:</p>

<ul>
  <li>Scalability and streaming</li>
  <li>Authoring with small or thin objects</li>
  <li>Probe aliasing</li>
  <li>Volume overlaps and blending</li>
  <li>Baking large scenes</li>
  <li>Strange mixtures of baked and real-time probe based volumes</li>
  <li>Baked lighting variants for day-night cycles or similar setups</li>
  <li>Re-using visibility buffers for other effects</li>
  <li>Going beyond diffuse GI</li>
</ul>

<p>The idea is to highlight each issue, present a small solution (not necessarily <em>the</em> solution) based off experience from developing similar volume based GI systems in other engines, and make more people from the community - both artists and programmers - aware of these issues so we can collectively come up with a proper system that has an easy workflow and provides great results when computing GI for a project. The talk will close with an outlook to some experiments, novel workflows and how the same system could be used to provide GI for large open worlds, maybe even on mobile devices.</p>


    

    
    <h2>Supplemental Video</h2>
    
    <div class="video default-size">
        <img class="default-size" src="https://www.tobias-franke.eu/publications/franke21sgifo/franke21sgifo_video.jpg" alt="" title="">
        
        <a class="fa fa-5x default-size" href="https://www.youtube.com/embed/5TeKAGE6LVg"></a>
        
    </div>
    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://www.tobias-franke.eu/publications/franke21sgifo/franke21sgifo_slides.pdf">Slides</a></li>
        
        
        
        
        <li><a href="https://o3decon2021.sched.com/event/mIcc/scalable-global-illumination-for-o3de-tobias-alexander-franke-huawei">O3DECon 2021</a></li>
        

        
        
        <li><a href="https://www.youtube.com/embed/5TeKAGE6LVg">Video</a></li>
        
        

        
        <li><a href="https://www.tobias-franke.eu/publications/franke21sgifo/franke21sgifo.bib">Bibtex</a></li>
        
    </ul>
</div> ]]></description>
            <pubDate>Tue, 12 Oct 2021 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/publications/franke21sgifo/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhwWjZBRUEvcnBYbHkvNlJmbnhsckxFV094TQpkQkw4ZkU1c3Ria3BwTU1WSDMwbm9oVUEvMEhsQUtDT0ZuTk1ROUhRMGQwSi9tQk4vbGdVNXN0LzVVQWxESFlaCjBzRVAKPVl1aWsKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Conference</category><category>Publication</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Five years at Unity</title>
            <description><![CDATA[ <h1 id="my-name-is-ozymandias">My name is Ozymandias</h1>

<p>In one of the more bizarre hiring encounters I've experienced, I sit together with my family at lunch when I receive a Twitter message followed by an email that can be paraphrased as <em>Saw your online profile, would you be interested in joining Unity?</em> A couple of weeks later I am in Copenhagen at the office. The atmosphere is relaxed, family-like even, and I am getting interviewed by someone called Joe. I am ignorant of course and do not realize this isn't Joe Shmoe, but <strong>the</strong> Joachim Ante, one of the founders and CTO of Unity.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2021_04_unity-office.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2021_04_unity-office.jpg" alt="First day at the office in Copenhagen" title="First day at the office in Copenhagen">
    
    </a>
    
    
    <figcaption>First day at the office in Copenhagen</figcaption>
    
</figure>

<p>My first day at Unity is 1st of January 2016, following my departure at Fraunhofer IGD after 2 years of doing a PhD. I'm getting on-boarded by David Helgason as the only graphics programmer in a room full of mathematicians. David shows a keen interest in everyone and their story, and wants to know one special quirk about everyone. It's a wild mixture under one company roof, and an analogy. His point of course is that one of the hard problems Unity needs to solve is catering to a wide variety of games using one single engine, something quite different from my former research environment where I used to have a very narrow and highly specified problem space in front of me and just wrote single-use code.</p>

<p>Today is my last day at Unity. I am very happy I got to work on this incredible engine. Having pushed code to millions of devices is simultaneously terrifying and exhilarating. In this post I will cover three things I learned during my time at Unity that - in hindsight - may seem obvious, but ultimately freed me from the bubble I came from. I had the privilege of working alongside most excellent people that helped me get there (in fact, many more than on this Twitter-only list): 
<a href="https://twitter.com/kubacupisz">Kuba</a>, 
<a href="https://twitter.com/pigselated">Jesper</a>, 
<a href="https://twitter.com/KEngelstoft">Kasper</a>, 
<a href="https://twitter.com/raroni86">Rasmus</a>, 
<a href="https://twitter.com/David_LLewelyn">David</a>,
<a href="https://twitter.com/robcupisz">Robert</a>, 
<a href="https://twitter.com/florentguinier">Florent</a>,
<a href="https://twitter.com/njohnny84">Ionut</a>,
<a href="https://twitter.com/PaulDemeulenaer">Paul</a>,
<a href="https://twitter.com/leonard_coder">Arnaud</a>,
<a href="https://twitter.com/vesseff">Veselin</a>, 
<a href="https://twitter.com/martinvkummel">Martin</a>,
<a href="https://twitter.com/panokani">Göksel</a>, 
<a href="https://twitter.com/arthurdufay">Arthur</a>,
<a href="https://twitter.com/pastasfuture">Nicholas</a>, 
<a href="https://twitter.com/jovercyril">Cyril</a>,
<a href="https://twitter.com/adrianlazar3d">Adrien</a>,
<a href="https://twitter.com/alelievr">Antoine</a>,
<a href="https://twitter.com/seblagarde">Sebastien</a>, 
<a href="https://twitter.com/FCifaCiar">Francesco</a>, 
<a href="https://twitter.com/igneus">Ben</a>, 
<a href="https://twitter.com/Auzaiffe">Anis</a>,
<a href="https://twitter.com/Chman">Thomas</a>,
<a href="https://twitter.com/lharduin">Laurent</a>,
<a href="https://twitter.com/peeweekVFX">Thomas</a>,
<a href="https://twitter.com/JulienIgnace">Julien</a>,
<a href="https://twitter.com/g_truc">Christophe</a>,
<a href="https://twitter.com/eric_heitz">Eric</a>, 
<a href="https://twitter.com/kemalakay">Kemal</a>,
<a href="https://twitter.com/_Laurent">Laurent</a>, 
<a href="https://twitter.com/julienfryer">Julien</a>,
<a href="https://twitter.com/phi_lira">Felipe</a>, 
<a href="https://twitter.com/zalbard">Evgenii</a>,
<a href="https://twitter.com/pbbastian">Peter</a>,
<a href="https://twitter.com/mirror2Mask">Natasha</a>,
<a href="https://twitter.com/aras_p">Aras</a>, 
<a href="https://twitter.com/kechogarcia">Kleber</a>,
<a href="https://twitter.com/__rej__">Rej</a> and 
<a href="https://twitter.com/baldurk">Baldur</a>.</p>

<h1 id="generate-lighting">Generate Lighting</h1>

<figure>
    
    
    <div class="video" style="width:740px; height:205px;">
        <a class="fa" style="width:740px; height:205px;" href="https://www.youtube-nocookie.com/embed//tN33YqhfVtI">
            <img style="width:740px; height:205px;" src="https://www.tobias-franke.eu/layout/logcache/2021_04_unite-berlin.jpg" alt="" title="">
        </a>
    </div>
    
    
    
</figure>

<p>Every feature of a modern game engine is a cover on top of a bottomless pit full of problems to solve. For me, that cover was a single button, or so I thought. Initially it felt great to hide behind it: Here is that one feature where you can truly capsule off from everyone else. That button precomputes all static lighting using one of the global illumination backends the user can select. There are a bunch of settings to adjust for like ray counts etc., but all in all it seemed like a very contained world. However, complexity tends to leak.</p>

<p>I had dedicated a good amount of time to a conundrum: As the name suggests, global illumination is a global phenomenon, but for the sake of memory consumption many scenes are often tiled in some form or another, and then computed independently. The disconnect, i.e. the global lighting not being so global anymore, needs to be managed later somehow. A lot of user-complaints and tickets I went through had the same core issue: Two tiles of a scene were precomputed independently, but with - accidental or not - differing settings. Sometimes there was a different skybox involved, sometimes the GI backend was flipped or the textures stored directional lighting in one but not the other tile. For half a year, I wrote a lot of messages explaining that when putting those tiles back together, the contradicting settings will cause all sorts of issues, not just visually.</p>

<p>Even though everyone understood the message, the tickets kept coming. Eventually I sat in front of a project so convoluted that I too made the very same mistakes I tried to educate everyone about, and so I added warning labels to everything. Initially they were quite unpopular, but the flood of tickets came to a grinding halt.</p>

<p>In essence, I learned the hard way that no amount of documentation, videos or talks will iron out the complexities of your software. If there are no visual indicators attached to parameters that can be used wrong very easily, everyone will keep using them wrong until the end of time, not because of ignorance or because it is hard to use, but simply because the software is complex and mistakes are easy to make.</p>

<h1 id="physically-based-ork-skin">Physically based ork-skin</h1>

<p>One of the things that used to baffle me before working in the games industry was the apparent excitement and ignorance artists and graphics programmers alike displayed when it came to physically based rendering. At SIGGRAPH and other conferences, courses dealing with rendering and lighting in games celebrated the fact they've adhered even slightly to the rules of reality and took away all their artists favorite sliders. Zero tolerance for shenanigans like lights with negative energy or materials that violate the conservation of energy! No longer would it be possible to abuse the lighting system, no longer would different game scenes have wildly different behavior and everything would be nice, coherent and predictable.</p>

<p>But did they not get the memo? The literature on physically based rendering was already ancient by graphics standards. Why the victory parade?</p>

<figure>
    
    
    <div class="video default-size">
        <a class="fa" href="https://www.youtube-nocookie.com/embed/iQZobAhgayA">
            <img class="default-size" src="https://www.tobias-franke.eu/layout/thumbcache/iQZobAhgayA.jpg" alt="" title="">
        </a>
    </div>
    
    
    
</figure>

<p>Unity's platform support <a href="https://unity.com/features/multiplatform">is huge</a>. Unlike tailored engines, games made with Unity often run on many combinations of graphics APIs, operating systems, CPU architectures and end-devices. Additionally, the type of graphics required for a certain look pile on top of the already complex situation: <a href="https://unity.com/madewith/cuphead">2D</a>, <a href="https://unity.com/madewith/drone">3D</a>, <a href="https://unity.com/the-heretic">realistic</a>, <a href="https://unknownworlds.com/subnautica/">open-world</a>, <a href="https://www.mechanicus40k.com/">top-down</a>, <a href="https://goose.game/">minimalistic</a>, <a href="https://www.firewatchgame.com/">stylized</a>, <a href="https://www.brilliantgamestudios.com/">massive</a>, <a href="https://unity.com/madewith/monument-valley-2">impossible</a>… Then there is the project itself: A game, a prototype, a demo, a movie (real-time or a glorified frame capturer), a UI or some interactive educational tool. All of these factors are <strong>just</strong> technicalities though. Yes you might end up on a device that is still unable to use linear intensities, yes you may not have RTX support, yes the platform could be unable to stream all your data and yes the game may not even be next-gen.</p>

<p>I remember a brief chat with one of the tech-artists about the perfect renderer, that in my opinion simply required to follow a strict set of physically-guided rules and was augmented by tools that helped artists deal with the assets they need, like <a href="https://unity.com/solutions/photogrammetry">the built-in photogrammetry tool</a>.</p>

<blockquote>
  <p>Sounds good, I just need that sample of ork-skin and we'll get the cameras.</p>
</blockquote>

<p>I sometimes tend to forget that most game settings are not situated in this universe. While tinkering with rendering architectures all day long where I focus to get one thing right - the transport of light - and dealing with all the technical issues - memory consumption, serialization, shader architecture, ray-throughput, threading etc. - it did not occur to me that the artist might actually want to break the rules of physics <em>on purpose</em>. In all cases the engine is both tool and stumbling block, either enabling a vision or preventing it. The engine programmer writing the underlying architecture is trapped between two extremes: On one side the desire to write code that behaves predictable and consistent across the wide variety of projects, styles and platforms, on the other to enable the freedom of the artists and their often conflicting and contradictory ideas of what they want see on screen.</p>

<p>At the end of the day, nothing is as important as the vision of the game though. Just like an artist can take a brush, an empty canvas and paint anything, be it an impression of the real world, a fantasy setting or an Escher-like mind-bender, so too should an engine deliver the flexibility to enable the game designer to achieve their goal, even if it is completely weird, unorthodox and against all reason.</p>

<p>And so I learned that in many cases the default implementation should just be easily replaceable. If the implementation does not work for the game, then it should be possible to use a custom one. Games are just different from other software in that regard, because they are pieces of art.</p>

<h1 id="the-engine-is-eternal">The engine is eternal</h1>

<figure>
    
    
    <div class="video default-size">
        <a class="fa" href="https://www.youtube-nocookie.com/embed/AG7DDXwYpD0">
            <img class="default-size" src="https://www.tobias-franke.eu/layout/thumbcache/AG7DDXwYpD0.jpg" alt="" title="">
        </a>
    </div>
    
    
    
</figure>

<p><em>Invent yourself and then reinvent yourself</em> is the slogan of the BMW demo showcasing the running version of <a href="https://unity.com/ray-tracing">the high-definition render pipeline using DXR ray tracing</a>, and it perfectly captures what goes on under the hood of Unity. The demo was the end-result of a long development cycle that started with a tiny hackweek project using the brand-new DXR API and its semi-correct documentation to render ambient occlusion. Like all paradigm shifts, the <em>new way</em> of doing things did not really fit nicely into the existing structures, at least in the beginning. Where should the DXR renderer live? Should it still work if the project is setup to use Vulkan, now that we're using two APIs rather than one? Can the shaders run in parallel with the deferred renderer, and if so how do we synchronize them? How are the materials translated? Who is in control of the reflection probes? There were quite a few questions like these that required some back and forth, but in the end the renderer would fit in.</p>

<p>Adapting code to make things fit is sometimes not enough. After many years of service, the built-in render pipeline was simply too monolithic to be used across vastly different devices and could not provide the flexibility that many games needed, be it custom adaptations to the renderer or simply the option to rip out entire features a game is not using. Hence one night over some pizza at the office with Joe and a colleague, he casually pondered how Unity could look like if the render pipeline would be written in C#, as opposed to being built-in. One hackweek later and the <a href="https://github.com/Unity-Technologies/Graphics">Scriptable Render Pipeline</a> was born. Though that was just a prototype. Now we suddenly had to decide who is in control of the flow. Should the renderer push its state down from C# to the rest of the base code in C++, or should it work the other way around? How are custom features - implemented in the a ScriptableRenderPipeline - reflected back, such as a whacky material with some new sliders that the global illumination backends are not aware of? Can the C++ code inject render calls, and if so <em>when</em>?</p>

<p>In 2018 we shipped the <a href="https://blogs.unity3d.com/2019/05/20/gpu-lightmapper-a-technical-deep-dive/">GPU Lightmapper</a>, an enhancement of the Progressive Lightmapping backend that so far only ran on the CPU. If the development machine has a capable GPU in store, the GPU backend will render at significantly more rays per second. After a long process of abstracting away most jobs running inside the Lightmapper, we went with OpenCL as the compute solution, as it would run on all supported Editor platforms. And then, at WWDC2018, whilst <a href="https://unity3d.com/book-of-the-dead">Book of the Dead</a> was shown on stage to demo the new eGPUs for MacBooks, Twitter began to notice that <a href="https://web.archive.org/web/20180605021733/https://twitter.com/evilbachus/status/1003722391627976705">Apple had just deprecated OpenCL on macOS</a> in favor of Metal.</p>

<figure>
    
    
    <div class="video default-size">
        <a class="fa" href="https://www.youtube-nocookie.com/embed/UThGcWBIMpU?start=7255">
            <img class="default-size" src="https://www.tobias-franke.eu/layout/thumbcache/UThGcWBIMpU.jpg" alt="" title="">
        </a>
    </div>
    
    
    
</figure>

<p>When I compare projects I do for fun with the code I write for work, I can see a strong divergence between the two. In my hobby projects all code is following a very strict order, and if it does not then it is usually time to fix that. Throughout the code identical concepts are used. Helper functions abstract away repeating patterns and everything looks nice and tidy. The code seems to have a long life ahead, but only because it serves a single purpose.</p>

<p>In comparison, game engine code looks a bit like tectonic plates shifting at the speed of sound. Entire systems exist multiple times over, the control flow is not obvious, and features can get ripped out from the codebase rather abruptly. Code is rewritten much more frequently than in other software. To maintain a bleeding-edge status, developers are forced to constantly re-evaluate their code. Because of that, the balance when planning out features shifts quite dramatically to the near rather than the far future, and that plan is always one round away of loosing the API-, Paper- or Dependency-Russian-Roulette.</p>

<p>Now it might all sound like a lot of duct tape and a constant struggle to keep a patient alive while exchanging all guts at once and simultaneously doing brain surgery. One could get the impression that nothing is here to stay, and that eventually it is all but Unity in name.</p>

<p>But I learned that Unity earned its reputation as one of the most intuitive game engines out there not because it has been around forever unchanged, but because it is constantly adapted and modified to make current and new technology easily accessible to everyone. There may be a lot going on under the hood to keep that reputation, but this is the reason why a decade from now I'll still be able to recognize the motor I helped build, and why <a href="https://www.youtube.com/embed/HciZ_7frXmQ">the engine is forever</a>.</p> ]]></description>
            <pubDate>Fri, 30 Apr 2021 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/log/2021/04/30/five-years-at-unity.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhxU3RnRUEzbHN4aGJvK0Zoalk2cVd5Zm9nTApxcTh4VVdwNTZKeTRuTFFOVTAyand4NEJBSWh1Y3JNdVkzWkZqOTJBTm5rVFVrbVJDYzdRb1FvNDhXamFWNGU5ClJxWUQKPUJvVkkKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Notes</category><category>Unity</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>The Heretic</title>
            <description><![CDATA[ <div class="publication">
    <h1>The Heretic</h1>
    <p>
        <span class="authors">Veselin Efremov et al. and Tobias Alexander Franke</span>
        <span class="journal">Unity Demos</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/efremov20heretic/efremov20heretic.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/efremov20heretic/efremov20heretic.jpg" alt="The Heretic." title="The Heretic.">
    
    </a>
    
    
    <figcaption>The Heretic.</figcaption>
    
</figure>

    <h2>Abstract</h2>
    <p>The Heretic is a short film created by Unity's Demo team. After debuting its first part at GDC 2019, the completed short is now available to watch in its entirety.</p>

<p>The Unity Demo team's mission is to push the boundaries of what is visually possible with Unity. With <em>The Heretic</em>, the team used every aspect of Unity's High Definition Rendering Pipeline, created advanced effects with the VFX Graph, and undertook the challenge of creating a realistic digital human.</p>


    

    
    <h2>Supplemental Video</h2>
    
    <div class="video default-size">
        <img class="default-size" src="https://www.tobias-franke.eu/publications/efremov20heretic/efremov20heretic_video.jpg" alt="" title="">
        
        <a class="fa fa-5x default-size" href="https://www.youtube.com/embed/iQZobAhgayA"></a>
        
    </div>
    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://unity3d.com/the-heretic">Project Page</a></li>
        
        
        
        
        <li><a href="https://www.youtube.com/embed/uHD8OfSzCsg">VFX Workflow</a></li>
        
        
        
        
        <li><a href="https://www.youtube.com/embed/5H9Jo2qjJXs">Making of</a></li>
        
        
        
        
        <li><a href="https://www.imdb.com/title/tt11669922/">IMDB</a></li>
        

        
        
        <li><a href="https://www.youtube.com/embed/iQZobAhgayA">Video</a></li>
        
        

        
    </ul>
</div> ]]></description>
            <pubDate>Wed, 22 Jan 2020 00:00:00 +0100</pubDate>
            <link>https://www.tobias-franke.eu/publications/efremov20heretic/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhxL1ZRRUF1RWtqU1M4SmhmWWdQK3lQZVJ6NQp6QVlyajd2U2txNXZnL1RqUUFjdG1VNEJBTEtkS3FlTlJpeFNPbHFnYnhDeVY5Y1BCM3FuMzIwMXBqdTQ1alEwCjVWUUMKPWFYMHoKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Publication</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Static Lighting with Light Probes</title>
            <description><![CDATA[ <div class="publication">
    <h1>Static Lighting with Light Probes</h1>
    <p>
        <span class="authors">Tobias Alexander Franke and David Llewelyn</span>
        <span class="journal">Unity Blog</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/franke19slwlp/franke19slwlp.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/franke19slwlp/franke19slwlp.jpg" alt="" title="">
    
    </a>
    
    
</figure>

    <h2>Abstract</h2>
    <p>In 2019.2, we removed the Lightmap Static Flag, replacing it with the Contribute Global Illumination Flag. We also introduced the ability to choose whether Global Illumination is received from Lightmap or Light Probes. These changes can have a huge impact on your baking performance, quality of Scene lighting, and more! Let’s explore this further.</p>


    

    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://blogs.unity3d.com/2019/08/28/static-lighting-with-light-probes/">Unity Blog Article</a></li>
        
        
        
        
        <li><a href="https://web.archive.org/web/20200510224436/https://blogs.unity3d.com/2019/08/28/static-lighting-with-light-probes/">Wayback Machine Archive</a></li>
        

        

        
    </ul>
</div> ]]></description>
            <pubDate>Wed, 28 Aug 2019 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/publications/franke19slwlp/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhxQ3FnRUEwV2JGeHRFcDh1Y1N6cmViUjIweQpyQktIWXk4Q3RxNG9xbDRxcGZETTlsa0EvaTYrMmlwRmN6VzVMYm1YRUkveEd0c1MzdFloQ2QxSDB2b2p4SHdVCkp2OEgKPXg1T2UKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Unity</category><category>Lightprobes</category><category>Publication</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>In praise of syndication</title>
            <description><![CDATA[ <h1 id="the-web-is-terrible">The web is terrible</h1>

<p>Useful things tend to become invisible over time. I have a router running <a href="https://www.openbsd.org/">OpenBSD</a> that I had not touched in a while. One day I logged in and got curious.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">uptime
</span>12:26PM  up 1583 days,  2:33, 1 user, load averages: 0.13, 0.12, 0.08
</code></pre></div></div>

<p>It had been running continuously for 4 years without interruption, happily doing its singular task. Similarly, tools like SSH, SCP, rsync, the Sublime text editor, VLC or that one script I wrote years ago all vanish in the daily noise because apart from doing their job, they are primarily <em>not annoying</em>.</p>

<p>The same cannot be said for browsing the web anymore. I use a somewhat dated but otherwise fully functional MacBook 13" from 2013 with the latest MacOS. Most things are working just fine, but as soon as I open a browser and go to any bigger website, the poor piece of silicon is choked to death by a barrage of trackers and useless features packed together in a lasagna of Javascript libraries, each running in the background using websockets and Ajax requests to continue the torture.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2019_08_firefox-cpu-usage.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2019_08_firefox-cpu-usage.png" alt="" title="">
    
    </a>
    
    
</figure>

<p>Even with a beefy machine I notice laggy input in every bigger web application. In some cases <a href="https://twitter.com/cpeterso/status/1021626510296285185">browser vendors running big websites are actively abusing APIs</a> to stop you from using their competitors. Apart from the dreaded interface changes that are imposed on visitors every now and then, content is often hidden in completely overloaded designs, buried under advertisements and self playing videos. The problem has become so endemic to everyday online life that several browsers now come with built-in "Reader Views" or services such as <a href="https://www.getpocket.com">Pocket</a> to trim down websites to their bare essence. Without an almost mandatory set of ad- and script blockers, redirect skippers, CDN caches, fingerprint scramblers and privacy containers (the list goes on), almost every modern website is a cocktail of ad-infested privacy nightmares. And then there is the fine threshold where running too many add-ons to curate the web is a bottleneck and security threat itself, so having multiple browsers on a device is also considered "normal" these days. Maciej Ceg&amp;;owski summarized the state of modern web design in his hilarious talk <a href="https://idlewords.com/talks/website_obesity.htm">The Website Obesity Crisis</a> with the following image:</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2019_08_the-web-pyramid.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2019_08_the-web-pyramid.jpg" alt="" title="">
    
    </a>
    
    
</figure>

<p>A great way to combat many of these problems is to use <a href="https://en.m.wikipedia.org/wiki/RSS">RSS</a>. I recently noticed that over the last year I had relocated most of what I consume online to an RSS reader. I had been using it for now more than 12 years, mostly to follow blogs, but now on some days I only open the reader instead of my browser. I am not alone in this move: <a href="https://www.wired.com/story/rss-readers-feedly-inoreader-old-reader/">Wired ran an article</a> about the resurgence of RSS because of the way the web had transformed. A reader on Hacker News soon after <a href="https://news.ycombinator.com/item?id=16722351">pointed out the irony</a> that the very same article - suggesting RSS as a solution to this hot mess - came with its own clown-car of trackers. In a very twisted way, this article made clear why most of the web is terrible.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2019_08_wired-ublock.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2019_08_wired-ublock.png" alt="" title="">
    
    </a>
    
    
</figure>

<h1 id="an-elegant-weapon-for-a-more-civilized-age">An elegant weapon for a more civilized age</h1>

<p>For those unfamiliar, RSS is a standardized container format for content updates, for instance blog posts or news articles. Such updates usually have a title, an author, a publication date, some content (i.e. text and images) and perhaps a list of tags. RSS encapsulates each update in a bit of XML. An RSS reader - often referred to as a feed aggregator -  which you point to an RSS stream will, in regular intervals, check the stream if any new updates appeared and download them for you to view later. In many ways, it is like having an inbox for website updates.</p>

<p>But it doesn't have to end at blog posts. Most of what we read and watch every day can be encapsulated with RSS: Facebook posts, Tweets, Instagram pictures, new videos uploaded Youtube/Twitch/Vimeo, Github commits etc. are all updates on websites we check repeatedly every day. That manual labor can be avoided by subscribing to their RSS streams.</p>

<p>Say you are interested in <em>computer graphics</em>. You may have a couple of people posting on Twitter that you follow. There is also the SIGGRAPH Facebook page you open every day to scan for interesting tidbits. You are also on Mastodon, because some people from the community are gathering there. Additionally, you read a ton of blogs, and you follow the Youtube channels of Khronos, GDC and some researchers, for which you get notified through your subscription on Youtube. Except this one person that hosts their videos on Vimeo, that one you need to check on your Vimeo account. At last there are the Github projects with the occasional releases and the starred repositories of people you follow. To sum up: The content category you are interested in - <em>computer graphics</em> - is splattered all over the place and every day you will need to go out and collect everything back together.</p>

<p>With an RSS reader, you can open a folder - let's call it <em>Computer Graphics</em> -  and simply add all those sources into it. It is now the RSS readers task to fetch new items in regular intervals and notify you. You will never have to visit any of those pages again to check for updates. After all, if it is just a category you are interested in, who cares were the content comes from?</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2019_08_music-subs.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2019_08_music-subs.png" alt="" title="">
    
    </a>
    
    
</figure>

<p>The image above shows a sample from my <em>Music</em> folder, where I follow Soundcloud accounts next to Youtube, Vimeo and a blog at the same time. All relevant data ends up in the same stream of notifications.</p>

<h1 id="what-do-i-gain">What do I gain?</h1>

<p>Apart from reducing the menial task of visiting a bunch of websites every day, RSS has some nice side-effects.</p>

<p><em>No walled gardens</em>: RSS readers allow you to fetch content from a multitude of sources and group it in any way you please. Decentralization comes naturally and is easily managed. Content is not bound to specific websites or providers.</p>

<p><em>No accounts</em>: On Youtube for instance, subscribing to channels requires an account. For each other walled garden where you subscribe to something, you will need an additional account. You can get away without any accounts by aggregating all subscriptions via RSS.</p>

<p><em>Limited tracking</em>: RSS encapsulates raw content of an update. A reader can show you a new item without you visiting any website. All the Javascript nonsense that comes with a page you would usually visit (like the omnipresent Google Analytics) is gone.</p>

<p><em>No interspersed ads</em>: Twitter is among one of many websites that inject advertisement posts called "promoted tweets" into your stream. Technically, that is possible with RSS too. Without accounts and scripts following your every move, building a profile about you is limited though.</p>

<p><em>Unfiltered and unsorted by machine learning</em>: Many services will employ some type of machine learning to show you something <em>they</em> deem interesting for <em>you</em>. Chronological order goes out the window, as these services push the "top" or "most liked post" to the beginning of your stream. RSS is not a service controlled by anyone, therefore the order is yours to control.</p>

<p><em>Faster browsing</em>: Content is served as raw text that may be formatted with simple HTML. Instead of opening 10 websites that each harass your browser, one feed with limited HTML or text-only messages and pictures remains. All content is served in a common theme.</p>

<p><em>No social features</em>: Without walled gardens and no centralized sources, there are also no common comment sections or share buttons.</p>

<p><em>Privacy</em>: Given all of the points above, privacy of whom you follow and what you read is maintained.</p>

<p><em>Security</em>: Generally, the less intelligent and powerful a system is, the less damage it can inflict. Technological blights like <a href="https://en.wikipedia.org/wiki/Cross-site_scripting">Cross Site Scripting attacks</a> are simply not a thing when you read raw text.</p>

<h1 id="what-can-go-wrong">What can go wrong?</h1>

<p>Of course not everything is suited for RSS, such as legitimate web applications like shops or banking portals, and there are some down-sides to consider when relying heavily on RSS.</p>

<p><em>RSS may not be supported</em>: A website you like and want get updates from may provide no or only inadequate RSS streams. For instance, a news page could give you an RSS stream for each category, or the entire site, or none at all. In these cases you might need to rely on <a href="https://github.com/RSS-Bridge/rss-bridge">third-party scrapers</a> that will generate RSS content from the website. However, a nice side-effect of those services is that they act as proxies: Since you are not directly subscribing to the pages RSS, it won't even know you where ever there.</p>

<p><em>Non-public data inaccessible</em>: Scrapers can turn any public website into an RSS stream, but anything locked behind an account can become a problem. Some websites implement private RSS streams using secret tokens (for instance the  personal GitHub news feed), but in most cases you will be out of luck.</p>

<p><em>Only previews available</em>: Many RSS streams do not provide the full content of the update, only a preview, forcing you to open the original website again. It is not the end of the world, but annoying. There are again <a href="https://rssbridge.net/fulltext/">third party services that will automatically expand to full content RSS streams</a> from preview-only streams.</p>

<p><em>No social features</em>: Without walled gardens and no centralized sources, there are also no common comment sections or share buttons.</p>

<p><em>No recommendations</em>: Platforms such as Vimeo or Instagram will, given user tracking through accounts or other means, compile recommendations of what to watch or whom to follow. Decentralized and without accounts, there is no recommendation page anymore. Online RSS readers such as Feedly might recommend more feeds, but in general terms this feature does not exist anymore.</p>

<p><em>Synchronization between devices</em>: To keep track which article on which feed was marked read or unread, RSS readers keep an index database. These index databases are not standardized and often cannot be synchronized easily between different readers. Online RSS readers such as <a href="https://feedly.com/">Feedly</a> solve this problem by synchronizing an online index with a proprietary  protocol. <a href="https://rss-sync.github.io/Open-Reader-API/">An effort is underway</a> to solves this issue.</p>

<p><em>Multimedia content</em>: Some items can be subscribed to via RSS, but not shown as purely raw text-content, such as video or music subscriptions. In these cases your RSS reader will use a built-in or external browser to open a link. However, getting notified this way is still preferable.</p>

<h1 id="on-readers">On Readers</h1>

<p>Armed with the knowledge of the pros and cons of using RSS, you can start by installing an RSS reader to subscribe to something.</p>

<p>Generally, you want to look out for a reader that supports import and export of your subscriptions. The commonly used format for this is called <a href="https://en.wikipedia.org/wiki/OPML">OPML</a>. Simply put, OPML is a standardized list of all your subscriptions. A current backup of those subscriptions can be used to quickly change from one reader to another, and they are very easy to edit in case one wants to mass-change RSS feed subscriptions to an alternative. For instance, <a href="https://support.google.com/youtube/answer/6224202?hl=en">you can export all your Youtube subscriptions as OPML</a> and easily migrate them to <a href="https://www.invidio.us">Invidious</a>, an open-source Youtube proxy, using a simple search-and-replace with your favorite text editor:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://www.youtube.com/feeds/videos.xml?channel_id=ID
-&gt; 
https://invidio.us/feed/channel/ID
</code></pre></div></div>

<p><em>Keeping a backup of all your subscriptions is essential to being independent of any third-party services or readers!</em></p>

<p>There are two types of readers: Online readers running in a browser, which keep all your subscriptions behind an online account, or local readers, which may synchronize with an online reader account. The go-to online reader and winner of the Google Reader exodus is <a href="https://www.feedly.com">Feedly</a>. Feedly supports OPML import-export and can synchronize with a wide range of mobile and desktop readers. If you have no reader application at hand, you can always use the web version. Feedly and other online RSS readers often limit the amount of RSS feeds you can subscribe to for free. An alternative is to use an offline RSS reader or to host your own online RSS reader. <a href="https://freshrss.org/">FreshRSS</a> is an open-source project that can be set up with minimal friction on a webserver with PHP.</p>

<p>There is <a href="https://en.wikipedia.org/wiki/Comparison_of_feed_aggregators">a multitude of very nice RSS readers</a> out there, but I'll mention only a few very basic ones here:</p>

<ul>
  <li>
<a href="https://www.thunderbird.net/">Thunderbird</a>, the well known Mozilla email client, supports local RSS subscriptions.</li>
  <li>
<a href="https://nodetics.com/feedbro/">Feedbro</a>, a browser extension, comes with OPML support and additionally can render <a href="https://www.mathjax.org">MathJax</a> in blog posts. Since it is browser-based, it can be used in conjunction with uBlock to weed out any additional requests coming from embedded media.</li>
  <li>
<a href="https://itunes.apple.com/us/app/rss-mobile/id533007246">RSS Mobile</a>, the most basic iOS RSS reader I could find. Free, no ads, runs without any web service.</li>
  <li>
<a href="https://github.com/ahmaabdo/ReadifyRSS">Readify</a>, an open-source reader and equivalent of RSS Mobile for Android.</li>
</ul>

<h1 id="rss-streams-of-common-services">RSS streams of common services</h1>

<p>Apart from blogs, one of the most interesting use cases of RSS is to escape social media bubbles and walled gardens. Because RSS prevents social media platforms from tracking you, most have <a href="https://mashable.com/2012/09/05/twitter-api-rss/?europe=true">dropped their RSS support</a> entirely. However, this is where scrapers such as <a href="https://github.com/RSS-Bridge/rss-bridge">RSS-Bridge</a> come to the rescue: These services parse website content and turn it into an RSS stream, shielding you away entirely from the site. In the list below, replace <code class="language-plaintext highlighter-rouge">RSS-BRIDGE-INSTANCE</code> with for example <a href="https://bridge.suumitsu.eu/">https://bridge.suumitsu.eu/</a>. There are many more publicly hosted instances of RSS-Bridge, and of course you can run your own.</p>

<p>Here is a sample of RSS streams you can use to replace typical social media:</p>

<ul>
  <li>
<em>Twitter</em>: <code class="language-plaintext highlighter-rouge">https://RSS-BRIDGE-INSTANCE/?action=display&amp;bridge=Twitter&amp;format=Atom&amp;u=USERNAME</code>
</li>
  <li>
<em>Mastodon</em>: <code class="language-plaintext highlighter-rouge">https://MASTODON-INSTANCE/@USERNAME</code>
</li>
  <li>
<em>Instagram</em>: <code class="language-plaintext highlighter-rouge">http://instatom.freelancis.net/USERNAME</code>
</li>
  <li>
<em>Facebook</em>: <code class="language-plaintext highlighter-rouge">https://RSS-BRIDGE-INSTANCE/?action=display&amp;bridge=Facebook&amp;media_type=all&amp;limit=-1&amp;format=Atom&amp;u=USERNAME</code>
</li>
  <li>
<em>Hacker News</em>: <code class="language-plaintext highlighter-rouge">http://feeds.feedburner.com/fullhackernews</code>
</li>
  <li>
<em>Reddit</em>: <code class="language-plaintext highlighter-rouge">http://inline-reddit.com/feed/?subreddit=SUBREDDIT</code>
</li>
  <li>
<em>Youtube</em>: <code class="language-plaintext highlighter-rouge">https://invidio.us/feed/channel/CHANNELID</code>
</li>
  <li>
<em>Vimeo</em>: <code class="language-plaintext highlighter-rouge">https://vimeo.com/USERNAME/videos/rss</code>
</li>
  <li>
<em>Twitch</em>: <code class="language-plaintext highlighter-rouge">https://twitchrss.appspot.com/vod/USERNAME</code>
</li>
  <li>
<em>Tumblr</em>: <code class="language-plaintext highlighter-rouge">https://USERNAME.tumblr.com/rss</code>
</li>
  <li>
<em>GitHub</em>: <code class="language-plaintext highlighter-rouge">https://github.com/USERNAME/PROJECT/releases.atom</code>
</li>
  <li>
<em>Soundcloud</em>: <code class="language-plaintext highlighter-rouge">https://rssbox.herokuapp.com/</code>
</li>
</ul>

<p>I keep a more comprehensive list updated on this <a href="https://gist.github.com/thefranke/63853a6f8c499dc97bc17838f6cedcc2">Github Gist</a>. Note that for most of these pages there are often multiple alternative scraper projects. If you cannot find an RSS stream of a website or service you want to subscribe to, consider contributing to projects such as <a href="https://github.com/RSS-Bridge/rss-bridge">RSS-Bridge</a>, <a href="https://github.com/DIYgod/RSSHub">RSSHub</a> or <a href="https://github.com/stefansundin/rssbox">RSSBox</a>. Multiple instances of these projects are hosted for free by volunteers, and you may want to consider running your own as well.</p>

<h1 id="conclusion">Conclusion</h1>

<p>RSS is a great way to keep track of content updates on any number of sources you visit daily. It naturally shields against unnecessary ad-tracking and separates actual content from virtual garbage, making the experience of reading and viewing more pleasant. RSS frees from the bonds of walled gardens and allows you to organize all of your sources by categories again instead of where they originate from: It is no longer your "Youtube subscriptions" or "people you follow on Instagram", but subscription bundles of "cat videos" or "food pictures", wherever they might come from. A well maintained subscription list will  reduce the time you spend mindlessly surfing the web in search for new stuff.</p>

<p>If you are as upset as I am about the current state of the WWW, I encourage you to consider this alternative way of consuming the web, or to revive your RSS reader if you have abandoned it a couple of years back.</p>

<p>Here is my recommendation:</p>

<ol>
  <li>Find a good reader you feel comfortable with or sign up to <a href="https://feedly.com/">Feedly</a>. I'm on MacOS and prefer <a href="https://reederapp.com/">Reeder</a>.</li>
  <li>Add a couple of blogs.</li>
  <li>Subscribe to some of your favorite Youtube channels (here is one I like called <a href="https://rsshub.app/youtube/channel/UCYO_jab_esuFRV4b17AJtAw">3Blue1Brown</a>).</li>
  <li><em>Use it for a couple of days or weeks to get a feel for it.</em></li>
  <li>Use <a href="https://gist.github.com/thefranke/63853a6f8c499dc97bc17838f6cedcc2">this list</a> to populate your reader with more content.</li>
  <li>If your reader supports a usage overview (like <a href="https://feedly.com/i/organize/my">Organize Sources</a> on Feedly), identify and eliminate spammy sources.</li>
</ol>

<p>And then, if you prefer it, slowly, but steadily, subscribe to to everything you watch, read or listen to online. You can <a href="https://www.tobias-franke.eu/log/rss/index.xml">start with my log right now</a>.</p>

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

<ol>
  <li>Wikipedia, <a href="https://en.m.wikipedia.org/wiki/RSS">RSS</a>
</li>
  <li>Tobias A. Franke, <a href="https://gist.github.com/thefranke/63853a6f8c499dc97bc17838f6cedcc2">A list of RSS endpoints, readers and resources</a>
</li>
  <li>Wikipedia, <a href="https://en.wikipedia.org/wiki/Comparison_of_feed_aggregators">Comparison of readers</a>
</li>
  <li>RSS-Bridge, <a href="https://github.com/RSS-Bridge/rss-bridge">RSS-Bridge Project</a>
</li>
</ol> ]]></description>
            <pubDate>Wed, 07 Aug 2019 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/log/2019/08/07/in-praise-of-syndication.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhxWTl3RCtNSmllUVNGVlhZa0pLTkQ3TUxvZgpKaWwyZjBiUVNSTndTbm8zaTduakdFNEJBT3VpMEFyYzJVTHVwM3k1VmppZmVBaHNtaWRsZEc1TVkvMHFPZnI1CmtlMEkKPSs0VHYKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Notes</category><category>RSS</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>An Introduction to Lightmapping in Unity</title>
            <description><![CDATA[ <div class="publication">
    <h1>An Introduction to Lightmapping in Unity</h1>
    <p>
        <span class="authors">Tobias Alexander Franke and Jakub Cupisz</span>
        <span class="journal">Unite Berlin 2018</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/franke18introlmu/franke18introlmu.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/franke18introlmu/franke18introlmu.jpg" alt="" title="">
    
    </a>
    
    
</figure>

    <h2>Abstract</h2>
    <p>Get an intro to the basics of lightmapping and the Progressive Lightmapper, and learn how to address and solve the most common issues our users experience. We also dig into the future roadmap for lightmapping in Unity.</p>


    

    
    <h2>Supplemental Video</h2>
    
    <div class="video default-size">
        <img class="default-size" src="https://www.tobias-franke.eu/publications/franke18introlmu/franke18introlmu_video.jpg" alt="" title="">
        
        <a class="fa fa-5x default-size" href="https://www.youtube.com/embed/tN33YqhfVtI"></a>
        
    </div>
    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://www.slideshare.net/unity3d/progressive-lightmapper-an-introduction-to-lightmapping-in-unity">Slides</a></li>
        
        
        
        
        <li><a href="https://unity3d.com/events/unite-berlin-2018">Unite Berlin 2018</a></li>
        

        
        
        <li><a href="https://www.youtube.com/embed/tN33YqhfVtI">Video</a></li>
        
        

        
        <li><a href="https://www.tobias-franke.eu/publications/franke18introlmu/franke18introlmu.bib">Bibtex</a></li>
        
    </ul>
</div> ]]></description>
            <pubDate>Tue, 19 Jun 2018 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/publications/franke18introlmu/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhyTzZ3RUExelFKT1A1eUJQQWkyODhxanI4QQpVZlFiT3RaWDZ2am14M1RqYmlDVktKb0JBTUM4cmo1aTBnZ05HbHd1L2VheXh0aFFaQ3dpdEtRa0Fqd1hTUjhzClVLTVAKPWZpc3gKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Conference</category><category>Publication</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Book of the Dead</title>
            <description><![CDATA[ <div class="publication">
    <h1>Book of the Dead</h1>
    <p>
        <span class="authors">Veselin Efremov et al. and Tobias Alexander Franke</span>
        <span class="journal">Unity Demos</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/efremov18botd/efremov18botd.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/efremov18botd/efremov18botd.jpg" alt="The opening shot." title="The opening shot.">
    
    </a>
    
    
    <figcaption>The opening shot.</figcaption>
    
</figure>

    <h2>Abstract</h2>
    <p>Unity's Demo Team, creators of <em>Adam</em> (2016) and <em>The Blacksmith</em> (2015), are excited to announce Book of the Dead, a first-person interactive story showcasing the capabilities of Unity 2018 for powering high-end visuals for game productions.</p>

<p>The trailer is representative of the actual gameplay experience.</p>

<p>Unity's Demo productions drive advanced use of the Unity engine through autonomous creative projects. The team consists of experienced game professionals led by Creative Director Veselin Efremov, who writes and directs the demos and is responsible for their final look.</p>


    

    
    <h2>Supplemental Video</h2>
    
    <div class="video default-size">
        <img class="default-size" src="https://www.tobias-franke.eu/publications/efremov18botd/efremov18botd_video.jpg" alt="" title="">
        
        <a class="fa fa-5x default-size" href="https://www.youtube.com/embed/DDsRfbfnC_A"></a>
        
    </div>
    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://unity3d.com/book-of-the-dead">Project Page</a></li>
        
        
        
        
        <li><a href="https://assetstore.unity.com/packages/essentials/tutorial-projects/book-of-the-dead-environment-121175">Assets</a></li>
        
        
        
        
        <li><a href="https://www.youtube.com/embed/WeVJIuyJq08">Environment</a></li>
        
        
        
        
        <li><a href="https://www.youtube.com/embed/NoemmTLnK6k">WWDC2018</a></li>
        

        
        
        <li><a href="https://www.youtube.com/embed/DDsRfbfnC_A">Video</a></li>
        
        

        
    </ul>
</div> ]]></description>
            <pubDate>Tue, 16 Jan 2018 00:00:00 +0100</pubDate>
            <link>https://www.tobias-franke.eu/publications/efremov18botd/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhyS0pnRC9hVGZqVFdLZUxyZXRDUFduTnBBVQp5bUQ1L1ZKRERPWmhjNlIwOEI5OTUrSUEvMDFKNzdXU3B0TmhQbFNVZS9iUytYVk91eHpvM3JodDZRbFIwdi9wCnBNQUUKPVRJVVkKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Publication</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Triple Product Integrals</title>
            <description><![CDATA[ <h1 id="the-grand-picture">The Grand Picture</h1>

<p>Most introductions and implementations of <em>Precomputed Radiance Transfer</em> will deal fairly well with the easiest use case: The double product integral for diffuse reflections. Beyond this scope however, few show how proper rotation of frequency-space encoded lighting works, and even fewer dive into the problem of view-dependency in PRT. Both of these are related, as they rely on the ability to transform one set of coefficients into another: Rotating a vector creates another vector, and view dependent reflection transforms incident light represented as coefficients into coefficients of reflected light. From linear algebra, we know how to deal with such a scenario already: To transform one vector into another we need a matrix, so in terms of a lighting scenario we need to talk about <em>Matrix Radiance Transfer</em> and the <em>Triple Product Integral</em>.</p>

<h1 id="function-transforms">Function transforms</h1>

<p>In <a href="/log/2016/10/18/the_convolution_theorem.html">the last post on function transforms</a> I took a quick look at the convolution theorem, which one can roughly describe as the ability to shortcut an integration over the product of two functions, i.e., a convolution, with a dot product in the frequency domain. Typically, the convolution theorem is introduced with the Fourier basis, but other function bases can be used as well.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}
    \int_S f(s) g(s) ds = \sum_i f_i  g_i
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-1">
        <mtext>(1)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <mi>f</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>g</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>s</mi>
        <mo>=</mo>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>i</mi>
        </munder>
        <msub>
          <mi>f</mi>
          <mi>i</mi>
        </msub>
        <msub>
          <mi>g</mi>
          <mi>i</mi>
        </msub>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>This holds true for any function basis that is <em>orthonormal</em>. To test whether a set of functions forms an orthonormal basis, one needs to do two things: Make sure that any two basis functions <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\Phi_i(s)">
  <msub>
    <mi mathvariant="normal">Φ<!-- Φ --></mi>
    <mi>i</mi>
  </msub>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\Phi_j(s)">
  <msub>
    <mi mathvariant="normal">Φ<!-- Φ --></mi>
    <mi>j</mi>
  </msub>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span> integrate to 0 if <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="i \neq j">
  <mi>i</mi>
  <mo>≠<!-- ≠ --></mo>
  <mi>j</mi>
</math></span>, and to 1 if <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="i = j">
  <mi>i</mi>
  <mo>=</mo>
  <mi>j</mi>
</math></span>.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}\label{dirac_delta}
\int_S \Phi_i(s) \cdot \Phi_j(s) ds = \delta_{ij} = 
    \left\{
        \begin{array}{c} 
             1, i = j    \\ 
             0, i \neq j
        \end{array} 
    \right.
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-dirac_delta">
        <mtext>(2)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>j</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>s</mi>
        <mo>=</mo>
        <msub>
          <mi>δ<!-- δ --></mi>
          <mrow class="MJX-TeXAtom-ORD">
            <mi>i</mi>
            <mi>j</mi>
          </mrow>
        </msub>
        <mo>=</mo>
        <mrow>
          <mo>{</mo>
          <mtable columnalign="left center">
            <mtr>
              <mtd>
                <mn>1</mn>
                <mo>,</mo>
                <mi>i</mi>
                <mo>=</mo>
                <mi>j</mi>
              </mtd>
            </mtr>
            <mtr>
              <mtd>
                <mn>0</mn>
                <mo>,</mo>
                <mi>i</mi>
                <mo>≠<!-- ≠ --></mo>
                <mi>j</mi>
              </mtd>
            </mtr>
          </mtable>
          <mo fence="true" stretchy="true" symmetric="true"></mo>
        </mrow>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>This works completely analogous to a vector basis.</p>

<h1 id="the-triple-product-integral">The Triple Product Integral</h1>

<p>So lets assume we have two functions <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="f(s)">
  <mi>f</mi>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="g(s)">
  <mi>g</mi>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span> and we want to compute the function product of both as a new function <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="e(s)">
  <mi>e</mi>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span>.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}\label{e-fun}
	e(s) = f(s) g(s)
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-e-fun">
        <mtext>(3)</mtext>
      </mtd>
      <mtd>
        <mi>e</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mo>=</mo>
        <mi>f</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>g</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>Assume further that we have projected <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="f(s)">
  <mi>f</mi>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="g(s)">
  <mi>g</mi>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span> into the frequency domain of the function basis <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\Phi_i(s)">
  <msub>
    <mi mathvariant="normal">Φ<!-- Φ --></mi>
    <mi>i</mi>
  </msub>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span>. This means that we have the coefficients for both at hand.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{eqnarray}
	f(s) &amp; = &amp; \sum_j f_j \Phi_j(s) \label{f-fun} \\
	g(s) &amp; = &amp; \sum_k g_k \Phi_k(s) \label{g-fun}
\end{eqnarray}">
  <mtable columnalign="left right center left" displaystyle="true">
    <mtr>
      <mtd id="mjx-eqn-f-fun">
        <mtext>(4)</mtext>
      </mtd>
      <mtd>
        <mi>f</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>j</mi>
        </munder>
        <msub>
          <mi>f</mi>
          <mi>j</mi>
        </msub>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>j</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-g-fun">
        <mtext>(5)</mtext>
      </mtd>
      <mtd>
        <mi>g</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>k</mi>
        </munder>
        <msub>
          <mi>g</mi>
          <mi>k</mi>
        </msub>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>k</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>It may be possible to do the same operation in Equation <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{e-fun}">
  <mrow class="MathJax_ref" href="#mjx-eqn-e-fun">
    <mtext>3</mtext>
  </mrow>
</math></span> - multiplying two functions together - in the frequency space of the basis <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\Phi">
  <mi mathvariant="normal">Φ<!-- Φ --></mi>
</math></span> that we chose. How would we be able to determine the coefficients <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="e_i">
  <msub>
    <mi>e</mi>
    <mi>i</mi>
  </msub>
</math></span> of <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="e(s)">
  <mi>e</mi>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span>? We can start the usual way by integrating it with the basis function.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}
	e_i = \int_S e(s) \Phi_i(s) ds
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-6">
        <mtext>(6)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mi>e</mi>
          <mi>i</mi>
        </msub>
        <mo>=</mo>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <mi>e</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>But since we already have the coefficients for both <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="f(s)">
  <mi>f</mi>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="g(s)">
  <mi>g</mi>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span>, we can first replace <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="e(s)">
  <mi>e</mi>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span> by Equation <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{e-fun}">
  <mrow class="MathJax_ref" href="#mjx-eqn-e-fun">
    <mtext>3</mtext>
  </mrow>
</math></span> and then replace further with both Equation <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{f-fun}">
  <mrow class="MathJax_ref" href="#mjx-eqn-f-fun">
    <mtext>4</mtext>
  </mrow>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{g-fun}">
  <mrow class="MathJax_ref" href="#mjx-eqn-g-fun">
    <mtext>5</mtext>
  </mrow>
</math></span>.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{eqnarray}
	e_i &amp; = &amp; \int_S \Phi_i(s) e(s) ds \\ 
	    &amp; = &amp; \int_S \Phi_i(s) \left[ f(s) g(s) \right] ds \\ 
	    &amp; = &amp; \int_S \Phi_i(s) \left[ \left( \sum_j f_j \Phi_j(s) \right) \left( \sum_k g_k \Phi_k(s) \right) \right] ds
\end{eqnarray}">
  <mtable columnalign="left right center left" displaystyle="true">
    <mtr>
      <mtd id="mjx-eqn-7">
        <mtext>(7)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mi>e</mi>
          <mi>i</mi>
        </msub>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>e</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-8">
        <mtext>(8)</mtext>
      </mtd>
      <mtd></mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mrow>
          <mo>[</mo>
          <mi>f</mi>
          <mo stretchy="false">(</mo>
          <mi>s</mi>
          <mo stretchy="false">)</mo>
          <mi>g</mi>
          <mo stretchy="false">(</mo>
          <mi>s</mi>
          <mo stretchy="false">)</mo>
          <mo>]</mo>
        </mrow>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-9">
        <mtext>(9)</mtext>
      </mtd>
      <mtd></mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mrow>
          <mo>[</mo>
          <mrow>
            <mo>(</mo>
            <munder>
              <mo>∑<!-- ∑ --></mo>
              <mi>j</mi>
            </munder>
            <msub>
              <mi>f</mi>
              <mi>j</mi>
            </msub>
            <msub>
              <mi mathvariant="normal">Φ<!-- Φ --></mi>
              <mi>j</mi>
            </msub>
            <mo stretchy="false">(</mo>
            <mi>s</mi>
            <mo stretchy="false">)</mo>
            <mo>)</mo>
          </mrow>
          <mrow>
            <mo>(</mo>
            <munder>
              <mo>∑<!-- ∑ --></mo>
              <mi>k</mi>
            </munder>
            <msub>
              <mi>g</mi>
              <mi>k</mi>
            </msub>
            <msub>
              <mi mathvariant="normal">Φ<!-- Φ --></mi>
              <mi>k</mi>
            </msub>
            <mo stretchy="false">(</mo>
            <mi>s</mi>
            <mo stretchy="false">)</mo>
            <mo>)</mo>
          </mrow>
          <mo>]</mo>
        </mrow>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>We can rearrange some things: All the terms that are not dependent on <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="s">
  <mi>s</mi>
</math></span> can be moved out of the integral.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{eqnarray}
	e_i &amp; = &amp; \int_S \Phi_i(s) e(s) ds \\ 
	    &amp; = &amp; \int_S \Phi_i(s) \left[ f(s) g(s) \right] ds \\ 
	    &amp; = &amp; \int_S \Phi_i(s) \left[ \left( \sum_j f_j \Phi_j(s) \right) \left( \sum_k g_k \Phi_k(s) \right) \right] ds \\ 
	    &amp; = &amp; \sum_j \sum_k f_j  g_k \int_S \Phi_i(s) \Phi_j(s) \Phi_k(s) ds \\  
	    &amp; = &amp; \sum_j \sum_k f_j  g_k C_{ijk} \label{f-tripling-coeff}
\end{eqnarray}">
  <mtable columnalign="left right center left" displaystyle="true">
    <mtr>
      <mtd id="mjx-eqn-10">
        <mtext>(10)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mi>e</mi>
          <mi>i</mi>
        </msub>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>e</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-11">
        <mtext>(11)</mtext>
      </mtd>
      <mtd></mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mrow>
          <mo>[</mo>
          <mi>f</mi>
          <mo stretchy="false">(</mo>
          <mi>s</mi>
          <mo stretchy="false">)</mo>
          <mi>g</mi>
          <mo stretchy="false">(</mo>
          <mi>s</mi>
          <mo stretchy="false">)</mo>
          <mo>]</mo>
        </mrow>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-12">
        <mtext>(12)</mtext>
      </mtd>
      <mtd></mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mrow>
          <mo>[</mo>
          <mrow>
            <mo>(</mo>
            <munder>
              <mo>∑<!-- ∑ --></mo>
              <mi>j</mi>
            </munder>
            <msub>
              <mi>f</mi>
              <mi>j</mi>
            </msub>
            <msub>
              <mi mathvariant="normal">Φ<!-- Φ --></mi>
              <mi>j</mi>
            </msub>
            <mo stretchy="false">(</mo>
            <mi>s</mi>
            <mo stretchy="false">)</mo>
            <mo>)</mo>
          </mrow>
          <mrow>
            <mo>(</mo>
            <munder>
              <mo>∑<!-- ∑ --></mo>
              <mi>k</mi>
            </munder>
            <msub>
              <mi>g</mi>
              <mi>k</mi>
            </msub>
            <msub>
              <mi mathvariant="normal">Φ<!-- Φ --></mi>
              <mi>k</mi>
            </msub>
            <mo stretchy="false">(</mo>
            <mi>s</mi>
            <mo stretchy="false">)</mo>
            <mo>)</mo>
          </mrow>
          <mo>]</mo>
        </mrow>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-13">
        <mtext>(13)</mtext>
      </mtd>
      <mtd></mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>j</mi>
        </munder>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>k</mi>
        </munder>
        <msub>
          <mi>f</mi>
          <mi>j</mi>
        </msub>
        <msub>
          <mi>g</mi>
          <mi>k</mi>
        </msub>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>j</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>k</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-f-tripling-coeff">
        <mtext>(14)</mtext>
      </mtd>
      <mtd></mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>j</mi>
        </munder>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>k</mi>
        </munder>
        <msub>
          <mi>f</mi>
          <mi>j</mi>
        </msub>
        <msub>
          <mi>g</mi>
          <mi>k</mi>
        </msub>
        <msub>
          <mi>C</mi>
          <mrow class="MJX-TeXAtom-ORD">
            <mi>i</mi>
            <mi>j</mi>
            <mi>k</mi>
          </mrow>
        </msub>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>Now that is an interesting looking integral! Three basis functions in one, and at first sight the whole process looks quite familiar too. It is almost identical to what we did to derive the convolution theorem, now we <em>only</em> need to get rid of this integral and we're done.</p>

<p>The term <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\int_S \Phi_i(s) \Phi_j(s) \Phi_k(s) ds">
  <msub>
    <mo>∫<!-- ∫ --></mo>
    <mi>S</mi>
  </msub>
  <msub>
    <mi mathvariant="normal">Φ<!-- Φ --></mi>
    <mi>i</mi>
  </msub>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
  <msub>
    <mi mathvariant="normal">Φ<!-- Φ --></mi>
    <mi>j</mi>
  </msub>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
  <msub>
    <mi mathvariant="normal">Φ<!-- Φ --></mi>
    <mi>k</mi>
  </msub>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
  <mi>d</mi>
  <mi>s</mi>
</math></span> is called <em>the triple product integral</em> for more or less obvious reasons, and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="C_{ijk}">
  <msub>
    <mi>C</mi>
    <mrow class="MJX-TeXAtom-ORD">
      <mi>i</mi>
      <mi>j</mi>
      <mi>k</mi>
    </mrow>
  </msub>
</math></span> is a <em>tripling coefficient</em>. Its smaller sibling, the double product integral, is reducable to a Kroenecker Delta if the basis functions <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\Phi_i(s)">
  <msub>
    <mi mathvariant="normal">Φ<!-- Φ --></mi>
    <mi>i</mi>
  </msub>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span> form an orthonormal basis. But for <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="C_{ijk}">
  <msub>
    <mi>C</mi>
    <mrow class="MJX-TeXAtom-ORD">
      <mi>i</mi>
      <mi>j</mi>
      <mi>k</mi>
    </mrow>
  </msub>
</math></span>, things are not so easy. In fact, there is no general analytical solution for any arbitrary function basis.</p>

<h1 id="computing-tripling-coefficients">Computing tripling coefficients</h1>

<p>Luckily, for the two most common bases used for PRT, Haar-Wavelets and Spherical Harmonics, an analytical formula to compute their tripling coefficients exists.</p>

<p>Tripling coefficients of Spherical Harmonics can be expressed through <a href="https://en.wikipedia.org/wiki/Clebsch%E2%80%93Gordan_coefficients#Relation_to_Wigner_3-j_symbols">a relationship with Clebsch-Gordan coefficients</a>, which in turn can be expressed by Wigner 3j symbols. Below is an older implementation of mine, but as far as I'm aware the <a href="https://www.gnu.org/software/gsl/">GNU Scientific Library</a> contains one as well.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="nc">T</span><span class="p">&gt;</span>
<span class="kr">inline</span> <span class="n">T</span> <span class="nf">wigner_3j</span><span class="p">(</span><span class="kt">int</span> <span class="n">j1</span><span class="p">,</span> <span class="kt">int</span> <span class="n">j2</span><span class="p">,</span> <span class="kt">int</span> <span class="n">j3</span><span class="p">,</span> 
                   <span class="kt">int</span> <span class="n">m1</span><span class="p">,</span> <span class="kt">int</span> <span class="n">m2</span><span class="p">,</span> <span class="kt">int</span> <span class="n">m3</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">assert</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">abs</span><span class="p">(</span><span class="n">m1</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">j1</span> <span class="o">&amp;&amp;</span> 
    	<span class="s">"wigner_3j: m1 is out of bounds"</span><span class="p">);</span>
    <span class="n">assert</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">abs</span><span class="p">(</span><span class="n">m2</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">j2</span> <span class="o">&amp;&amp;</span> 
    	<span class="s">"wigner_3j: m2 is out of bounds"</span><span class="p">);</span>
    <span class="n">assert</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">abs</span><span class="p">(</span><span class="n">m3</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">j3</span> <span class="o">&amp;&amp;</span> 
    	<span class="s">"wigner_3j: m3 is out of bounds"</span><span class="p">);</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">triangle_inequality</span><span class="p">(</span><span class="n">j1</span><span class="p">,</span> <span class="n">j2</span><span class="p">,</span> <span class="n">j3</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">m1</span><span class="o">+</span><span class="n">m2</span><span class="o">+</span><span class="n">m3</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">abs</span><span class="p">(</span><span class="n">m1</span><span class="p">)</span> <span class="o">&gt;</span> <span class="n">j1</span><span class="p">)</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">abs</span><span class="p">(</span><span class="n">m2</span><span class="p">)</span> <span class="o">&gt;</span> <span class="n">j2</span><span class="p">)</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">abs</span><span class="p">(</span><span class="n">m3</span><span class="p">)</span> <span class="o">&gt;</span> <span class="n">j3</span><span class="p">)</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    
    <span class="kt">int</span> <span class="n">t1</span> <span class="o">=</span> <span class="n">j2</span> <span class="o">-</span> <span class="n">m1</span> <span class="o">-</span> <span class="n">j3</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">t2</span> <span class="o">=</span> <span class="n">j1</span> <span class="o">+</span> <span class="n">m2</span> <span class="o">-</span> <span class="n">j3</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">t3</span> <span class="o">=</span> <span class="n">j1</span> <span class="o">+</span> <span class="n">j2</span> <span class="o">-</span> <span class="n">j3</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">t4</span> <span class="o">=</span> <span class="n">j1</span> <span class="o">-</span> <span class="n">m1</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">t5</span> <span class="o">=</span> <span class="n">j2</span> <span class="o">+</span> <span class="n">m2</span><span class="p">;</span>
    
    <span class="kt">int</span> <span class="n">tmin</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">max</span><span class="p">(</span><span class="n">t1</span><span class="p">,</span> <span class="n">t2</span><span class="p">));</span>
    <span class="kt">int</span> <span class="n">tmax</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">min</span><span class="p">(</span><span class="n">t3</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">min</span><span class="p">(</span><span class="n">t4</span><span class="p">,</span> <span class="n">t5</span><span class="p">));</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="n">tmin</span> <span class="o">&gt;</span> <span class="n">tmax</span><span class="p">)</span>
        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    
    <span class="n">T</span> <span class="n">wigner</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    
    <span class="k">auto</span> <span class="n">f</span> <span class="o">=</span> <span class="n">factorial</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">;</span>
    
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">t</span> <span class="o">=</span> <span class="n">tmin</span><span class="p">;</span> <span class="n">t</span> <span class="o">&lt;=</span> <span class="n">tmax</span><span class="p">;</span> <span class="o">++</span><span class="n">t</span><span class="p">)</span>
        <span class="n">wigner</span> <span class="o">=</span> <span class="n">wigner</span> <span class="o">+</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">pow</span><span class="p">(</span><span class="o">-</span><span class="mf">1.0</span><span class="p">,</span> <span class="n">t</span><span class="p">))</span><span class="o">/</span>
            <span class="p">(</span><span class="n">f</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>    <span class="o">*</span> <span class="n">f</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t</span><span class="o">-</span><span class="n">t1</span><span class="p">)</span> <span class="o">*</span> <span class="n">f</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t</span><span class="o">-</span><span class="n">t2</span><span class="p">)</span> <span class="o">*</span>
             <span class="n">f</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t3</span><span class="o">-</span><span class="n">t</span><span class="p">)</span> <span class="o">*</span> <span class="n">f</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t4</span><span class="o">-</span><span class="n">t</span><span class="p">)</span> <span class="o">*</span> <span class="n">f</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t5</span><span class="o">-</span><span class="n">t</span><span class="p">));</span>
    
    <span class="n">wigner</span> <span class="o">=</span> <span class="n">wigner</span> <span class="o">*</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">pow</span><span class="p">(</span><span class="o">-</span><span class="mf">1.0</span><span class="p">,</span> <span class="n">j1</span><span class="o">-</span><span class="n">j2</span><span class="o">-</span><span class="n">m3</span><span class="p">))</span> <span class="o">*</span> 
    	<span class="k">static_cast</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">sqrt</span><span class="p">(</span>
    		<span class="n">f</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">j1</span><span class="o">+</span><span class="n">j2</span><span class="o">-</span><span class="n">j3</span><span class="p">)</span> <span class="o">*</span> <span class="n">f</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">j1</span><span class="o">-</span><span class="n">j2</span><span class="o">+</span><span class="n">j3</span><span class="p">)</span> <span class="o">*</span> 
    		<span class="n">f</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="o">-</span><span class="n">j1</span><span class="o">+</span><span class="n">j2</span><span class="o">+</span><span class="n">j3</span><span class="p">)</span> <span class="o">/</span> <span class="n">f</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">j1</span><span class="o">+</span><span class="n">j2</span><span class="o">+</span><span class="n">j3</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span> <span class="o">*</span> 
    		<span class="n">f</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">j1</span><span class="o">+</span><span class="n">m1</span><span class="p">)</span> <span class="o">*</span> <span class="n">f</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">j1</span><span class="o">-</span><span class="n">m1</span><span class="p">)</span> <span class="o">*</span> 
    		<span class="n">f</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">j2</span><span class="o">+</span><span class="n">m2</span><span class="p">)</span> <span class="o">*</span> <span class="n">f</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">j2</span><span class="o">-</span><span class="n">m2</span><span class="p">)</span> <span class="o">*</span> 
    		<span class="n">f</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">j3</span><span class="o">+</span><span class="n">m3</span><span class="p">)</span> <span class="o">*</span> <span class="n">f</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">j3</span><span class="o">-</span><span class="n">m3</span><span class="p">)</span>
    	<span class="p">));</span>
    
    <span class="k">return</span> <span class="n">wigner</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>With the tripling coefficients at hand, we can create a function product of arbitrary functions encoded in the same frequency domain. A major downside of regular PRT is that the BRDF, visibility and Lambert factor are all represented simply as one transfer function, which therefore gets encoded as one transfer vector. The Lambert factor however is a very low-frequency signal, whilst visibility and BRDF may contain high-frequency peaks. Packing them all together means choosing either to produce a lot of waste coefficients or compromising on the quality of the representation. Ng et al. get around this by <a href="https://graphics.stanford.edu/papers/allfreqmat/">decoupling visibility and the BRDF in the transfer function again</a> with a triple product composition.</p>

<p>Another use case presented in <a href="https://www.microsoft.com/en-us/research/publication/precomputed-shadow-fields-for-dynamic-scenes/">Precomputed Shadow Fields for Dynamic Scenes</a> wraps an object in a shell which, at several sample points, has coefficients encoding a visibility function from the point on that shell. If the shell collides with another PRT-object, coefficients of both objects visibility can be combined to compute dynamic shadowing between the two, getting rid of some of the rigidness requirements that PRT imposes on the scene. This paper is naturally enhanced in <a href="https://diglib.eg.org/handle/10.2312/CGF.v26i3pp485-493">Precomputed Radiance Transfer Field for Rendering Interreflections in Dynamic Scenes</a>, where the authors not just encode visibility, but indirect diffuse transfer between objects as well.</p>

<h1 id="transforming-coefficients">Transforming coefficients</h1>

<p>Returning to Equation <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{f-tripling-coeff}">
  <mrow class="MathJax_ref" href="#mjx-eqn-f-tripling-coeff">
    <mtext>14</mtext>
  </mrow>
</math></span> we can easily construct <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="e_i">
  <msub>
    <mi>e</mi>
    <mi>i</mi>
  </msub>
</math></span> in another way.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{eqnarray}    
    e_i &amp; = &amp; \sum_j \sum_k f_j g_k C_{ijk} \\  
    e_i &amp; = &amp; \sum_j f_j T_{ij}
\end{eqnarray}">
  <mtable columnalign="left right center left" displaystyle="true">
    <mtr>
      <mtd id="mjx-eqn-15">
        <mtext>(15)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mi>e</mi>
          <mi>i</mi>
        </msub>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>j</mi>
        </munder>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>k</mi>
        </munder>
        <msub>
          <mi>f</mi>
          <mi>j</mi>
        </msub>
        <msub>
          <mi>g</mi>
          <mi>k</mi>
        </msub>
        <msub>
          <mi>C</mi>
          <mrow class="MJX-TeXAtom-ORD">
            <mi>i</mi>
            <mi>j</mi>
            <mi>k</mi>
          </mrow>
        </msub>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-16">
        <mtext>(16)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mi>e</mi>
          <mi>i</mi>
        </msub>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>j</mi>
        </munder>
        <msub>
          <mi>f</mi>
          <mi>j</mi>
        </msub>
        <msub>
          <mi>T</mi>
          <mrow class="MJX-TeXAtom-ORD">
            <mi>i</mi>
            <mi>j</mi>
          </mrow>
        </msub>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>Instead of using the tripling coefficient tensor <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="C_{ijk}">
  <msub>
    <mi>C</mi>
    <mrow class="MJX-TeXAtom-ORD">
      <mi>i</mi>
      <mi>j</mi>
      <mi>k</mi>
    </mrow>
  </msub>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="g_k">
  <msub>
    <mi>g</mi>
    <mi>k</mi>
  </msub>
</math></span>, we can also use a matrix <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="T_{ij}">
  <msub>
    <mi>T</mi>
    <mrow class="MJX-TeXAtom-ORD">
      <mi>i</mi>
      <mi>j</mi>
    </mrow>
  </msub>
</math></span> (one way to construct it would be to multiply <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="C_{ijk}">
  <msub>
    <mi>C</mi>
    <mrow class="MJX-TeXAtom-ORD">
      <mi>i</mi>
      <mi>j</mi>
      <mi>k</mi>
    </mrow>
  </msub>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="g_k">
  <msub>
    <mi>g</mi>
    <mi>k</mi>
  </msub>
</math></span>). This matrix will <em>transform</em> coefficients <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="f_j">
  <msub>
    <mi>f</mi>
    <mi>j</mi>
  </msub>
</math></span> into coefficients <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="e_i">
  <msub>
    <mi>e</mi>
    <mi>i</mi>
  </msub>
</math></span>, which represent the function <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="e(s)">
  <mi>e</mi>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span> in the basis <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\Phi">
  <mi mathvariant="normal">Φ<!-- Φ --></mi>
</math></span> that we chose.</p>

<p>The concept of a matrix to transform a coefficient vector is sometimes mentioned in PRT tutorials for signal rotation. Rather than having an environment map <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="m(s)">
  <mi>m</mi>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span>, rotate it with <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{R}(m)(s)">
  <mrow class="MJX-TeXAtom-ORD">
    <mi mathvariant="bold">R</mi>
  </mrow>
  <mo stretchy="false">(</mo>
  <mi>m</mi>
  <mo stretchy="false">)</mo>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span> and then resample and recompute the Spherical Harmonic coefficients for it, we can likewise <em>rotate</em> the coefficients <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="m_i">
  <msub>
    <mi>m</mi>
    <mi>i</mi>
  </msub>
</math></span> with a special frequency-space rotation matrix <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{R^{\star}}">
  <mrow class="MJX-TeXAtom-ORD">
    <msup>
      <mi mathvariant="bold">R</mi>
      <mrow class="MJX-TeXAtom-ORD">
        <mo>⋆<!-- ⋆ --></mo>
      </mrow>
    </msup>
  </mrow>
</math></span>. In essence, we would produce a new set of coefficients that simply match those of the rotated environment map in image space.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{eqnarray}
    m(s) &amp; = &amp; \sum_i m_i \Phi_i(s) ds \\
    \mathbf{R}(m)(s) &amp; = &amp; \sum_i \sum_j (m_j \cdot R^{\star}_{ij}) \Phi_i(s) ds \\
\end{eqnarray}">
  <mtable columnalign="left right center left" displaystyle="true">
    <mtr>
      <mtd id="mjx-eqn-17">
        <mtext>(17)</mtext>
      </mtd>
      <mtd>
        <mi>m</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>i</mi>
        </munder>
        <msub>
          <mi>m</mi>
          <mi>i</mi>
        </msub>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-18">
        <mtext>(18)</mtext>
      </mtd>
      <mtd>
        <mrow class="MJX-TeXAtom-ORD">
          <mi mathvariant="bold">R</mi>
        </mrow>
        <mo stretchy="false">(</mo>
        <mi>m</mi>
        <mo stretchy="false">)</mo>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>i</mi>
        </munder>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>j</mi>
        </munder>
        <mo stretchy="false">(</mo>
        <msub>
          <mi>m</mi>
          <mi>j</mi>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <msubsup>
          <mi>R</mi>
          <mrow class="MJX-TeXAtom-ORD">
            <mi>i</mi>
            <mi>j</mi>
          </mrow>
          <mrow class="MJX-TeXAtom-ORD">
            <mo>⋆<!-- ⋆ --></mo>
          </mrow>
        </msubsup>
        <mo stretchy="false">)</mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p><a href="https://github.com/thefranke/deimos/blob/master/math/spherical_harmonics.h#L344">Here is an old implementation of mine</a> of one such matrix to do the job. The input is a regular 3x3 rotation matrix, but the result is a matrix that needs to be as big as the coefficient vector you want to rotate. In case of 4-band Spherical Harmonic vector for instance, the result will be a 16x16 <em>Spherical Harmonic rotation matrix</em>.</p>

<h1 id="matrix-radiance-transfer">Matrix Radiance Transfer</h1>

<p>In the diffuse case of PRT, we only need to compute a single number/color, because diffuse surfaces reflect the same intensity in all directions. The double product integral fits the job, because the convolution acts like a blur-filter that adds up all the light hitting the surface and reflects a single value. But for non-diffuse surfaces like metals this outgoing radiance varies in different directions, so we need more than just one number; we need a function that represents all light reflected into all different directions. We need a coefficient vector of the reflected light that we generate from the incident light coefficient vector.</p>

<p>For diffuse PRT, we turn a transfer function of <em>just</em> the incidenct light direction into the transfer coefficient vector. Now however we must turn a transfer function of an incident and outgoing direction into a transfer matrix. But how to construct the matrix for a function that has two variables? We can do so by imagining that we have a special set of transfer coefficients <em>for each outgoing direction</em>  <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{\omega_o}">
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi>ω<!-- ω --></mi>
      <mi mathvariant="bold">o</mi>
    </msub>
  </mrow>
</math></span>, rather than just a single set for all of them. Where before we would turn a transfer function into a set of coefficients, we now basically convert it into a set of functions <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="T_m(\mathbf{\omega_o})">
  <msub>
    <mi>T</mi>
    <mi>m</mi>
  </msub>
  <mo stretchy="false">(</mo>
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi>ω<!-- ω --></mi>
      <mi mathvariant="bold">o</mi>
    </msub>
  </mrow>
  <mo stretchy="false">)</mo>
</math></span> which return the <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="m">
  <mi>m</mi>
</math></span>-th coefficient of the reflected light for an outgoing direction <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{\omega_o}">
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi>ω<!-- ω --></mi>
      <mi mathvariant="bold">o</mi>
    </msub>
  </mrow>
</math></span>. To do this, we first integrate <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="T">
  <mi>T</mi>
</math></span> along all incident directions <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{\omega_i}">
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi>ω<!-- ω --></mi>
      <mi mathvariant="bold">i</mi>
    </msub>
  </mrow>
</math></span>.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}
    T_m(\mathbf{\omega_o}) = \int_{\Omega} T(\mathbf{\omega_i}, \mathbf{\omega_o}) \Phi_m(\mathbf{\omega_i}) d{\mathbf{\omega_i}}
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-19">
        <mtext>(19)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mi>T</mi>
          <mi>m</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">o</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <mo>=</mo>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mrow class="MJX-TeXAtom-ORD">
            <mi mathvariant="normal">Ω<!-- Ω --></mi>
          </mrow>
        </msub>
        <mi>T</mi>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
        <mo>,</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">o</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>m</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mrow class="MJX-TeXAtom-ORD">
          <mrow class="MJX-TeXAtom-ORD">
            <msub>
              <mi>ω<!-- ω --></mi>
              <mi mathvariant="bold">i</mi>
            </msub>
          </mrow>
        </mrow>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>We then integrate each resulting function <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="T_m(\mathbf{\omega_o})">
  <msub>
    <mi>T</mi>
    <mi>m</mi>
  </msub>
  <mo stretchy="false">(</mo>
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi>ω<!-- ω --></mi>
      <mi mathvariant="bold">o</mi>
    </msub>
  </mrow>
  <mo stretchy="false">)</mo>
</math></span>, this time over all outgoing directions <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{\omega_o}">
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi>ω<!-- ω --></mi>
      <mi mathvariant="bold">o</mi>
    </msub>
  </mrow>
</math></span>.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}
    T_{mn} = \int_{\Omega} T_m(\mathbf{\omega_o}) \Phi_n(\mathbf{\omega_o}) d{\mathbf{\omega_o}}
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-20">
        <mtext>(20)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mi>T</mi>
          <mrow class="MJX-TeXAtom-ORD">
            <mi>m</mi>
            <mi>n</mi>
          </mrow>
        </msub>
        <mo>=</mo>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mrow class="MJX-TeXAtom-ORD">
            <mi mathvariant="normal">Ω<!-- Ω --></mi>
          </mrow>
        </msub>
        <msub>
          <mi>T</mi>
          <mi>m</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">o</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>n</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">o</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mrow class="MJX-TeXAtom-ORD">
          <mrow class="MJX-TeXAtom-ORD">
            <msub>
              <mi>ω<!-- ω --></mi>
              <mi mathvariant="bold">o</mi>
            </msub>
          </mrow>
        </mrow>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>In <a href="/log/2016/10/18/the_convolution_theorem.html">the previous article</a>, the double integral product was used to compute the outgoing reflected light from the incident light coefficients <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="l_n">
  <msub>
    <mi>l</mi>
    <mi>n</mi>
  </msub>
</math></span> and the surface transfer coefficients <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="t_n">
  <msub>
    <mi>t</mi>
    <mi>n</mi>
  </msub>
</math></span>. We <em>transferred</em> incident light into single value (or, if you want to think about it this way, into function which equals a constant value), which is perfect for diffuse reflection. Now with <a href="https://mediatech.aalto.fi/~jaakko/publications/lehtinen2003i3d_paper.pdf">Matrix Radiance Transfer</a> a matrix <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="T_{mn}">
  <msub>
    <mi>T</mi>
    <mrow class="MJX-TeXAtom-ORD">
      <mi>m</mi>
      <mi>n</mi>
    </mrow>
  </msub>
</math></span> instead represents the transfer of incident to exit light.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{eqnarray}
	L(\mathbf{\omega_o}) &amp; = &amp; L_e(\mathbf{\omega_o}) + \int_\Omega L(\mathbf{\omega_i}) \cdot f( \mathbf{\omega_i}, \mathbf{\omega_o}) \cdot \langle \mathbf{n}, \mathbf{\omega_i} \rangle d\mathbf{\omega_i} \\ 
                         &amp; = &amp; L_e(\mathbf{\omega_o}) + \int_\Omega L(\mathbf{\omega_i}) \cdot T( \mathbf{\omega_i}, \mathbf{\omega_o}) d\mathbf{\omega_i} \\ 
	                     &amp; = &amp; L_e(\mathbf{\omega_o}) + \sum_n \sum_m \left( l_m T_{mn} \right) \Phi_n(\mathbf{\omega_o}) \\
                         &amp; = &amp; L_e(\mathbf{\omega_o}) + \langle \langle \mathbf{l}, \mathbf{T} \rangle, \mathbf{\Phi}(\mathbf{\omega_o}) \rangle \\
\end{eqnarray}">
  <mtable columnalign="left right center left" displaystyle="true">
    <mtr>
      <mtd id="mjx-eqn-21">
        <mtext>(21)</mtext>
      </mtd>
      <mtd>
        <mi>L</mi>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">o</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <msub>
          <mi>L</mi>
          <mi>e</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">o</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <mo>+</mo>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi mathvariant="normal">Ω<!-- Ω --></mi>
        </msub>
        <mi>L</mi>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <mo>⋅<!-- ⋅ --></mo>
        <mi>f</mi>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
        <mo>,</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">o</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <mo>⋅<!-- ⋅ --></mo>
        <mo fence="false" stretchy="false">⟨<!-- ⟨ --></mo>
        <mrow class="MJX-TeXAtom-ORD">
          <mi mathvariant="bold">n</mi>
        </mrow>
        <mo>,</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
        <mo fence="false" stretchy="false">⟩<!-- ⟩ --></mo>
        <mi>d</mi>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-22">
        <mtext>(22)</mtext>
      </mtd>
      <mtd></mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <msub>
          <mi>L</mi>
          <mi>e</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">o</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <mo>+</mo>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi mathvariant="normal">Ω<!-- Ω --></mi>
        </msub>
        <mi>L</mi>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <mo>⋅<!-- ⋅ --></mo>
        <mi>T</mi>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
        <mo>,</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">o</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-23">
        <mtext>(23)</mtext>
      </mtd>
      <mtd></mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <msub>
          <mi>L</mi>
          <mi>e</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">o</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <mo>+</mo>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>n</mi>
        </munder>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>m</mi>
        </munder>
        <mrow>
          <mo>(</mo>
          <msub>
            <mi>l</mi>
            <mi>m</mi>
          </msub>
          <msub>
            <mi>T</mi>
            <mrow class="MJX-TeXAtom-ORD">
              <mi>m</mi>
              <mi>n</mi>
            </mrow>
          </msub>
          <mo>)</mo>
        </mrow>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>n</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">o</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-24">
        <mtext>(24)</mtext>
      </mtd>
      <mtd></mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <msub>
          <mi>L</mi>
          <mi>e</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">o</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <mo>+</mo>
        <mo fence="false" stretchy="false">⟨<!-- ⟨ --></mo>
        <mo fence="false" stretchy="false">⟨<!-- ⟨ --></mo>
        <mrow class="MJX-TeXAtom-ORD">
          <mi mathvariant="bold">l</mi>
        </mrow>
        <mo>,</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <mi mathvariant="bold">T</mi>
        </mrow>
        <mo fence="false" stretchy="false">⟩<!-- ⟩ --></mo>
        <mo>,</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <mi mathvariant="bold">Φ<!-- Φ --></mi>
        </mrow>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">o</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <mo fence="false" stretchy="false">⟩<!-- ⟩ --></mo>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>Where we used to have a dot product of two vectors, we now produce a <em>reflection coefficient vector</em> from incident light, and then reconstruct the light reflected into a <em>specific</em> direction <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{\omega_o}">
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi>ω<!-- ω --></mi>
      <mi mathvariant="bold">o</mi>
    </msub>
  </mrow>
</math></span> by multiplying it with the basis <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\Phi">
  <mi mathvariant="normal">Φ<!-- Φ --></mi>
</math></span>.</p>

<h1 id="conclusion">Conclusion</h1>

<p>Like <em>regular</em> vectors in 3D, coefficient vectors can likewise be transformed with matrices. This is useful when a function - encoded as such a vector - should be transformed into a different function, such as unoccluded to occluded light, normal to rotated environment, incident to exit radiance or when simply decoupling one transfer function into several parts and then multiplying them back together again.</p>

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

<ol>
  <li>Wikipedia, <a href="https://en.wikipedia.org/wiki/Clebsch%E2%80%93Gordan_coefficients#Relation_to_Wigner_3-j_symbols">Clebsch-Gordon Coefficients as Wigner-3j expression</a>
</li>
  <li>Free Software Foundation, <a href="https://www.gnu.org/software/gsl/">GNU Scientific Library</a>
</li>
  <li>Ng et al., <a href="https://graphics.stanford.edu/papers/allfreqmat/">Triple Product Wavelet Integrals for All-Frequency Relighting</a>
</li>
  <li>Zhou et al., <a href="https://www.microsoft.com/en-us/research/publication/precomputed-shadow-fields-for-dynamic-scenes/">Precomputed Shadow Fields for Dynamic Scenes</a>
</li>
  <li>Pan et al., <a href="https://diglib.eg.org/handle/10.2312/CGF.v26i3pp485-493">Precomputed Radiance Transfer Field for Rendering Interreflections in Dynamic Scenes</a>
</li>
  <li>Tobias Alexander Franke, <a href="https://github.com/thefranke/deimos/blob/master/math/spherical_harmonics.h#L344">SH rotation matrix implementation</a>
</li>
  <li>Lehtinen and Kautz, <a href="https://mediatech.aalto.fi/~jaakko/publications/lehtinen2003i3d_paper.pdf">Matrix Radiance Transfer</a>
</li>
</ol> ]]></description>
            <pubDate>Wed, 19 Apr 2017 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/log/2017/04/19/triple-products.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhyK0hRRUFqVWk1Q0JxN0NuMXhoSWpxa1J0QwpDL1NjUzBpK21NbFZjUUVTTnpwVURuVUEvamdpbTgvcFU4aU11N3ZrSzB2dVFnVXB4dnd6TTJiN0FYSzY4cjI3CmZGSUEKPXA4b2sKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Notes</category><category>FunctionTransform</category><category>MRT</category><category>PRT</category><category>RadianceField</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>On nomenclature</title>
            <description><![CDATA[ <p>Ever since the graphics field got expanded by a variety of new gadgets such as the Oculus Rift, Microsoft’s Kinect and HoloLens or the Leap Motion, and new parties moved in to expand their usage (in particular Magic Leap), I have seen <a href="https://twitter.com/search?q=%23MixedReality">a whole brigade of marketeers and fanboys</a> come with them who have simply made stuff up or <a href="https://www.thefoundry.co.uk/solutions/virtual-reality/vr-ar-mr-sorry-im-confused/">juggled around with terms</a> they are <a href="https://www.gamasutra.com/view/news/281838/Magic_Leaps_chief_game_wizard_has_big_ideas_for_the_mixed_reality_future.php">evidently confused about at best</a>.</p>

<p>The first group likes to revise or invent new terms. For instance, using a different word to hide inefficiencies in a certain method. So instead of having <em>augmenting objects</em>, Microsoft prefers to call them <em>Holograms</em>. Why? Because in a see-through display like the HoloLens, you still can’t render proper black (that is, block light from passing through the lens entirely). Better find a new word for those translucent things on the screen. Additionally there are those who come up with entirely meaningless marketing terms like Cross-Reality (XR) or, for maximum chaos, <a href="https://www.youtube-nocookie.com/embed//DIIk89cmcsU">Merged Reality</a>.</p>

<p>The second is a consistently growing group of people who like to claim one of the following things:</p>

<ul>
  <li>Mixed Reality is Augmented Reality and vice versa</li>
  <li>Mixed Reality = Virtual Reality + Augmented Reality</li>
  <li>Mixed Reality is a superset of Virtual Reality</li>
  <li>Mixed Reality = Some new I/O device</li>
  <li>Mixed Reality and Augmented Reality are unrelated</li>
  <li>Augmented Reality is the opposite of Virtual Reality</li>
</ul>

<p>This group is not just simply wrong, but also responsible for the spread of mass-confusion around these terms which have been clearly defined for a long time and which have been used in many research papers.</p>

<h1 id="mixed-reality">Mixed Reality</h1>

<p>The term Mixed Reality was introduced by Paul Milgram et al. in a 1994 publication called <a href="https://etclab.mie.utoronto.ca/publication/1994/Milgram_Takemura_SPIE1994.pdf"><em>Augmented Reality: A class of displays on the reality-virtuality continuum</em></a>. It presented the idea that between virtual and real there is a spectrum of different mixtures of both.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2016_12_mr-continuum.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2016_12_mr-continuum.png" alt="From Milgram et al., The Reality-Virtuality Continuum" title="From Milgram et al., The Reality-Virtuality Continuum">
    
    </a>
    
    
    <figcaption>From Milgram et al., The Reality-Virtuality Continuum</figcaption>
    
</figure>

<p>For instance, there could be a real news-anchor inside a virtual news-room (a thing called <em>Augmented Virtuality</em>). Or a virtual object in a real setting, like Gollum in Lord of the Rings (a.k.a. <em>Augmented Reality</em>). Things don’t necessarily have to look real. The spectrum could be 2D, with one axis defining if something is added or even removed (the latter being called <em>Diminished Reality</em>). The gist of it though is that you can arbitrarily <em>lerp</em> between real and virtual with any degree to define realism.</p>

<p>The paper introduces the term <em>Mixed Reality</em> as follows:</p>

<blockquote>
  <p>Within this framework it is straightforward to define a generic Mixed Reality (MR) environment as one in which real world and virtual world objects are presented together within a single display, that is, anywhere <strong>between</strong> the extrema of the RV continuum.</p>
</blockquote>

<p>This <strong>excludes</strong> two realities: For lack of a better term “Real Reality”, and the polar opposite, Virtual Reality. You don’t need a definition for the obvious though, because Mixed Reality <em>implies</em> that you are <strong>mixing</strong> realities in the first place.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2016_12_18_ar_continuum.gif">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2016_12_18_ar_continuum.gif" alt="Four stages on the RV continuum. Courtesy Vincent McCurley @vmccurley" title="Four stages on the RV continuum. Courtesy Vincent McCurley @vmccurley">
    
    </a>
    
    
    <figcaption>Four stages on the RV continuum. Courtesy Vincent McCurley @vmccurley</figcaption>
    
</figure>

<p>There are those who think they might come up with a clever argument that the observer/controller/player of a Virtual Reality simulation is somehow the mixed-in real element and therefore any interaction with Virtual Reality constitutes a Mixed Reality. This is of course silly, since that would include <em>people using a computer</em> and render the term meaningless. It seems that the reasoning behind this line of thinking is that there is a fancy new input device such as the Kinect or the Leap Motion which reads “real gestures”, whereas your old-school mouse or keyboard is just not magical enough.</p>

<h1 id="conclusion">Conclusion</h1>

<ul>
  <li>Virtual Reality is <strong>NOT</strong> Mixed Reality or included by it</li>
  <li>Virtual Reality is <strong>NOT</strong> Augmented Reality or included by it</li>
  <li>Augmented Reality <strong>IS</strong> a sub-type of Mixed Reality</li>
  <li>Augmented Reality is <strong>NOT</strong> the opposite of Virtual Reality</li>
  <li>Mixed Reality is <strong>MORE</strong> than Augmented Reality</li>
  <li>Mixed Reality is <strong>NOT</strong> a new I/O device</li>
  <li>AR + VR <strong>IS</strong> meaningless</li>
</ul>

<p>Or to make it really simple: If you are not mixing two or more realities, you’re not doing Mixed Reality.</p>

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

<ol>
  <li>Michael Abrash, <a href="https://blogs.valvesoftware.com/abrash/why-you-wont-see-hard-ar-anytime-soon/">Why You Won’t See Hard AR Anytime Soon</a>
</li>
  <li>Gamasutra, <a href="https://www.gamasutra.com/view/news/281838/Magic_Leaps_chief_game_wizard_has_big_ideas_for_the_mixed_reality_future.php">Magic Leap’s ‘chief game wizard’ has big ideas for the mixed reality future</a>
</li>
  <li>The Foundry, <a href="https://www.thefoundry.co.uk/solutions/virtual-reality/vr-ar-mr-sorry-im-confused/">VR? AR? MR? Sorry, I’m confused.</a>
</li>
  <li>Twitter, <a href="https://twitter.com/search?q=%23MixedReality">#MixedReality</a>
</li>
  <li>Intel, <a href="https://www.youtube-nocookie.com/embed//DIIk89cmcsU">Merged Reality</a>
</li>
  <li>Milgram et al., <a href="https://etclab.mie.utoronto.ca/publication/1994/Milgram_Takemura_SPIE1994.pdf">Augmented Reality: A class of displays on the reality-virtuality continuum</a>
</li>
</ol> ]]></description>
            <pubDate>Wed, 28 Dec 2016 00:00:00 +0100</pubDate>
            <link>https://www.tobias-franke.eu/log/2016/12/28/on_nomenclature.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhxbGpnRUF3ZmR5QUNldTh1bHU2Yld3RzFpKwp2UkpPbThMMWc5R2I0UjY0U0doVkJWUUJBSm9vYWc3ZnJIN2QrZ1RvM043YWMxbnRjRDZYMGU5MFNTY1M0Z3lpCkZNSU8KPVRqVjgKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>General</category><category>Introduction</category><category>Publication</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>The Convolution Theorem</title>
            <description><![CDATA[ <h1 id="the-basis">The Basis</h1>

<p>In Linear Algebra, we're used to build a vector <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{v}">
  <mrow class="MJX-TeXAtom-ORD">
    <mi mathvariant="bold">v</mi>
  </mrow>
</math></span> out of other vectors <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{v_1}, \mathbf{v_2}">
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi mathvariant="bold">v</mi>
      <mn mathvariant="bold">1</mn>
    </msub>
  </mrow>
  <mo>,</mo>
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi mathvariant="bold">v</mi>
      <mn mathvariant="bold">2</mn>
    </msub>
  </mrow>
</math></span>.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}
  \mathbf{v} = \mathbf{v_1} + \mathbf{v_2}
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-1">
        <mtext>(1)</mtext>
      </mtd>
      <mtd>
        <mrow class="MJX-TeXAtom-ORD">
          <mi mathvariant="bold">v</mi>
        </mrow>
        <mo>=</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi mathvariant="bold">v</mi>
            <mn mathvariant="bold">1</mn>
          </msub>
        </mrow>
        <mo>+</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi mathvariant="bold">v</mi>
            <mn mathvariant="bold">2</mn>
          </msub>
        </mrow>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>Each vector is, at the very least, implicitly constructed out of its basis vectors. If there is no specific basis mentioned anywhere, we assume it to be a basis of unit vectors. For instance, for a two-dimensional space these are <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{\Phi_1} = (1, 0)">
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi mathvariant="bold">Φ<!-- Φ --></mi>
      <mn mathvariant="bold">1</mn>
    </msub>
  </mrow>
  <mo>=</mo>
  <mo stretchy="false">(</mo>
  <mn>1</mn>
  <mo>,</mo>
  <mn>0</mn>
  <mo stretchy="false">)</mo>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{\Phi_2} = (0, 1)">
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi mathvariant="bold">Φ<!-- Φ --></mi>
      <mn mathvariant="bold">2</mn>
    </msub>
  </mrow>
  <mo>=</mo>
  <mo stretchy="false">(</mo>
  <mn>0</mn>
  <mo>,</mo>
  <mn>1</mn>
  <mo stretchy="false">)</mo>
</math></span>, and that a vector <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{v} = (3, 2)">
  <mrow class="MJX-TeXAtom-ORD">
    <mi mathvariant="bold">v</mi>
  </mrow>
  <mo>=</mo>
  <mo stretchy="false">(</mo>
  <mn>3</mn>
  <mo>,</mo>
  <mn>2</mn>
  <mo stretchy="false">)</mo>
</math></span> is just the sum of scaled basis vectors.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}
\left(
    \begin{array}{c} 
         3\\ 
         2
    \end{array} 
\right) = 3 \cdot \mathbf{\Phi_1} + 2 \cdot \mathbf{\Phi_2}
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-2">
        <mtext>(2)</mtext>
      </mtd>
      <mtd>
        <mrow>
          <mo>(</mo>
          <mtable columnalign="left center">
            <mtr>
              <mtd>
                <mn>3</mn>
              </mtd>
            </mtr>
            <mtr>
              <mtd>
                <mn>2</mn>
              </mtd>
            </mtr>
          </mtable>
          <mo>)</mo>
        </mrow>
        <mo>=</mo>
        <mn>3</mn>
        <mo>⋅<!-- ⋅ --></mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi mathvariant="bold">Φ<!-- Φ --></mi>
            <mn mathvariant="bold">1</mn>
          </msub>
        </mrow>
        <mo>+</mo>
        <mn>2</mn>
        <mo>⋅<!-- ⋅ --></mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi mathvariant="bold">Φ<!-- Φ --></mi>
            <mn mathvariant="bold">2</mn>
          </msub>
        </mrow>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>The numbers <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="3">
  <mn>3</mn>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="2">
  <mn>2</mn>
</math></span> are the <em>coefficients</em> of the basis vectors <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{\Phi_i}">
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi mathvariant="bold">Φ<!-- Φ --></mi>
      <mi mathvariant="bold">i</mi>
    </msub>
  </mrow>
</math></span>. We can call them <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="c_i">
  <msub>
    <mi>c</mi>
    <mi>i</mi>
  </msub>
</math></span>.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}\label{vector_reconstruction_sample}
    \mathbf{v} = c_1 \cdot \mathbf{\Phi_1} + c_2 \cdot \mathbf{\Phi_2}
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-vector_reconstruction_sample">
        <mtext>(3)</mtext>
      </mtd>
      <mtd>
        <mrow class="MJX-TeXAtom-ORD">
          <mi mathvariant="bold">v</mi>
        </mrow>
        <mo>=</mo>
        <msub>
          <mi>c</mi>
          <mn>1</mn>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi mathvariant="bold">Φ<!-- Φ --></mi>
            <mn mathvariant="bold">1</mn>
          </msub>
        </mrow>
        <mo>+</mo>
        <msub>
          <mi>c</mi>
          <mn>2</mn>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi mathvariant="bold">Φ<!-- Φ --></mi>
            <mn mathvariant="bold">2</mn>
          </msub>
        </mrow>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>The same is true for functions. We can build a function <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="v(x)">
  <mi>v</mi>
  <mo stretchy="false">(</mo>
  <mi>x</mi>
  <mo stretchy="false">)</mo>
</math></span> out of other functions <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="v_1(x)">
  <msub>
    <mi>v</mi>
    <mn>1</mn>
  </msub>
  <mo stretchy="false">(</mo>
  <mi>x</mi>
  <mo stretchy="false">)</mo>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="v_2(x)">
  <msub>
    <mi>v</mi>
    <mn>2</mn>
  </msub>
  <mo stretchy="false">(</mo>
  <mi>x</mi>
  <mo stretchy="false">)</mo>
</math></span>.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}
  v(x) = v_1(x) + v_2(x)
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-4">
        <mtext>(4)</mtext>
      </mtd>
      <mtd>
        <mi>v</mi>
        <mo stretchy="false">(</mo>
        <mi>x</mi>
        <mo stretchy="false">)</mo>
        <mo>=</mo>
        <msub>
          <mi>v</mi>
          <mn>1</mn>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>x</mi>
        <mo stretchy="false">)</mo>
        <mo>+</mo>
        <msub>
          <mi>v</mi>
          <mn>2</mn>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>x</mi>
        <mo stretchy="false">)</mo>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>We can likewise represent one function as the sum of a set of scaled basis functions, just like we can represent one vector as a sum of scaled basis vectors.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}
  v(x) = c_1 \cdot \Phi_1(x) + c_2 \cdot \Phi_2(x)
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-5">
        <mtext>(5)</mtext>
      </mtd>
      <mtd>
        <mi>v</mi>
        <mo stretchy="false">(</mo>
        <mi>x</mi>
        <mo stretchy="false">)</mo>
        <mo>=</mo>
        <msub>
          <mi>c</mi>
          <mn>1</mn>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mn>1</mn>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>x</mi>
        <mo stretchy="false">)</mo>
        <mo>+</mo>
        <msub>
          <mi>c</mi>
          <mn>2</mn>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mn>2</mn>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>x</mi>
        <mo stretchy="false">)</mo>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>The only question is, how do we know the scaling coefficients (i.e., <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="c_1">
  <msub>
    <mi>c</mi>
    <mn>1</mn>
  </msub>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="c_2">
  <msub>
    <mi>c</mi>
    <mn>2</mn>
  </msub>
</math></span>) to build the desired function with a set of basis functions?</p>

<h1 id="function-transforms">Function Transforms</h1>

<p>When transforming a vector <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{v}">
  <mrow class="MJX-TeXAtom-ORD">
    <mi mathvariant="bold">v</mi>
  </mrow>
</math></span> from one basis to another, what we do is to multiply the vector <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{v}">
  <mrow class="MJX-TeXAtom-ORD">
    <mi mathvariant="bold">v</mi>
  </mrow>
</math></span> by each basis vector <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{\Phi_i}">
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi mathvariant="bold">Φ<!-- Φ --></mi>
      <mi mathvariant="bold">i</mi>
    </msub>
  </mrow>
</math></span> to get the new coefficients. The multiplication operation that we do is the dot product, or more generally the <em>inner product</em> <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\langle, \rangle">
  <mo fence="false" stretchy="false">⟨<!-- ⟨ --></mo>
  <mo>,</mo>
  <mo fence="false" stretchy="false">⟩<!-- ⟩ --></mo>
</math></span>, a kind of matrix multiplication to project <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{v}">
  <mrow class="MJX-TeXAtom-ORD">
    <mi mathvariant="bold">v</mi>
  </mrow>
</math></span> onto each basis vector <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{\Phi_i}">
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi mathvariant="bold">Φ<!-- Φ --></mi>
      <mi mathvariant="bold">i</mi>
    </msub>
  </mrow>
</math></span>.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}
    \langle \mathbf{v}, \mathbf{\Phi_i} \rangle = c_i
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-6">
        <mtext>(6)</mtext>
      </mtd>
      <mtd>
        <mo fence="false" stretchy="false">⟨<!-- ⟨ --></mo>
        <mrow class="MJX-TeXAtom-ORD">
          <mi mathvariant="bold">v</mi>
        </mrow>
        <mo>,</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi mathvariant="bold">Φ<!-- Φ --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
        <mo fence="false" stretchy="false">⟩<!-- ⟩ --></mo>
        <mo>=</mo>
        <msub>
          <mi>c</mi>
          <mi>i</mi>
        </msub>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>This can also be written as a product of the individual vector components <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="v_k">
  <msub>
    <mi>v</mi>
    <mi>k</mi>
  </msub>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\Phi_{ik}">
  <msub>
    <mi mathvariant="normal">Φ<!-- Φ --></mi>
    <mrow class="MJX-TeXAtom-ORD">
      <mi>i</mi>
      <mi>k</mi>
    </mrow>
  </msub>
</math></span>.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}\label{vector_basis_transform}
    \sum_k v_k \cdot \Phi_{ik} = c_i
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-vector_basis_transform">
        <mtext>(7)</mtext>
      </mtd>
      <mtd>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>k</mi>
        </munder>
        <msub>
          <mi>v</mi>
          <mi>k</mi>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mrow class="MJX-TeXAtom-ORD">
            <mi>i</mi>
            <mi>k</mi>
          </mrow>
        </msub>
        <mo>=</mo>
        <msub>
          <mi>c</mi>
          <mi>i</mi>
        </msub>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>To reconstruct vector <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{v}">
  <mrow class="MJX-TeXAtom-ORD">
    <mi mathvariant="bold">v</mi>
  </mrow>
</math></span> in the new basis, we simply multiply the basis by the coefficients.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}\label{vector_basis_reconstruction}
    \sum_i c_i \cdot \mathbf{\Phi_i} = \mathbf{v}
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-vector_basis_reconstruction">
        <mtext>(8)</mtext>
      </mtd>
      <mtd>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>i</mi>
        </munder>
        <msub>
          <mi>c</mi>
          <mi>i</mi>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi mathvariant="bold">Φ<!-- Φ --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
        <mo>=</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <mi mathvariant="bold">v</mi>
        </mrow>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>Notice how Equation <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{vector_basis_reconstruction}">
  <mrow class="MathJax_ref" href="#mjx-eqn-vector_basis_reconstruction">
    <mtext>8</mtext>
  </mrow>
</math></span> is just the general form of Equation <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{vector_reconstruction_sample}">
  <mrow class="MathJax_ref" href="#mjx-eqn-vector_reconstruction_sample">
    <mtext>3</mtext>
  </mrow>
</math></span>. Transforming a <em>function</em> <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="v(s)">
  <mi>v</mi>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span> into a set of coefficients <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="c_i">
  <msub>
    <mi>c</mi>
    <mi>i</mi>
  </msub>
</math></span> for a <em>function basis</em> <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\Phi_i(s)">
  <msub>
    <mi mathvariant="normal">Φ<!-- Φ --></mi>
    <mi>i</mi>
  </msub>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span> is almost the same process as in Equation <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{vector_basis_transform}">
  <mrow class="MathJax_ref" href="#mjx-eqn-vector_basis_transform">
    <mtext>7</mtext>
  </mrow>
</math></span>. Here we have to integrate it over the function domain <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="S">
  <mi>S</mi>
</math></span> with each basis function <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\Phi_i">
  <msub>
    <mi mathvariant="normal">Φ<!-- Φ --></mi>
    <mi>i</mi>
  </msub>
</math></span> individually.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}\label{function_basis_transform}
    \int_S v(s) \Phi_i(s) ds = c_i
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-function_basis_transform">
        <mtext>(9)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <mi>v</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>s</mi>
        <mo>=</mo>
        <msub>
          <mi>c</mi>
          <mi>i</mi>
        </msub>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>Note that the only difference between Equation <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{vector_basis_transform}">
  <mrow class="MathJax_ref" href="#mjx-eqn-vector_basis_transform">
    <mtext>7</mtext>
  </mrow>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{function_basis_transform}">
  <mrow class="MathJax_ref" href="#mjx-eqn-function_basis_transform">
    <mtext>9</mtext>
  </mrow>
</math></span> is that in one case we sum up a discrete representation (i.e., the vector components) and in the other we have to integrate it instead. This is why we write down an <em>inner product</em> rather than a dot product, because it is <em>independent</em> of the fact that the basis is one of functions, or vectors or anything else.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}
    \langle v, \Phi_i \rangle = c_i
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-10">
        <mtext>(10)</mtext>
      </mtd>
      <mtd>
        <mo fence="false" stretchy="false">⟨<!-- ⟨ --></mo>
        <mi>v</mi>
        <mo>,</mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo fence="false" stretchy="false">⟩<!-- ⟩ --></mo>
        <mo>=</mo>
        <msub>
          <mi>c</mi>
          <mi>i</mi>
        </msub>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>Reconstructing the original function works just as in Equation <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{vector_basis_reconstruction}">
  <mrow class="MathJax_ref" href="#mjx-eqn-vector_basis_reconstruction">
    <mtext>8</mtext>
  </mrow>
</math></span>.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}\label{function_basis_reconstruction}
    \sum_i c_i \cdot \Phi_i(s) = v(s)
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-function_basis_reconstruction">
        <mtext>(11)</mtext>
      </mtd>
      <mtd>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>i</mi>
        </munder>
        <msub>
          <mi>c</mi>
          <mi>i</mi>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mo>=</mo>
        <mi>v</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<h1 id="function-basis-properties">Function Basis Properties</h1>

<p>With vectors, we can use the inner product (that is, a dot product) to determine some properties about their relationship. For instance, we can tell from the projection whether or not vectors <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{\Phi_i}">
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi mathvariant="bold">Φ<!-- Φ --></mi>
      <mi mathvariant="bold">i</mi>
    </msub>
  </mrow>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{\Phi_j}">
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi mathvariant="bold">Φ<!-- Φ --></mi>
      <mi mathvariant="bold">j</mi>
    </msub>
  </mrow>
</math></span> are orthonormal to one another.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}\label{dirac_delta}
c = \langle \mathbf{\Phi_i}, \mathbf{\Phi_j} \rangle = \mathbf{\Phi_i} \cdot \mathbf{\Phi_j} = \delta_{ij} = 
    \left\{
        \begin{array}{c} 
             1, i = j    \\ 
             0, i \neq j
        \end{array} 
    \right.
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-dirac_delta">
        <mtext>(12)</mtext>
      </mtd>
      <mtd>
        <mi>c</mi>
        <mo>=</mo>
        <mo fence="false" stretchy="false">⟨<!-- ⟨ --></mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi mathvariant="bold">Φ<!-- Φ --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
        <mo>,</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi mathvariant="bold">Φ<!-- Φ --></mi>
            <mi mathvariant="bold">j</mi>
          </msub>
        </mrow>
        <mo fence="false" stretchy="false">⟩<!-- ⟩ --></mo>
        <mo>=</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi mathvariant="bold">Φ<!-- Φ --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
        <mo>⋅<!-- ⋅ --></mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi mathvariant="bold">Φ<!-- Φ --></mi>
            <mi mathvariant="bold">j</mi>
          </msub>
        </mrow>
        <mo>=</mo>
        <msub>
          <mi>δ<!-- δ --></mi>
          <mrow class="MJX-TeXAtom-ORD">
            <mi>i</mi>
            <mi>j</mi>
          </mrow>
        </msub>
        <mo>=</mo>
        <mrow>
          <mo>{</mo>
          <mtable columnalign="left center">
            <mtr>
              <mtd>
                <mn>1</mn>
                <mo>,</mo>
                <mi>i</mi>
                <mo>=</mo>
                <mi>j</mi>
              </mtd>
            </mtr>
            <mtr>
              <mtd>
                <mn>0</mn>
                <mo>,</mo>
                <mi>i</mi>
                <mo>≠<!-- ≠ --></mo>
                <mi>j</mi>
              </mtd>
            </mtr>
          </mtable>
          <mo fence="true" stretchy="true" symmetric="true"></mo>
        </mrow>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>If the coefficient <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="c">
  <mi>c</mi>
</math></span> is <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="0">
  <mn>0</mn>
</math></span>, then both vectors <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{\Phi_i}">
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi mathvariant="bold">Φ<!-- Φ --></mi>
      <mi mathvariant="bold">i</mi>
    </msub>
  </mrow>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{\Phi_j}">
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi mathvariant="bold">Φ<!-- Φ --></mi>
      <mi mathvariant="bold">j</mi>
    </msub>
  </mrow>
</math></span> are orthogonal to each other. Additionally if <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{\Phi_i}">
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi mathvariant="bold">Φ<!-- Φ --></mi>
      <mi mathvariant="bold">i</mi>
    </msub>
  </mrow>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\mathbf{\Phi_j}">
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi mathvariant="bold">Φ<!-- Φ --></mi>
      <mi mathvariant="bold">j</mi>
    </msub>
  </mrow>
</math></span> happen to be identical and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="c">
  <mi>c</mi>
</math></span> turns out to be <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="1">
  <mn>1</mn>
</math></span> and not an arbitrary number <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="n">
  <mi>n</mi>
</math></span>, then we know that both vectors also form an orthonormal basis. <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\delta_{ij}">
  <msub>
    <mi>δ<!-- δ --></mi>
    <mrow class="MJX-TeXAtom-ORD">
      <mi>i</mi>
      <mi>j</mi>
    </mrow>
  </msub>
</math></span> is called <em>Kronecker Delta</em> and is usually just a shorthand of such a basis behavior. With functions, the same concept is true, and the formula is identical. Instead of a dot product, the inner product for two functions <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\Phi_i(s)">
  <msub>
    <mi mathvariant="normal">Φ<!-- Φ --></mi>
    <mi>i</mi>
  </msub>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\Phi_j(s)">
  <msub>
    <mi mathvariant="normal">Φ<!-- Φ --></mi>
    <mi>j</mi>
  </msub>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span> represents an integration. But again, if <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="c">
  <mi>c</mi>
</math></span> turns out to be <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="0">
  <mn>0</mn>
</math></span>, we know both functions are orthogonal to each other, and if every other result is <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="1">
  <mn>1</mn>
</math></span>, then the function basis is orthonormal.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}\label{dirac_delta_functions}
    c = \langle \Phi_i, \Phi_j \rangle = \int_S \Phi_i(s) \cdot \Phi_j(s) ds = \delta_{ij} = 
        \left\{
            \begin{array}{c} 
                 1, i = j    \\
                 0, i \neq j
            \end{array} 
        \right.
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-dirac_delta_functions">
        <mtext>(13)</mtext>
      </mtd>
      <mtd>
        <mi>c</mi>
        <mo>=</mo>
        <mo fence="false" stretchy="false">⟨<!-- ⟨ --></mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo>,</mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>j</mi>
        </msub>
        <mo fence="false" stretchy="false">⟩<!-- ⟩ --></mo>
        <mo>=</mo>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>j</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>s</mi>
        <mo>=</mo>
        <msub>
          <mi>δ<!-- δ --></mi>
          <mrow class="MJX-TeXAtom-ORD">
            <mi>i</mi>
            <mi>j</mi>
          </mrow>
        </msub>
        <mo>=</mo>
        <mrow>
          <mo>{</mo>
          <mtable columnalign="left center">
            <mtr>
              <mtd>
                <mn>1</mn>
                <mo>,</mo>
                <mi>i</mi>
                <mo>=</mo>
                <mi>j</mi>
              </mtd>
            </mtr>
            <mtr>
              <mtd>
                <mn>0</mn>
                <mo>,</mo>
                <mi>i</mi>
                <mo>≠<!-- ≠ --></mo>
                <mi>j</mi>
              </mtd>
            </mtr>
          </mtable>
          <mo fence="true" stretchy="true" symmetric="true"></mo>
        </mrow>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>We now have all the tools necessary to define properties of a function space: Equation <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{dirac_delta_functions}">
  <mrow class="MathJax_ref" href="#mjx-eqn-dirac_delta_functions">
    <mtext>13</mtext>
  </mrow>
</math></span> can be used to define orthogonality and orthonormality of two functions, and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{function_basis_reconstruction}">
  <mrow class="MathJax_ref" href="#mjx-eqn-function_basis_reconstruction">
    <mtext>11</mtext>
  </mrow>
</math></span> can be used to define linear dependency (one function <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\Phi_i">
  <msub>
    <mi mathvariant="normal">Φ<!-- Φ --></mi>
    <mi>i</mi>
  </msub>
</math></span> is a sum of scalar multiples of other functions <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\Phi_j">
  <msub>
    <mi mathvariant="normal">Φ<!-- Φ --></mi>
    <mi>j</mi>
  </msub>
</math></span> of the same basis) and therefore also the rank and determinant of a function basis.</p>

<h1 id="the-convolution-theorem">The Convolution Theorem</h1>

<p>Now that we know how to transform a function into coefficients of a function basis, and how to test whether or not a function basis has certain properties like orthonormality, let's perform a magic trick. Imagine we have an <em>orthonormal</em> basis <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\Phi_i">
  <msub>
    <mi mathvariant="normal">Φ<!-- Φ --></mi>
    <mi>i</mi>
  </msub>
</math></span>, i.e. two functions of that basis will always integrate to <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="1">
  <mn>1</mn>
</math></span> if they are identical, or <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="0">
  <mn>0</mn>
</math></span> if they are not.</p>

<p>We can represent any function <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="f(s)">
  <mi>f</mi>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span> by a bunch of coefficients in that basis. Imagine now that we have two functions: <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="f(s)">
  <mi>f</mi>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="g(s)">
  <mi>g</mi>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span>.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{eqnarray}
    f(s) = \sum_i f_i \cdot \Phi_i(s) \label{f_reconstruction}\\ 
    g(s) = \sum_j g_j \cdot \Phi_j(s) \label{g_reconstruction}
\end{eqnarray}">
  <mtable columnalign="left center" displaystyle="true">
    <mtr>
      <mtd id="mjx-eqn-f_reconstruction">
        <mtext>(14)</mtext>
      </mtd>
      <mtd>
        <mi>f</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mo>=</mo>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>i</mi>
        </munder>
        <msub>
          <mi>f</mi>
          <mi>i</mi>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-g_reconstruction">
        <mtext>(15)</mtext>
      </mtd>
      <mtd>
        <mi>g</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mo>=</mo>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>j</mi>
        </munder>
        <msub>
          <mi>g</mi>
          <mi>j</mi>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>j</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>Let's multiply them together and integrate the result. We can replace the functions with the sum in Equation <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{f_reconstruction}">
  <mrow class="MathJax_ref" href="#mjx-eqn-f_reconstruction">
    <mtext>14</mtext>
  </mrow>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{g_reconstruction}">
  <mrow class="MathJax_ref" href="#mjx-eqn-g_reconstruction">
    <mtext>15</mtext>
  </mrow>
</math></span>.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}\label{convolution_step1}
    \int_S f(s) \cdot g(s) ds = \int_S \left( \sum_i f_i \cdot \Phi_i(s) \right) \cdot \left( \sum_j g_j \cdot \Phi_j(s) \right)ds
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-convolution_step1">
        <mtext>(16)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <mi>f</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mo>⋅<!-- ⋅ --></mo>
        <mi>g</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>s</mi>
        <mo>=</mo>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <mrow>
          <mo>(</mo>
          <munder>
            <mo>∑<!-- ∑ --></mo>
            <mi>i</mi>
          </munder>
          <msub>
            <mi>f</mi>
            <mi>i</mi>
          </msub>
          <mo>⋅<!-- ⋅ --></mo>
          <msub>
            <mi mathvariant="normal">Φ<!-- Φ --></mi>
            <mi>i</mi>
          </msub>
          <mo stretchy="false">(</mo>
          <mi>s</mi>
          <mo stretchy="false">)</mo>
          <mo>)</mo>
        </mrow>
        <mo>⋅<!-- ⋅ --></mo>
        <mrow>
          <mo>(</mo>
          <munder>
            <mo>∑<!-- ∑ --></mo>
            <mi>j</mi>
          </munder>
          <msub>
            <mi>g</mi>
            <mi>j</mi>
          </msub>
          <mo>⋅<!-- ⋅ --></mo>
          <msub>
            <mi mathvariant="normal">Φ<!-- Φ --></mi>
            <mi>j</mi>
          </msub>
          <mo stretchy="false">(</mo>
          <mi>s</mi>
          <mo stretchy="false">)</mo>
          <mo>)</mo>
        </mrow>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>In Equation <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{convolution_step1}">
  <mrow class="MathJax_ref" href="#mjx-eqn-convolution_step1">
    <mtext>16</mtext>
  </mrow>
</math></span> we can reorder some things because everything is linear: We can move the two sums <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\sum_i">
  <munder>
    <mo>∑<!-- ∑ --></mo>
    <mi>i</mi>
  </munder>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\sum_j">
  <munder>
    <mo>∑<!-- ∑ --></mo>
    <mi>j</mi>
  </munder>
</math></span> out of the integral, together with their coefficients, because they do not depend on <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="s">
  <mi>s</mi>
</math></span>.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{eqnarray}
    \int_S f(s) \cdot g(s) ds &amp; = &amp; \int_S \left( \sum_i f_i \cdot \Phi_i(s) \right) \cdot \left( \sum_j g_j \cdot \Phi_j(s) \right)ds \\ 
                              &amp; = &amp; \sum_i \sum_j f_i \cdot g_j \cdot \int_S \Phi_i(s) \cdot \Phi_j(s) ds \label{convolution_step2}
\end{eqnarray}">
  <mtable columnalign="left right center left" displaystyle="true">
    <mtr>
      <mtd id="mjx-eqn-17">
        <mtext>(17)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <mi>f</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mo>⋅<!-- ⋅ --></mo>
        <mi>g</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <mrow>
          <mo>(</mo>
          <munder>
            <mo>∑<!-- ∑ --></mo>
            <mi>i</mi>
          </munder>
          <msub>
            <mi>f</mi>
            <mi>i</mi>
          </msub>
          <mo>⋅<!-- ⋅ --></mo>
          <msub>
            <mi mathvariant="normal">Φ<!-- Φ --></mi>
            <mi>i</mi>
          </msub>
          <mo stretchy="false">(</mo>
          <mi>s</mi>
          <mo stretchy="false">)</mo>
          <mo>)</mo>
        </mrow>
        <mo>⋅<!-- ⋅ --></mo>
        <mrow>
          <mo>(</mo>
          <munder>
            <mo>∑<!-- ∑ --></mo>
            <mi>j</mi>
          </munder>
          <msub>
            <mi>g</mi>
            <mi>j</mi>
          </msub>
          <mo>⋅<!-- ⋅ --></mo>
          <msub>
            <mi mathvariant="normal">Φ<!-- Φ --></mi>
            <mi>j</mi>
          </msub>
          <mo stretchy="false">(</mo>
          <mi>s</mi>
          <mo stretchy="false">)</mo>
          <mo>)</mo>
        </mrow>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-convolution_step2">
        <mtext>(18)</mtext>
      </mtd>
      <mtd></mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>i</mi>
        </munder>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>j</mi>
        </munder>
        <msub>
          <mi>f</mi>
          <mi>i</mi>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mi>g</mi>
          <mi>j</mi>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>j</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>However, we just noticed in the previous section that a function basis can be orthonormal. An orthonormal function basis is very nice to have, because the integrated product of the function basis vectors (i.e., the inner product of two functions) will be either <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="1">
  <mn>1</mn>
</math></span> or <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="0">
  <mn>0</mn>
</math></span>, so the integral in Equation <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{convolution_step2}">
  <mrow class="MathJax_ref" href="#mjx-eqn-convolution_step2">
    <mtext>18</mtext>
  </mrow>
</math></span> <em>can be replaced with the Kronecker Delta from Equation <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{dirac_delta_functions}">
  <mrow class="MathJax_ref" href="#mjx-eqn-dirac_delta_functions">
    <mtext>13</mtext>
  </mrow>
</math></span>!</em></p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{eqnarray}
    \int_S f(s) \cdot g(s) ds &amp; = &amp; \int_S \left( \sum_i f_i \cdot \Phi_i(s) \right) \cdot \left( \sum_j g_j \cdot \Phi_j(s) \right)ds \\ 
                              &amp; = &amp; \sum_i \sum_j f_i \cdot g_j \cdot \int_S \Phi_i(s) \cdot \Phi_j(s) ds \\
                              &amp; = &amp; \sum_i \sum_j f_i \cdot g_j \cdot \delta_{ij} \label{convolution_step3}
\end{eqnarray}">
  <mtable columnalign="left right center left" displaystyle="true">
    <mtr>
      <mtd id="mjx-eqn-19">
        <mtext>(19)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <mi>f</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mo>⋅<!-- ⋅ --></mo>
        <mi>g</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <mrow>
          <mo>(</mo>
          <munder>
            <mo>∑<!-- ∑ --></mo>
            <mi>i</mi>
          </munder>
          <msub>
            <mi>f</mi>
            <mi>i</mi>
          </msub>
          <mo>⋅<!-- ⋅ --></mo>
          <msub>
            <mi mathvariant="normal">Φ<!-- Φ --></mi>
            <mi>i</mi>
          </msub>
          <mo stretchy="false">(</mo>
          <mi>s</mi>
          <mo stretchy="false">)</mo>
          <mo>)</mo>
        </mrow>
        <mo>⋅<!-- ⋅ --></mo>
        <mrow>
          <mo>(</mo>
          <munder>
            <mo>∑<!-- ∑ --></mo>
            <mi>j</mi>
          </munder>
          <msub>
            <mi>g</mi>
            <mi>j</mi>
          </msub>
          <mo>⋅<!-- ⋅ --></mo>
          <msub>
            <mi mathvariant="normal">Φ<!-- Φ --></mi>
            <mi>j</mi>
          </msub>
          <mo stretchy="false">(</mo>
          <mi>s</mi>
          <mo stretchy="false">)</mo>
          <mo>)</mo>
        </mrow>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-20">
        <mtext>(20)</mtext>
      </mtd>
      <mtd></mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>i</mi>
        </munder>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>j</mi>
        </munder>
        <msub>
          <mi>f</mi>
          <mi>i</mi>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mi>g</mi>
          <mi>j</mi>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>j</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-convolution_step3">
        <mtext>(21)</mtext>
      </mtd>
      <mtd></mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>i</mi>
        </munder>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>j</mi>
        </munder>
        <msub>
          <mi>f</mi>
          <mi>i</mi>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mi>g</mi>
          <mi>j</mi>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mi>δ<!-- δ --></mi>
          <mrow class="MJX-TeXAtom-ORD">
            <mi>i</mi>
            <mi>j</mi>
          </mrow>
        </msub>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>But if we have a Kronecker Delta, that just means that every time <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="i">
  <mi>i</mi>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="j">
  <mi>j</mi>
</math></span> are not equal, the product is just <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="0">
  <mn>0</mn>
</math></span>. So we can simplify even further and remove one variable.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{eqnarray}
    \int_S f(s) \cdot g(s) ds &amp; = &amp; \int_S \left( \sum_i f_i \cdot \Phi_i(s) \right) \cdot \left( \sum_j g_j \cdot \Phi_j(s) \right)ds \\ 
                              &amp; = &amp; \sum_i \sum_j f_i \cdot g_j \cdot \int_S \Phi_i(s) \cdot \Phi_j(s) ds \\
                              &amp; = &amp; \sum_i \sum_j f_i \cdot g_j \cdot \delta_{ij} \\ 
                              &amp; = &amp; \sum_i f_i \cdot g_i \label{convolution_step4}
\end{eqnarray}">
  <mtable columnalign="left right center left" displaystyle="true">
    <mtr>
      <mtd id="mjx-eqn-22">
        <mtext>(22)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <mi>f</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mo>⋅<!-- ⋅ --></mo>
        <mi>g</mi>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <mrow>
          <mo>(</mo>
          <munder>
            <mo>∑<!-- ∑ --></mo>
            <mi>i</mi>
          </munder>
          <msub>
            <mi>f</mi>
            <mi>i</mi>
          </msub>
          <mo>⋅<!-- ⋅ --></mo>
          <msub>
            <mi mathvariant="normal">Φ<!-- Φ --></mi>
            <mi>i</mi>
          </msub>
          <mo stretchy="false">(</mo>
          <mi>s</mi>
          <mo stretchy="false">)</mo>
          <mo>)</mo>
        </mrow>
        <mo>⋅<!-- ⋅ --></mo>
        <mrow>
          <mo>(</mo>
          <munder>
            <mo>∑<!-- ∑ --></mo>
            <mi>j</mi>
          </munder>
          <msub>
            <mi>g</mi>
            <mi>j</mi>
          </msub>
          <mo>⋅<!-- ⋅ --></mo>
          <msub>
            <mi mathvariant="normal">Φ<!-- Φ --></mi>
            <mi>j</mi>
          </msub>
          <mo stretchy="false">(</mo>
          <mi>s</mi>
          <mo stretchy="false">)</mo>
          <mo>)</mo>
        </mrow>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-23">
        <mtext>(23)</mtext>
      </mtd>
      <mtd></mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>i</mi>
        </munder>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>j</mi>
        </munder>
        <msub>
          <mi>f</mi>
          <mi>i</mi>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mi>g</mi>
          <mi>j</mi>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi>S</mi>
        </msub>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>i</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mi mathvariant="normal">Φ<!-- Φ --></mi>
          <mi>j</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>s</mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>s</mi>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-24">
        <mtext>(24)</mtext>
      </mtd>
      <mtd></mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>i</mi>
        </munder>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>j</mi>
        </munder>
        <msub>
          <mi>f</mi>
          <mi>i</mi>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mi>g</mi>
          <mi>j</mi>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mi>δ<!-- δ --></mi>
          <mrow class="MJX-TeXAtom-ORD">
            <mi>i</mi>
            <mi>j</mi>
          </mrow>
        </msub>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-convolution_step4">
        <mtext>(25)</mtext>
      </mtd>
      <mtd></mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <munder>
          <mo>∑<!-- ∑ --></mo>
          <mi>i</mi>
        </munder>
        <msub>
          <mi>f</mi>
          <mi>i</mi>
        </msub>
        <mo>⋅<!-- ⋅ --></mo>
        <msub>
          <mi>g</mi>
          <mi>i</mi>
        </msub>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>What is this? Equation <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{convolution_step4}">
  <mrow class="MathJax_ref" href="#mjx-eqn-convolution_step4">
    <mtext>25</mtext>
  </mrow>
</math></span> looks like a dot product. So in essence that means <em>we can shortcut an integration of a product of two functions <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="f(s)">
  <mi>f</mi>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="g(s)">
  <mi>g</mi>
  <mo stretchy="false">(</mo>
  <mi>s</mi>
  <mo stretchy="false">)</mo>
</math></span> by a dot product of their coefficients <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="f_i">
  <msub>
    <mi>f</mi>
    <mi>i</mi>
  </msub>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="g_i">
  <msub>
    <mi>g</mi>
    <mi>i</mi>
  </msub>
</math></span> if the function basis is orthonormal!</em></p>

<p>Why is this important?</p>

<h1 id="filtering">Filtering</h1>

<p>Filter operations always take on a formula just like Equation <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{convolution_step1}">
  <mrow class="MathJax_ref" href="#mjx-eqn-convolution_step1">
    <mtext>16</mtext>
  </mrow>
</math></span>. For instance, a Gauss Filter is a Gauss function that is, in a small window, multiplied with another function. One can do that in a pixel-based domain, where each pixel is multiplied with a value from the Gauss function at the same position, but it is really just two functions multiplied together.</p>

<p>However, if one part of the function is already available in a harmonic basis function, for instance as a JPEG image, then we can apply the filter in the function basis directly and do an operation which would normally be very expensive with a cheap dot product! If the number of coefficients is smaller than the operations necessary on the original domain <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="S">
  <mi>S</mi>
</math></span> of the function (that is, the number of pixels we need to sum up), we can save a tremendous amount of computation time.</p>

<h1 id="precomputed-radiance-transfer">Precomputed Radiance Transfer</h1>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2016_10_prt.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2016_10_prt.jpg" alt="Transfer function visualized. Every white pixel in this $360^{\circ}$ image is one which, evaluated by a raytracer, was blocked by some geometry. All other pixels represent values where a ray shot from this point could travel freely away from all geometry." title="Transfer function visualized. Every white pixel in this $360^{\circ}$ image is one which, evaluated by a raytracer, was blocked by some geometry. All other pixels represent values where a ray shot from this point could travel freely away from all geometry.">
    
    </a>
    
    
    <figcaption>Transfer function visualized. Every white pixel in this <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="360^{\circ}">
  <msup>
    <mn>360</mn>
    <mrow class="MJX-TeXAtom-ORD">
      <mo>∘<!-- ∘ --></mo>
    </mrow>
  </msup>
</math></span> image is one which, evaluated by a raytracer, was blocked by some geometry. All other pixels represent values where a ray shot from this point could travel freely away from all geometry.</figcaption>
    
</figure>

<p>A main observation of Precomputed Radiance Transfer is the following: One can simplify the rendering equation to something we have seen above by throwing out self-emittance, and unify everything in the integral that isn't incoming light into one homogeneous diffuse <em>transfer function</em> <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="T(\mathbf{\omega_i})">
  <mi>T</mi>
  <mo stretchy="false">(</mo>
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi>ω<!-- ω --></mi>
      <mi mathvariant="bold">i</mi>
    </msub>
  </mrow>
  <mo stretchy="false">)</mo>
</math></span>.</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{eqnarray}
L(\mathbf{\omega_o}) &amp; = &amp; L_e(\mathbf{\omega_o}) + \int_\Omega L(\mathbf{\omega_i}) \cdot f(x, \mathbf{\omega_i}, \mathbf{\omega_o}) \cdot \langle \mathbf{n}, \mathbf{\omega_i} \rangle d\mathbf{\omega_i}
\end{eqnarray}">
  <mtable columnalign="left right center left" displaystyle="true">
    <mtr>
      <mtd id="mjx-eqn-26">
        <mtext>(26)</mtext>
      </mtd>
      <mtd>
        <mi>L</mi>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">o</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <msub>
          <mi>L</mi>
          <mi>e</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">o</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <mo>+</mo>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi mathvariant="normal">Ω<!-- Ω --></mi>
        </msub>
        <mi>L</mi>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <mo>⋅<!-- ⋅ --></mo>
        <mi>f</mi>
        <mo stretchy="false">(</mo>
        <mi>x</mi>
        <mo>,</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
        <mo>,</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">o</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <mo>⋅<!-- ⋅ --></mo>
        <mo fence="false" stretchy="false">⟨<!-- ⟨ --></mo>
        <mrow class="MJX-TeXAtom-ORD">
          <mi mathvariant="bold">n</mi>
        </mrow>
        <mo>,</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
        <mo fence="false" stretchy="false">⟩<!-- ⟩ --></mo>
        <mi>d</mi>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{eqnarray}\label{prt_integral}
            &amp; \rightarrow &amp; \int_\Omega L(\mathbf{\omega_i}) \cdot T(\mathbf{\omega_i}) d\mathbf{\omega_i}
\end{eqnarray}">
  <mtable columnalign="left right center left" displaystyle="true">
    <mtr>
      <mtd id="mjx-eqn-prt_integral">
        <mtext>(27)</mtext>
      </mtd>
      <mtd></mtd>
      <mtd>
        <mi></mi>
        <mo stretchy="false">→<!-- → --></mo>
      </mtd>
      <mtd>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi mathvariant="normal">Ω<!-- Ω --></mi>
        </msub>
        <mi>L</mi>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <mo>⋅<!-- ⋅ --></mo>
        <mi>T</mi>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>The transfer function is simply everything - materials, visibility, Lambert factor etc. - packed into a single function. A simple visualization is in the above figure, where the transfer function of a point shows a visibility function. Imagine now that next to a transfer function, we also have a function representing our environment light. The crucial observation is that Equation <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{prt_integral}">
  <mrow class="MathJax_ref" href="#mjx-eqn-prt_integral">
    <mtext>27</mtext>
  </mrow>
</math></span> looks just like Equation <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{convolution_step1}">
  <mrow class="MathJax_ref" href="#mjx-eqn-convolution_step1">
    <mtext>16</mtext>
  </mrow>
</math></span>, and this means that if we can get both as coefficients of some basis function, we can compute the integral in Equation <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\ref{prt_integral}">
  <mrow class="MathJax_ref" href="#mjx-eqn-prt_integral">
    <mtext>27</mtext>
  </mrow>
</math></span> with just one simple dot product of their coefficients!</p>

<p>Why is this practical? Getting the coefficients is really expensive, so before we can do this we would need to compute all transfer functions (probably with a raytracer) which is already very costly, and then do the Monte Carlo estimation to get their coefficients. This is way too expensive to do in real-time. <em>But</em>, if the car in the figure never moves, then the transfer function never changes! So if we compute all transfer in an offline step and save it (one might almost be tempted to call this <em><a href="https://www.cs.jhu.edu/~misha/ReadingSeminar/Papers/Sloan02.pdf">pre-computing the transfer</a></em>), then we only need to get the coefficients for the light at runtime, and that should be fairly easy to do. In fact, if we only have a bunch of different lighting configurations, we can precompute the coefficient vectors for all of them, and at runtime when switching between skyboxes fetch the respective coefficients.</p>

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

<ol>
  <li>Sloan et al, <a href="https://www.cs.jhu.edu/~misha/ReadingSeminar/Papers/Sloan02.pdf">Precomputed Radiance Transfer for Real-Time Rendering in Dynamic, Low- Frequency Lighting Environments. Peter-Pike</a>
</li>
  <li>Robin Green, <a href="https://silviojemma.com/public/papers/lighting/spherical-harmonic-lighting.pdf">Spherical Harmonic Lighting: The Gritty Details</a>
</li>
</ol> ]]></description>
            <pubDate>Tue, 18 Oct 2016 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/log/2016/10/18/the_convolution_theorem.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhwbytBRDZBaHJMVU9IUUNEUkZOWEozRW5UQgpxSUFhOVpRU2lNYzAwaGIzZm5Mc2tQb0JBTFdiS3pQMTduUzBybFVtUkxiTmRnRmF4Q05nYkRCQnZ4eVRpSTJmCjNsSUYKPU41engKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Notes</category><category>PRT</category><category>FunctionTransform</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Differential Rendering</title>
            <description><![CDATA[ <p>Every graphics sub-discipline has its own holy grail, some go-to paper that everyone must have read to get into the necessary basics of the field: Physically Based Rendering has <a href="https://www.pbrt.org/">a book by the same name</a>, Path Tracing has <a href="https://graphics.stanford.edu/papers/veach_thesis/">Eric Veach's dissertation</a>, real-time global illumination has <a href="https://dl.acm.org/citation.cfm?id=1053460">Reflective Shadow Maps</a>.</p>

<p>For Augmented Reality rendering, this go-to publication is <a href="https://www.pauldebevec.com/Research/IBL/">Paul Debevec's <em>Rendering Synthetic Objects into Real Scenes</em></a>.</p>

<h1 id="fusing-two-realities">Fusing two realities</h1>

<p>Imagine the following situation: You have a green-ish surface, and you want to project a virtual object on top of it, like this Stanford Buddha.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2016_10_diff-1.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2016_10_diff-1.jpg" alt="" title="">
    
    </a>
    
    
</figure>

<p>Just as other real objects on this surface would do, this Stanford Buddha should also drop a shadow onto it. To do this, you figure out the position of a real light source (let's pretend the room in which this shot was taken has only one light), and put a virtual copy of it into the virtual scene at the same relative location.</p>

<p>What now? To render a proper shadow, we also need a surface to drop the shadow onto. We can use a green-ish virtual surface as a placeholder for the green-ish looking real one to receive the shadow. Let's give it a try.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2016_10_diff-2.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2016_10_diff-2.jpg" alt="" title="">
    
    </a>
    
    
</figure>

<p>But how do we now fuse this image with the real background? Do we just paste it over the image?</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2016_10_diff-3.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2016_10_diff-3.jpg" alt="" title="">
    
    </a>
    
    
</figure>

<p>That doesn't look quite right. So far we only had a vague interpretation of what the underlying real surface looks like: Green-ish. The description is not very accurate, and the result shows.</p>

<p>Maybe we could reconstruct the real surface more accurately and then get a more correct looking image. For instance, we could texture the so far green-ish placeholder with an image of the real surface. Of course from that texture we need to carefully remove all the interfering lights first (a process sometimes called <em>delighting</em>) so we're really left with just the albedo. Assume we did this correctly and now have this texture ready. Just put it on top of the surface and try again.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2016_10_diff-4.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2016_10_diff-4.jpg" alt="" title="">
    
    </a>
    
    
</figure>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2016_10_diff-5.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2016_10_diff-5.jpg" alt="" title="">
    
    </a>
    
    
</figure>

<p>It looks better, but now we run into other problems:</p>

<ol>
  <li>The scaling of the virtual plane and its texture is slightly off, which makes it look a bit blurry.</li>
  <li>The tracker didn't reconstruct the real world position of this patch down to the nanometer, so we end up with some very annoying seams around the border.</li>
  <li>The renderer uses a point light to model the real world light source. You can see the falloff toward the back of the plane, where it is darker than it should be. The real light source in that room is a large neon tube and incident light spreads much more equally across the surface.</li>
</ol>

<p>Also, it looks like there is some type of coloring issue. Maybe it's a gamma problem? Is the reconstructed albedo texture really correct? Maybe we need a path-tracer? Is there a red bounce missing from the plane in the back? Maybe this, maybe that, maybe maybe maybe…</p>

<p>Early Augmented Reality applications in the 90s dealt with this <strong>fusion-problem</strong> in various ways:</p>

<ul>
  <li>Just leave it as it is.</li>
  <li>If you need shadows, just make the pixels darker. Even if there are multiple non-white lights in the room no one will notice.</li>
  <li>Reconstruct the entire object rather than just a patch of it. At least then there won't be any visible seams or other texture-related differences.</li>
  <li>Blend the partly reconstructed surface with the real background image.</li>
  <li>Why invest so much hard work rendering correctly when you can make the problem space much easier? Just use simpler real world objects! For instance a diffuse, homogeneous green surface with no texture at all.</li>
</ul>

<h1 id="differential-rendering">Differential Rendering</h1>

<p>While all of these tactics <em>can</em> work, they are unsatisfying at best and often require manual tweaking to make just that one scene look good.</p>

<p>The main weakness in the above scenarios is this: They rely on the placeholder to represent reality as close as possible. Could we perhaps get rid of the placeholder entirely and just be left with the actual <strong>change</strong> that the Buddha introduces to the real scene (i.e., the shadow)?</p>

<p>This is the question that <em>Differential Rendering</em> deals with. The idea is simple: Extract the difference introduced by the object and leave out the rest. How? Let's look at it again. We have a patch of the green floor that is supposed to be the a reconstruction of the real world. If we tag all the pixels of a reconstructed surface in a mask, we can isolate the green placeholder.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2016_10_diff-6.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2016_10_diff-6.jpg" alt="" title="">
    
    </a>
    
    
</figure>

<p>We can render the same patch again, but without the object, and repeat the same process of masking out the result. Subtracting both images from one another will leave us with only <strong>the difference</strong> due to the objects presence in one of the scenes.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2016_10_diff-7.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2016_10_diff-7.jpg" alt="" title="">
    
    </a>
    
    
</figure>

<p>The shadowed area will produce <em>negative</em> energy, as there is less energy present on the augmented patch than before, while the rest of the image will just be a zero-sum. If we add this difference back to our very first image, the shadow ends up in the real world and looks like it belongs there.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2016_10_diff-8.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2016_10_diff-8.jpg" alt="" title="">
    
    </a>
    
    
</figure>

<h1 id="conclusion">Conclusion</h1>

<p>The main strength of Differential Rendering is its simplicity, as it boils down to a post-processing effect (you can easily repeat the above steps with Gimp, Krita or Photoshop) to extract the influence a virtual object has onto its surrounding, such as blocking real light, bouncing off reflections or transmitting light. This difference is then simply added onto a real background image together with the object. The viewer is left with a sense that the object is actually <em>in</em> the scene rather than being glued on top of it.</p>

<p>As a side note, the extracted difference contains positive contributions (such as bounce light) as well as negative ones (the shadows subtract light from the background). The latter is a quantity called <em>antiradiance</em> and has been discussed in a few publications, most notable <a href="https://www-sop.inria.fr/reves/Basilic/2007/DSDD07/">Implicit Visibility and Antiradiance for Interactive Global Illumination</a>. I will come back to this some other time.</p>

<p>More importantly, if the renderer is able to compute global illumination, the entire extracted difference should integrate to zero: The object is merely "re-routing" light to a different location, and therefore all energy is conserved.</p>

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

<ol>
  <li>Paul Debevec, <a href="https://www.pauldebevec.com/Research/IBL/">Rendering Synthetic Objects into Real Scenes</a>
</li>
  <li>Dachsbacher et al, <a href="https://www-sop.inria.fr/reves/Basilic/2007/DSDD07/">Implicit Visibility and Antiradiance for Interactive Global Illumination</a>
</li>
</ol> ]]></description>
            <pubDate>Tue, 04 Oct 2016 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/log/2016/10/04/differential-rendering.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhyUUtnRUFvcGZEV21iMWhUUzhBa09OVzZYVgpRc2FmUDhBSVQ5T0RFNVpyTjkvTTlGWUJBUEphOS9YVUFCOTBIR0t5Y2lNU3hFSHhwbmtpNHoyRmVwUEttU003CmdlVUkKPTZ4RGwKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>General</category><category>Relighting</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>The Delta Radiance Field</title>
            <description><![CDATA[ <div class="publication">
    <h1>The Delta Radiance Field</h1>
    <p>
        <span class="authors">Tobias Alexander Franke</span>
        <span class="journal">Technische Universität Darmstadt</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/franke15phd/franke15phd.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/franke15phd/franke15phd.jpg" alt="" title="">
    
    </a>
    
    
</figure>

    <h2>Abstract</h2>
    <p>The wide availability of mobile devices capable of computing high fidelity graphics in real-time has sparked a renewed interest in the development and research of Augmented Reality applications. Within the large spectrum of mixed real and virtual elements one specific area is dedicated to produce realistic augmentations with the aim of presenting virtual copies of real existing objects or soon to be produced products. Surprisingly though, the current state of this area leaves much to be desired: Augmenting objects in current systems are often presented without any reconstructed lighting whatsoever and therefore transfer an impression of being glued over a camera image rather than augmenting reality. In light of the advances in the movie industry, which has handled cases of mixed realities from one extreme end to another, it is a legitimate question to ask why such advances did not fully reflect onto Augmented Reality simulations as well.</p>

<p>Generally understood to be real-time applications which reconstruct the spatial relation of real world elements and virtual objects, Augmented Reality has to deal with several uncertainties. Among them, unknown illumination and real scene conditions are the most important. Any kind of reconstruction of real world properties in an ad-hoc manner must likewise be incorporated into an algorithm responsible for shading virtual objects and transferring virtual light to real surfaces in an ad-hoc fashion. The immersiveness of an Augmented Reality simulation is, next to its realism and accuracy, primarily dependent on its responsiveness. Any computation affecting the final image must be computed in real-time. This condition rules out many of the methods used for movie production.</p>

<p>The remaining real-time options face three problems: The shading of virtual surfaces under real natural illumination, the relighting of real surfaces according to the change in illumination due to the introduction of a new object into a scene, and the believable global interaction of real and virtual light. This dissertation presents contributions to answer the problems at hand.</p>

<p>Current state-of-the-art methods build on Differential Rendering techniques to fuse global illumination algorithms into AR environments. This simple approach has a computationally costly downside, which limits the options for believable light transfer even further. This dissertation explores new shading and relighting algorithms built on a mathematical foundation replacing Differential Rendering. The result not only presents a more efficient competitor to the current state-of-the-art in global illumination relighting, but also advances the field with the ability to simulate effects which have not been demonstrated by contemporary publications until now.</p>


    

    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://tuprints.ulb.tu-darmstadt.de/4992/">PHD thesis</a></li>
        
        
        
        
        <li><a href="https://d-nb.info/107836110X">German National Library</a></li>
        
        
        
        
        <li><a href="https://diglib.eg.org/handle/10.2312/14373">EG DGLIB</a></li>
        
        
        
        
        <li><a href="https://www-old.igd.fraunhofer.de/Presse/AktuellesNews/Der-Erweiterten-Realitaet-das-Spiegeln-lehren">IGD</a></li>
        

        

        
        <li><a href="https://www.tobias-franke.eu/publications/franke15phd/franke15phd.bib">Bibtex</a></li>
        
    </ul>
</div> ]]></description>
            <pubDate>Wed, 29 Jul 2015 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/publications/franke15phd/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhyS29RRUFyV2t2NlFsSjFxM3lRM2ZKbWZOZwowUHg1NHdDb3ZOMStibkpBVHpNam9vZ0EvamJtU0puVHpXbWprdFk3UDFvaUpRL1prOVViMmVVVzArZGxmNHVtCjBwQUMKPURUbWcKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Publication</category><category>Thesis</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>The State of the Art in Real-time Relighting for Augmented Reality</title>
            <description><![CDATA[ <div class="publication">
    <h1>The State of the Art in Real-time Relighting for Augmented Reality</h1>
    <p>
        <span class="authors">Tobias Alexander Franke</span>
        <span class="journal">ETH Zurich Visual Computing Talk</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/franke15starethz/franke15starethz.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/franke15starethz/franke15starethz.jpg" alt="" title="">
    
    </a>
    
    
</figure>

    <h2>Abstract</h2>
    <p>With the high availability of powerful mobile devices a renewed interest in augmented reality and the plausible fusion of virtual objects into real environments emerged. This in turn has recently lead to new research seeking to adapt real-time global illumination algorithms for AR relighting.</p>

<p>Because augmenting objects are directly exposed to the physical surrounding, discrepancies to reality, errors in the shading model and inconsistencies in the mutual interaction of real and virtual light are easily spotted by human observers. The problem of shading virtual surfaces according to real world illumination has been attacked before with image-based lighting, but the proper fusion of both virtual and real spaces requires physically plausible illumination exchange on both ends. True immersiveness additionally requires that any solution operates on a tight per-frame budget.</p>

<p>This talk will introduce the relighting problem, cover the current state of the art in global illumination relighting in AR and will conclude with an analysis of the different methods currently available - from Instant Radiosity to Irradiance Caching to my own research, which seeks to solve the problem of simulating glossy reflections off real surfaces with varying degrees of roughness - and their practical application in modern AR renderers.</p>


    

    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://www.tobias-franke.eu/publications/franke15starethz/franke15starethz_slides.pdf">Slides</a></li>
        
        
        
        
        <li><a href="https://www.inf.ethz.ch/news-and-events/events/event-detail.html?eventFeedId=24886">ETHZ</a></li>
        

        

        
        <li><a href="https://www.tobias-franke.eu/publications/franke15starethz/franke15starethz.bib">Bibtex</a></li>
        
    </ul>
</div> ]]></description>
            <pubDate>Fri, 30 Jan 2015 00:00:00 +0100</pubDate>
            <link>https://www.tobias-franke.eu/publications/franke15starethz/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhvY0JnRUFnVE55dXdpRUFrMUcxRThOVGcweAo0YVZ0bWVTRGJEVWs5dnFiK3pUWkM2c0EvMHcxUGx1Z09HS3hQeGtzUnM0aDJzT2YzYnY0MHkwM1VwNXZOeHRkCnFmd0UKPXE1RGcKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Conference</category><category>Publication</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Delta Voxel Cone Tracing</title>
            <description><![CDATA[ <div class="publication">
    <h1>Delta Voxel Cone Tracing</h1>
    <p>
        <span class="authors">Tobias Alexander Franke</span>
        <span class="journal">2014 IEEE International Symposium on Mixed and Augmented Reality</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/franke14dvct/franke14dvct.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/franke14dvct/franke14dvct.jpg" alt="Mutual indirect reflections for arbitrary rough surfaces are supported with Delta Voxel Cone Tracing. The augmenting Buddha receives red real indirect reflected light from the paper wall on the right, and is reflected on a rough metal surface on the ground." title="Mutual indirect reflections for arbitrary rough surfaces are supported with Delta Voxel Cone Tracing. The augmenting Buddha receives red real indirect reflected light from the paper wall on the right, and is reflected on a rough metal surface on the ground.">
    
    </a>
    
    
    <figcaption>Mutual indirect reflections for arbitrary rough surfaces are supported with Delta Voxel Cone Tracing. The augmenting Buddha receives red real indirect reflected light from the paper wall on the right, and is reflected on a rough metal surface on the ground.</figcaption>
    
</figure>

    <h2>Abstract</h2>
    <p>Mixed reality applications which must provide visual coherence between synthetic and real objects need relighting solutions for both: synthetic objects have to match lighting conditions of their real counterparts, while real surfaces need to account for the change in illumination introduced by the presence of an additional synthetic object. In this paper we present a novel relighting solution called Delta Voxel Cone Tracing to compute both direct shadows and first bounce mutual indirect illumination. We introduce a voxelized, pre-filtered representation of the combined real and synthetic surfaces together with the extracted illumination difference due to the augmentation. In a final gathering step this representation is cone-traced and superimposed onto both types of surfaces, adding additional light from indirect bounces and synthetic shadows from antiradiance present in the volume. The algorithm computes results at interactive rates, is temporally coherent and to our knowledge provides the first real-time rasterizer solution for mutual diffuse, glossy and perfect specular indirect reflections between synthetic and real surfaces in mixed reality.</p>


    
    <h2>Preview</h2>
    <p class="images">
    
    <img src="https://www.tobias-franke.eu/publications/franke14dvct/preview/franke14dvct-0.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/franke14dvct/preview/franke14dvct-1.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/franke14dvct/preview/franke14dvct-2.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/franke14dvct/preview/franke14dvct-3.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/franke14dvct/preview/franke14dvct-4.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/franke14dvct/preview/franke14dvct-5.png" alt="" title="">
    
    </p>
    

    
    <h2>Supplemental Video</h2>
    
    <div class="video default-size">
        <img class="default-size" src="https://www.tobias-franke.eu/publications/franke14dvct/franke14dvct_video.jpg" alt="" title="">
        
        <a class="fa fa-5x default-size" href="https://www.tobias-franke.eu/publications/franke14dvct/franke14dvct.mp4"></a>
        
    </div>
    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://www.tobias-franke.eu/publications/franke14dvct/franke14dvct.pdf">Paper</a></li>
        
        
        
        
        <li><a href="https://www.tobias-franke.eu/publications/franke14dvct/franke14dvct_slides.pdf">Slides</a></li>
        
        
        
        
        <li><a href="https://github.com/thefranke/dirtchamber">Source</a></li>
        
        
        
        
        <li><a href="https://ieeexplore.ieee.org/xpl/articleDetails.jsp?tp=&amp;arnumber=6948407">IEEE</a></li>
        
        
        
        
        <li><a href="https://ismar2014.vgtc.org/ismar/2014/paper/delta-voxel-cone-tracing.html">ISMAR 2014</a></li>
        

        
        
        <li><a href="https://www.tobias-franke.eu/publications/franke14dvct/franke14dvct.mp4">Video</a></li>
        
        

        
        <li><a href="https://www.tobias-franke.eu/publications/franke14dvct/franke14dvct.bib">Bibtex</a></li>
        
    </ul>
</div> ]]></description>
            <pubDate>Wed, 10 Sep 2014 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/publications/franke14dvct/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhvQjBBRC9lM0JOWDVaRkltM0ZMUFluUTdUVApMVk50MWg1TXVrZUJwWm55bVd5ajNwQUJBSktDM1hoTGVoVGpZbEF6bTJ2ekpKRGl0ZE1NdGlOMXFoMjZUdVRmClhMQUsKPUdTbk4KLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Conference</category><category>Publication</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Screen space cone tracing for glossy reflections</title>
            <description><![CDATA[ <div class="publication">
    <h1>Screen space cone tracing for glossy reflections</h1>
    <p>
        <span class="authors">Lukas Hermanns and Tobias Alexander Franke</span>
        <span class="journal">ACM SIGGRAPH 2014 Posters</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/hermanns14ssct/hermanns14ssct.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/hermanns14ssct/hermanns14ssct.jpg" alt="In the image, a checker board pattern in the specular buffer simulates tiles of different roughness. SSCT is used to simulate the appearance of a glossy reflection on rough tiles." title="In the image, a checker board pattern in the specular buffer simulates tiles of different roughness. SSCT is used to simulate the appearance of a glossy reflection on rough tiles.">
    
    </a>
    
    
    <figcaption>In the image, a checker board pattern in the specular buffer simulates tiles of different roughness. SSCT is used to simulate the appearance of a glossy reflection on rough tiles.</figcaption>
    
</figure>

    <h2>Abstract</h2>
    <p>A typical modern engine has a postprocessing pipeline which can be used to augment the final image from a previous render process with several effects. These usually include depth-of-field, crepuscular rays, tonemapping or morphological antialiasing. Such effects can be easily added to any existing renderer, since they usually rely only on information readily available in screen space. Recently, global illumination algorithms have been mapped to postprocessing effects, such as the wide selection of Screen Space Ambient Occlusion methods. An insight in [<a href="https://people.mpi-inf.mpg.de/~ritschel/SSDO/">Ritschel et al. 2009</a>] is that screen space algorithms can sample more information than just occlusion: in addition to visibility Screen Space Direct Occlusion samples neighboring pixels to gather indirect bounces. Soler et al. [<a href="https://hal.inria.fr/docs/00/53/61/02/PDF/ssil_Sig2010_Poster.160d653cb8ad491308e0fec689f29114.svn_184.pdf">Soler et al. 2010</a>] use mipmapped buffers to sample diffuse far- field indirect illumination and importance sample specular cones for glossy reflections, but do not consider to use mipmaps to access prefiltered bounces for specular cones. We present Screen Space Cone Tracing (SSCT), a method to simulate glossy and specular reflections. Instead of regular screen-space ray tracing, we adopt the concept of cone-tracing on hierarchical, prefiltered buffers to reduce integration costs.</p>


    
    <h2>Preview</h2>
    <p class="images">
    
    <img src="https://www.tobias-franke.eu/publications/hermanns14ssct/preview/hermanns14ssct-0.png" alt="" title="">
    
    </p>
    

    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://dl.acm.org/doi/pdf/10.1145/2614217.2614274">Abstract</a></li>
        
        
        
        
        <li><a href="https://www.tobias-franke.eu/publications/hermanns14ssct/hermanns14ssct_poster.pdf">Poster</a></li>
        
        
        
        
        <li><a href="https://dl.acm.org/doi/10.1145/2614217.2614274">ACM</a></li>
        
        
        
        
        <li><a href="https://s2014.siggraph.org/attendees/posters/complete-list-posters.html">S2014</a></li>
        

        

        
        <li><a href="https://www.tobias-franke.eu/publications/hermanns14ssct/hermanns14ssct.bib">Bibtex</a></li>
        
    </ul>
</div> ]]></description>
            <pubDate>Sun, 27 Jul 2014 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/publications/hermanns14ssct/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhvbFhnRUFyUEU0cW5JOU9IM1lKUnRDU1FxSwo2ZEozVVc5eHJleFVtL3J5UkVVUHpCc0EvMGM0Q1lFQUpLSms4cVFrb2R5aTNVYWhTdnpJMURjUGdQbWxpY1FBCkZEWUoKPWZLaWkKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Conference</category><category>Publication</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Interactive Relighting of Arbitrary Rough Surfaces</title>
            <description><![CDATA[ <div class="publication">
    <h1>Interactive Relighting of Arbitrary Rough Surfaces</h1>
    <p>
        <span class="authors">Tobias Alexander Franke</span>
        <span class="journal">ACM SIGGRAPH 2014 Posters</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/franke14irars/franke14irars.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/franke14irars/franke14irars.jpg" alt="A relit Cornell Box augmented by the Buddha statue (left diffuse, right specular). The lower row shows a 16x enhanced difference image between a regularly lit scene and a scene augmented by the Buddha and relit with DVCT." title="A relit Cornell Box augmented by the Buddha statue (left diffuse, right specular). The lower row shows a 16x enhanced difference image between a regularly lit scene and a scene augmented by the Buddha and relit with DVCT.">
    
    </a>
    
    
    <figcaption>A relit Cornell Box augmented by the Buddha statue (left diffuse, right specular). The lower row shows a 16x enhanced difference image between a regularly lit scene and a scene augmented by the Buddha and relit with DVCT.</figcaption>
    
</figure>

    <h2>Abstract</h2>
    <p>When presenting synthetic objects in a real environment - for instance for pre-visualization in advertisements - special attention needs to be directed at the mutual interaction of light reflecting off synthetic and real surfaces to form a coherent appearance. If the user is to be convinced that the synthetic object is part of the real scene, a relighting method has to handle shadowing and reflecting illumination between both synthetic and real surfaces. Even though a range of relighting methods are available for static scenes such as photographs, this aspect has been traditionally ignored in <em>real-time augmented reality</em> (AR) systems. A method often employed to merge synthetic light and shadows cast from synthetic objects with a real background is Differential Rendering, leaving out indirect illumination. Attempts have been made to resolve this issue with Differential Instant Radiosity [<a href="https://ieeexplore.ieee.org/xpl/login.jsp?tp=&amp;arnumber=6402547">Lensing and Broll 2012</a>], however requiring many VPLs to suppress flickering. Delta Light Propagation Volumes [<a href="https://ieeexplore.ieee.org/xpl/articleDetails.jsp?tp=&amp;arnumber=6671772">Franke 2013</a>] cluster many VPLs in a small volume, but suffer from bleeding artifacts. A GPU raytracer in [<a href="https://ieeexplore.ieee.org/xpl/articleDetails.jsp?tp=&amp;arnumber=6671773">Kan and Kaufmann 2013</a>] supports diffuse bounces with Differential Irradiance Caching, albeit at much higher cost than rasterizer based counterparts. We present a novel relighting solution called <em>Delta Voxel Cone Tracing</em> (DVCT) to enable mutual diffuse, glossy and specular indirect bounces between real and synthetic geometries. The method is temporally coherent and to our knowledge the first real-time solution to support arbitrary glossy reflections in AR.</p>


    
    <h2>Preview</h2>
    <p class="images">
    
    <img src="https://www.tobias-franke.eu/publications/franke14irars/preview/franke14irars-0.png" alt="" title="">
    
    </p>
    

    
    <h2>Supplemental Video</h2>
    
    <div class="video default-size">
        <img class="default-size" src="https://www.tobias-franke.eu/publications/franke14irars/franke14irars_video.jpg" alt="" title="">
        
        <a class="fa fa-5x default-size" href="https://www.tobias-franke.eu/publications/franke14irars/franke14irars.mp4"></a>
        
    </div>
    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://dl.acm.org/doi/pdf/10.1145/2614217.2614225">Abstract</a></li>
        
        
        
        
        <li><a href="https://www.tobias-franke.eu/publications/franke14irars/franke14irars_poster.pdf">Poster</a></li>
        
        
        
        
        <li><a href="https://dl.acm.org/doi/10.1145/2614217.2614225">ACM</a></li>
        
        
        
        
        <li><a href="https://s2014.siggraph.org/attendees/posters/complete-list-posters.html">S2014</a></li>
        

        
        
        <li><a href="https://www.tobias-franke.eu/publications/franke14irars/franke14irars.mp4">Video</a></li>
        
        

        
        <li><a href="https://www.tobias-franke.eu/publications/franke14irars/franke14irars.bib">Bibtex</a></li>
        
    </ul>
</div> ]]></description>
            <pubDate>Sun, 27 Jul 2014 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/publications/franke14irars/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhweWZRRUFuemtQYTRpYTJpSEJHd2dTVkRQMwozVHJLVFhXRG5WanhzYkRnaXQwYkVkb0EvMzA0cWNxd0JqbCtMYVhFZHpoY3pxRFVoWTExa2dtNThRUVJwbk5rCkh6Y1AKPWJZbSsKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Conference</category><category>Publication</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>On publication videos</title>
            <description><![CDATA[ <p>I have reviewed my fair share of papers over the years and seen some heinous crimes in video editing for the customary media attachment: blurry, too large, strange codecs only recognized by VLC, mouse cursor and other GUI elements visible on screen, double letterboxing or outright <em>interesting</em> choices of aspect ratio. Several factors play into this, such as the usual last minute pressure when compiling a paper, the <em>oh-gawd-we-surpassed-the-maximum-attachment-size</em> limitations of most submission systems or that the constant recompression just takes too long and the authors are happy with what they got.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2014_07_moarjpeg.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2014_07_moarjpeg.jpg" alt="" title="">
    
    </a>
    
    
</figure>

<p>It is clear however that the cause for this confusion is that video encoding is firmly situated at the borderline to black magic. There are certainly tools like iMovie which tremendously help to overcome most troubles in standard editing and encoding, but somewhere in between incompatible codecs and file containers, video bitrates, quantization and scaling issues, frame re-timing, color compression, and total boredom because you <em>just wanted a video</em> everyone will give up sooner or later.</p>

<p>In this post I'm going to cover the topic of how to create a web-browser compatible, pixel-matching HD or Full-HD, H.264 compressed MP4 of a reasonable size from a recording of a graphics application. But first, we need some ground rules.</p>

<h1 id="getting-it-right">Getting it right</h1>

<p>The first commandment, from which all others are derived, is that you shall absolutely and unequivocally avoid re-encoding your video multiple times. With each re-encoding, the quality of your video will degenerate into the image above. This is <em>especially</em> true if you know there will be yet another re-encoding step ahead where the settings are beyond your reach, for instance when uploading to Youtube: in this case a mostly unprocessed video is your best option.</p>

<p>These days the world has come down to some fairly standard aspect ratios: 16:9 for movies, 16:10 for displays. If you are recording 4:3 videos you're doing it wrong! Because we're creating a movie for the web, you may end up uploading it to various streaming platforms such as Youtube and Vimeo, which only deal with 16:9 ratios, so let's just stick with that.</p>

<p>On the resolution front of these ratios the choices have been reduced to 720p (1280x720 pixels) and 1080p (1920x1080 pixels). We'll most likely see new standards emerge for the upcoming 4k madness. In order to get a nice video file at the end, make sure that whatever you intend to record comes in either one of those two resolutions.</p>

<p>Not going for a standard aspect ratio and a standard resolution will force editing tools to rescale your video, causing sampling issues which will lead to blurry image quality. Sometimes you can't avoid having a resolution that isn't 720p or 1080p (capturing webcam videos for instance), but if you control the output just stick with the default. Videos which do not conform to either aspect ratio or resolution should be cropped and scaled manually with an external tool so that you control the quality and output instead of getting lucky with whatever your editing tool does to those videos.</p>

<p>Finally, all your videos should run with the same frame rate! If they don't you either have to re-time everything to the lowest common denominator (which doesn't require anything special but may not be great), or magically re-construct the missing frames with some optical-flow tool like Apple Cinema Tools or Twixtor (a lot of manual work ahead).</p>

<h1 id="dumping-data">Dumping data</h1>

<p>A popular way to obtain videos of a D3D or GL application are hooks like Fraps, NVIDIA ShadowPlay or DXTory. On Mac OS X, you can use Quicktime (File -&gt; New Screen Recording) to record portions of the screen. If you haven't done so already, check out a demo of one of them and try to record from a running game. An important decision has to be made upfront about resource division: record uncompressed videos for maximum quality and speed at very high data rates which can easily fill even the largest of hard disks, or sacrifice potentially vital CPU time to compress live during recording. There is no easy answer to this and it depends on your preferences and hardware settings. Personally, I prefer to record uncompressed and later downsize the videos into smaller, compressed segments for archiving. This certainly is no option to record World of Warcraft Boss fights of 10 minutes and more though.</p>

<p>I haven't played around with Fraps too much and <a href="https://blog.mmacklin.com/2013/06/11/real-time-video-capture-with-ffmpeg/">recently found a way to completely avoid it</a> by piping raw RGBA frames from my D3D app <a href="https://www.ffmpeg.org/">directly into FFmpeg</a>. You may use the following class to do the same.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;iomanip&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;ctime&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sstream&gt;</span><span class="cp">
</span>
<span class="cp">#include</span> <span class="cpf">&lt;D3D11.h&gt;</span><span class="cp">
</span>
<span class="cp">#define tstringstream std::wstringstream
#define tcerr std::wcerr
#define tclog std::wclog
</span>
<span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="nc">T</span><span class="p">&gt;</span>
<span class="kt">void</span> <span class="nf">safe_release</span><span class="p">(</span><span class="n">T</span><span class="o">&amp;</span> <span class="n">obj</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">obj</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">obj</span><span class="o">-&gt;</span><span class="n">Release</span><span class="p">();</span>
        <span class="n">obj</span> <span class="o">=</span> <span class="nb">nullptr</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">class</span> <span class="nc">video_recorder</span>
<span class="p">{</span>
<span class="nl">protected:</span>
    <span class="n">UINT</span>                    <span class="n">width_</span><span class="p">,</span>
                            <span class="n">height_</span><span class="p">,</span>
                            <span class="n">fps_</span><span class="p">;</span>
    <span class="n">ID3D11Texture2D</span><span class="o">*</span>        <span class="n">ffmpeg_texture_</span><span class="p">;</span>
    <span class="kt">FILE</span><span class="o">*</span>                   <span class="n">ffmpeg_</span><span class="p">;</span>
    <span class="n">tstring</span>                 <span class="n">path_</span><span class="p">;</span>

<span class="nl">public:</span>
    <span class="kt">void</span> <span class="n">create</span><span class="p">(</span><span class="n">ID3D11Device</span><span class="o">*</span> <span class="n">device</span><span class="p">,</span> <span class="n">UINT</span> <span class="n">width</span><span class="p">,</span> <span class="n">UINT</span> <span class="n">height</span><span class="p">,</span> <span class="n">UINT</span> <span class="n">fps</span><span class="p">,</span> <span class="k">const</span> <span class="n">tstring</span><span class="o">&amp;</span> <span class="n">path</span> <span class="o">=</span> <span class="s">L"../../data"</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">width_</span> <span class="o">=</span> <span class="n">width</span><span class="p">;</span>
        <span class="n">height_</span> <span class="o">=</span> <span class="n">height</span><span class="p">;</span>
        <span class="n">fps_</span> <span class="o">=</span> <span class="n">fps</span><span class="p">;</span>
        <span class="n">path_</span> <span class="o">=</span> <span class="n">path</span><span class="p">;</span>

        <span class="n">D3D11_TEXTURE2D_DESC</span> <span class="n">desc</span><span class="p">;</span>
        <span class="n">desc</span><span class="p">.</span><span class="n">Width</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">UINT</span><span class="o">&gt;</span><span class="p">(</span><span class="n">width</span><span class="p">);</span>
        <span class="n">desc</span><span class="p">.</span><span class="n">Height</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">UINT</span><span class="o">&gt;</span><span class="p">(</span><span class="n">height</span><span class="p">);</span>
        <span class="n">desc</span><span class="p">.</span><span class="n">MipLevels</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="n">desc</span><span class="p">.</span><span class="n">ArraySize</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="n">desc</span><span class="p">.</span><span class="n">SampleDesc</span><span class="p">.</span><span class="n">Count</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="n">desc</span><span class="p">.</span><span class="n">SampleDesc</span><span class="p">.</span><span class="n">Quality</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="n">desc</span><span class="p">.</span><span class="n">Format</span> <span class="o">=</span> <span class="n">DXGI_FORMAT_R8G8B8A8_UNORM</span><span class="p">;</span>
        <span class="n">desc</span><span class="p">.</span><span class="n">Usage</span> <span class="o">=</span> <span class="n">D3D11_USAGE_STAGING</span><span class="p">;</span>
        <span class="n">desc</span><span class="p">.</span><span class="n">BindFlags</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="n">desc</span><span class="p">.</span><span class="n">CPUAccessFlags</span> <span class="o">=</span> <span class="n">D3D11_CPU_ACCESS_READ</span><span class="p">;</span>
        <span class="n">desc</span><span class="p">.</span><span class="n">MiscFlags</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">device</span><span class="o">-&gt;</span><span class="n">CreateTexture2D</span><span class="p">(</span><span class="o">&amp;</span><span class="n">desc</span><span class="p">,</span> <span class="nb">nullptr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ffmpeg_texture_</span><span class="p">)</span> <span class="o">!=</span> <span class="n">S_OK</span><span class="p">)</span>
            <span class="n">tcerr</span> <span class="o">&lt;&lt;</span> <span class="s">L"Failed to create staging texture for recording"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kt">void</span> <span class="nf">start_recording</span><span class="p">(</span><span class="kt">bool</span> <span class="n">compressed</span> <span class="o">=</span> <span class="nb">false</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">ffmpeg_texture_</span><span class="p">)</span>
            <span class="k">return</span><span class="p">;</span>

        <span class="n">std</span><span class="o">::</span><span class="kt">time_t</span> <span class="n">t</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">time</span><span class="p">(</span><span class="nb">nullptr</span><span class="p">);</span>

        <span class="n">std</span><span class="o">::</span><span class="n">tm</span> <span class="n">tm</span><span class="p">;</span>
        <span class="n">localtime_s</span><span class="p">(</span><span class="o">&amp;</span><span class="n">tm</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">t</span><span class="p">);</span>

        <span class="n">tstringstream</span> <span class="n">file</span><span class="p">;</span>
        <span class="n">file</span> <span class="o">&lt;&lt;</span> <span class="n">path_</span> <span class="o">&lt;&lt;</span> <span class="s">"/record-"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">put_time</span><span class="p">(</span><span class="o">&amp;</span><span class="n">tm</span><span class="p">,</span> <span class="s">L"%Y%m%d-%H%M%S"</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="s">L".mp4"</span><span class="p">;</span>

        <span class="c1">// adapted from https://blog.mmacklin.com/2013/06/11/real-time-video-capture-with-ffmpeg/</span>
        <span class="n">tstringstream</span> <span class="n">cmd</span><span class="p">;</span>
        <span class="n">cmd</span> <span class="o">&lt;&lt;</span> <span class="s">L"ffmpeg -r "</span> <span class="o">&lt;&lt;</span> <span class="n">fps_</span> <span class="o">&lt;&lt;</span> <span class="s">" -f rawvideo -pix_fmt rgba "</span>
            <span class="o">&lt;&lt;</span> <span class="s">L"-s "</span> <span class="o">&lt;&lt;</span> <span class="n">width_</span> <span class="o">&lt;&lt;</span> <span class="s">"x"</span> <span class="o">&lt;&lt;</span> <span class="n">height_</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span>
            <span class="o">&lt;&lt;</span> <span class="s">L"-i - "</span>
            <span class="o">&lt;&lt;</span> <span class="s">L"-threads 2 -y "</span>
            <span class="o">&lt;&lt;</span> <span class="s">L"-c:v libx264 "</span>
            <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="n">compressed</span><span class="p">)</span> <span class="o">?</span> <span class="s">L"-preset ultrafast -qp 0 "</span> <span class="o">:</span> <span class="s">L"-preset fast "</span>
            <span class="o">&lt;&lt;</span> <span class="n">make_absolute_path</span><span class="p">(</span><span class="n">file</span><span class="p">.</span><span class="n">str</span><span class="p">())</span>
            <span class="p">;</span>

        <span class="n">tclog</span> <span class="o">&lt;&lt;</span> <span class="s">L"Recording video with: "</span> <span class="o">&lt;&lt;</span> <span class="n">cmd</span><span class="p">.</span><span class="n">str</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

<span class="cp">#ifdef UNICODE
</span>        <span class="n">ffmpeg_</span> <span class="o">=</span> <span class="n">_wpopen</span><span class="p">(</span><span class="n">cmd</span><span class="p">.</span><span class="n">str</span><span class="p">().</span><span class="n">c_str</span><span class="p">(),</span> <span class="s">L"wb"</span><span class="p">);</span>
<span class="cp">#else
</span>        <span class="n">ffmpeg_</span> <span class="o">=</span> <span class="n">_popen</span><span class="p">(</span><span class="n">cmd</span><span class="p">.</span><span class="n">str</span><span class="p">().</span><span class="n">c_str</span><span class="p">(),</span> <span class="s">"wb"</span><span class="p">);</span>
<span class="cp">#endif
</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">ffmpeg_</span><span class="p">)</span>
            <span class="n">tcerr</span> <span class="o">&lt;&lt;</span> <span class="s">L"Failed to initialize ffmpeg"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kt">void</span> <span class="nf">stop_recording</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">ffmpeg_</span><span class="p">)</span>
            <span class="n">_pclose</span><span class="p">(</span><span class="n">ffmpeg_</span><span class="p">);</span>

        <span class="n">ffmpeg_</span> <span class="o">=</span> <span class="nb">nullptr</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kt">void</span> <span class="nf">add_frame</span><span class="p">(</span><span class="n">ID3D11DeviceContext</span><span class="o">*</span> <span class="n">context</span><span class="p">,</span> <span class="n">ID3D11RenderTargetView</span><span class="o">*</span> <span class="n">rtv</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">ID3D11Resource</span><span class="o">*</span> <span class="n">resource</span><span class="p">;</span>
        <span class="n">rtv</span><span class="o">-&gt;</span><span class="n">GetResource</span><span class="p">(</span><span class="o">&amp;</span><span class="n">resource</span><span class="p">);</span>
        <span class="n">add_frame</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">resource</span><span class="p">);</span>
        <span class="n">safe_release</span><span class="p">(</span><span class="n">resource</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kt">void</span> <span class="nf">add_frame</span><span class="p">(</span><span class="n">ID3D11DeviceContext</span><span class="o">*</span> <span class="n">context</span><span class="p">,</span> <span class="n">ID3D11Resource</span><span class="o">*</span> <span class="n">resource</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">context</span><span class="o">-&gt;</span><span class="n">CopyResource</span><span class="p">(</span><span class="n">ffmpeg_texture_</span><span class="p">,</span> <span class="n">resource</span><span class="p">);</span>

        <span class="n">D3D11_MAPPED_SUBRESOURCE</span> <span class="n">msr</span><span class="p">;</span>
        <span class="n">UINT</span> <span class="n">subresource</span> <span class="o">=</span> <span class="n">D3D11CalcSubresource</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
        <span class="n">context</span><span class="o">-&gt;</span><span class="n">Map</span><span class="p">(</span><span class="n">ffmpeg_texture_</span><span class="p">,</span> <span class="n">subresource</span><span class="p">,</span> <span class="n">D3D11_MAP_READ</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">msr</span><span class="p">);</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">msr</span><span class="p">.</span><span class="n">pData</span> <span class="o">&amp;&amp;</span> <span class="n">ffmpeg_</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">fwrite</span><span class="p">(</span><span class="n">msr</span><span class="p">.</span><span class="n">pData</span><span class="p">,</span> <span class="p">(</span><span class="n">width_</span> <span class="o">*</span> <span class="n">height_</span> <span class="o">*</span> <span class="mi">4</span><span class="p">),</span> <span class="mi">1</span><span class="p">,</span> <span class="n">ffmpeg_</span><span class="p">);</span>
            <span class="n">context</span><span class="o">-&gt;</span><span class="n">Unmap</span><span class="p">(</span><span class="n">ffmpeg_texture_</span><span class="p">,</span> <span class="n">subresource</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kt">void</span> <span class="nf">destroy</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="n">stop_recording</span><span class="p">();</span>
        <span class="n">safe_release</span><span class="p">(</span><span class="n">ffmpeg_texture_</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>

<p>Essentially the <em>video_encoder</em> class creates an open pipe to ffmpeg using the libx264 codec with zero compression. <strong>add_frame()</strong> copies a resource behind a RenderTargetView to a staging texture, which is then CPU read and fed to the pipe.</p>

<p>The crucial line is where the cmd stringstream is compiled: here you may want toggle <strong>compressed</strong> to have direct compression enabled and choose custom settings for <em>-qp</em> and <em>-crf</em> to control the compression rate. Either way, be warned that the resulting video is <strong>NOT</strong> compatible with MP4 support in browsers, because most of them only play content in YUV 4:2:0 color space and these settings output RGBA. I don't have access to D3D11.1 where the staging texture can have <strong>DXGI_FORMAT_NV12</strong>, but if you do you may want to try this.</p>

<p>If you need maximum performance though, save the video uncompressed and compress it afterwards.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ffmpeg <span class="nt">-i</span> record-xxxx-xxxx.mp4 <span class="nt">-c</span>:v libx264<span class="se">\</span>
         <span class="nt">-crf</span> 23 out.mp4
</code></pre></div></div>

<p>This will give you a nice, compressed MP4 which modern browser can directly display without plugins. You can upload it to your webpage and embed the video with an HTML5 video tag or put it on a Dropbox folder and people can directly watch it. If you plan to cut and edit the video though, skip this step.</p>

<p>Embedding videos into a webpage is easily done with the following code.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;video</span> <span class="na">style=</span><span class="s">"width:100%"</span> <span class="na">controls</span> <span class="na">autoplay</span><span class="nt">&gt;</span>
	<span class="nt">&lt;source</span> <span class="na">src=</span><span class="s">"https://foo/bar.mp4"</span> <span class="na">type=</span><span class="s">"video/mp4"</span><span class="nt">&gt;</span>
	Your browser does not support the video tag.
<span class="nt">&lt;/video&gt;</span>
</code></pre></div></div>

<p>The video should run on any modern browser without any plugins.</p>

<h1 id="editing">Editing</h1>

<p>When it comes to video editing my weapon of choice is Final Cut 7, which I'll use to illustrate an important point: most if not all bigger editing tools use a special codec for cutting a video. Apple tools such as FCP and iMovie use the ProRes codec, which comes in several forms (Proxy, LT, HQ). If you value your time, don't want your editing tool to arbitrarily recompress your snippets and maintain full control consider re-encoding your raw FFmpeg dump manually with such a codec. In case of FCP, this will also allow you to use blending operations and other effects without FCPs further interference.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2014_07_mpegstreamclip.png">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2014_07_mpegstreamclip.png" alt="" title="">
    
    </a>
    
    
</figure>

<p>At this point I cannot overstate <a href="https://www.squared5.com/">the magnificent gloriosity of MPEGStreamClip</a>: it's an easy to use free frontend for the Quicktime compressor on Windows and Mac OS X and it has a GUI for batch processing. I usually dump all my videos into the batch list and encode them with <em>Apple ProRes 422 (HQ)</em> at 100% quality. You can also use FFmpeg and simply script this behavior if you're good with the command line. If you have a funny resolution, MPEGStreamClip also has some options to crop and scale the video. A very similar aweseome tool is <a href="https://handbrake.fr/">Handbrake</a> which comes with some neat settings for common enddevices such as smartphone and tablets. Yet another tool for simple cutting, scaling and cropping operations is <a href="https://www.virtualdub.org/">Virtual Dub</a>, which however doesn't handle the encoding side as nicely.</p>

<h1 id="converting-to-the-final-format">Converting to the final format</h1>

<p>When you are done with the clip, the video is exported into its final format. Here you should take care to use a standard container (no, not WMV/ASF) with a standard video and audio codec (no, not Fraps or Cinepak or DivX or …) so that the video will work in Safari, Chrome, Firefox and Opera directly and play with the default video player of the operating system, even on mobile devices. You may use the command above for FFMpeg (and add audio settings if necessary), or use MPEGStreamclip like this:</p>

<ul>
  <li>Open your uncompressed, edited video</li>
  <li>Go to File -&gt; Export to MPEG-4</li>
  <li>Choose H.264 compression</li>
  <li>Select 100% quality</li>
  <li>Limit the data rate to 3000-8000 kbps</li>
  <li>Disable sound if there is none in the video</li>
  <li>Export the video</li>
</ul>

<p>And that's it, you're done. Prepare some template Photoshop intro and outro images (which contain paper title, author names, logos of the conference) and creating new videos from recorded material should be an effort of minutes!</p>

<h1 id="the-checklist">The checklist</h1>

<ul>
  <li>All videos have the same resolution and framerate</li>
  <li>All videos are either 720p or 1080p</li>
  <li>You have re-encoded them with the intermediate codec of your editing tool</li>
  <li>You have created a project in said editing tool with the same resolution and framerate of your videos</li>
  <li>The final output is H.264</li>
</ul>

<p>Another helpful resource is <a href="https://support.google.com/youtube/answer/1722171?hl=en">the Youtube advanced encoding page</a>, which has two tables for recommended bitrates for standard and high quality movies.</p>

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

<ol>
  <li><a href="https://www.ffmpeg.org/">FFmpeg</a></li>
  <li><a href="https://www.squared5.org/">MPEGStreamClip</a></li>
  <li><a href="https://www.fraps.com/">Fraps</a></li>
  <li><a href="https://exkode.com/dxtory-features-en.html">DXTory</a></li>
  <li>NVIDIA, <a href="https://www.geforce.com/geforce-experience/shadowplay">Shadowplay</a>
</li>
  <li>Miles Macklin, <a href="https://blog.mmacklin.com/2013/06/11/real-time-video-capture-with-ffmpeg/">Real-Time Video Capture with FFmpeg</a>
</li>
  <li>Google, <a href="https://support.google.com/youtube/answer/1722171?hl=en">Youtube Advanced Encoding</a>
</li>
</ol> ]]></description>
            <pubDate>Sun, 06 Jul 2014 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/log/2014/07/06/on-publication-videos.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhyOUdRRDlISEF5aHg5OWl5WGNRTzJ6RXQ1awpTZ2NZeUFRNWNoREhJREpqamVzYU95Z0EvanBJSjdMbk9QT3FpaGJwdEN6Q0wxalBZQzFVWGlTK3E2WG9HWmplCldDUVAKPTAwWFgKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Video</category><category>Notes</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Notes on importance sampling</title>
            <description><![CDATA[ <p>Some tutorials on importance sampling specular terms that are out in the wild have what I found to be an information gap: the step from the PDF to the actual sampling function is missing. Hopefully this step-by-step guide can help out one or two other confused readers. I checked all integrals with <a href="https://maxima.sourceforge.net/">Maxima</a> and <a href="https://www.wolframalpha.com/">Wolfram Alpha</a>. If you just have a free account at Wolfram Alpha, you might run into issues with exceeding the computation time, which is why I will also write down the commands to do everything in Maxima. If your result doesn't look like the one noted here, try simplifying it first (in the Maxima menu <em>Simplify -&gt; Simplify Expression</em>) or put it into Wolfram Alpha and check the <em>Alternate form</em> section!</p>

<h1 id="phong">Phong</h1>

<p>The PDF for a normalized Phong BRDF with the specular power notation is:</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}
	p(\theta, \phi) = \frac{\left(n+1\right)}{2 \pi} \cos^n \theta \sin \theta
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-1">
        <mtext>(1)</mtext>
      </mtd>
      <mtd>
        <mi>p</mi>
        <mo stretchy="false">(</mo>
        <mi>θ<!-- θ --></mi>
        <mo>,</mo>
        <mi>ϕ<!-- ϕ --></mi>
        <mo stretchy="false">)</mo>
        <mo>=</mo>
        <mfrac>
          <mrow>
            <mo>(</mo>
            <mi>n</mi>
            <mo>+</mo>
            <mn>1</mn>
            <mo>)</mo>
          </mrow>
          <mrow>
            <mn>2</mn>
            <mi>π<!-- π --></mi>
          </mrow>
        </mfrac>
        <msup>
          <mi>cos</mi>
          <mi>n</mi>
        </msup>
        <mo>⁡<!-- ⁡ --></mo>
        <mi>θ<!-- θ --></mi>
        <mi>sin</mi>
        <mo>⁡<!-- ⁡ --></mo>
        <mi>θ<!-- θ --></mi>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>We generate two functions to sample theta and phi independently. To do this, <a href="https://www.wolframalpha.com/input/?i=integrate_0%5E%282pi%29+%28n%2B1%29%2F%282*pi%29+*+cos%28t%29%5En+*+sin%28t%29+dp">we first integrate <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="p(\theta, \phi)">
  <mi>p</mi>
  <mo stretchy="false">(</mo>
  <mi>θ<!-- θ --></mi>
  <mo>,</mo>
  <mi>ϕ<!-- ϕ --></mi>
  <mo stretchy="false">)</mo>
</math></span> along the domain of  <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\phi">
  <mi>ϕ<!-- ϕ --></mi>
</math></span></a>:</p>

<blockquote>
  <p>integrate((n+1)/(2 * %pi) * cos(t)^n * sin(t), p, 0, 2 * %pi)</p>
</blockquote>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}
	p(\theta) = \int^{2 \pi}_0 p(\theta, \phi) d\phi = \left(n+1\right) \cos^n \theta \sin \theta
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-2">
        <mtext>(2)</mtext>
      </mtd>
      <mtd>
        <mi>p</mi>
        <mo stretchy="false">(</mo>
        <mi>θ<!-- θ --></mi>
        <mo stretchy="false">)</mo>
        <mo>=</mo>
        <msubsup>
          <mo>∫<!-- ∫ --></mo>
          <mn>0</mn>
          <mrow class="MJX-TeXAtom-ORD">
            <mn>2</mn>
            <mi>π<!-- π --></mi>
          </mrow>
        </msubsup>
        <mi>p</mi>
        <mo stretchy="false">(</mo>
        <mi>θ<!-- θ --></mi>
        <mo>,</mo>
        <mi>ϕ<!-- ϕ --></mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>ϕ<!-- ϕ --></mi>
        <mo>=</mo>
        <mrow>
          <mo>(</mo>
          <mi>n</mi>
          <mo>+</mo>
          <mn>1</mn>
          <mo>)</mo>
        </mrow>
        <msup>
          <mi>cos</mi>
          <mi>n</mi>
        </msup>
        <mo>⁡<!-- ⁡ --></mo>
        <mi>θ<!-- θ --></mi>
        <mi>sin</mi>
        <mo>⁡<!-- ⁡ --></mo>
        <mi>θ<!-- θ --></mi>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>This is the <em>marginal density function</em> of <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\theta ">
  <mi>θ<!-- θ --></mi>
</math></span>. Note that the extra <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\sin \theta">
  <mi>sin</mi>
  <mo>⁡<!-- ⁡ --></mo>
  <mi>θ<!-- θ --></mi>
</math></span> at the end is there because we are dealing with differential solid angles in spherical coordinates. We can retrieve the PDF for <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\phi">
  <mi>ϕ<!-- ϕ --></mi>
</math></span> with the conditional probability <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="p(\phi | \theta)">
  <mi>p</mi>
  <mo stretchy="false">(</mo>
  <mi>ϕ<!-- ϕ --></mi>
  <mrow class="MJX-TeXAtom-ORD">
    <mo stretchy="false">|</mo>
  </mrow>
  <mi>θ<!-- θ --></mi>
  <mo stretchy="false">)</mo>
</math></span>, the <em>conditional density function</em>:</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}
	p(\phi \| \theta) = \frac{p(\theta, \phi)}{p(\theta)} = \frac{1}{2 \pi}
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-3">
        <mtext>(3)</mtext>
      </mtd>
      <mtd>
        <mi>p</mi>
        <mo stretchy="false">(</mo>
        <mi>ϕ<!-- ϕ --></mi>
        <mo fence="false" stretchy="false">‖<!-- ‖ --></mo>
        <mi>θ<!-- θ --></mi>
        <mo stretchy="false">)</mo>
        <mo>=</mo>
        <mfrac>
          <mrow>
            <mi>p</mi>
            <mo stretchy="false">(</mo>
            <mi>θ<!-- θ --></mi>
            <mo>,</mo>
            <mi>ϕ<!-- ϕ --></mi>
            <mo stretchy="false">)</mo>
          </mrow>
          <mrow>
            <mi>p</mi>
            <mo stretchy="false">(</mo>
            <mi>θ<!-- θ --></mi>
            <mo stretchy="false">)</mo>
          </mrow>
        </mfrac>
        <mo>=</mo>
        <mfrac>
          <mn>1</mn>
          <mrow>
            <mn>2</mn>
            <mi>π<!-- π --></mi>
          </mrow>
        </mfrac>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>For isotropic NDFs this is always the case, no matter which PDF you will integrate over the domain of <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\phi">
  <mi>ϕ<!-- ϕ --></mi>
</math></span>, so all you need to calculate for the next PDF is the function for <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\theta">
  <mi>θ<!-- θ --></mi>
</math></span>.</p>

<p>Now that we have two functions for each variable, we can integrate both to generate a CDF each. <a href="https://www.wolframalpha.com/input/?i=integrate_0%5Es+1%2F%282pi%29+dp">The case of <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\phi">
  <mi>ϕ<!-- ϕ --></mi>
</math></span> is trivial</a>:</p>

<blockquote>
  <p>integrate(1/(2 * %pi), p, 0, s)</p>
</blockquote>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}
	P(s_\phi) = \int_0^{s_\phi} p(\phi)d\phi = \int_0^{s_\phi} \frac{1}{2 \pi} d\phi = \frac{s_\phi}{2 \pi} 
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-4">
        <mtext>(4)</mtext>
      </mtd>
      <mtd>
        <mi>P</mi>
        <mo stretchy="false">(</mo>
        <msub>
          <mi>s</mi>
          <mi>ϕ<!-- ϕ --></mi>
        </msub>
        <mo stretchy="false">)</mo>
        <mo>=</mo>
        <msubsup>
          <mo>∫<!-- ∫ --></mo>
          <mn>0</mn>
          <mrow class="MJX-TeXAtom-ORD">
            <msub>
              <mi>s</mi>
              <mi>ϕ<!-- ϕ --></mi>
            </msub>
          </mrow>
        </msubsup>
        <mi>p</mi>
        <mo stretchy="false">(</mo>
        <mi>ϕ<!-- ϕ --></mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>ϕ<!-- ϕ --></mi>
        <mo>=</mo>
        <msubsup>
          <mo>∫<!-- ∫ --></mo>
          <mn>0</mn>
          <mrow class="MJX-TeXAtom-ORD">
            <msub>
              <mi>s</mi>
              <mi>ϕ<!-- ϕ --></mi>
            </msub>
          </mrow>
        </msubsup>
        <mfrac>
          <mn>1</mn>
          <mrow>
            <mn>2</mn>
            <mi>π<!-- π --></mi>
          </mrow>
        </mfrac>
        <mi>d</mi>
        <mi>ϕ<!-- ϕ --></mi>
        <mo>=</mo>
        <mfrac>
          <msub>
            <mi>s</mi>
            <mi>ϕ<!-- ϕ --></mi>
          </msub>
          <mrow>
            <mn>2</mn>
            <mi>π<!-- π --></mi>
          </mrow>
        </mfrac>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>If we set <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="P(s_\phi)">
  <mi>P</mi>
  <mo stretchy="false">(</mo>
  <msub>
    <mi>s</mi>
    <mi>ϕ<!-- ϕ --></mi>
  </msub>
  <mo stretchy="false">)</mo>
</math></span> to a random variable <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\xi_\phi">
  <msub>
    <mi>ξ<!-- ξ --></mi>
    <mi>ϕ<!-- ϕ --></mi>
  </msub>
</math></span> and solve for <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="s_\phi ">
  <msub>
    <mi>s</mi>
    <mi>ϕ<!-- ϕ --></mi>
  </msub>
</math></span>, we get:</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{eqnarray}
	P(s_\phi) &amp; = &amp; \frac{s_\phi}{2 \pi} = \xi_\phi \\
	s_\phi &amp; = &amp; 2 \pi \xi_\phi \\
\end{eqnarray}">
  <mtable columnalign="left right center left" displaystyle="true">
    <mtr>
      <mtd id="mjx-eqn-5">
        <mtext>(5)</mtext>
      </mtd>
      <mtd>
        <mi>P</mi>
        <mo stretchy="false">(</mo>
        <msub>
          <mi>s</mi>
          <mi>ϕ<!-- ϕ --></mi>
        </msub>
        <mo stretchy="false">)</mo>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <mfrac>
          <msub>
            <mi>s</mi>
            <mi>ϕ<!-- ϕ --></mi>
          </msub>
          <mrow>
            <mn>2</mn>
            <mi>π<!-- π --></mi>
          </mrow>
        </mfrac>
        <mo>=</mo>
        <msub>
          <mi>ξ<!-- ξ --></mi>
          <mi>ϕ<!-- ϕ --></mi>
        </msub>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-6">
        <mtext>(6)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mi>s</mi>
          <mi>ϕ<!-- ϕ --></mi>
        </msub>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <mn>2</mn>
        <mi>π<!-- π --></mi>
        <msub>
          <mi>ξ<!-- ξ --></mi>
          <mi>ϕ<!-- ϕ --></mi>
        </msub>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>Again, sampling around the azimuthal angle of an isotropic NDF is always the same because the material is rotationally invariant along this angle, so you don't need to derive this formula again.</p>

<p>We now repeat the process and <a href="https://www.wolframalpha.com/input/?i=integrate_0%5Es+%28n%2B1%29+*+cos%28t%29%5En+*+sin%28t%29+dt">create a CDF for <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="p(\theta)">
  <mi>p</mi>
  <mo stretchy="false">(</mo>
  <mi>θ<!-- θ --></mi>
  <mo stretchy="false">)</mo>
</math></span></a>:</p>

<blockquote>
  <p>integrate((n+1) * cos(t)^n * sin(t), t, 0, s)</p>
</blockquote>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}
	P(s_\theta) = \int_0^{s_\theta} p(\theta) d\theta = 1 - cos^{n+1} s_\theta
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-7">
        <mtext>(7)</mtext>
      </mtd>
      <mtd>
        <mi>P</mi>
        <mo stretchy="false">(</mo>
        <msub>
          <mi>s</mi>
          <mi>θ<!-- θ --></mi>
        </msub>
        <mo stretchy="false">)</mo>
        <mo>=</mo>
        <msubsup>
          <mo>∫<!-- ∫ --></mo>
          <mn>0</mn>
          <mrow class="MJX-TeXAtom-ORD">
            <msub>
              <mi>s</mi>
              <mi>θ<!-- θ --></mi>
            </msub>
          </mrow>
        </msubsup>
        <mi>p</mi>
        <mo stretchy="false">(</mo>
        <mi>θ<!-- θ --></mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>θ<!-- θ --></mi>
        <mo>=</mo>
        <mn>1</mn>
        <mo>−<!-- − --></mo>
        <mi>c</mi>
        <mi>o</mi>
        <msup>
          <mi>s</mi>
          <mrow class="MJX-TeXAtom-ORD">
            <mi>n</mi>
            <mo>+</mo>
            <mn>1</mn>
          </mrow>
        </msup>
        <msub>
          <mi>s</mi>
          <mi>θ<!-- θ --></mi>
        </msub>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>Just like in the case of <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\phi ">
  <mi>ϕ<!-- ϕ --></mi>
</math></span>, we set <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="P(s_\theta)">
  <mi>P</mi>
  <mo stretchy="false">(</mo>
  <msub>
    <mi>s</mi>
    <mi>θ<!-- θ --></mi>
  </msub>
  <mo stretchy="false">)</mo>
</math></span> to a random variable again and solve for <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="s ">
  <mi>s</mi>
</math></span>:</p>

<blockquote>
  <p>solve(1 - cos(s)^(n+1) = x, s)</p>
</blockquote>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{eqnarray}
	P(s_\theta) &amp; = &amp; 1 - cos^{n+1} s_\theta = \xi_\theta \\
	s_\theta &amp; = &amp;  cos^{-1}\left( \left( 1 - \xi_\theta \right)^\frac{1}{n+1} \right) \\
\end{eqnarray}">
  <mtable columnalign="left right center left" displaystyle="true">
    <mtr>
      <mtd id="mjx-eqn-8">
        <mtext>(8)</mtext>
      </mtd>
      <mtd>
        <mi>P</mi>
        <mo stretchy="false">(</mo>
        <msub>
          <mi>s</mi>
          <mi>θ<!-- θ --></mi>
        </msub>
        <mo stretchy="false">)</mo>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <mn>1</mn>
        <mo>−<!-- − --></mo>
        <mi>c</mi>
        <mi>o</mi>
        <msup>
          <mi>s</mi>
          <mrow class="MJX-TeXAtom-ORD">
            <mi>n</mi>
            <mo>+</mo>
            <mn>1</mn>
          </mrow>
        </msup>
        <msub>
          <mi>s</mi>
          <mi>θ<!-- θ --></mi>
        </msub>
        <mo>=</mo>
        <msub>
          <mi>ξ<!-- ξ --></mi>
          <mi>θ<!-- θ --></mi>
        </msub>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-9">
        <mtext>(9)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mi>s</mi>
          <mi>θ<!-- θ --></mi>
        </msub>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <mi>c</mi>
        <mi>o</mi>
        <msup>
          <mi>s</mi>
          <mrow class="MJX-TeXAtom-ORD">
            <mo>−<!-- − --></mo>
            <mn>1</mn>
          </mrow>
        </msup>
        <mrow>
          <mo>(</mo>
          <msup>
            <mrow>
              <mo>(</mo>
              <mn>1</mn>
              <mo>−<!-- − --></mo>
              <msub>
                <mi>ξ<!-- ξ --></mi>
                <mi>θ<!-- θ --></mi>
              </msub>
              <mo>)</mo>
            </mrow>
            <mfrac>
              <mn>1</mn>
              <mrow>
                <mi>n</mi>
                <mo>+</mo>
                <mn>1</mn>
              </mrow>
            </mfrac>
          </msup>
          <mo>)</mo>
        </mrow>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>Therefore, a GLSL shader which generates important directions for a Phong NDF from random, uniform values looks like this:</p>

<div class="language-glsl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">vec2</span> <span class="nf">importance_sample_phong</span><span class="p">(</span><span class="kt">vec2</span> <span class="n">xi</span><span class="p">)</span>
<span class="p">{</span>
  <span class="kt">float</span> <span class="n">phi</span> <span class="o">=</span> <span class="mi">2</span><span class="p">.</span><span class="mi">0</span><span class="n">f</span> <span class="o">*</span> <span class="n">PI</span> <span class="o">*</span> <span class="n">xi</span><span class="p">.</span><span class="n">x</span><span class="p">;</span>
  <span class="kt">float</span> <span class="n">theta</span> <span class="o">=</span> <span class="n">acos</span><span class="p">(</span><span class="n">pow</span><span class="p">(</span><span class="mi">1</span><span class="p">.</span><span class="mi">0</span><span class="n">f</span> <span class="o">-</span> <span class="n">xi</span><span class="p">.</span><span class="n">y</span><span class="p">,</span> <span class="mi">1</span><span class="p">.</span><span class="mi">0</span><span class="n">f</span><span class="o">/</span><span class="p">(</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="p">.</span><span class="mi">0</span><span class="n">f</span><span class="p">)));</span>
  <span class="k">return</span> <span class="kt">vec2</span><span class="p">(</span><span class="n">phi</span><span class="p">,</span> <span class="n">theta</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Note that since <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\xi_\theta \in [0,1)">
  <msub>
    <mi>ξ<!-- ξ --></mi>
    <mi>θ<!-- θ --></mi>
  </msub>
  <mo>∈<!-- ∈ --></mo>
  <mo stretchy="false">[</mo>
  <mn>0</mn>
  <mo>,</mo>
  <mn>1</mn>
  <mo stretchy="false">)</mo>
</math></span> the expression <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="1 - \xi_\theta">
  <mn>1</mn>
  <mo>−<!-- − --></mo>
  <msub>
    <mi>ξ<!-- ξ --></mi>
    <mi>θ<!-- θ --></mi>
  </msub>
</math></span> is also a random variable in the same range.</p>

<p>You can for instance use <a href="https://en.wikipedia.org/wiki/Constructions_of_low-discrepancy_sequences">a low-discrepancy sequence</a> as a source for uniformly distributed random  values <em>xi</em> and generate important samples which aren't clumped.</p>

<h1 id="trowbridge-reitz-aka-ggx">Trowbridge-Reitz aka GGX</h1>

<p>The GGX normal distribution function is part of a microfacet model, which gives you the probability of micro-surface normals oriented along a certain direction. The term has to be normalized though before integrating over the hemisphere to account for the projected micro-surface area:</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}
	\int_\Omega D(\mathbf{m})(\mathbf{n} \cdot \mathbf{m}) d\mathbf{m} = 1 
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-10">
        <mtext>(10)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi mathvariant="normal">Ω<!-- Ω --></mi>
        </msub>
        <mi>D</mi>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <mi mathvariant="bold">m</mi>
        </mrow>
        <mo stretchy="false">)</mo>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <mi mathvariant="bold">n</mi>
        </mrow>
        <mo>⋅<!-- ⋅ --></mo>
        <mrow class="MJX-TeXAtom-ORD">
          <mi mathvariant="bold">m</mi>
        </mrow>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mrow class="MJX-TeXAtom-ORD">
          <mi mathvariant="bold">m</mi>
        </mrow>
        <mo>=</mo>
        <mn>1</mn>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>The NDF itself is defined as:</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}
	D(\mathbf{m}) = \frac{\alpha^2}{\pi \left( \cos^2 (\mathbf{n} \cdot \mathbf{m}) \left(\alpha^2 - 1 \right) +1 \right)^2} 
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-11">
        <mtext>(11)</mtext>
      </mtd>
      <mtd>
        <mi>D</mi>
        <mo stretchy="false">(</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <mi mathvariant="bold">m</mi>
        </mrow>
        <mo stretchy="false">)</mo>
        <mo>=</mo>
        <mfrac>
          <msup>
            <mi>α<!-- α --></mi>
            <mn>2</mn>
          </msup>
          <mrow>
            <mi>π<!-- π --></mi>
            <msup>
              <mrow>
                <mo>(</mo>
                <msup>
                  <mi>cos</mi>
                  <mn>2</mn>
                </msup>
                <mo>⁡<!-- ⁡ --></mo>
                <mo stretchy="false">(</mo>
                <mrow class="MJX-TeXAtom-ORD">
                  <mi mathvariant="bold">n</mi>
                </mrow>
                <mo>⋅<!-- ⋅ --></mo>
                <mrow class="MJX-TeXAtom-ORD">
                  <mi mathvariant="bold">m</mi>
                </mrow>
                <mo stretchy="false">)</mo>
                <mrow>
                  <mo>(</mo>
                  <msup>
                    <mi>α<!-- α --></mi>
                    <mn>2</mn>
                  </msup>
                  <mo>−<!-- − --></mo>
                  <mn>1</mn>
                  <mo>)</mo>
                </mrow>
                <mo>+</mo>
                <mn>1</mn>
                <mo>)</mo>
              </mrow>
              <mn>2</mn>
            </msup>
          </mrow>
        </mfrac>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>Just like above, we start out with the PDF for GGX:</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}
	p(\theta, \phi) = \frac{\alpha^2}{\pi \left( \cos^2 \theta \left(\alpha^2 - 1 \right) +1 \right)^2} \cos\theta \sin\theta 
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-12">
        <mtext>(12)</mtext>
      </mtd>
      <mtd>
        <mi>p</mi>
        <mo stretchy="false">(</mo>
        <mi>θ<!-- θ --></mi>
        <mo>,</mo>
        <mi>ϕ<!-- ϕ --></mi>
        <mo stretchy="false">)</mo>
        <mo>=</mo>
        <mfrac>
          <msup>
            <mi>α<!-- α --></mi>
            <mn>2</mn>
          </msup>
          <mrow>
            <mi>π<!-- π --></mi>
            <msup>
              <mrow>
                <mo>(</mo>
                <msup>
                  <mi>cos</mi>
                  <mn>2</mn>
                </msup>
                <mo>⁡<!-- ⁡ --></mo>
                <mi>θ<!-- θ --></mi>
                <mrow>
                  <mo>(</mo>
                  <msup>
                    <mi>α<!-- α --></mi>
                    <mn>2</mn>
                  </msup>
                  <mo>−<!-- − --></mo>
                  <mn>1</mn>
                  <mo>)</mo>
                </mrow>
                <mo>+</mo>
                <mn>1</mn>
                <mo>)</mo>
              </mrow>
              <mn>2</mn>
            </msup>
          </mrow>
        </mfrac>
        <mi>cos</mi>
        <mo>⁡<!-- ⁡ --></mo>
        <mi>θ<!-- θ --></mi>
        <mi>sin</mi>
        <mo>⁡<!-- ⁡ --></mo>
        <mi>θ<!-- θ --></mi>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>As in the case of Phong, we create two functions for <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\theta">
  <mi>θ<!-- θ --></mi>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\phi ">
  <mi>ϕ<!-- ϕ --></mi>
</math></span>. First <a href="https://www.wolframalpha.com/input/?i=integrate_0%5E%282pi%29+%28a%5E2*cos%28t%29*sin%28t%29%29%2F%28pi*%28%28a%5E2%E2%88%921%29*cos%28t%29%5E2%2B1%29%5E2%29+dp">let's create <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="p(\theta) ">
  <mi>p</mi>
  <mo stretchy="false">(</mo>
  <mi>θ<!-- θ --></mi>
  <mo stretchy="false">)</mo>
</math></span></a>:</p>

<blockquote>
  <p>integrate((a^2 * cos(t) * sin(t))/(%pi * ((a^2−1) * cos(t)^2+1)^2), p, 0, 2 * %pi)</p>
</blockquote>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{equation}
	p(\theta) = \int^{2 \pi}_0 p(\theta, \phi) d\phi = \frac{2 \alpha^2}{\left( \cos^2 \theta \left( \alpha^2 -1 \right) + 1 \right)^2} \cos \theta \sin \theta
\end{equation}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-13">
        <mtext>(13)</mtext>
      </mtd>
      <mtd>
        <mi>p</mi>
        <mo stretchy="false">(</mo>
        <mi>θ<!-- θ --></mi>
        <mo stretchy="false">)</mo>
        <mo>=</mo>
        <msubsup>
          <mo>∫<!-- ∫ --></mo>
          <mn>0</mn>
          <mrow class="MJX-TeXAtom-ORD">
            <mn>2</mn>
            <mi>π<!-- π --></mi>
          </mrow>
        </msubsup>
        <mi>p</mi>
        <mo stretchy="false">(</mo>
        <mi>θ<!-- θ --></mi>
        <mo>,</mo>
        <mi>ϕ<!-- ϕ --></mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>ϕ<!-- ϕ --></mi>
        <mo>=</mo>
        <mfrac>
          <mrow>
            <mn>2</mn>
            <msup>
              <mi>α<!-- α --></mi>
              <mn>2</mn>
            </msup>
          </mrow>
          <msup>
            <mrow>
              <mo>(</mo>
              <msup>
                <mi>cos</mi>
                <mn>2</mn>
              </msup>
              <mo>⁡<!-- ⁡ --></mo>
              <mi>θ<!-- θ --></mi>
              <mrow>
                <mo>(</mo>
                <msup>
                  <mi>α<!-- α --></mi>
                  <mn>2</mn>
                </msup>
                <mo>−<!-- − --></mo>
                <mn>1</mn>
                <mo>)</mo>
              </mrow>
              <mo>+</mo>
              <mn>1</mn>
              <mo>)</mo>
            </mrow>
            <mn>2</mn>
          </msup>
        </mfrac>
        <mi>cos</mi>
        <mo>⁡<!-- ⁡ --></mo>
        <mi>θ<!-- θ --></mi>
        <mi>sin</mi>
        <mo>⁡<!-- ⁡ --></mo>
        <mi>θ<!-- θ --></mi>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>The integration for <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\phi">
  <mi>ϕ<!-- ϕ --></mi>
</math></span> is the same as above, so we skip it and instead now <a href="https://www.wolframalpha.com/input/?i=integrate_0%5Es+%28a%5E2*cos%28t%29*sin%28t%29%29%2F%28pi*%28%28a%5E2%E2%88%921%29*cos%28t%29%5E2%2B1%29%5E2%29+dt">create the CDF for <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="p(\theta) ">
  <mi>p</mi>
  <mo stretchy="false">(</mo>
  <mi>θ<!-- θ --></mi>
  <mo stretchy="false">)</mo>
</math></span></a>:</p>

<blockquote>
  <p>integrate((2 * a^2 * cos(t) * sin(t))/((a^2−1) * cos(t)^2+1)^2, t, 0, s)</p>
</blockquote>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{eqnarray}
	P(s_\theta) &amp; = &amp; \int_0^{s_\theta} p(\theta) d\theta \\
				&amp; = &amp; 2 \alpha^2 \left( \frac{1}{ \left( 2 \alpha^4 - 4 \alpha^2 + 2 \right) \cos^2 s_\theta + 2 \alpha^2 - 2} - \frac{1}{2 \alpha^4 - 2 \alpha^2 }  \right) \\
\end{eqnarray}">
  <mtable columnalign="left right center left" displaystyle="true">
    <mtr>
      <mtd id="mjx-eqn-14">
        <mtext>(14)</mtext>
      </mtd>
      <mtd>
        <mi>P</mi>
        <mo stretchy="false">(</mo>
        <msub>
          <mi>s</mi>
          <mi>θ<!-- θ --></mi>
        </msub>
        <mo stretchy="false">)</mo>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <msubsup>
          <mo>∫<!-- ∫ --></mo>
          <mn>0</mn>
          <mrow class="MJX-TeXAtom-ORD">
            <msub>
              <mi>s</mi>
              <mi>θ<!-- θ --></mi>
            </msub>
          </mrow>
        </msubsup>
        <mi>p</mi>
        <mo stretchy="false">(</mo>
        <mi>θ<!-- θ --></mi>
        <mo stretchy="false">)</mo>
        <mi>d</mi>
        <mi>θ<!-- θ --></mi>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-15">
        <mtext>(15)</mtext>
      </mtd>
      <mtd></mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <mn>2</mn>
        <msup>
          <mi>α<!-- α --></mi>
          <mn>2</mn>
        </msup>
        <mrow>
          <mo>(</mo>
          <mfrac>
            <mn>1</mn>
            <mrow>
              <mrow>
                <mo>(</mo>
                <mn>2</mn>
                <msup>
                  <mi>α<!-- α --></mi>
                  <mn>4</mn>
                </msup>
                <mo>−<!-- − --></mo>
                <mn>4</mn>
                <msup>
                  <mi>α<!-- α --></mi>
                  <mn>2</mn>
                </msup>
                <mo>+</mo>
                <mn>2</mn>
                <mo>)</mo>
              </mrow>
              <msup>
                <mi>cos</mi>
                <mn>2</mn>
              </msup>
              <mo>⁡<!-- ⁡ --></mo>
              <msub>
                <mi>s</mi>
                <mi>θ<!-- θ --></mi>
              </msub>
              <mo>+</mo>
              <mn>2</mn>
              <msup>
                <mi>α<!-- α --></mi>
                <mn>2</mn>
              </msup>
              <mo>−<!-- − --></mo>
              <mn>2</mn>
            </mrow>
          </mfrac>
          <mo>−<!-- − --></mo>
          <mfrac>
            <mn>1</mn>
            <mrow>
              <mn>2</mn>
              <msup>
                <mi>α<!-- α --></mi>
                <mn>4</mn>
              </msup>
              <mo>−<!-- − --></mo>
              <mn>2</mn>
              <msup>
                <mi>α<!-- α --></mi>
                <mn>2</mn>
              </msup>
            </mrow>
          </mfrac>
          <mo>)</mo>
        </mrow>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>Setting the CDF to a random variable and solving for <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="s">
  <mi>s</mi>
</math></span> yields:</p>

<blockquote>
  <p>solve(2 * a^2 * (1/((2 * a^4−4 * a^2+2) * cos(s)^2+2 * a^2−2)−1/(2 * a^4−2 * a^2)) = x, s)</p>
</blockquote>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\begin{eqnarray}
	P(s_\theta) &amp; = &amp; \xi_\theta \\
	s_\theta &amp; = &amp; cos^{-1} \left( \sqrt{ \frac{1 - \xi_\theta}{\left( \alpha^2 -1 \right) \xi_\theta + 1}} \right) \\
\end{eqnarray}">
  <mtable columnalign="left right center left" displaystyle="true">
    <mtr>
      <mtd id="mjx-eqn-16">
        <mtext>(16)</mtext>
      </mtd>
      <mtd>
        <mi>P</mi>
        <mo stretchy="false">(</mo>
        <msub>
          <mi>s</mi>
          <mi>θ<!-- θ --></mi>
        </msub>
        <mo stretchy="false">)</mo>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <msub>
          <mi>ξ<!-- ξ --></mi>
          <mi>θ<!-- θ --></mi>
        </msub>
      </mtd>
    </mtr>
    <mtr>
      <mtd id="mjx-eqn-17">
        <mtext>(17)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mi>s</mi>
          <mi>θ<!-- θ --></mi>
        </msub>
      </mtd>
      <mtd>
        <mi></mi>
        <mo>=</mo>
      </mtd>
      <mtd>
        <mi>c</mi>
        <mi>o</mi>
        <msup>
          <mi>s</mi>
          <mrow class="MJX-TeXAtom-ORD">
            <mo>−<!-- − --></mo>
            <mn>1</mn>
          </mrow>
        </msup>
        <mrow>
          <mo>(</mo>
          <msqrt>
            <mfrac>
              <mrow>
                <mn>1</mn>
                <mo>−<!-- − --></mo>
                <msub>
                  <mi>ξ<!-- ξ --></mi>
                  <mi>θ<!-- θ --></mi>
                </msub>
              </mrow>
              <mrow>
                <mrow>
                  <mo>(</mo>
                  <msup>
                    <mi>α<!-- α --></mi>
                    <mn>2</mn>
                  </msup>
                  <mo>−<!-- − --></mo>
                  <mn>1</mn>
                  <mo>)</mo>
                </mrow>
                <msub>
                  <mi>ξ<!-- ξ --></mi>
                  <mi>θ<!-- θ --></mi>
                </msub>
                <mo>+</mo>
                <mn>1</mn>
              </mrow>
            </mfrac>
          </msqrt>
          <mo>)</mo>
        </mrow>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>A simple GLSL function to generate important directions looks like this:</p>

<div class="language-glsl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">vec2</span> <span class="nf">importance_sample_ggx</span><span class="p">(</span><span class="kt">vec2</span> <span class="n">xi</span><span class="p">)</span>
<span class="p">{</span>
  <span class="kt">float</span> <span class="n">phi</span> <span class="o">=</span> <span class="mi">2</span><span class="p">.</span><span class="mi">0</span><span class="n">f</span> <span class="o">*</span> <span class="n">PI</span> <span class="o">*</span> <span class="n">xi</span><span class="p">.</span><span class="n">x</span><span class="p">;</span>
  <span class="kt">float</span> <span class="n">theta</span> <span class="o">=</span> <span class="n">acos</span><span class="p">(</span><span class="n">sqrt</span><span class="p">((</span><span class="mi">1</span><span class="p">.</span><span class="mi">0</span><span class="n">f</span> <span class="o">-</span> <span class="n">xi</span><span class="p">.</span><span class="n">y</span><span class="p">)</span><span class="o">/</span>
                          <span class="p">((</span><span class="n">a</span><span class="o">*</span><span class="n">a</span> <span class="o">-</span> <span class="mi">1</span><span class="p">.</span><span class="mi">0</span><span class="n">f</span><span class="p">)</span> <span class="o">*</span> <span class="n">xi</span><span class="p">.</span><span class="n">y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">.</span><span class="mi">0</span><span class="n">f</span><span class="p">)</span>
                         <span class="p">));</span>
  <span class="k">return</span> <span class="kt">vec2</span><span class="p">(</span><span class="n">phi</span><span class="p">,</span> <span class="n">theta</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h1 id="conclusion">Conclusion</h1>

<blockquote>
  <p>The ultimate goal of mathematics is to eliminate any need for intelligent thought.</p>
</blockquote>

<p>Other NDFs can be used to create important samples with the exact same procedure: given a hemispherical PDF (i.e. the specular part of the BRDF), create two independent PDFs for <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\theta">
  <mi>θ<!-- θ --></mi>
</math></span> and <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\phi">
  <mi>ϕ<!-- ϕ --></mi>
</math></span>, integrate both from <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="0">
  <mn>0</mn>
</math></span> to <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="s">
  <mi>s</mi>
</math></span> (i.e. create a CDF for each), set the result to <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\xi">
  <mi>ξ<!-- ξ --></mi>
</math></span> and solve for <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext="s">
  <mi>s</mi>
</math></span>.</p>

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

<ol>
  <li>Walter et al., <a href="https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.html">Microfacet Models for Refraction through Rough Surfaces</a>
</li>
  <li>Wikipedia, <a href="https://en.wikipedia.org/wiki/Constructions_of_low-discrepancy_sequences">Constructions of low-discrepancy sequences</a>
</li>
</ol> ]]></description>
            <pubDate>Sun, 30 Mar 2014 00:00:00 +0100</pubDate>
            <link>https://www.tobias-franke.eu/log/2014/03/30/notes_on_importance_sampling.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhwNHp3RUF2YzdhY3hJSFFKeWM0OEdxT2hYNQpjVEJjendnRUpxMFlQOURhbWN1SGFDZ0JBTitQQ0JkSWVFZzVBb2NrTmViaEhBa0ltRS9uay9UN3FBUmNiVXplCmxZTUwKPTZmR1oKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>PBS</category><category>ImportanceSampling</category><category>Notes</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Physically based AR</title>
            <description><![CDATA[ <h1 id="introduction">Introduction</h1>

<p>A particularly brutal aspect of real-time rendering in AR environments is that your object is directly exposed to the physical reality around it for comparison. Unlike your typical game-engine, masking or hiding physical inaccuracies in the model used to simulate real reflection of light on your virtual object is not going to work very well when all the other objects on screen somehow behave differently. If the augmented object doesn't match up with reality, a knee-jerk reaction is to edit the responsible shader and simply tweak the output with a bunch of random multipliers until all looks right again. However, before all the magic-number adjustments get out of hand, it might be time to review the basics and avoid a non-physically-based-shading-post-traumatic-stress-disorder™.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2014_02_dragon.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2014_02_dragon.jpg" alt="Image based lit Stanford dragon: diffuse SH + specular environment map sampling." title="Image based lit Stanford dragon: diffuse SH + specular environment map sampling.">
    
    </a>
    
    
    <figcaption>Image based lit Stanford dragon: diffuse SH + specular environment map sampling.</figcaption>
    
</figure>

<p>Many real-time engine writers have concentrated on getting their equations right. The most recent result is the <em>Siggraph Physically Based Shading Course</em> (<a href="https://blog.selfshadow.com/publications/s2012-shading-course/">2012 Edition</a>, <a href="https://blog.selfshadow.com/publications/s2013-shading-course/">2013 Edition</a>). I will roughly lay out the basic principles of PBS and refer to other very good online blogs which have covered most of the details that do not need to be repeated ad absurdum.</p>

<p>Consider the standard form of the rendering equation:</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="L\left(x, \mathbf{\omega_o}\right) = \int_\Omega f_r \left(x, \mathbf{\omega_i}, \mathbf{\omega_o}\right) L\left(x, \mathbf{\omega_i}\right) \cos \theta_i d\mathbf{\omega_i}">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-1">
        <mtext>(1)</mtext>
      </mtd>
      <mtd>
        <mi>L</mi>
        <mrow>
          <mo>(</mo>
          <mi>x</mi>
          <mo>,</mo>
          <mrow class="MJX-TeXAtom-ORD">
            <msub>
              <mi>ω<!-- ω --></mi>
              <mi mathvariant="bold">o</mi>
            </msub>
          </mrow>
          <mo>)</mo>
        </mrow>
        <mo>=</mo>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi mathvariant="normal">Ω<!-- Ω --></mi>
        </msub>
        <msub>
          <mi>f</mi>
          <mi>r</mi>
        </msub>
        <mrow>
          <mo>(</mo>
          <mi>x</mi>
          <mo>,</mo>
          <mrow class="MJX-TeXAtom-ORD">
            <msub>
              <mi>ω<!-- ω --></mi>
              <mi mathvariant="bold">i</mi>
            </msub>
          </mrow>
          <mo>,</mo>
          <mrow class="MJX-TeXAtom-ORD">
            <msub>
              <mi>ω<!-- ω --></mi>
              <mi mathvariant="bold">o</mi>
            </msub>
          </mrow>
          <mo>)</mo>
        </mrow>
        <mi>L</mi>
        <mrow>
          <mo>(</mo>
          <mi>x</mi>
          <mo>,</mo>
          <mrow class="MJX-TeXAtom-ORD">
            <msub>
              <mi>ω<!-- ω --></mi>
              <mi mathvariant="bold">i</mi>
            </msub>
          </mrow>
          <mo>)</mo>
        </mrow>
        <mi>cos</mi>
        <mo>⁡<!-- ⁡ --></mo>
        <msub>
          <mi>θ<!-- θ --></mi>
          <mi>i</mi>
        </msub>
        <mi>d</mi>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>When shading your object, it is important that light reflection off the surface behaves physically plausible, that is the BRDF <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext=" f_r ">
  <msub>
    <mi>f</mi>
    <mi>r</mi>
  </msub>
</math></span> has to respect certain conditions:</p>

<ul>
  <li>
<strong>Positivity</strong>: the value of the BRDF is always positive.</li>
</ul>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="f_r \left(x, \mathbf{\omega_i}, \mathbf{\omega_o} \right) \geq 0">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-2">
        <mtext>(2)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mi>f</mi>
          <mi>r</mi>
        </msub>
        <mrow>
          <mo>(</mo>
          <mi>x</mi>
          <mo>,</mo>
          <mrow class="MJX-TeXAtom-ORD">
            <msub>
              <mi>ω<!-- ω --></mi>
              <mi mathvariant="bold">i</mi>
            </msub>
          </mrow>
          <mo>,</mo>
          <mrow class="MJX-TeXAtom-ORD">
            <msub>
              <mi>ω<!-- ω --></mi>
              <mi mathvariant="bold">o</mi>
            </msub>
          </mrow>
          <mo>)</mo>
        </mrow>
        <mo>≥<!-- ≥ --></mo>
        <mn>0</mn>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<ul>
  <li>
<strong>Energy conservation</strong>: the total amount of energy reflected over all directions of the surface must be less or equal to the total amount of energy incident to it. In practical terms this means that the visible energy (i.e. reflected light) can at best decrease after bouncing off a surface, while the rest turns to heat or some other form which isn't part of the simulation. A non-emissive surface however cannot emit more light than it received.</li>
</ul>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="M = \int_\Omega L \left(x, \mathbf{\omega_o}\right) \cos \theta_o d\mathbf{\omega_o} \le \int_\Omega L \left(x, \mathbf{\omega_i}\right) \cos \theta_i d\mathbf{\omega_i} = E">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-3">
        <mtext>(3)</mtext>
      </mtd>
      <mtd>
        <mi>M</mi>
        <mo>=</mo>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi mathvariant="normal">Ω<!-- Ω --></mi>
        </msub>
        <mi>L</mi>
        <mrow>
          <mo>(</mo>
          <mi>x</mi>
          <mo>,</mo>
          <mrow class="MJX-TeXAtom-ORD">
            <msub>
              <mi>ω<!-- ω --></mi>
              <mi mathvariant="bold">o</mi>
            </msub>
          </mrow>
          <mo>)</mo>
        </mrow>
        <mi>cos</mi>
        <mo>⁡<!-- ⁡ --></mo>
        <msub>
          <mi>θ<!-- θ --></mi>
          <mi>o</mi>
        </msub>
        <mi>d</mi>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">o</mi>
          </msub>
        </mrow>
        <mo>≤<!-- ≤ --></mo>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi mathvariant="normal">Ω<!-- Ω --></mi>
        </msub>
        <mi>L</mi>
        <mrow>
          <mo>(</mo>
          <mi>x</mi>
          <mo>,</mo>
          <mrow class="MJX-TeXAtom-ORD">
            <msub>
              <mi>ω<!-- ω --></mi>
              <mi mathvariant="bold">i</mi>
            </msub>
          </mrow>
          <mo>)</mo>
        </mrow>
        <mi>cos</mi>
        <mo>⁡<!-- ⁡ --></mo>
        <msub>
          <mi>θ<!-- θ --></mi>
          <mi>i</mi>
        </msub>
        <mi>d</mi>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
        <mo>=</mo>
        <mi>E</mi>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\forall \mathbf{\omega_o}, \int_\Omega f_r(x, \mathbf{\omega_o}, \mathbf{\omega_i}) cos \theta_i d\mathbf{\omega_i} \leq 1">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-4">
        <mtext>(4)</mtext>
      </mtd>
      <mtd>
        <mi mathvariant="normal">∀<!-- ∀ --></mi>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">o</mi>
          </msub>
        </mrow>
        <mo>,</mo>
        <msub>
          <mo>∫<!-- ∫ --></mo>
          <mi mathvariant="normal">Ω<!-- Ω --></mi>
        </msub>
        <msub>
          <mi>f</mi>
          <mi>r</mi>
        </msub>
        <mo stretchy="false">(</mo>
        <mi>x</mi>
        <mo>,</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">o</mi>
          </msub>
        </mrow>
        <mo>,</mo>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
        <mo stretchy="false">)</mo>
        <mi>c</mi>
        <mi>o</mi>
        <mi>s</mi>
        <msub>
          <mi>θ<!-- θ --></mi>
          <mi>i</mi>
        </msub>
        <mi>d</mi>
        <mrow class="MJX-TeXAtom-ORD">
          <msub>
            <mi>ω<!-- ω --></mi>
            <mi mathvariant="bold">i</mi>
          </msub>
        </mrow>
        <mo>≤<!-- ≤ --></mo>
        <mn>1</mn>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<ul>
  <li>
<strong>Helmholtz reciprocity</strong>: the standard assumption in geometric optics is that exchanging in- and outgoing light direction <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext=" \mathbf{\omega_o} ">
  <mrow class="MJX-TeXAtom-ORD">
    <msub>
      <mi>ω<!-- ω --></mi>
      <mi mathvariant="bold">o</mi>
    </msub>
  </mrow>
</math></span> in the BRDF doesn't change the outcome.</li>
</ul>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="f_r \left(x, \mathbf{\omega_i}, \mathbf{\omega_o} \right) = f_r \left(x, \mathbf{\omega_o}, \mathbf{\omega_i} \right)">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-5">
        <mtext>(5)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mi>f</mi>
          <mi>r</mi>
        </msub>
        <mrow>
          <mo>(</mo>
          <mi>x</mi>
          <mo>,</mo>
          <mrow class="MJX-TeXAtom-ORD">
            <msub>
              <mi>ω<!-- ω --></mi>
              <mi mathvariant="bold">i</mi>
            </msub>
          </mrow>
          <mo>,</mo>
          <mrow class="MJX-TeXAtom-ORD">
            <msub>
              <mi>ω<!-- ω --></mi>
              <mi mathvariant="bold">o</mi>
            </msub>
          </mrow>
          <mo>)</mo>
        </mrow>
        <mo>=</mo>
        <msub>
          <mi>f</mi>
          <mi>r</mi>
        </msub>
        <mrow>
          <mo>(</mo>
          <mi>x</mi>
          <mo>,</mo>
          <mrow class="MJX-TeXAtom-ORD">
            <msub>
              <mi>ω<!-- ω --></mi>
              <mi mathvariant="bold">o</mi>
            </msub>
          </mrow>
          <mo>,</mo>
          <mrow class="MJX-TeXAtom-ORD">
            <msub>
              <mi>ω<!-- ω --></mi>
              <mi mathvariant="bold">i</mi>
            </msub>
          </mrow>
          <mo>)</mo>
        </mrow>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<ul>
  <li>
<strong>Superposition</strong>: the BRDF is a linear function. Contribution of different light sources may be added up independently. There is a debate about whether this is a property of the BRDF or light.</li>
</ul>

<p>It is usually the energy conservation in the specular term where things go wrong first. Without normalization Blinn-Phong for instance, a popular and easy way to model specular reflection, can easily be too bright with small specular powers and quickly loose too much energy when increasing the term.</p>

<p>But there is more! Many renderers assume that there are materials which are perfectly diffuse, i.e. they scatter incident light in all directions equally. There is no such thing. John Hable <a href="https://filmicgames.com/archives/547">demonstrated this by showing materials</a> which would be considered to be diffuse reflectors. You can read more in his article <a href="https://filmicgames.com/archives/557">Everything has Fresnel</a>.</p>

<p>So here we are, with a BRDF that can output too much energy, darkens too quickly and can't simulate the shininess of real world objects because the model is built with unrealistic parameters. How do we proceed?</p>

<p>One solution is to <a href="https://www.thetenthplanet.de/archives/255">find a normalization factor for Blinn-Phong</a> to fix the energy issues with the model and add a Fresnel term. There are also several other reflection models to choose from: Oren-Nayar, Cook-Torrance, Ashikhmin-Shirley, Ward…</p>

<h1 id="microfacet-models">Microfacet Models</h1>

<p>Physically based BRDF models are built on the theory of microfacets, which states that the surface of an object is composed of many tiny flat mirrors, each with its own orientation. The idea of a microfacet model is to capture the appearance of a macro-surface not by integration over its micro-factets, but by statistical means.</p>

<p>A microfacet model looks like this (with the notation for direct visibility):</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="f_\mu \left( \mathbf{l}, \mathbf{v} \right) = \frac{k_d}{\pi} + \frac{ F \left( \mathbf{l}, \mathbf{h} \right) G \left( \mathbf{l}, \mathbf{v}, \mathbf{h} \right) D \left( \mathbf{h} \right) } { 4 \left( \mathbf{n} \cdot \mathbf{l} \right) \left( \mathbf{n} \cdot \mathbf{v} \right) }">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-6">
        <mtext>(6)</mtext>
      </mtd>
      <mtd>
        <msub>
          <mi>f</mi>
          <mi>μ<!-- μ --></mi>
        </msub>
        <mrow>
          <mo>(</mo>
          <mrow class="MJX-TeXAtom-ORD">
            <mi mathvariant="bold">l</mi>
          </mrow>
          <mo>,</mo>
          <mrow class="MJX-TeXAtom-ORD">
            <mi mathvariant="bold">v</mi>
          </mrow>
          <mo>)</mo>
        </mrow>
        <mo>=</mo>
        <mfrac>
          <msub>
            <mi>k</mi>
            <mi>d</mi>
          </msub>
          <mi>π<!-- π --></mi>
        </mfrac>
        <mo>+</mo>
        <mfrac>
          <mrow>
            <mi>F</mi>
            <mrow>
              <mo>(</mo>
              <mrow class="MJX-TeXAtom-ORD">
                <mi mathvariant="bold">l</mi>
              </mrow>
              <mo>,</mo>
              <mrow class="MJX-TeXAtom-ORD">
                <mi mathvariant="bold">h</mi>
              </mrow>
              <mo>)</mo>
            </mrow>
            <mi>G</mi>
            <mrow>
              <mo>(</mo>
              <mrow class="MJX-TeXAtom-ORD">
                <mi mathvariant="bold">l</mi>
              </mrow>
              <mo>,</mo>
              <mrow class="MJX-TeXAtom-ORD">
                <mi mathvariant="bold">v</mi>
              </mrow>
              <mo>,</mo>
              <mrow class="MJX-TeXAtom-ORD">
                <mi mathvariant="bold">h</mi>
              </mrow>
              <mo>)</mo>
            </mrow>
            <mi>D</mi>
            <mrow>
              <mo>(</mo>
              <mrow class="MJX-TeXAtom-ORD">
                <mi mathvariant="bold">h</mi>
              </mrow>
              <mo>)</mo>
            </mrow>
          </mrow>
          <mrow>
            <mn>4</mn>
            <mrow>
              <mo>(</mo>
              <mrow class="MJX-TeXAtom-ORD">
                <mi mathvariant="bold">n</mi>
              </mrow>
              <mo>⋅<!-- ⋅ --></mo>
              <mrow class="MJX-TeXAtom-ORD">
                <mi mathvariant="bold">l</mi>
              </mrow>
              <mo>)</mo>
            </mrow>
            <mrow>
              <mo>(</mo>
              <mrow class="MJX-TeXAtom-ORD">
                <mi mathvariant="bold">n</mi>
              </mrow>
              <mo>⋅<!-- ⋅ --></mo>
              <mrow class="MJX-TeXAtom-ORD">
                <mi mathvariant="bold">v</mi>
              </mrow>
              <mo>)</mo>
            </mrow>
          </mrow>
        </mfrac>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>where</p>

<span class="mjpage mjpage__block"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block" alttext="\mathbf{h} = \left| \mathbf{l} + \mathbf{v} \right|">
  <mtable displaystyle="true" columnalign="left center">
    <mtr>
      <mtd id="mjx-eqn-7">
        <mtext>(7)</mtext>
      </mtd>
      <mtd>
        <mrow class="MJX-TeXAtom-ORD">
          <mi mathvariant="bold">h</mi>
        </mrow>
        <mo>=</mo>
        <mrow>
          <mo>|</mo>
          <mrow class="MJX-TeXAtom-ORD">
            <mi mathvariant="bold">l</mi>
          </mrow>
          <mo>+</mo>
          <mrow class="MJX-TeXAtom-ORD">
            <mi mathvariant="bold">v</mi>
          </mrow>
          <mo>|</mo>
        </mrow>
      </mtd>
    </mtr>
  </mtable>
</math></span>

<p>This specular part of the BRDF has three important components:</p>

<ul>
  <li>the <em>Fresnel factor</em> <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext=" F ">
  <mi>F</mi>
</math></span>
</li>
  <li>a <em>geometric term</em> <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext=" G ">
  <mi>G</mi>
</math></span>
</li>
  <li>a <em>Normal Distribution Function</em> (NDF) <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext=" D ">
  <mi>D</mi>
</math></span>
</li>
</ul>

<p>The Fresnel term <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext=" F ">
  <mi>F</mi>
</math></span> simulates the Fresnel behavior and can be implemented with the <a href="https://en.wikipedia.org/wiki/Schlick's_approximation">Schlick approximation</a>.</p>

<p>The geometric term <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext=" G ">
  <mi>G</mi>
</math></span> models the self-occlusion behavior of the microfacets on the surface and can be thought of as a visibility factor for a micro-landscape which simply depends on one parameter for the surface roughness.</p>

<p>The NDF <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext=" D ">
  <mi>D</mi>
</math></span> is the term that gives you the distribution of microfacet normals across the surface from a certain point of view. If more microfacets are oriented in the half-vector direction <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext=" \mathbf{h} ">
  <mrow class="MJX-TeXAtom-ORD">
    <mi mathvariant="bold">h</mi>
  </mrow>
</math></span>, the specular highlight will be brighter. <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext=" D ">
  <mi>D</mi>
</math></span> is  the <em>density</em> of normals oriented in direction <span class="mjpage"><math xmlns="http://www.w3.org/1998/Math/MathML" alttext=" \mathbf{h} ">
  <mrow class="MJX-TeXAtom-ORD">
    <mi mathvariant="bold">h</mi>
  </mrow>
</math></span>.</p>

<h1 id="conclusion">Conclusion</h1>

<p>To sum up, the idea here is to start out with the correct shading model to avoid inconsistencies that might turn up later (I'm speaking out of negative experience tweaking arbitrary parameters of my old AR renderer). Turning to such a model might not produce visible differences immediately, but will later be noticeable once GI is added to the system where the light bounces multiply any error one made right at the start.</p>

<p>A neat way to check how you are on the reality-scale is <a href="https://www.disneyanimation.com/technology/brdf.html">Disney's BRDF Explorer</a>, which comes with GLSL implementations of several microfacet terms and other BRDFs (have a look at the brdf/ subdirectory and open one of the .brdf files).</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2014_02_brdfexp.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2014_02_brdfexp.jpg" alt="Disney's BRDF explorer" title="Disney's BRDF explorer">
    
    </a>
    
    
    <figcaption>Disney's BRDF explorer</figcaption>
    
</figure>

<p>You can download measured materials <a href="https://www.merl.com/brdf/">from the MERL database</a>, load them in the BRDF Explorer and compare them to see how well analytical BRDFs match up to reality.</p>

<h1 id="more-references">More References</h1>

<ol>
  <li>Hill et al., <a href="https://blog.selfshadow.com/publications/s2012-shading-course/">Siggraph PBS 2012</a>
</li>
  <li>Hill et al., <a href="https://blog.selfshadow.com/publications/s2013-shading-course/">Siggraph PBS 2013</a>
</li>
  <li>Brian Karis, <a href="https://graphicrants.blogspot.de/2013/08/specular-brdf-reference.html">Specular BRDF Reference</a>
</li>
  <li>John Hable, <a href="https://filmicgames.com/archives/557">Everything has Fresnel</a>
</li>
  <li>Rory Driscoll, <a href="https://www.rorydriscoll.com/2009/01/25/energy-conservation-in-games/">Energy Conservation In Games</a>
</li>
  <li>Rory Driscoll, <a href="https://www.rorydriscoll.com/2013/11/22/physically-based-shading/">Physically-Based Shading</a>
</li>
  <li>Simon Yeung, <a href="https://simonstechblog.blogspot.de/2011/12/microfacet-brdf.html">Microfacet BRDF</a>
</li>
  <li>Sébastien Lagarde, <a href="https://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/">PI or not to PI in game lighting equation</a>
</li>
  <li>Wikipedia, <a href="https://en.wikipedia.org/wiki/Schlick's_approximation">Schlick's approximation</a>
</li>
  <li>Christian Schüler, <a href="https://www.thetenthplanet.de/archives/255">The Blinn-​Phong Normalization Zoo</a>
</li>
  <li>D3D Book, <a href="https://content.gpwiki.org/index.php/D3DBook:(Lighting)_Cook-Torrance">Cook-Torrance</a>
</li>
  <li>Disney, <a href="https://www.disneyanimation.com/technology/brdf.html">BRDF Explorer</a>
</li>
  <li>MERL, <a href="https://www.merl.com/brdf/">MERL BRDF Database</a>
</li>
</ol> ]]></description>
            <pubDate>Sun, 02 Feb 2014 00:00:00 +0100</pubDate>
            <link>https://www.tobias-franke.eu/log/2014/02/02/physically-based-ar.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhyT2dRRUFoVFZFUXNLdHJ5eUhrOGlpTWRSRQpNdlY4YkZDVWlFREVubHZ6M3hFdnRmY0EvMy9RVnNYdk4wa2h1a3p3OVRzSitOd0ZJMk1qUHNBSm9yVmZTSnFkCjJsWUsKPU1OR1EKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Introduction</category><category>PBS</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>A magic lantern</title>
            <description><![CDATA[ <h1 id="prologue">Prologue</h1>

<p>When adding a virtual object into a real scene, the renderer has to shade the object depending on the incident light it receives. Furthermore, the renderer might also take care of changes on reality which were caused by the object, such as a drop-shadow from it. But before we proceed, let's not reinvent the wheel here: great inspiration can be drawn from the knowledge gathered in the film industry on how to augment reality properly, and by taking a step back and learn from history.</p>

<h1 id="introduction-to-the-macabre">Introduction to the macabre</h1>

<p>The earliest form of an augmented reality can be found shortly after the introduction of the <a href="https://en.wikipedia.org/wiki/Magic_lantern">Lanterna Magica</a> in the mid 16th century.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2014_01_ml.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2014_01_ml.jpg" alt="" title="">
    
    </a>
    
    
</figure>

<p>Giovanni Fontana, Christian Huygens and/or Athanasius Kircher invented the first apparatus to project images onto a flat surface with a type of lantern that focuses light through a painted glass image. The glass can be moved through a slit on the side of the projection apparatus, much like a more modern slide projector.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2014_01_woo.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2014_01_woo.jpg" alt="From F. Marion, The Wonders Of Optics, 1869" title="From F. Marion, The Wonders Of Optics, 1869">
    
    </a>
    
    
    <figcaption>From F. Marion, The Wonders Of Optics, 1869</figcaption>
    
</figure>

<p>It wasn't long until the device became known as the "lantern of fear" when it was used to conjure up the occult. Apparitions of the dead projected onto smoke via rapidly exchanging images convinced an entire generation that some stage performers had actually attained a special connection to the afterlife. Combined with the bizarre fascination for spiritualism at that time, these shows caused such commotion that eventually the authorities stepped in to end it all.</p>

<p>What remained however was a new kind of artist: a cross-breed between magician and scientist.</p>

<h1 id="moving-images">Moving images</h1>

<p>The French illusionist and filmmaker Georges Méliès accidentally discovered what became known as the <em>stop-trick</em>, whereby a movie shot is halted and the filmed scene is substituted with something different. A great example is his short movie <em>The Hilarious Poster</em>, filmed in 1906. A  regular poster on a street takes on a life of its own with horseplay directed at passengers which happen to walk by.</p>

<figure>
    
    
    <div class="video default-size">
        <a class="fa" href="https://www.youtube-nocookie.com/embed/dPPP2MrlYr8">
            <img class="default-size" src="https://www.tobias-franke.eu/layout/thumbcache/dPPP2MrlYr8.jpg" alt="The Hilarious Poster by Georges Méliès" title="The Hilarious Poster by Georges Méliès">
        </a>
    </div>
    
    
    
    <figcaption>The Hilarious Poster by Georges Méliès</figcaption>
    
</figure>

<p>In the modern film industry, early effects such as the light saber glow seen in the first Star Wars movies where inserted via <a href="https://en.wikipedia.org/wiki/Rotoscoping">rotoscoping</a>, a technique where the movie is basically edited and enhanced frame-by-frame with a new painted overlay. Initially, the laser sword was a rod with scotchlite attached to it (the material used for traffic sign reflectors), but it turned out that it didn't reflect enough light for the movie, so the effect was added later.</p>

<p>Rotoscoping however is tedious, and surely there are better, more automated ways to add effects to a movie after it has been shot. A curious case is William's and Chou's "Interface", shown at SIGGRAPH 1985 featuring a shiny robot kissed by a woman. The textured-mapped robot however was rendered and added afterwards, while the reflection of this romantic scene on its polished surfaces was filmed using a mirroring ball. Lance Williams <a href="https://dl.acm.org/citation.cfm?id=801126">published a paper two years earlier</a> which introduced mip-mapping to the world of computer graphics. In an almost Fermat-like manner, the paper contains a paragraph which reads:</p>

<blockquote>
  <p>If we represent the illumination of a scene as a two-dimensional map, highlights can be effectively antialiased in much the same way as textures. Blinn and Newell [I] demonstrated specular reflection using  an illumination map. The map was an image of the environment (a spherical projection of the scene, indexed by the X and Y components of the surface normals) which could be used to cast reflections onto specular surfaces. The impression of mirrored facets and chrome objects which can be achieved with this method is striking; Figure (16) provides an illustration. Reflectance mapping is not, however, accurate for local reflections. To achieve similar results with three dimensional accuracy requires ray-tracing.</p>
</blockquote>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2014_01_lw.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2014_01_lw.jpg" alt="Figure 16, courtesy of Lance Williams" title="Figure 16, courtesy of Lance Williams">
    
    </a>
    
    
    <figcaption>Figure 16, courtesy of Lance Williams</figcaption>
    
</figure>

<p>Using Jimm Blinn's modified <a href="https://en.wikipedia.org/wiki/Reflection_mapping">environment mapping technique</a> with real images instead of computer generated ones did the trick. <a href="https://www.pauldebevec.com/ReflectionMapping/illumap.pdf">Course notes from Miller and Hoffman at SIGGRAPH 1984</a> additionally added the idea of pre-convolving these images to simulate reflection off other glossy or diffuse materials. Eventually, the concept art became a reality when the technique was used in <em>The Flight of the Navigator</em> in 1986, followed by <em>The Abyss</em> in 1989 and <em>Terminator 2</em> in 1991.</p>

<figure>
    
    
    <div class="video default-size">
        <a class="fa" href="https://www.youtube-nocookie.com/embed/8LlcqO3mq9Q">
            <img class="default-size" src="https://www.tobias-franke.eu/layout/thumbcache/8LlcqO3mq9Q.jpg" alt="Trailer: The Flight of the Navigator" title="Trailer: The Flight of the Navigator">
        </a>
    </div>
    
    
    
    <figcaption>Trailer: The Flight of the Navigator</figcaption>
    
</figure>

<p>What makes this technique so elegant is that it circumvents the need to track light sources in your scene. One could also extract single point light sources from a hemispherical image, but small reflected highlights from shiny objects can easily distract the algorithm and cause flickering when using low sampling rates. A reflection map instead represents the entire lighting configuration and is therefore physically more plausible than a bunch of point lights.</p>

<p>There is however no inherent necessity to augment reality with objects that have to look physically plausible. First and foremost, it is important that whatever has been added to the scene is at its proper place at all times (i.e. <em>geometrically registered</em>). One grandiose example is Jessica Rabbit's <em>Why don't you do it right</em>:</p>

<figure>
    
    
    <div class="video default-size">
        <a class="fa" href="https://www.youtube-nocookie.com/embed/yy5THitqPBw">
            <img class="default-size" src="https://www.tobias-franke.eu/layout/thumbcache/yy5THitqPBw.jpg" alt="A scene from: Who Framed Roger Rabbit" title="A scene from: Who Framed Roger Rabbit">
        </a>
    </div>
    
    
    
    <figcaption>A scene from: Who Framed Roger Rabbit</figcaption>
    
</figure>

<p>As you can see, Jessica sometimes vanishes behind other objects or people as if she were actually at the correct position in the 3D scene. Of course being a comic character, Jessica's painters took care of this, but one can use invisible impostors in a renderer to create a virtualized depth buffer of the real scene if none exists.</p>

<p>Note that the clip features another important property, which is scene consistency: Jessica's reflection on the real catwalk can be seen 14 seconds into the clip. The editors know about the reflective material and can draw the reflection that appears with the viewing angle of the camera. The shadow of her hand is also visible on some dude's face at exactly minute one. Jessica's presence in the scene changes reality (and not just by making the audience go nuts)!</p>

<p>Of course, ever since stop-motion was declared extinct by Phil Tippet while producing the animations for Jurassic Park, having computer-generated effects in movies has become the norm. But irrespective of the technique used to augment something into a real scene, capturing reality correctly and having consistent light interaction is key to create the proper illusion of a mixed reality. Real and virtual light sources, direct and indirect, influence the virtual and real space. The importance of this is nowhere more clear and apparent than when watching a bad, low-budget sci-fi movie.</p>

<p>Because your typical SFX director may go overboard with the special effects in a movie scene, it might happen that the only real thing left is the actor himself. Humans are sensitive to facial features, and it is therefore necessary to edit the appearance of skin reflections to match the scene. You don't want to end up with a green aura from the studio set engulfing the actor. SIGGRAPH 2000 brought a revolution to the film industry with the introduction of <a href="https://gl.ict.usc.edu/LightStages/">the Light Stage</a>. In it, the actors face is captured multiple times under varying illumination. After enough samples have been gathered, for each pixel on the actors face one can effectively invert the rendering equation and extract the BSDF of the skin and reproduce it under <em>different</em> illumination.</p>

<figure>
    
    
    <div class="video default-size">
        <a class="fa" href="https://www.youtube-nocookie.com/embed/piJ4Zke7EUw">
            <img class="default-size" src="https://www.tobias-franke.eu/layout/thumbcache/piJ4Zke7EUw.jpg" alt="The Digital Emily Project" title="The Digital Emily Project">
        </a>
    </div>
    
    
    
    <figcaption>The Digital Emily Project</figcaption>
    
</figure>

<p>This method doesn't just work with faces, but with materials in general. There is a broad range of reconstruction algorithms for materials, but the basic method still includes a dome which captures samples of a surface under varying illumination conditions. Every time simulated light is supposed to interact with a real surface, its properties have to be known upfront.</p>

<h1 id="light-and-magic">Light and Magic</h1>

<p>So in essence, the evolution of the film industry from a simple projector used in 17th century theater to the intricate details produced for the <em>The Lord of the Rings</em> movies already provides enough knowledge on the problem of correct augmentation. Over time, artists figured out several methods to capture real light, honor light interaction with real and virtual surfaces and improve overall consistency by reconstructing the world in a more elaborate manner.</p>

<p>In the next articles, I will go into details on how to translate most of these methods to real-time algorithms. We'll start off creating a basic pipeline for an AR renderer and then proceed to create shaded virtual geometry first before relighting reality.</p>

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

<ol>
  <li>Fulgence Marion, <a href="https://archive.org/details/wondersofoptics00mariiala">The Wonders Of Optics</a>
</li>
  <li>Jimm Blinn, <a href="https://dl.acm.org/citation.cfm?id=360353">Texture and reflection in computer generated images</a>
</li>
  <li>Wikipedia, <a href="https://en.wikipedia.org/wiki/Rotoscoping">Rotoscoping</a>
</li>
  <li>Lance Williams, <a href="https://dl.acm.org/citation.cfm?id=801126">Pyramidal parametrics</a>
</li>
  <li>Disney, <a href="https://www.youtube-nocookie.com/embed//8LlcqO3mq9Q">The Flight of the Navigator (Trailer)</a>
</li>
  <li>Paul Debevec, <a href="https://www.pauldebevec.com/ReflectionMapping/">The Story of Reflection Mapping</a>
</li>
  <li>Georges Méliès, <a href="https://www.youtube-nocookie.com/embed//dPPP2MrlYr8">The Hilarious Poster</a>
</li>
  <li>Wikipedia, <a href="https://en.wikipedia.org/wiki/Georges_M%C3%A9li%C3%A8s">Georges Méliès</a>
</li>
  <li>Robert Zemeckies, <a href="https://www.youtube-nocookie.com/embed//yy5THitqPBw">Who Framed Roger Rabbit</a>
</li>
  <li>USC, <a href="https://gl.ict.usc.edu/LightStages/">The Light Stages at UC Berkeley and USC ICT</a>
</li>
</ol> ]]></description>
            <pubDate>Tue, 07 Jan 2014 00:00:00 +0100</pubDate>
            <link>https://www.tobias-franke.eu/log/2014/01/07/a-magic-lantern.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhyRnRnRUE2bjJ4ZUY0aDExR21SWHUxa1BtaApIZjM3NWhEc05GdXR0QjJrQnE2RTdQUUJBSUlqYytBbW05RTlNT0hNSHRoQWV2VTQxZllYRXBaV1ZiM3FaS3JPCkVlSUgKPVNJdXkKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>General</category><category>Relighting</category><category>Introduction</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Simulacrum</title>
            <description><![CDATA[ <p>Before we begin, let's review the basics of an AR application that enhances the image by one or more additional 3D objects. Several components are necessary to get going, so lets just make a list of everything:</p>

<ul>
  <li>
<strong>Display</strong> - This is an easy one. We need some kind of display through which we perceive our newly enhanced world. This can be a mobile device such as a tablet, a smartphone or a HMD such as <a href="https://willsteptoe.com/post/66968953089/ar-rift-part-1">this  Oculus Rift mod</a> with additional cameras, or for instance one of these fancy but expensive <a href="https://www.engadget.com/2013/02/21/canon-mreal/">MR HMDs from Canon</a>. Each of these devices need a frontal camera with which the scene is recorded, preferably one with a high resolution and low image artifacts such as noise. Some of these devices can even come with two cameras to directly provide a 3D stereo effect.</li>
  <li>
<strong>Tracking algorithm</strong> - Putting objects into real space requires geometric registration, that is if the camera moves, the virtual object has to stay at its position in the real space. There's a myriad of tracking algorithms ranging from simple marker "tracking" (in reality it's just constant initialization of a tracker, but they are very neat for debugging purposes) to algorithms like line or feature trackers, possibly enhanced by additional gyroscopic sensor to help fight any lag or drift. For starters, <a href="https://www.hitl.washington.edu/artoolkit/">ARToolKit</a> is an old but open framework for simple marker tracking. If you want to get started, this is easy to set up. <a href="https://www.robots.ox.ac.uk/~gk/PTAM/">PTAM</a> and others can later be used for much more stable solutions. Since in this blog I will mostly cover rendering techniques, I'll rely on ARToolKit for most of the time.</li>
  <li>
<strong>Reconstruction</strong> - This one is actually many things. To have our virtual object interact with the real world, we need a reconstruction of the real world. So for instance, if our virtual object will drop a shadow from a real light source we've reconstructed, we also need to know where the shadow will be visible on. Depending on the work done in this part is how dynamic the application can react to changes in the scene, such as someone switching the light on and off. I will get to this point in detail below. In any case, the minimum input for a renderer is a bunch of light sources, so we need to reconstruct the lighting configuration of the scene, and the surfaces/geometry with which to interact with.</li>
  <li>
<strong>Renderer</strong> - The final fragment is the one which creates the pixels. This piece of software will render an object, mask out real and virtual parts of the new composition, render light interaction, shadows, interreflections etc. and glue it all together into one frame. All previous parts come together in this particular piece of code, and here we have to take care that reality and virtuality will match up.</li>
</ul>

<p>Having these parts is the minimum input to start rendering something nice. Additional hardware can however make the entire system more dynamic. This is of course limited by how mobile your AR system should end up. Bullet number 3, the reconstruction of reality, is a rather complex issue which can be heavily supported by additional hardware. Let's have a look at some of them:</p>

<ul>
  <li>
<strong>Depth sensors</strong> provide the ability to record not just the incident radiance at each point in the scene from a camera, but also it's distance, which helps to distinguish between objects nearer to the camera than others. A virtual object then isn't always drawn over the background image, but can end up behind a real one. There are several depth cameras available with different methods of sensing distance: time of flight for sound and ultraviolet light can be recorded to figure out the distance of an object to the camera. The <a href="https://www.microsoft.com/en-us/kinectforwindows/">Microsoft Kinect</a> uses a fixed pattern which is also measured for deformations.</li>
  <li>Furthermore, <strong>depth sensors</strong> can also be used to reconstruct scene geometry. One of the more famous examples of this is <a href="https://research.microsoft.com/en-us/projects/surfacerecon/">Microsoft's KinectFusion</a>, which is available in the KinectSDK. Of course one has to be aware that a depth sensor can only be used - in a single frame - to reconstruct the front-side of things.</li>
  <li>
<strong>Light probes</strong> are sensors which capture real world illumination of the scene. The "low-end version" of this used to be a simple shiny ball (still used quite often for movie production), which gives you some sort of environment map of the real world. Another idea is to place an additional camera somewhere with a nice wide angle lens to capture a hemispherical image. A couple of those cameras which can record full HDR HD videos at 30 FPS can be acquired from Point Grey, <a href="https://www.ptgrey.com/products/ladybug2/ladybug2_360_video_camera.asp">such as the Ladybug series</a>. These can greatly help to figure out dynamically where light comes from!</li>
  <li>Other sensors directly integrated into switches (for instance light switches) can also be used. Usually one will find many of those in ambient assisted living areas, but these are still rather exotic. I won't go into details here.</li>
</ul>

<p>The take-away message is this: a great relighting solution can get even better if we add a little bang to it with some neat active sensors. The renderer can survive with fewer assumptions about reality, which is always a good thing! Overdoing this however can impact the mobility and flexibility of the overall system.</p> ]]></description>
            <pubDate>Tue, 24 Dec 2013 00:00:00 +0100</pubDate>
            <link>https://www.tobias-franke.eu/log/2013/12/24/simulacrum.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhyTDVBRUF2Q05HTDFQa2taNndibkR0MUZTUQpWdlFzVjlhaTVNYzNwRkdtT1JLWmRwZ0JBT0IwU3lTSFB4eGxjMHVkeWpEQjR6K3NZVUk0cnA3ZVBMN1hRZXMzCnE3RUgKPSswSGoKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>General</category><category>Tracking</category><category>Hardware</category><category>Reconstruction</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Augmented vs. Spatial Augmented</title>
            <description><![CDATA[ <p>A quick note I wanted to make is about the difference between <em>Augmented Reality</em> and <em>Spatial Augmented Reality</em> aka <em>Projection Mapping</em>. The first is concerned with inserting objects or information into a real context with whatever means necessary. The second is a neat subset in the whole mixed reality field concerned with augmenting real surfaces with new properties.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2013_12_13_spatial_ar.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2013_12_13_spatial_ar.jpg" alt="Optix-based SAR setup from the University of South Australia at ISMAR 2013" title="Optix-based SAR setup from the University of South Australia at ISMAR 2013">
    
    </a>
    
    
    <figcaption>Optix-based SAR setup from the University of South Australia at ISMAR 2013</figcaption>
    
</figure>

<p>In Spatial Augmented Reality or SAR, the real scene has some diffuse dummy objects made from Styrofoam or similar <em>onto which, broadly speaking, a new BRDF is projected</em>. That's right, SAR changes the appearance of real objects by computing a new image which is usually cast onto the object with a projector. Several problems have to be solved within SAR:</p>

<ul>
  <li>
<strong>Visibility</strong> is an issue when projecting images onto non-planar surfaces. Most SAR setups therefore use several projectors to cover most of the surfaces.</li>
  <li>Projecting images onto non-planar surfaces, for instance a diffuse ball which should appear glossy, requires the source image to be <strong>distorted</strong> so that after the projection real and virtual distortion cancel each other out. Here one will sooner rather than later run into the same parameterization issues as with texture mapping in general.</li>
  <li>Using several projectors to cover for the visibility problem introduces <strong>overlapping artifacts</strong>. The worst issues are visible in highly distorted areas. Even small differences in the calibration of the projectors will produce very bad moiré patterns.</li>
  <li>Since scene and projectors are tied together, it is now the user which has to be tracked instead of an anchor in the scene to simulate <strong>view-dependent effects</strong> such as glossy reflections.</li>
  <li>Projecting an image onto diffuse surfaces will simply by its nature diffusely reflect on its surrounding, automagically creating <strong>unwanted global illumination effects</strong> such as diffuse secondary bounces. These have to be countered in the simulation which produces the projected images. <a href="https://graphics.cs.rpi.edu/eg2010/">There's a neat paper for that</a>.</li>
</ul>

<figure>
    
    
    <div class="video default-size">
        <a class="fa" href="https://player.vimeo.com/video/57861201">
            <img class="default-size" src="https://www.tobias-franke.eu/layout/thumbcache/401286347_640.jpg" alt="" title="">
        </a>
    </div>
    
    
    
</figure>

<p>SAR is an interesting field when relighting real world objects to see them with different materials. In the video above for instance, one can preview a car in different colors before buying the real thing. <a href="https://www.wired.com/design/2013/08/this-augmented-reality-sandbox-turns-dirt-into-an-interactive-interface/">A recent piece on Wired</a> showed off an overhead projection into a sandbox tracked by a Kinect, where different heights in the sand will correspond to different layers of a landscape. Nevertheless, the next posts will <em>not</em> be on SAR, but I didn't want to skip over this cool field.</p> ]]></description>
            <pubDate>Fri, 13 Dec 2013 00:00:00 +0100</pubDate>
            <link>https://www.tobias-franke.eu/log/2013/12/13/augmented-vs-spatial-augmented.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhyMnRBRUFsVFB3U3VUUk5qaUtnZG1jYm5vdwpXb0dGTFhLWlZRVm9uSjJxRWh3ZCtMQUEvamJuZGdwckVld21TYmFwNlNMTG5aSHc5aUZSaFU4U3lUUGpndTFwCmNhUUIKPWdjcW4KLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>SAR</category><category>Relighting</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>On the current state of AR</title>
            <description><![CDATA[ <p>Augmented reality is popping up <em>everywhere</em>! Of course the wide bandwidth of what everybody understands to be AR is a big factor. Primarily you've got your mobile apps which track your position and <a href="https://www.wikitude.com/app/">add more information to your surrounding</a>, or the fun and educational <a href="https://play.google.com/store/apps/details?id=com.escapistgames.starchart&amp;hl=en">stargazing tool</a>. And let's not forget <a href="https://www.google.com/glass/start/">Google Glass</a>.</p>

<p>Every now and then, things get interesting: people want to see an additional, virtual 3D <em>object</em> in the context of a real scene. The most recent buzz was generated by the <a href="https://www.ikea.com/ms/en_CA/mobile/mobile_splash.html">IKEA app</a> accompanying their catalog. The very real catalog is used as an anchor point in your very real kitchen, living room or whatever and with the camera image used as a background, you can select one of the pieces of furniture and have a look at it, neatly positioned on top of where the catalog is lying around.</p>

<figure>
    
    
    <div class="video default-size">
        <a class="fa" href="https://www.youtube-nocookie.com/embed/uaxtLru4-Vw">
            <img class="default-size" src="https://www.tobias-franke.eu/layout/thumbcache/uaxtLru4-Vw.jpg" alt="" title="">
        </a>
    </div>
    
    
    
</figure>

<p>Trouble, however, is imminent. The first thing everyone will realize a few seconds into playing around with tools like this is that there's something odd about the depth: the augmented object is <em>literally</em> always on top of things. The camera of course, without any depth sensor, cannot provide the necessary information to figure out which object in reality is in front of the virtual one. Cleverly positioning your camera, as seen in the trailer, will hide this issue quite well.</p>

<p>The human visual system will not be as easily fooled by the presentation of lights and shadows though. Augmented reality is all about getting two distinct worlds to behave coherently in one frame. The virtual object should behave as if it were real. Your very first impression of it is the way it looks, and the renderer which is responsible for this image has to deal with how light reflects on the objects surface. Question is: how does the renderer know where the light - the <em>real</em> light - actually comes from? Ignoring this question, as apparently many AR renderers do, will instantly reveal the fake object in the room. <a href="https://www.google.com/search?q=augmented+reality+virtual+object&amp;tbm=isch">Google for some AR apps</a> and see how the virtual objects almost always look like plastic. This is because they have been shaded - if at all - most often with some Phong-like appearance and fixed light sources. If someone comes into the room and dims the lights, the virtual object won't react to it. You can hide this error by rendering a mostly ambiently lit object with some ambient occlusion and place it into an equally ambiently lit room, as was done in the IKEA video. Other real world lighting configurations are not so forgiving.</p>

<figure>
    
    
    <div class="video default-size">
        <a class="fa" href="https://www.youtube-nocookie.com/embed/PGu0N3eL2D0">
            <img class="default-size" src="https://www.tobias-franke.eu/layout/thumbcache/PGu0N3eL2D0.jpg" alt="" title="">
        </a>
    </div>
    
    
    
</figure>

<p>Of course, not knowing where your light comes from isn't too good if you want to drop a shadow somewhere. A whole bunch of AR simulations "solve" this problem simply by drawing some shadow blob beneath the object and secretly hope that the user doesn't flip it on its head, place it on some edge or non-planar surface where the shadow floats in mid-air.</p>

<p>If you have an iOS device, I invite you to check out the <a href="https://itunes.apple.com/us/app/pointcloud-browser/id492142085">Pointcloud browser</a>: this app is initialized with a sweep across a surface with enough texture to extract some features for tracking. After that you can experience first hand how local-lighting and no depth will destroy your mixed reality experience in seconds. Simply hold your hand in front of the camera or switch off the light in the room. (The app by the way is fantastic, you can write AR applications with small XML descriptors in no-time!)</p>

<p>So what's the point here? Tracking your object with some kind of anchor in the real scene is called <em>geometric registration</em>. This step is important, but not sufficient. You need to track the rest of reality with it. If we want to have a coherent appearance, we need to look more closely at <em>photometric registration</em> as well.</p> ]]></description>
            <pubDate>Sat, 07 Dec 2013 00:00:00 +0100</pubDate>
            <link>https://www.tobias-franke.eu/log/2013/12/07/on-the-current-state-of-ar.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhydmZ3RDZBaGdUUmhqdi82Wi9FVFFPa0crNwpNbjVISllrVnN0NzZkMWJuNUU2Tjc4VUJBT3lDK05iQVBGWll4eUFxSmxETEN3dWJla281MjViaWJHeFN0ZUN1CjIyTUUKPVlCbW0KLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>General</category><category>Relighting</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Delta Light Propagation Volumes for Mixed Reality</title>
            <description><![CDATA[ <div class="publication">
    <h1>Delta Light Propagation Volumes for Mixed Reality</h1>
    <p>
        <span class="authors">Tobias Alexander Franke</span>
        <span class="journal">2013 IEEE International Symposium on Mixed and Augmented Reality</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/franke13dlpv/franke13dlpv.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/franke13dlpv/franke13dlpv.jpg" alt="A textured bust (left) is inserted into the scene, receiving red indirect light from the ground and casting a slight shadow." title="A textured bust (left) is inserted into the scene, receiving red indirect light from the ground and casting a slight shadow.">
    
    </a>
    
    
    <figcaption>A textured bust (left) is inserted into the scene, receiving red indirect light from the ground and casting a slight shadow.</figcaption>
    
</figure>

    <h2>Abstract</h2>
    <p>Indirect illumination is an important visual cue which has traditionally been neglected in mixed reality applications. We present Delta Light Propagation Volumes, a novel volumetric relighting method for real-time mixed reality applications which allows to simulate the effect of first bounce indirect illumination of synthetic objects onto a real geometry and vice versa. Inspired by Radiance Transfer Fields, we modify Light Propagation Volumes in such a way as to propagate the change in illumination caused by the introduction of a synthetic object into a real scene. This method combines real and virtual light in one representation, provides improved temporal coherence for indirect light compared to previous solutions and implicitly includes smooth shadows.</p>


    
    <h2>Preview</h2>
    <p class="images">
    
    <img src="https://www.tobias-franke.eu/publications/franke13dlpv/preview/franke13dlpv-0.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/franke13dlpv/preview/franke13dlpv-1.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/franke13dlpv/preview/franke13dlpv-2.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/franke13dlpv/preview/franke13dlpv-3.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/franke13dlpv/preview/franke13dlpv-4.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/franke13dlpv/preview/franke13dlpv-5.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/franke13dlpv/preview/franke13dlpv-6.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/franke13dlpv/preview/franke13dlpv-7.png" alt="" title="">
    
    </p>
    

    
    <h2>Supplemental Video</h2>
    
    <div class="video default-size">
        <img class="default-size" src="https://www.tobias-franke.eu/publications/franke13dlpv/franke13dlpv_video.jpg" alt="" title="">
        
        <a class="fa fa-5x default-size" href="https://www.tobias-franke.eu/publications/franke13dlpv/franke13dlpv.mp4"></a>
        
    </div>
    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://www.tobias-franke.eu/publications/franke13dlpv/franke13dlpv.pdf">Paper</a></li>
        
        
        
        
        <li><a href="https://www.tobias-franke.eu/publications/franke13dlpv/franke13dlpv_slides.pdf">Slides</a></li>
        
        
        
        
        <li><a href="https://github.com/thefranke/dirtchamber">Source</a></li>
        
        
        
        
        <li><a href="https://ieeexplore.ieee.org/xpl/articleDetails.jsp?arnumber=6671772">IEEE</a></li>
        
        
        
        
        <li><a href="https://ismar2013.vgtc.org/ismar/2013/paper/sct/delta-light-propagation-volumes-mixed-reality.html">ISMAR 2013</a></li>
        

        
        
        <li><a href="https://www.tobias-franke.eu/publications/franke13dlpv/franke13dlpv.mp4">Video</a></li>
        
        

        
        <li><a href="https://www.tobias-franke.eu/publications/franke13dlpv/franke13dlpv.bib">Bibtex</a></li>
        
    </ul>
</div> ]]></description>
            <pubDate>Fri, 04 Oct 2013 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/publications/franke13dlpv/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhyK3pRRUF3K3hPTmVHUmdobUhTSTUvMzYxQgozWWRhWmhtQUxQOUdKUk11Qmd4bmFmQUJBTyt0alBpVVpSSmxvUUxuaENvdWFUak01K01YUFprQXFxNk0yL2YvCm8wOEoKPWU4RkkKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Conference</category><category>Publication</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Near-Field Illumination for Mixed Reality with Delta Radiance Fields</title>
            <description><![CDATA[ <div class="publication">
    <h1>Near-Field Illumination for Mixed Reality with Delta Radiance Fields</h1>
    <p>
        <span class="authors">Tobias Alexander Franke</span>
        <span class="journal">ACM SIGGRAPH 2013 Posters</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/franke13drf/franke13drf.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/franke13drf/franke13drf.jpg" alt="Infinite Head model inserted into a real scene with one reconstructed light source: (a) a synthetic object is inserted without illumination, (b) visible first bounce around the base as well as low resolution shadow (32 propagations, 512^2 VPLs, 11ms per frame), (d) indirect effects without synthetic object for better visualization, (d) visualization of the DLPV (red dots indicate negative values)." title="Infinite Head model inserted into a real scene with one reconstructed light source: (a) a synthetic object is inserted without illumination, (b) visible first bounce around the base as well as low resolution shadow (32 propagations, 512^2 VPLs, 11ms per frame), (d) indirect effects without synthetic object for better visualization, (d) visualization of the DLPV (red dots indicate negative values).">
    
    </a>
    
    
    <figcaption>Infinite Head model inserted into a real scene with one reconstructed light source: (a) a synthetic object is inserted without illumination, (b) visible first bounce around the base as well as low resolution shadow (32 propagations, 512^2 VPLs, 11ms per frame), (d) indirect effects without synthetic object for better visualization, (d) visualization of the DLPV (red dots indicate negative values).</figcaption>
    
</figure>

    <h2>Abstract</h2>
    <p>Apart from geometric registration, fusing synthetic objects with a real context requires believable interaction of real and virtual light. Where shadows provide the visual cues to locate a synthetic object in a real scene and transferred light from real light sources let it appear in harmony with its surroundings, the mutual indirect interaction of illumination is a necessary detail to convince an observer that the rendered result is not merely augmented but part of the scene. Such a mixed reality (MR) system has applications in movie production, advertisement of unfinished products or cultural heritage visualization. While there are a range of relighting tools available for offline renderers or static scenes (e.g. photos), interactive MR systems usually disregard proper lighting entirely or greatly simplify shading. A method often employed is to merge virtual light and shadows cast from synthetic objects with a real background with Differential Rendering, leaving out any other light interaction such as indirect light bounces. Attempts have been made to resolve this issue with Instant Radiosity [<a href="https://www.cg.tuwien.ac.at/research/publications/2010/knecht_martin_2010_DIR/">Knecht et al. 2010</a>; <a href="https://ieeexplore.ieee.org/xpl/login.jsp?tp=&amp;arnumber=6402547">Lensing and Broll 2012</a>]. To suppress flickering, a large number of virtual point lights (VPL) is necessary, drastically taxing execution speed. We propose to model all light, virtual and real, as a unified radiance field instead to avoid performance issues from oversampling and to maintain temporal coherence.</p>


    
    <h2>Preview</h2>
    <p class="images">
    
    <img src="https://www.tobias-franke.eu/publications/franke13drf/preview/franke13drf-0.png" alt="" title="">
    
    </p>
    

    
    <h2>Supplemental Video</h2>
    
    <div class="video default-size">
        <img class="default-size" src="https://www.tobias-franke.eu/publications/franke13drf/franke13drf_video.jpg" alt="" title="">
        
        <a class="fa fa-5x default-size" href="https://www.tobias-franke.eu/publications/franke13drf/franke13drf.mp4"></a>
        
    </div>
    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://dl.acm.org/doi/pdf/10.1145/2503385.2503468">Abstract</a></li>
        
        
        
        
        <li><a href="https://www.tobias-franke.eu/publications/franke13drf/franke13drf_poster.pdf">Poster</a></li>
        
        
        
        
        <li><a href="https://dl.acm.org/doi/10.1145/2503385.2503468">ACM</a></li>
        
        
        
        
        <li><a href="https://s2013.siggraph.org/attendees/complete-list-posters.html">S2013</a></li>
        

        
        
        <li><a href="https://www.tobias-franke.eu/publications/franke13drf/franke13drf.mp4">Video</a></li>
        
        

        
        <li><a href="https://www.tobias-franke.eu/publications/franke13drf/franke13drf.bib">Bibtex</a></li>
        
    </ul>
</div> ]]></description>
            <pubDate>Sun, 21 Jul 2013 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/publications/franke13drf/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhwRVJRRUF0cksvcDUybFJCY3hPeWZYR3p4NwpqSWZhczBKZlNtK05JRnlZSTdWb1pSOEJBSm5jUXpQSzNxT29wYitLNk5aLzlUbnlZa2xFR29ZWTRpaVFrRlJtCitEb0sKPWlLa1MKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Conference</category><category>Publication</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Adapting Precomputed Radiance Transfer to Real-time Spectral Rendering</title>
            <description><![CDATA[ <div class="publication">
    <h1>Adapting Precomputed Radiance Transfer to Real-time Spectral Rendering</h1>
    <p>
        <span class="authors">Karsten Schwenk, Tobias Alexander Franke et al.</span>
        <span class="journal">Proceedings of Eurographics 2010</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/schwenk10spectralprt/schwenk10spectralprt.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/schwenk10spectralprt/schwenk10spectralprt.jpg" alt="A replication of the Metacow image rendered with our spectral PRT method. Left: Illuminant D65, center: Illuminant A, right: Illuminant F2. RGB rendering would not resolve the metamers under illuminants A and F2." title="A replication of the Metacow image rendered with our spectral PRT method. Left: Illuminant D65, center: Illuminant A, right: Illuminant F2. RGB rendering would not resolve the metamers under illuminants A and F2.">
    
    </a>
    
    
    <figcaption>A replication of the Metacow image rendered with our spectral PRT method. Left: Illuminant D65, center: Illuminant A, right: Illuminant F2. RGB rendering would not resolve the metamers under illuminants A and F2.</figcaption>
    
</figure>

    <h2>Abstract</h2>
    <p>Spectral rendering takes the full visible spectrum into account when calculating light-surface interaction and can overcome the well-known deficiencies of rendering with tristimulus color models. We present a variant of the precomputed radiance transfer algorithm that is tailored towards real-time spectral rendering on modern graphics hardware. Our method renders diffuse, self-shadowing objects with spatially varying spectral reflectance properties under distant, dynamic, full-spectral illumination. To achieve real-time frame rates and practical memory requirements we split the light transfer function into an achromatic part that varies per vertex and a wavelength-dependent part that represents a spectral albedo texture map. As an additional optimization, we project reflectance and illuminant spectra into an orthonormal basis. One area of application for our research is virtual design applications that require relighting objects with high color fidelity at interactive frame rates.</p>


    
    <h2>Preview</h2>
    <p class="images">
    
    <img src="https://www.tobias-franke.eu/publications/schwenk10spectralprt/preview/schwenk10spectralprt-0.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/schwenk10spectralprt/preview/schwenk10spectralprt-1.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/schwenk10spectralprt/preview/schwenk10spectralprt-2.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/schwenk10spectralprt/preview/schwenk10spectralprt-3.png" alt="" title="">
    
    </p>
    

    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://www.tobias-franke.eu/publications/schwenk10spectralprt/schwenk10spectralprt.pdf">Paper</a></li>
        
        
        
        
        <li><a href="https://diglib.eg.org/items/6b6ded90-67b1-42f9-a0c2-1d5dc28e3b71">EG DigLib</a></li>
        
        
        
        
        <li><a href="https://www.eurographics2010.se/">EG 2010</a></li>
        

        

        
        <li><a href="https://www.tobias-franke.eu/publications/schwenk10spectralprt/schwenk10spectralprt.bib">Bibtex</a></li>
        
    </ul>
</div> ]]></description>
            <pubDate>Mon, 03 May 2010 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/publications/schwenk10spectralprt/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhvTVJBRUF1RXM4cnJtWllQVlR2S0czc2cwVQovMnYvWjFwa29WRmEwcUFSekpQcXFlUUJBSlp6ZGlCRkNqSUw0eU0wTUxaMk1NN2Q1OWhkdHJySCthdGxYUDE4CjRHTUgKPVJhMGoKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Conference</category><category>Publication</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Precomputed Radiance Transfer for X3D based Mixed Reality Applications</title>
            <description><![CDATA[ <div class="publication">
    <h1>Precomputed Radiance Transfer for X3D based Mixed Reality Applications</h1>
    <p>
        <span class="authors">Tobias Alexander Franke and Yvonne Jung</span>
        <span class="journal">Proceedings of the 13th international conference on 3D web technology</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/franke08prtx3d/franke08prtx3d.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/franke08prtx3d/franke08prtx3d.jpg" alt="Based on an HDR environment map in latitude-longitude format the dragon is rendered via PRT within an X3D browser." title="Based on an HDR environment map in latitude-longitude format the dragon is rendered via PRT within an X3D browser.">
    
    </a>
    
    
    <figcaption>Based on an HDR environment map in latitude-longitude format the dragon is rendered via PRT within an X3D browser.</figcaption>
    
</figure>

    <h2>Abstract</h2>
    <p>We present a lightweight framework composed of two extensions to introduce precomputed radiance transfer (PRT) to X3D. Depending on the basis function, PRT is used to simulate shading, complex light transfer and effects like subsurface scattering and caustics in real time by projecting incident light and transfer functions into frequency space, which allows the solution of the integration of the rendering equation through a simple dot product. Light transfer from incident to transferred radiance thus becomes a matter of solving dot products of coefficient vectors, which can be easily evaluated on the GPU. In this paper, we identify the minimal set of components needed for an X3D application to use spherical harmonic based PRT, and discuss how further enhancements to PRT can be implemented, like new basis functions and other features.</p>


    
    <h2>Preview</h2>
    <p class="images">
    
    <img src="https://www.tobias-franke.eu/publications/franke08prtx3d/preview/franke08prtx3d-0.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/franke08prtx3d/preview/franke08prtx3d-1.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/franke08prtx3d/preview/franke08prtx3d-2.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/franke08prtx3d/preview/franke08prtx3d-3.png" alt="" title="">
    
    </p>
    

    
    <h2>Supplemental Video</h2>
    
    <div class="video default-size">
        <img class="default-size" src="https://www.tobias-franke.eu/publications/franke08prtx3d/franke08prtx3d_video.jpg" alt="" title="">
        
        <a class="fa fa-5x default-size" href="https://www.tobias-franke.eu/publications/franke08prtx3d/franke08prtx3d.mp4"></a>
        
    </div>
    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://dl.acm.org/doi/pdf/10.1145/1394209.1394213">Paper</a></li>
        
        
        
        
        <li><a href="https://dl.acm.org/doi/10.1145/1394209.1394213">ACM</a></li>
        
        
        
        
        <li><a href="https://web3d2008.web3d.org/">Web3D 2008</a></li>
        

        
        
        <li><a href="https://www.tobias-franke.eu/publications/franke08prtx3d/franke08prtx3d.mp4">Video</a></li>
        
        

        
        <li><a href="https://www.tobias-franke.eu/publications/franke08prtx3d/franke08prtx3d.bib">Bibtex</a></li>
        
    </ul>
</div> ]]></description>
            <pubDate>Sat, 09 Aug 2008 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/publications/franke08prtx3d/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhvbXRnRUF6WFBXb2dqUjVmam5XTGIwaForTAp4TjA2S05qREo3ZzdmMHhzQklZTXFhUUEvajVnS1AyaWdOSENuQmNMTkxBRjl5SUFYdlBsWkMzM21ieGd6TWpYCjJ3Y0QKPUdmK0gKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Conference</category><category>Publication</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Real-Time Mixed Reality with GPU Techniques</title>
            <description><![CDATA[ <div class="publication">
    <h1>Real-Time Mixed Reality with GPU Techniques</h1>
    <p>
        <span class="authors">Tobias Alexander Franke and Yvonne Jung</span>
        <span class="journal">Proceedings of the Third International Conference on Computer Graphics Theory and Applications</span>
    </p>

    
    


<figure>

    
    <a href="https://www.tobias-franke.eu/publications/franke08rtmrgpu/franke08rtmrgpu.jpg">
    
        <img src="https://www.tobias-franke.eu/publications/franke08rtmrgpu/franke08rtmrgpu.jpg" alt="Shadows and occlusion are handled via differential rendering and reconstructed geometry." title="Shadows and occlusion are handled via differential rendering and reconstructed geometry.">
    
    </a>
    
    
    <figcaption>Shadows and occlusion are handled via differential rendering and reconstructed geometry.</figcaption>
    
</figure>

    <h2>Abstract</h2>
    <p>In this paper, we propose a combination of modern GPU-based methods that are able to generate high-quality, interactive real-time rendering for augmented and mixed reality applications. We also present a new approach to estimate surface reflection functions and materials from images using genetic algorithms.</p>


    
    <h2>Preview</h2>
    <p class="images">
    
    <img src="https://www.tobias-franke.eu/publications/franke08rtmrgpu/preview/franke08rtmrgpu-0.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/franke08rtmrgpu/preview/franke08rtmrgpu-1.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/franke08rtmrgpu/preview/franke08rtmrgpu-2.png" alt="" title="">
    
    <img src="https://www.tobias-franke.eu/publications/franke08rtmrgpu/preview/franke08rtmrgpu-3.png" alt="" title="">
    
    </p>
    

    
    <h2>Supplemental Video</h2>
    
    <div class="video default-size">
        <img class="default-size" src="https://www.tobias-franke.eu/publications/franke08rtmrgpu/franke08rtmrgpu_video.jpg" alt="" title="">
        
        <a class="fa fa-5x default-size" href="https://www.tobias-franke.eu/publications/franke08rtmrgpu/franke08rtmrgpu.mp4"></a>
        
    </div>
    

    <h2>Links</h2>       
    <ul class="links">
        
        
        
        
        <li><a href="https://www.tobias-franke.eu/publications/franke08rtmrgpu/franke08rtmrgpu.pdf">Paper</a></li>
        
        
        
        
        <li><a href="https://publica.fraunhofer.de/starweb/servlet.starweb?path=pub.web&amp;search=N-69082">FHG</a></li>
        
        
        
        
        <li><a href="https://grapp.visigrapp.org/GRAPP2008/">GRAPP 2008</a></li>
        

        
        
        <li><a href="https://www.tobias-franke.eu/publications/franke08rtmrgpu/franke08rtmrgpu.mp4">Video</a></li>
        
        

        
        <li><a href="https://www.tobias-franke.eu/publications/franke08rtmrgpu/franke08rtmrgpu.bib">Bibtex</a></li>
        
    </ul>
</div> ]]></description>
            <pubDate>Tue, 22 Jan 2008 00:00:00 +0100</pubDate>
            <link>https://www.tobias-franke.eu/publications/franke08rtmrgpu/index.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhxZ2t3RC9VczJVWGFRTE9sOHF1dW9xSDcvUgpqODNjbExjRDd5MDV2RkYxNnAwYUZsa0JBUGZxTmgyanNWSWRUTEVJM0g3c1FXQjdDY0M4elVRYjJXOVdMUjdFCithb0kKPWFpU3EKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>Conference</category><category>Publication</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Erweiterte Konzepte in C++ - Multithreading</title>
            <description><![CDATA[ <blockquote>
  <p>Herb also added that I can quote him in telling you that 'The C++ Standardization Committee is <em>insanely interested</em> in this proposal'.</p>

  <p>– Andrei Alexandrescu, cpp-threads Mailingliste</p>
</blockquote>

<h1 id="einleitung">Einleitung</h1>

<p>Das im C++ ISO Standard definierte Modell zur Ausführung von Programmen ist single-threaded. Im Zeitalter von Multicore Prozessoren und Hyperthreading-Techniken ist es jedoch für viele Entwickler wünschenswert, sichere, korrekte und standard-konforme Programme schreiben zu können, die sich der Multithreading-Technik bedienen. Statt dessen zeichnet sich auf Seite der C++-Nutzer ein durchwachsenes Bild mit vielen unterschiedlichen Threading-Bibliotheken ab, die sich nicht nur allesamt in ihrer Syntax mehr oder minder stark unterscheiden, sondern nicht einmal die von C++ zur Verfügung gestellten Mittel unterstützen bzw. C Interfaces zu Tage legen, und die Möglichkeit für <em>portablen</em> Code im Keim ersticken. Die Situation wird deutlich unangenehmer, wenn man den Blick auf die Einzelheiten und Details lenkt, die sich durch Multithreading ergeben: <em>Speichersichtbarkeit</em> und <em>Locking</em> sind Schlagwörter, die viel Diskussion über Effizienz und Korrektheit auslösen.</p>

<p>Neuere Erkenntnisse auf dem Gebiet zeigen, dass der Ursprung all dieser Probleme nicht auf den Programmierer oder die Bibliothek zurückzuführen ist, sondern sich viel fundamentaler in der Sprachdefinition selbst findet. Kurz gesagt: Multithreading-Unterstützung lässt sich nicht per Bibliothek implementieren, wenn der Compiler sich der Anwesenheit von Threads nicht bewusst ist und den Code dahingehend optimiert. Die Sprache muss, rein semantisch, mit Threads von Grund auf vertraut sein. In dem von der Arbeitsgruppe 21 veröffentlichten Paper On the Future Evolution of C++ <a href="#referenzen">10</a> wird daher als erster Punkt besprochen, wie der Thread-Support des neuen Standard C++0x definiert werden soll. Es existieren sowohl Vorschläge zum Speichermodell als auch zu einer möglichen Spracherweiterung. Außerdem werden diverse Bibliotheken wie PThreads oder Boost.Thread auf ihre Nutzbarkeit evaluiert, sollte das Speichermodell angepasst sein.</p>

<h1 id="grundlagen">Grundlagen</h1>

<p>Dieses Kapitel führt in die Grundelemente von Multithreading ein und bietet gleichzeitig einen Überblick darüber, was eine Multithreading-Bibliothek im gröbsten bieten muss.</p>

<h2 id="threads">Threads</h2>

<p>Die parallele Abarbeitung mehrerer Instruktionen auf einem Rechnersystem lässt sich auf viele Arten bewerkstelligen, die alle unter dem Begriff der Nebenläufigkeit - die kausale Unabhängigkeit mehrerer in Beziehung stehender Ereignisse - zusammengefasst werden. Darunter fallen sowohl asynchrone Kommunikation, Signal-/Interrupt-Handler, Multithreading und Multitasking, die aber allesamt grundlegende Unterschiede aufweisen.</p>

<p>Fängt man auf unterster Ebene an, so sind <em>Interrupthandler</em> die hardwarenahe Lösung der Nebenläufigkeit. Durch einen Interrupt oder eine Exception auf Seiten der CPU oder des BIOS wird ein Signal ausgelöst, durch das das Betriebssystem den laufenden Instruktionsfluss unterbricht, um eine Routine zur Bearbeitung des Interrupts zu starten. Beispielsweise ist der vom BIOS zur Verfügung gestellte Interrupt 13h für das Ansprechen von Festplatten zuständig. Andere Interrupts dienen z.B. dem Abfragen der Tastatur.</p>

<p>Auf der Ebene des Betriebssystem finden sich zwei Begriffe wieder, die leicht miteinander gleichgesetzt oder verwechselt werden, nämlich die des Prozess und des <em>Threads</em>. Beide teilen die Eigenschaft, einzelne, sequentielle Instruktionspfade zu beschreiben, die parallel zu anderen Sequenzen durch so genanntes <em>Time Slicing</em> oder über Multiprozessor Systeme ausgeführt werden können. Prozesse unterscheiden sich von Threads in dem Punkt, dass sie einen eigenen Zustand und Adressraum besitzen, und ausschließlich über die vom Betriebssystem bereitgestellten Mittel untereinander kommunizieren können. Prozesse beherbergen üblicherweise Threads, die sich den Heap des Prozess miteinander teilen, und somit über einen gemeinsamen Speicherbereich kommunizieren können. Der Kontextwechsel zweier Prozesse ist demnach aufwendiger, als der zweier Threads eines Prozess (wobei dieser Umstand abhängig von der Implementation eines Betriebssystems ist). Threads werden daher manchmal auch als <em>leichtgewichtige Prozesse</em> beschrieben.</p>

<p>Abstrahiert man vollständig vom Betriebssystem sowie der darunter angeordneten Hardware, so ist der Begriff <em>asynchrone Kommunikation</em> passend, um Nebenläufigkeit zu beschreiben: Ein Teilstück einer Aktion, das nebenläufig ausgeführt wird, kann Nachrichten an andere Teile absenden oder empfangen, ohne auf die Antwort warten zu müssen. In dieser Zeit wird die Arbeit weiter fortgesetzt, bis das Ergebnis eintrifft.</p>

<h2 id="synchronisation">Synchronisation</h2>

<p>In der Literatur findet sich keine durchgehende Definition für das Wort <em>Multithreading</em>, das von der konzeptionellen Sicht bis hin zum Software Engineering unterschiedlich interpretiert wird. Es finden sich Definitionen wie <em>etwas, das sich jedes mal total unvorhersehbar verhält wenn es gestartet wird</em> oder ein <em>Modell zur nebenläufigen Programmierung</em>. Sieht man von den Definitionen ab, so bleibt der Indeterminismus die Grundproblematik bei der formalen Analyse nebenläufiger Programme, durch den die Vorhersage eines eindeutigen Programmablaufs unmöglich wird. So kann ein Thread z.B. sowohl in der Zuweisung einer Variable unterbrochen werden, als auch davor oder danach. Die Probleme die sich hierdurch ergeben, wurden erstmals 1965 von Edsger W. Dijkstra in seinem Artikel Solution of a problem in concurrent programming control <a href="#referenzen">7</a> durch den von ihm eingeführten <em>kritischen Abschnitt</em> gelöst, in dem durch gegenseitigen Ausschluss ein Codebereich atomar gegenüber anderen Threads ausgeführt wird. Die später folgende ausführliche Abhandlung <a href="#referenzen">8</a> erklärt mit dem Mechanismus der <em>Semaphore</em> ein Konzept, das bis heute bei der Implementierung von Multithreading Bibliotheken weitestgehend unverändert ist.</p>

<p>Durch die Parallelisierung diverser Programmteile können Fehler zu Tage treten, die sich im Code und per Debugger nur schwer auffinden lassen. Da das Schreiben korrekter Multithreading-Programme nur durch die richtige Anwendung von Synchronisation an kritischen Stellen gewährleistet ist, kann ein Fehler leicht entstehen, wenn diese Abschnitte nicht richtig gesichert wurden. Die zwei großen Vertreter dieser Fehlerklassen sind so genannte <em>Race Conditions</em> und <em>Live-/Deadlocks</em>.</p>

<p>Eine detailreiche Einführung in die Problematik findet sich <a href="#referenzen">13</a>.</p>

<h1 id="speichermodell">Speichermodell</h1>

<p>Sicherlich der wichtigste Teil einer Multithreadingimplementation ist das Speichermodell. Bevor eine Entwicklung an den eigentlichen Multithreading-Primitiven beginnen kann, muss Klarheit darüber herrschen, wie sich der Code unter den neuen Bedingungen zu verhalten hat. Darunter fallen folgende Definitionen:</p>

<ul>
  <li>
<strong>Atomare Operationen</strong>: Operationen, welche garantiert unterbrechungsfrei ausgeführt werden können. Ohne solche Operationen ist die Umsetzung von z.B. Synchronisationsmechanismen praktisch unmöglich.</li>
  <li>
<strong>Partielle Ordnungen von Operationen</strong>: Unter bestimmten Umständen spielt die Reihenfolge von Operationen eine wichtige Rolle, die der Compiler nicht verändern darf. Durch ein Ordnungsprimitv wie <em>happens-before</em> kann definiert werden, welche Operationen wie zu ordnen sind (besonders bei Synchronisation zu beachten).</li>
  <li>
<strong>Speichersichtbarkeit</strong>: Der Zeitpunkt, ab dem der gemeinsame Speicher für alle Threads den gleichen Inhalt besitzt. Dies ist sowohl aus softwaretechnischer Sicht als auch auf Seite der Hardware zu beachten.</li>
  <li>
<strong>Data Race Semantik</strong>: Die Umstände, unter denen es zu einem Data Race kommen kann, und wie diese Operation endet (z.B. undefiniert oder via Exception). Viel Arbeit zu diesem Thema wurde in das Java Speichermodell <a href="#referenzen">12</a> investiert, um den Schaden zu minimieren.</li>
</ul>

<p>Das Speichermodell ist maßgebend für den Compiler, um Code zu transformieren, ohne dass dieser seine ursprüngliche Bedeutung verliert. Ein entsprechendes Modell für Multithreading gibt daher vor, welche Optimierungen in Anwesenheit von Threads erlaubt sind, und welche die korrekte Abarbeitung mit Threads stören. Allerdings greift das Speichermodell dabei über die Grenzen des Compilers hinweg auch die Hardware auf. Beispielhaft dafür steht der noch in der Entwicklung befindliche Cell-Prozessor <a href="#referenzen">9</a>, der mit mehreren Recheneinheiten (SPE: <em>Synnergetic Processing Element</em>), die jeweils einen eigenen Cache besitzen, Multithreading effizienter gestalten soll. Hier sticht die Bedeutung der <em>Speichersichtbarkeit</em> besonders hervor, da der lokal gehaltene Cache jedes SPE nicht unbedingt sofort mit allen anderen Recheineinheiten abgeglichen ist. Gerade bei der Initialisierung großer Objekte muss daher im Speichermodell festgelegt sein, wann der Zugriff einzelner Threads auf ein neu angelegtes Objekt letzten Endes erfolgen kann und darf.</p>

<h2 id="thread-safety">Thread Safety</h2>

<p>Die oben erwähnten Races und Live-/Deadlocks sind Merkmale von Programmen, die nicht thread-safe sind. Eine der vielen Definitionen von <em>Thread-Safety</em> ist, dass ein Programm genau dann <em>sicher</em> ist, wenn es durch die Verwendung mehrerer Threads im Programmablauf nicht zu unerwartetem oder instabilem Verhalten kommen kann. Synchronisation ist dabei ein Hilfsmittel, um diese Sicherheit zu erreichen. Da es jedoch einfach ist, gerade bei der Synchronisation Fehler zu machen, ist es durch reines Lesen des Source Code oft nicht möglich festzustellen, ob ein Programm thread-safe ist oder nicht. Es gibt nur eine Reihe Indikatoren, die darauf hinweisen, dass das Gegenteil der Fall ist, wie z.B. Zugriff auf globale Variablen. Ein verwandtes Problem unter C++ ist Exception-Safety, bei dem ähnliche Prinzipien verfolgt werden. Generell kann man sagen, dass es schwierig ist, ein Programm mit sicherer Verwendung von Threads zu schreiben.</p>

<h2 id="weiteres">Weiteres</h2>

<p>Die meisten Multithreading-Bibliotheken unterstützen, neben der reinen Erzeugung von Threads und deren Synchronisation durch Semaphoren, Condition Variables oder die Verwaltung mehrerer Attribute eines Threads wie z.B. Scheduling-Parameter oder Stackgröße. Eine weitere Besprechung dieser Mechanismen entfällt, um den Rahmen der Ausarbeitung nicht zu sprengen.</p>

<h1 id="multithreading-und-c">Multithreading und C++</h1>

<h2 id="compileroptimierungen">Compileroptimierungen</h2>

<p>Die bisherige Erfahrung hat gezeigt, dass es trotz der fehlenden Multithreading-Spezifikation im C++ Standard möglich ist, entsprechende Funktionalität über Bibliotheken zu nutzen. Nicht ganz evident hingegen blieb vorerst die Frage, ob der vom Compiler generierte Code auch korrekt gegenüber der eigentlichen Bedeutung des Programms ist. In <a href="#referenzen">6</a> wird erstmals gezeigt, dass ein Compiler in Anwesenheit von Multithreading ein Programm, das semantisch als <em>thread safe</em> gilt, fehlerhaft übersetzen kann, sobald der entsprechende Code optimiert wird. Drei Fehlerklassen werden im Folgenden vorgestellt.</p>

<p><a id="figure-1"></a></p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">flag1</span> <span class="o">=</span> <span class="n">flag2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    
<span class="c1">// Thread 1</span>
<span class="k">while</span><span class="p">(</span><span class="n">flag1</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
<span class="n">flag2</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>

<span class="c1">// Thread 2</span>
<span class="n">flag1</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">while</span><span class="p">(</span><span class="n">flag2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</code></pre></div></div>
<p class="caption">Figure 1: Cacheing (aus <a href="#referenzen">6</a>)</p>

<p>Das in <a href="#figure-1">Abbildung 1</a> gezeigte Programm beendet, rein intuitiv, nach der Ausführung von Thread 2 den restlichen Ablauf. Compiler greifen jedoch gerne auf ein Hilfsmittel zurück, sobald auf eine Variable mehrfach in einem Abschnitt zugegriffen wird (siehe <code class="language-plaintext highlighter-rouge">/Og</code> Option in Visual Studio .NET 2003). Statt den Inhalt der beiden von den Threads geteilten Variablen <code class="language-plaintext highlighter-rouge">flag1</code> und <code class="language-plaintext highlighter-rouge">flag2</code> direkt abzufragen, wird dieser vorher aus Performancegründen in ein Register geladen. Dies führt dazu, das beide Threads außerhalb des Kontext betrachtet unverändert bleiben, allerdings das Endresultat ein ganz anderes ist: <code class="language-plaintext highlighter-rouge">flag1</code> wird trotz der Modifikation durch Thread 2 nicht mehr ausgelesen, wodurch das Programm in einem Deadlock endet. Auch die Absicherung durch einen Mutex beim Zugriff auf die Variablen kann hier nicht viel bewirken, wenn deren Inhalt zum schnelleren Bearbeiten außerhalb des Mutex in ein Register geladen wird.</p>

<p>Eine Lösung für diese Problemklasse bietet in einigen Situationen das <code class="language-plaintext highlighter-rouge">volatile</code> Schlüsselwort, durch das signalisiert wird, dass ein Variableninhalt sich jeder Zeit auf für den Compiler unvorhersehbare Weise ändern kann. Mit diesem Mittel können Variablen wie z.B. <code class="language-plaintext highlighter-rouge">flag1/flag2</code> gegen Caching geschützt werden, allerdings zu einem hohen Preis: Sämtliche Optimierungen entfallen zum momentanen Zeitpunkt für den Zugriff auf <code class="language-plaintext highlighter-rouge">volatile</code> Variablen.</p>

<p><a id="figure-2"></a></p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="p">{</span> <span class="kt">int</span> <span class="n">a</span><span class="o">:</span><span class="mi">17</span><span class="p">;</span> <span class="kt">int</span> <span class="n">b</span><span class="o">:</span><span class="mi">15</span><span class="p">;</span> <span class="p">}</span> <span class="n">x</span><span class="p">;</span>
<span class="n">x</span><span class="p">.</span><span class="n">a</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>

<span class="mo">0041122</span><span class="n">E</span>  <span class="n">mov</span>         <span class="n">eax</span><span class="p">,</span><span class="n">dword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">x</span> <span class="p">(</span><span class="mx">416564h</span><span class="p">)]</span> 
<span class="mo">00411233</span>  <span class="n">and</span>         <span class="n">eax</span><span class="p">,</span><span class="mi">0</span><span class="n">FFFE0000h</span> 
<span class="mo">0041123</span><span class="mi">8</span>  <span class="n">or</span>          <span class="n">eax</span><span class="p">,</span><span class="mi">2</span><span class="n">Ah</span> 
<span class="mo">0041123</span><span class="n">B</span>  <span class="n">mov</span>         <span class="n">dword</span> <span class="n">ptr</span> <span class="p">[</span><span class="n">x</span> <span class="p">(</span><span class="mx">416564h</span><span class="p">)],</span><span class="n">eax</span> 
</code></pre></div></div>
<p class="caption">Figure 2: Codesubstitution bei Bitfeldern (VS.NET 2003 Disassemblierung)</p>

<p>Eine weitere Problemklasse fällt speziell in den Bereich der Behandlung von Bitfeldern. Beim Laden und Schreiben von Feldern, deren Breite keine Potenz von 2 oder weniger als 8bit ist, wird der Code (abhängig von dem darunter liegenden Maschinencode) an die Situation angepasst. In <a href="#figure-2">Abbildung 2</a> wird der Zugriff auf das 17bit Feld ersetzt, indem ein 32bit breites Feld den Gesamtinhalt der Struktur übernimmt, verändert und zurückgeschrieben wird. Die neue Situation führt allerdings potentiell zu einem Data Race: Die gesamte Struktur wird zu Anfang der Operation in einem gleichgroßen Temporärspeicher abgelegt, und wird am Ende mit der veränderten Kopie überschrieben. Ein in der Zwischenzeit gesetzter Wert in x.b geht dadurch verloren.</p>

<p><a id="figure-3"></a></p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">p</span><span class="p">(</span><span class="n">lock</span><span class="p">);</span>
<span class="p">...</span> <span class="n">kritischer</span> <span class="n">Abschnitt</span> <span class="p">...</span>
<span class="n">v</span><span class="p">(</span><span class="n">lock</span><span class="p">);</span>
</code></pre></div></div>
<p class="caption">Figure 3: Sequenzänderung</p>

<p>Das letzte Beispiel behandelt Sequenzänderung, die vom Compiler durchgeführt werden, um einen möglichst schnellen Bearbeitungszyklus zu erzwingen (siehe <code class="language-plaintext highlighter-rouge">man gcc</code> zu der Option <code class="language-plaintext highlighter-rouge">-fschedule-insns</code>). Dazu können beispielsweise Speicherzugriffe zusammengefasst oder umgeschichtet werden, solange diese die Abhängigkeiten innerhalb des Programmablaufs unverändert lassen (dies gilt ebenfalls für optimierende Hardware). Vor allem Code, der per <code class="language-plaintext highlighter-rouge">inline</code> ersetzt wird, kann dieser Gefahr unterliegen: In <a href="#figure-3">Abbildung 3</a> kann der kritische Abschnitt mit den Operationen <code class="language-plaintext highlighter-rouge">p()</code> oder <code class="language-plaintext highlighter-rouge">v()</code> vermischt werden, sollte deren Code via <code class="language-plaintext highlighter-rouge">inline</code> an dieser Stelle eingefügt werden. Sollte sich für den Compiler ergeben, dass weder <code class="language-plaintext highlighter-rouge">p()</code> noch <code class="language-plaintext highlighter-rouge">v()</code> Einfluss auf den Zugriff einer Variable innerhalb des kritischen Abschnitts haben, so kann dieser im schlimmsten Fall den Zugriff aus dem gesicherten Bereich hinaus bewegen.</p>

<p>Gerade das letzte Problem wurde von Compilern, die explizit PThreads unterstützen, durch folgende Mechanismen gelöst (siehe <a href="#referenzen">5</a>): Zum einen enthalten Befehle wie <code class="language-plaintext highlighter-rouge">pthread_mutex_lock()</code> so genannte Hardware Barrieren, die die Hardware am umordnen von Speicheroperationen hindern sollen, zum anderen werden sie vom Compiler als undurchsichtige Funktionen, also Funktionen, die der Compiler nicht weiter zerlegen und parsen kann, betrachtet. In diesem Fall muss der Compiler davon ausgehen, dass dieser Befehl praktisch jede globale Variable ändern kann, und somit seine Position im Code eine wichtige Bedeutung hat. Allerdings helfen diese Mechanismen nur, das Problem einzudämmen, denn die vorangegangenen Fälle aus <a href="#figure-1">Abbildung 1</a> und <a href="#figure-2">2</a> werden dadurch nicht gelöst.</p>

<p>Das in <a href="#referenzen">6</a> behandelte Problem wurde in <a href="#referenzen">5</a> weiter ausgearbeitet, mit dem Schluss, dass eine Implementation von Multithreading als reine Bibliothek nicht möglich ist. Wie Boehm korrekt erwähnt, kommt die Unterstützung von PThreads auf Seiten des Compilers einem ersten Schritt zur Spracherweiterung gleich.</p>

<h2 id="function-local-statics">Function-Local Statics</h2>

<p>Weiterhin problematisch gestalten sich lokale static Variablen von Funktionen. Ist einer Menge von Threads die gleiche Funktion zugeordnet, so bleibt zu klären, wie und wann die entsprechende Variable initialisiert wird, um die u.U. teuren Initialisierungskosten nicht mehrfach zu tragen (siehe /GT Option in Visual Studio .NET 2003). Dazu ist ein Flag in der Art is_initialized nötig, das allerdings ohne entsprechende atomare Eigenschaften bisher zu einem potentiellen Race führt: Durch den vom Compiler neu generierten Code, der bisher ohne Lock-Mechanismen vollkommen unsynchronisiert arbeitet, kann unter ungünstigen Umständen ein Thread ein static Feld als bereits initialisiert erachten, während ein anderer Thread an der lang andauernden Initialisierung selbst arbeitet. In diesem Fall kommt es zum Zugriff auf ein uninitialisiertes Feld, und das weitere Verhalten des Programms ist undefiniert.</p>

<h1 id="aktuelle-diskussion">Aktuelle Diskussion</h1>

<h2 id="spracherweiterung">Spracherweiterung</h2>

<p>Wie im vorherigen Abschnitt erarbeitet wurde, ist das C++ Single-Thread Modell nicht für Multithread-Code geeignet und muss an die neue Situation angepasst werden. In einem <em>Strawman Proposal</em> <a href="#referenzen">4</a> werden erste Modifikationen an der Sprache vorgeschlagen, die sowohl die Ordnung von Befehlen, Bitfelder, Synchronisation, Function-Local-Statics, das <code class="language-plaintext highlighter-rouge">volatile</code> Schlüsselwort als auch Thread-Local Variablen ansprechen.</p>

<p>In <a href="#referenzen">2</a> wird die Semantik von Data Races angesprochen: Der momentane Konsens der Gruppe ist, die Semantik weiterhin undefiniert zu lassen, um Compilern Optimierungsmöglichkeiten nicht zu untersagen. Allerdings bleiben dadurch einige der besprochenen Probleme bestehen. Ob sich diese Meinung weiterhin durchsetzt ist abhängig von der Entscheidung des Komitees. Im Strawman Proposal wird die Thematik weiter klassifiziert: Mit neuen Relationen wie <em>synchronized-with</em> und <em>happens-before</em> soll die Ordnung von Operationen, die für Multithreading kritisch sind, gesichert werden. Innerhalb dieser Bereiche ist klar definiert, das ein Data Race nur dann auftritt, wenn das beschriebene Regelwerk verletzt wird (z.B. durch einen Befehl, der keiner Ordnung unterliegt, aber auf dem selben Speicherbereich arbeitet).</p>

<p>Das volatile Schlüsselwort soll weiter gestärkt werden, um für gemeinsame Variablen in Multithreadapplikationen besser und kosteneffizienter genutzt zu werden, statt, wie bisher, nur für IO Operationen. Um hier eine Unterscheidung zu treffen, wann es sich um eine normale Anwendung von volatile, und wann um eine Verwendung für Multithreading handelt, wird die Schreibweise <code class="language-plaintext highlighter-rouge">__async</code> volatile für Multithread-Code vorgeschlagen. Gleichzeitig handelt es sich bei dieser Qualifikation um eine im neuen Standard definierte Synchronisations-Operation, in der garantiert wird, das die Ordnung des Befehls nicht verloren geht. Damit der Compiler allerdings korrekten Code mit <code class="language-plaintext highlighter-rouge">__async</code> volatile produzieren kann, ist vorher zu klären, wie die qualifizierten Datensätze atomar geschrieben werden können, ohne teure Speicherbarrieren zu verwenden. Dazu sind vor allem für größere Datenstrukturen, die nicht mit einem einzigen Store Befehl geschrieben werden können, u.U. mehrere wieder-ausführbare atomare Operationen nötig, um die Einheit zu gewährleisten. Prozessoren ohne jegliche atomare Lese-/Schreiboperationen müssen in diesem Fall auf eine Emulation zurückgreifen, oder können die Unterstützung nicht anbieten. Weitere atomare Primitive wie CAS (siehe <a href="#lock-freie-datenstrukturen">Abschnitt 4.2</a>) benötigen ebenfalls Prozessorunterstützung.</p>

<p>Function-Local Statics sind ein umstrittenes Thema. Es existieren verschiedene Vorschläge von Boehm, Lea und Alexandrescu zur Syntaxerweiterung bzw. Anpassung, um <code class="language-plaintext highlighter-rouge">static</code> Variablen korrekt zu initialisieren, wie z.B. <code class="language-plaintext highlighter-rouge">static (synchronized) var</code> oder <code class="language-plaintext highlighter-rouge">protected static var</code>. Allgemein wird die Lösung, den Compiler entsprechenden Synchronistationscode hinzufügen zu lassen, als beste Wahl angesehen, wobei die Gruppe zu der drastischen Maßnahme tendiert, das <code class="language-plaintext highlighter-rouge">static</code> Schlüsselwort vollständig zu entfernen. Hierzu wird noch auf mehr Informationen der einzelnen Compilerhersteller gewartet.</p>

<p>Um einen Großteil der Optimierung durch Absicherung gegen Races nicht zu verlieren, wird für ein neues Schlüsselwort <code class="language-plaintext highlighter-rouge">__thread</code>, das seit einiger Zeit als Extension bekannt ist, geworben. In diesem Fall lassen sich Speicherzugriffe, die nur innerhalb eines Threads vorkommen (<em>Thread-Local</em>), weiterhin optimieren. Beispielsweise kann somit eine Unterscheidung zwischen einem Smartpointer, der über eine Thread-Grenze hinweg genutzt wird, und einem lokalen Smartpointer, für den keine Synchronisation erforderlich ist, getroffen werden.</p>

<p>Ein noch scheinbar unbehandeltes Thema ist das Verhalten von Exceptions in Multithreadanwendungen, und wie diese sich durch den Code propagieren. Ohne ein Speichermodell wird die Behandlung dieser Thematik allerdings nicht möglich sein.</p>

<p>Über die Threading API, die getrennt vom Speichermodell behandelt werden soll, herrscht noch Unklarheit. Auf der einen Seite ist es wünschenswert, einen gemeinsamen großen Standard zu haben, z.B. in ähnlicher Form wie Boost.Threads. Auf der anderen Seite sind gerade Bibliotheken wie PThreads weit verbreitet. Sollte das Speichermodell tatsächlich getrennt von der Threading API umgesetzt werden, dann ist die Behandlung einer Standardbibliothek vorerst nicht erforderlich, da sich alle bereits bestehenden Bibliotheken verwenden lassen. Auch hierzu muss sich das Komitee noch äußern.</p>

<h2 id="lock-freie-datenstrukturen">Lock-freie Datenstrukturen</h2>

<p>Um Multithreading in C++ korrekt implementieren zu können, sind, wie in den vorangegangenen Abschnitten demonstriert, atomare Locking-Mechanismen erforderlich. Allerdings sind gerade diese anfällig für eine Reihe von Problemen, wie die in <a href="#synchronisation">Abschnitt 2.2</a> angesprochenen Live-/Deadlocks. Ohne Synchronisation entstehen allerdings auf Dauer Data Races, die den korrekten Ablauf des Programms mehr oder minder dem Zufall überlassen.</p>

<p><a id="figure-4"></a></p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">template</span> <span class="o">&lt;</span><span class="k">class</span> <span class="nc">T</span><span class="p">&gt;</span> 
<span class="kt">bool</span> <span class="nf">CAS</span><span class="p">(</span><span class="n">T</span><span class="o">*</span> <span class="n">addr</span><span class="p">,</span> <span class="n">T</span> <span class="n">expected</span><span class="p">,</span> <span class="n">T</span> <span class="n">fresh</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">addr</span> <span class="o">!=</span> <span class="n">expected</span><span class="p">)</span> 
        <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>     
    <span class="o">*</span><span class="n">addr</span> <span class="o">=</span> <span class="n">fresh</span><span class="p">;</span>     
    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span> 
<span class="p">}</span> 
</code></pre></div></div>
<p class="caption">Figure 4: CAS Beispiel (aus <a href="#referenzen">1</a>)</p>

<p>In <a href="#referenzen">1</a> stellt der Autor eine Methode zur Synchronisation vor, die nicht auf Locking-Mechanismen zurückgreift. Er nutzt stattdessen ein Primitiv, das nach <a href="#referenzen">11</a> ausreichend ist, um jede Datenstruktur thread-safe zu implementieren. Die CAS-Methode (Compare-And-Swap) ist dabei aus einigen anderen Anwendungsfällen lange bekannt: Source-Control-Management Systeme wie Subversion legen, statt für die Dauer der Bearbeitung einer Datei diese für alle anderen Teilnehmer zu sperren, eine lokale Kopie an. Der Benutzer verändert diese und schreibt sie zu einem anderen Zeitpunkt zurück. Hat sich der Zustand des Source-Archivs in dieser Zeit verändert, so muss der Nutzer ggf. Modifikationen an seinem eigenen Datensatz vornehmen, um das Endergebnis synchron zu halten. In <a href="#figure-4">Abbildung 4</a> ist eine Beispielimplementation dieses Mechanismus zu sehen. Viele moderen Prozessoren haben mittlerweile für einige Datentypen diesen Mechanismus als atomare Einheit implementiert (z.B. <code class="language-plaintext highlighter-rouge">CMPXCHG</code>).</p>

<p>Die Motivation für CAS ist, dass gemeinsame Daten weitaus öfter nur zum lesen genutzt werden, als zum beschreiben, wodurch die hohen Kosten der lokalen Kopie relativiert werden. Abgesehen davon werden implizit alle vorherigen Problematiken wie z.B. Deadlocks umgangen. Gegen CAS sprechen unkontrollierbare Prioritäten und die Schwierigkeit, Datenstrukturen an das neue Prinzip anzupassen. Daher ist der allgemeine Konsens, dass beide Verfahren gebraucht werden um den Code an passenden Stellen in Bezug auf die Performance zu verbessern, und gleichzeitig die Übersicht zu wahren.</p>

<p>Ein Umstand von CAS ist die Frage, wann der gemeinsam genutzte Speicher gelöscht werden soll. Sollte beim Löschen des Speichers noch ein Thread den Speicher in einer lokalen Kopie bearbeiten und anschliessend versuchen, zurückzuschreiben, so käme es zu einem undefinierten Verhalten. Um den Rahmen dieser Ausarbeitung nicht zu sprengen sei auf die Lösung in <a href="#referenzen">3</a> verwiesen.</p>

<h1 id="fazit">Fazit</h1>

<p>C++ ist die dominierende Sprache in der Softwareentwicklung. Mit den in den kommenden Jahren weiter zunehmenden, parallelisierten Architekturen ist ein Weg zu Optimierungen verfügbar, der jedoch die Anpassung des C++ Standards erfordert um sichere und korrekte Implementierung von Multithreadanwendungen zu erlauben. Dazu sind semantische Korrekturen an der Sprache, sowie eine Standardbibliothek für Multithreading nötig, um gleichzeitig Portabilität zu gewährleisten.</p>

<h1 id="referenzen">Referenzen</h1>

<ol>
  <li>Andrei Alexandrescu. Lock-free data structures. C/C++ User Journal, October 2004.</li>
  <li>Andrei Alexandrescu, Hans Boehm, Kevlin Henney, Ben Hutchings, Doug Lea, and Bill Pugh. Memory model for multithreaded c++: Issues. Technical Report WG21/N1777=J16/05-0037, ISO/IEC Information Technology Task Force, March 2005.</li>
  <li>Andrei Alexandrescu and Maged Michael. Lock-free data structures with hazard pointers. C/C++ User Journal, December 2004.</li>
  <li>Boehm. A memory model for c++: Strawman proposal, 2005. http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/mm.html.</li>
  <li>Hans Boehm. Threads cannot be implemented as a library. Technical Report HPL-2004-209, HP Laboratories Palo Alto, December 2004.</li>
  <li>Peter A. Buhr. Are safe concurrency libraries possible? Commun. ACM, 38(2):117-120, 1995.</li>
  <li>E. W. Dijkstra. Solution of a problem in concurrent programming control. Commun. ACM, 8(9):569, 1965. http://doi.acm.org/10.1145/365559.365617.</li>
  <li>Edsger W. Dijkstra. Cooperating sequential processes. In F. Genuys, editor, Programming Languages: NATO Advanced Study Institute, pages 43-112. Academic Press, 1968.</li>
  <li>D. Pham et al. The design and implementation of a first-generation cell processor. In ISSCC 2005 IEEE Int. Solid-State Circuits Conf. Dig. Tech. Papers, pages 184-185. 2005.</li>
  <li>Lois Goldthwaite. On the future evolution of c++. Technical Report JTC1/SC22/WG21 N1774=05-0034, ISO/IEC Information Technology Task Force, March 2005.</li>
  <li>Maurice Herlihy. Wait-free synchronization. ACM Trans. Program. Lang. Syst., 13(1):124-149, 1991. http://doi.acm.org/10.1145/114005.102808.</li>
  <li>Jeremy Manson, William Pugh, and Sarita V. Adve. The java memory model. In POPL '05: Proceedings of the 32nd ACM SIGPLAN-SIGACT symposium on Principles of programming languages, pages 378-391, New York, NY, USA, 2005. ACM Press. http://doi.acm.org/10.1145/1040305.1040336.</li>
  <li>Andrew S. Tanenbaum. Modern Operating Systems. Prentice Hall PTR, Upper Saddle River, NJ, USA, 2001.</li>
</ol> ]]></description>
            <pubDate>Thu, 11 Aug 2005 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/log/2005/08/11/erweiterte-konzepte.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhxNFpnRC9WbHdFRXV1UHM4blNsbGRPdzlHYQpyc1VUWmhtc3pabmhPRnF2NVk4OURaRUEvMjdMY2lDMS91VXcxamJ5RHlFN3RtR05MK2FYRXBIeGdIYVc1SkYwCm9RVUQKPTNmNk4KLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>General</category><category>Compilers</category><category>Security</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Compiler Extensions To Catch Security Holes - Dissected</title>
            <description><![CDATA[ <h1 id="abstract">Abstract</h1>

<p>Programming errors that lead to unreliable and insecure programs are a problem that has been around since the very beginning of computer science. Most of these errors are caught via extensive testing and debugging. However, more serious bugs are often left in the code unnoticed, because either the interaction through which they can occur is too complex to understand, given a certain time frame, or the testing phase is too expensive. This paper presents various automated methods, with a special focus on code analysis (both static and dynamic) to contain or isolate the damage.</p>

<h1 id="introduction">Introduction</h1>

<p>On August 11th of 2003, the rapidly spreading <em>Lovesan</em> worm (also known as <em>Blaster</em>), responsible for shutting down unpatched Windows XP machines every 60 seconds, used an error that was common in the world of programming for a long time: a buffer overflow. The massive damage that this particular worm caused with its dozen of siblings and modified copies is a reminder of what such a trivial thing like bounds checking can prevent if done properly.</p>

<p>However, in the age of the open source paradigm, the situation is more complex: verbatim copies of source snippets or imported libraries may contain bugs that will eventually contaminate ones own project (see <a href="#references">Kettlewell2003</a>). Different forks of the same codebase might be more or less secure, depending on the context it was modified in. Finally, to add to the already bad situation, not all programmers have the same notion for security: <em>additional checks will decrease performance</em> or <em>the cost to manage this or that for all input is too high</em> are two of many arguments that are often used against additional security and in favor of speed and memory. In the end, manually checking the code is tedious and error prone, while testing might require too much time investment. Thus tools exist which automate the process of security audits and checks to ensure that the most common errors are avoided.</p>

<p>This paper is based on <a href="#references">Ashcraft and Engler2002</a> and presents various methods to increase security aspects of source code, with a focus on compiler extensions for code analysis. The main attention lies on C code (since it is in wide use and very susceptible to most types of security errors), but the concepts can be transferred to any other language as well.</p>

<h1 id="a-brief-overview-of-all-methods">A brief overview of all methods</h1>

<p>The programmer scene spawned a variety of tools and methods to enhance security of any type of program with some simple, often non-intrusive steps. These methods and tools are presented in this section. A brief discussion of the possible pros and contras is appended to each presentation.</p>

<h2 id="using-language-features">Using language features</h2>

<p>Memory leaks in unmanaged code often lead to programs susceptible to DOS style attacks, filling the systems memory up with garbage until the machine locks up. One can use the language features to detect such leaks: C and C++ allow the substitution of global memory allocation and deallocation functions like C's malloc/free or the equivalent operator new/operator delete in C++. This method is used by various libraries to count the malloc/new calls and subtract their respective free/delete calls, leaving a value of 0 if there was no allocated memory left undeleted. For instance, the Microsoft Visual Studio[ Microsoft2004] C++ compiler comes with such a small library <a href="#references">MSDN2004</a>, which is active in debug mode and outputs all memory leaks (see <a href="#Figure-1">Figure 1</a>). A more sophisticated approach was taken in <a href="#references">Nettle2004</a>, but the basic concepts for detecting memory leaks are the same.</p>

<p>Rewriting global memory-specific functions is an easy, non-intrusive task and requires very little effort to be included into already existing code. Also, one can use the current compile mode to either use the memory management facilities or not, so the final release build is not affected. Of course, the method is limited to the language and cannot be used to detect all kinds of problems within the code.</p>

<p><a id="Figure-1"></a></p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">"stdafx.h"</span><span class="cp">
</span>
<span class="cp">#define CRTDBG_MAP_ALLOC
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;crtdbg.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">_tmain</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="n">_TCHAR</span><span class="o">*</span> <span class="n">argv</span><span class="p">[])</span>
<span class="p">{</span>
    <span class="kt">int</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">int</span><span class="p">[</span><span class="mi">3</span><span class="p">];</span>
    <span class="n">_CrtDumpMemoryLeaks</span><span class="p">();</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-batch highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">---------------------------------------------------------</span>
<span class="kd">Debug</span> <span class="kd">Output</span>:
<span class="kd">Detected</span> <span class="kd">memory</span> <span class="kd">leaks</span><span class="err">!</span>
<span class="kd">Dumping</span> <span class="kd">objects</span> <span class="o">-&gt;</span>
<span class="o">{</span><span class="m">42</span><span class="o">}</span> <span class="kd">normal</span> <span class="kd">block</span> <span class="nb">at</span> <span class="mh">0x002F10B0</span><span class="o">,</span> <span class="m">12</span> <span class="kd">bytes</span> <span class="kd">long</span>.
 <span class="kd">Data</span>: <span class="o">&lt;</span>            <span class="o">&gt;</span> <span class="kd">CD</span> <span class="kd">CD</span> <span class="kd">CD</span> <span class="kd">CD</span> <span class="kd">CD</span> <span class="kd">CD</span> <span class="kd">CD</span> <span class="kd">CD</span> <span class="kd">CD</span> <span class="kd">CD</span> <span class="kd">CD</span> <span class="kd">CD</span>
<span class="kd">Object</span> <span class="kd">dump</span> <span class="kd">complete</span>.
</code></pre></div></div>
<p class="caption">Figure 1: MS Visual Studio CrtDebug.</p>

<h2 id="replacement-functionslibraries">Replacement functions/libraries</h2>

<p>A straightforward method is to empirically analyze abused vulnerabilities from the past and look for common patterns. By doing this, project members of the OpenBSD team discovered that most of the errors in the kernel tree where due to C string library calls (see [ Miller and de Raadt1999]) that were either unsafe or simply used wrong. Those calls come in two flavors: the fast str*() and the <em>safe</em> strn*() (like strcpy() and strncpy()).</p>

<p>The downside of replacing unsafe functions in huge projects is obvious: If the interface has changed, the workload for rewriting the code is often too much. Simply replacing the calls automatically will eventually lead to wrong usage or code that didn't need the modification at all.</p>

<h2 id="compiler-extensions">Compiler extensions</h2>

<p>As described in <a href="#references">Ashcraft and Engler2002</a>, bugs can be spotted through static code analysis, either by external parsers in a meta-compilation step, or by internal compiler extensions such as described in the paper. Similar compiler extensions are already available as of <em>GCC</em> 2.95 <a href="#references">GCC2004</a>: <em>Stack-Smashing Protector</em> <a href="#references">Etoh2004</a> (formerly known as <em>ProPolice</em> or <em>StackGuard</em>) is a patch to the official distribution of <em>GCC</em>, which adds a new option <em>-fstack-protector</em>. By compiling C code with this option turned on, <em>GCC</em> will detect buffer overflows, prevent changes of return addresses and reorder variables to avoid memory corruption caused by an attacker. Additional code will be inserted to guarantee memory protection wherever necessary.</p>

<p>The downside of compiler extensions is mostly that they are proprietary to the compiler. Especially in cross-platform development, when one has to deal with different compilers and different architectures, the plug-ins or extensions might not be available or expose a completely new usage/syntax. For instance, the OpenBSD project has enhanced the <em>GCC</em> with one such compiler flag <em>-Wbounded</em> that will search for an extra code attribute to scan for boundary violations (see <a href="#references">OpenBSD2004</a>). This flag however is not available for other architectures.</p>

<h2 id="hardware-protection">Hardware protection</h2>

<p>One of the most typical exploits abused in todays software has its roots in the von Neumann architecture of modern computers: executable code that was once data. The basic principle of <em>John von Neumann</em> was that the two parts of a program, namely code and data, share a common part of the computers memory. Todays viruses and worms make use of this architectural design through buffer overflows, injecting new code to be executed under the current rights the system has granted to the binary that was attacked. To counter this misuse in a non-intrusive manner, newer CPU's support a filesystem-like convention to flag memory as either writable or executable. This flag - called <em>NX-Bit</em> - can be used by the operating system to protect parts of the memory.</p>

<p>Since the NX-Bit usage remains at kernel-level, software writers usually don't need to worry about memory protection at all. However, they cannot rely on it either, since the availability of this flag is CPU-dependent. On a different level, programs that make use of self-modifying code will run right into troubles: no virtual-machine (i.e. the one used by Java) or emulator will run properly, since these processes are likely to be killed for trying to execute memory they've just accessed for writing.</p>

<h2 id="software-protection">Software protection</h2>

<p>On more exotic hardware, the NX-Bit might not be available. Thus typical kernel-implementations or enhancements of the NX-Bit functionality often provide another solution by emulating the flag. Currently, there are five major implementations: WX of the OpenBSD project, Exec Shield by Red Hat kernel developer Ingo Molnar, PaX by the PaX Team, the Linux NX Patch as of Linux 2.6.8 and the Microsoft implementation for Windows. Out of these five, the first four mentioned can emulate NX-Bit capabilities on CPU's where it is not available.</p>

<p>While the emulation of such a flag on legacy hardware is a huge advantage, implementers often struggle with speed penalties.</p>

<h1 id="code-analysis">Code analysis</h1>

<p>Hardware protection itself is an added bonus to the security of the underlying system. However, preventing the bugs in the first place eliminates the need for such protection if done properly, otherwise code will tend to become insecure, if the reliance upon such protection is too big. Catching bugs in source code is supported through code analysis, a method to automatically derive information about the behavior of a program, which comes in two flavors: static and dynamic. This section will discuss both methods in more detail.</p>

<h2 id="static-code-analysis">Static code analysis</h2>

<p>Static code analysis involves a so called pre- or meta-compilation step, in which a second compiler will run through the code and (depending on the implementation) will search for patterns that might be a source of concern. Simple forms of static code analysis are performed at compile time by modern compilers like <em>GCC</em> or <em>Microsoft Visual C++</em> (see <a href="#Figure-2">Figure 2</a>). Different implementations of static code analysis for special classes of errors have been shown in <a href="#references">Larochelle and Evans2001</a> and <a href="#references">Viega et al. 2000</a>. While the first deals with buffer overflow analysis exclusively, the second approach uses a database of know vulnerability patterns to detect these kinds of errors in the source. Three other tools, which use a similar approach, are Microsoft's <em>PREfix</em>, <em>PREfast</em> and <em>Slam</em> (however, these tools use a hybrid technique that will be discussed in more detail later). Before a checkin is performed, all three tools can scan the code for common sources of bugs including</p>

<ul>
  <li>wrong memory management (double free, freeing non allocated blocks etc.)</li>
  <li>wrong pointer management (dereferencing NULL or invalid pointers or pointers to freed memory)</li>
  <li>missing initialization of variables</li>
  <li>bounds violations (validation failure, buffer overruns and underflows)
resource leakage</li>
</ul>

<p><a id="Figure-2"></a></p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">**</span> <span class="n">argv</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">int</span> <span class="n">a</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span> <span class="p">};</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>gcc bounds.c
bounds.c: In <span class="k">function</span> <span class="sb">`</span>main<span class="s1">':
bounds.c:5: warning: excess elements in array initializer
bounds.c:5: warning: (near initialization for `a'</span><span class="o">)</span>
</code></pre></div></div>
<p class="caption">Figure 2: Output of GCC after compiling malformed code.</p>

<p>By analyzing different code paths with all possible assumptions, these tools are able to catch bugs that would otherwise require much effort to be detected or wouldn't even be noticed at all, since all possible combinations are too complex to go through manually. The analyzing process of course needs the proper language information to detect error-patterns. The next two subsections will discuss how these patterns are acquired and used.</p>

<h3 id="rule-based">Rule-based</h3>

<p>Instead of hardwiring known bug-patterns into databases or the scanners themselves, <a href="#references">Ashcraft and Engler2002</a> discusses a general framework to detect flaws in source code through an extensible scripting system called <em>metal</em>. The so called <em>checkers</em>, which are written in a special script-like language, describe a state machine that is applied to match patterns in the source code. For instance, a range checker would start by analyzing variables whose value was entered by an external source (network packet, system call or user input) and flag this variable as <em>tainted</em>. Following the variables path through the source code by intercepting all return calls etc., the range checker will notice if the variable itself is compared at some time in the code against other values for an upper or lower bound before reaching a sink (that is, before the variable is not being passed on any further). If the bounds check on that variable (if any one occurred) was not satisfying enough, because for instance there was only an upper bounds check on a signed value, then the state machine will reach the <em>error-state</em> and reports the exact path the tainted value has traveled. Otherwise, the variables tainted-flag is removed.</p>

<p>The challenges that remain beside writing the actual checker are to reduce <em>false positives/negatives</em> (detected or undetected states that are both wrong), and often to overcome language or OS specific analyzing difficulties. For example, the range checker will need to identify incoming variables from external network sources. In this case, the following method can be applied: packets are both received or sent by filling a structure. To differentiate between incoming and outgoing packets, one can analyze the code if the structure is being read or written more often. However, this approach will eventually lead to false positives, since the usage of the structure depends more on the programmers <em>style</em> than on actual conventions.</p>

<p>On the other hand, the more dangerous <em>false negatives</em> need to be addressed. These are patterns that pose a threat to the security of the program, yet they haven't been detected by the checker. The most obvious type of pattern were this problem occurs is when for instance a tainted value is being passed through functions of a library. Since the source code for that library might not be available for further parsing, the value could be passed to a function of that library and be extracted by a subsequent call of another function of that library. Because the state is unknown while it resides inside the library calls, there are two possible solutions to this problem: either leave the tainted flag (which might produce false positives, because the library itself checks the incoming values), or remove the flag.</p>

<p>To sum up, the checkers are refined iteratively until they reach a general state with a minimum of false hits.</p>

<h3 id="belief-based">Belief-based</h3>

<p>Section <a href="#rule-based">3.1.1</a> discussed static code analysis, given that either a rule set exists or is created and refined manually. However, finding the appropriate rules for a system is tedious and often incomplete. To overcome this problem, <a href="#references">Engler et al. 2001</a> discusses an even more general approach by automatically extracting rules from the code itself without any prior knowledge. So instead of searching and examining regular patterns, the code is now being analyzed for <em>beliefs</em> that match simple templates like <code class="language-plaintext highlighter-rouge">&lt; b &gt; must not follow &lt; a &gt;</code> or <code class="language-plaintext highlighter-rouge">&lt; a &gt; is coupled with &lt; b &gt;</code>. By providing some of these templates, the code is being searched for contradictions (see <a href="#Figure-3">Figure 3</a> for an example, where a pointer is checked for validity after it has been dereferenced, implying that it must be valid). These contradictions are found by dividing all beliefs into two classes: MUST-beliefs, which are directly implied by the code, and MAY-beliefs that may or may not be a coincidence, depending on further statistical analysis. While contradictions of MUST-beliefs are flagged as an error, contradictions of MAY-beliefs need to be sorted and separated into actual beliefs and coincidences (if a certain pattern is found 999 of 1000 times in the code, it might be alright). For instance, in <a href="#Figure-3">Figure 3</a> there is a MUST-belief that the pointer tty is valid. This belief is contradicted by the statement two lines later, when it is checked for validity, thus provoking an error.</p>

<p>Looking at the big picture, the belief-based approach is an excellent way to deal with systems for which virtually no initial knowledge is available. Because the context in which the checks are applied is independent of the problem-space, one may find many errors in any project. This attribute is not only favorable while analyzing completely unknown programs, but also useful in big open-source projects where most contributors are only proficient in small parts of the source-tree. Of course, belief-based analysis suffers from false positives/negatives as well, because MAY-beliefs might be wrongly categorized when the statistics for a certain situation don't offer enough data.</p>

<p><a id="Figure-3"></a></p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">mxser_write</span><span class="p">(</span><span class="k">struct</span> <span class="nc">tty_struct</span> <span class="o">*</span><span class="n">tty</span><span class="p">,</span> <span class="p">...)</span> <span class="p">{</span>
    <span class="k">struct</span> <span class="nc">mxser_struct</span> <span class="o">*</span><span class="n">info</span> <span class="o">=</span> <span class="n">tty</span><span class="o">-&gt;</span><span class="n">driver_data</span><span class="p">;</span>
    <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">flags</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">tty</span> <span class="o">||</span> <span class="o">!</span><span class="n">info</span><span class="o">-&gt;</span><span class="n">xmit_buf</span><span class="p">)</span>
       <span class="k">return</span> <span class="p">(</span><span class="mi">0</span><span class="p">);</span>

    <span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>
<p class="caption">Figure 3: Impossible null-pointer check for tty (Source: <a href="#references">Engler et al. 2001</a>)</p>

<h2 id="dynamic-code-analysis">Dynamic code analysis</h2>

<p>While static code analysis does a good job at capturing even complex errors, many others can only be detected dynamically at run-time, since they involve the programs dynamic behavior. To address these errors, there are two major methods: The first one has been presented in <a href="#using-language-features">2.1</a>, but due to its language-specific nature, it can't be applied to detect all types of errors.</p>

<p>The second approach is to <em>execute</em> the program after compilation with some possible automatically inserted code portions, and evaluate its run-time behavior. This procedure can be compared to typical debugging, but with some extra analyzing functionality. A variation of this method is taken by the technique behind <em>PREfix</em>, which is presented in <a href="#references">Bush et al. 2000</a>. With this methodology, the analyzing process remains static, while the model that is used to capture bugs is dynamic. To do this, the whole analyzing process is split into two steps: First, the code is parsed and a complete syntax-tree is generated (as in the parsing step of a normal compiler). The syntax tree is used to determine caller-callee relationships and order of function calls. Second, the function-calls are either traced <em>bottom-up</em> (from a function back to the initial caller) or from the root or main function, simulating the current memory state of the <em>pseudo-executed</em> code in an isolated virtual machine that was set up for this task. Other implementations will insert additional code to monitor the current state of execution and then compile the program as usual. Because a complete map exists for all variables, arrays, functions and pointers, checks can be performed that would be impossible with static code analysis alone. These include checks for boundary violations of unknown or dynamic arrays, pointer and system call validity (for instance, open or closed file references), race conditions or wrong behavior after an exception. Especially the last case is a common source for memory leaks, open file descriptors and many other issues that fall into a completely new category that came to life after the introduction of <em>exceptions</em> in C++. Proper handling of these situations is summarized by <em>exception safety</em>, which is not discussed in this paper. The virtual machine is also useful to simulate different operating environments: memory exhaustion can be tested under several conditions without changing the actual hardware, as well as execution speed.</p>

<p>Dynamic analysis isn't only used for debugging: CPU vendors offer so called profilers for their architectures to enhance performance in programs. These tools output listings that show the execution speed of certain operations, so a programmer is able to locate bottlenecks in the code. One such profiler is available at <a href="#references">AMD2004</a>.</p>

<p>In direct contrast to static code analysis, the dynamic approach has its limitations: Depending on the implementation, not all code will be checked. If the program is executed, all possible tests need to be conducted (that is, all functionality needs to be tested) to cover the complete code. In case of <em>PREfix</em>, this is automated and not needed.</p>

<h1 id="summary">Summary</h1>

<p>Code analysis can save a lot of time and it is the next generation of debugging. Instead of protecting the system against vulnerabilities through sandboxing techniques or memory protection, bugs are caught at the source. The main difference here is that in a protected environment, programmers eventually tend to write less secure code, because they feel protected by the underlying system, while with code analysis, the program itself is more reliable and secure.</p>

<p>This paper has shown two approaches to code analysis: The static approach is used like an enhanced parser, which matches certain <em>bug-patterns</em>. Because those patterns can, on code level, yield complex results, code analysis tools are likely to find bugs that wouldn't have been spotted by the programmer. However, the crucial part to this operation is that either these patterns are known and a rule set to detect them can be written, or it was already available. In neither case, a <em>belief-based</em> method can help to extract the rules from the code, by searching for contradictions.</p>

<p>The dynamic approach will <em>execute</em> the program and analyze the state while it is running. There are different implementations on how the binary is executed, some of which require the program to compile normally, and others that emulate the state of all variables through a virtual machine. This is needed to address problems that involve the runtime behavior of a program or which are too hard to capture through static analysis.</p>

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

<ol>
  <li>AMD2004, <a href="http://www.developwithamd.com/appPartnerProg/codeanalyst/home/">CodeAnalyst Profiler</a>
</li>
  <li>Ashcraft and Engler2002, Ken Ashcraft and Dawson R. Engler. Using programmer-written compiler extensions to catch security holes. In Proceedings of the 2002 IEEE Symposium on Security and Privacy, pages 143-159, Los Alamitos, CA, May 12-15 2002. IEEE Computer Society.</li>
  <li>Bush et al. 2000, William R. Bush, Jonathan D. Pincus, and David J. Sielaff. A static analyzer for finding dynamic programming errors. Softw. Pract. Exper., 30(7):775-802, 2000.</li>
  <li>Engler et al. 2001, Dawson Engler, David Yu Chen, Seth Hallem, Andy Chou, and Benjamin Chelf. Bugs as deviant behavior: a general approach to inferring errors in systems code. SIGOPS Oper. Syst. Rev., 35(5):57-72, 2001.</li>
  <li>Etoh2004, Hiroaki Etoh. <a href="http://www.trl.ibm.com/projects/security/ssp/">Stack Smashing Protection</a>
</li>
  <li>GCC2004, <a href="http://gcc.gnu.org/">GNU Compiler Collection</a>
</li>
  <li>Kettlewell2003, Richard Kettlewell. <a href="http://seclists.org/lists/bugtraq/2003/Feb/0271.html">Bugtraq: buffer overrun in zlib 1.1.4</a>
</li>
  <li>Larochelle and Evans2001, David Larochelle and David Evans. <a href="http://www.usenix.org/events/sec01/larochelle.html">Statically detecting likely buffer overflow vulnerabilities. In 10th USENIX Security Symposium, pages 177-190. University of Virginia, Department of Computer Science, USENIX Association, August 2001</a>.</li>
  <li>Microsoft2004, Microsoft. <a href="http://msdn.microsoft.com/vstudio/">Visual Studio .net</a>
</li>
  <li>Miller and de Raadt1999, Todd C. Miller and Theo de Raadt. <a href="http://www.openbsd.org/papers/strlcpy-paper.ps">strlcpy and strlcat - consistent, safe, string copy and concatenation. In USENIX, editor, Usenix Annual Technical Conference. June 6-11, 1999. Monterey, California, USA, pages 175-178, Berkeley, CA, USA, 1999. USENIX</a>.</li>
  <li>MSDN2004, Microsoft MSDN. <a href="http://msdn.microsoft.com/library/en-us/vsdebug/html/vxconenablingmemoryleakdetection.asp">Enabling Memory Leak Detection</a>
</li>
  <li>Nettle 2004, Paul Nettle. <a href="http://www.fluidstudios.com/pub/FluidStudios/MemoryManagers/">Memory Manager</a>
</li>
  <li>OpenBSD 2004, OpenBSD. <a href="http://www.openbsd.org/cgi-bin/man.cgi?query=gcc-local">Manual pages: gcc-local, 2004</a>.</li>
  <li>Viega et al. 2000, J. Viega, J. T. Bloch, Y. Kohno, and G. McGraw. Its4: A static vulnerability scanner for c and c++ code. In ACSAC '00: Proceedings of the 16th Annual Computer Security Applications Conference, page 257. IEEE Computer Society, 2000.</li>
</ol> ]]></description>
            <pubDate>Mon, 11 Apr 2005 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/log/2005/04/11/compiler-extensions.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhxZ3J3RC9YVjBoSldWV0ZIaTErbjA2aDBTSApSSkdrbjVHMEYxbUZ4Rms3TTFmRkh6QUEvM1VXWG9ZTkdNZTd0U0plUUN5NnlLaDJEZk9MeXROdU9UUi9nWHc3CktUc00KPU41OVMKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>General</category><category>Compilers</category><category>Security</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Static Terrain Shadowmapping</title>
            <description><![CDATA[ <h1 id="introduction">Introduction</h1>

<p>There are various ways to make your terrain look realistic in respect to
lighting. But because of the huge amount of data to be processed, dynamic
lighting for instance isn't a good choice if your lightsource, the sun, never
moves because it is on a static skybox. And even if the sun would change its
position from time to time, it wouldn't make great sense to slow down your
engine with calculating lights and shadows per frame. Also, the calculation of
drop shadows is even more a burden to your FPS.</p>

<h1 id="the-trick">The Trick</h1>

<p>Our approach to the problem is to pregenerate a shadowmap that will be used as
a texture. To generate this map, we need two things: the heightmap and the
sun's position. Given the position of the sun, the calculation is very simple:
Draw a straight line from the sun to the point of interest in the heightmap.
If at any point the line's height is lower than the current height of the map,
the sun ray won't reach the point, which therefore will be black. In other
terms, as long as the sun ray isn't blocked by something in the terrain (like
a hill), it will directly hit the point we are examining, which in this case
will be white. Figure 1 will clear things up.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2003_04_sts_image1.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2003_04_sts_image1.jpg" alt="Figure 1: Sunray and covered pixels." title="Figure 1: Sunray and covered pixels.">
    
    </a>
    
    
    <figcaption>Figure 1: Sunray and covered pixels.</figcaption>
    
</figure>

<p>Some explanation for the image: The sunray v is a vector, its height at point
<em>p</em> is <em>v(p)</em>. <em>h(p)</em> is the height of the terrain at point <em>p</em>.
To process the whole landscape, we simply iterate through all pixels in the
heightmap and find out if they will be influenced by the light or not. This
is done with the steps in the following list:</p>

<ul>
  <li>Fetch point of interest (POI) <em>p</em>
</li>
  <li>Calculate vector <em>v</em> from <em>p</em> to sun</li>
  <li>Iterate through all pixels covered by <em>v</em>
</li>
  <li>If <em>v(p)</em> ≤ <em>h(p)</em> at any pixel, stop iteration and mark it as black</li>
</ul>

<h1 id="implementation-issues">Implementation Issues</h1>

<p>As easy as it seems at first glance, there are some obstacles to overcome.
Like the sun, all scene elements in this calculation will be represented 
through vectors. So, the sun's position will be stored in a vector, as well as the 
ray. The problem now is that the heightmap itself is a discrete representation 
of the terrain, which doesn't fit too well into our floating-point vector-model. So, to 
evaluate which "pixel" is covered by our sun ray vector, we need to interpolate
the float values of the vector into int values of the heightmap. To minimize 
the error, the best solution is to use Lerp, but I chose to just round the 
values to the next int (this will result in some small numerical errors).
Warning: Do not simply cast floats to int! It'll result in horrible errors,
mostly recognizable in the areas where the vectors have a negative sign in one
of their x or z components. Another question is how to iterate along the vector
v: Because we don't know the direction of v (it constantly changes from one 
POI to another), we can't simply "move to the next pixel", so we follow the 
vector v back to the sun step by step. But how long is a step? The normalized
v should be enough! Illustration 2 is the final result for the heightmap 
shown below with the sun's position at the top-center.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2003_04_sts_image2.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2003_04_sts_image2.jpg" alt="Figure 2: Turning a heightmap into a shadowmap." title="Figure 2: Turning a heightmap into a shadowmap.">
    
    </a>
    
    
    <figcaption>Figure 2: Turning a heightmap into a shadowmap.</figcaption>
    
</figure>

<h1 id="code">Code</h1>

<p>The code itself isn't a big issue. The comments should help you out!</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">round</span><span class="p">(</span><span class="kt">float</span> <span class="n">n</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="p">((</span><span class="kt">int</span><span class="p">)</span><span class="n">n</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="mf">0.5</span><span class="p">)</span>
        <span class="k">return</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="p">;</span>
    <span class="k">else</span>
        <span class="k">return</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">n</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">Vector3</span> <span class="n">CurrentPos</span><span class="p">;</span>
    <span class="n">Vector3</span> <span class="n">LightDir</span><span class="p">;</span>
    <span class="n">Vector3</span> <span class="n">Sun</span><span class="p">(</span><span class="mf">128.0f</span><span class="p">,</span> <span class="mf">512.0f</span><span class="p">,</span> <span class="mf">256.0f</span><span class="p">);</span>
    
    <span class="n">Heightmap</span> <span class="n">hmap</span><span class="p">(</span><span class="s">"hmap.bmp"</span><span class="p">);</span>
    <span class="n">Heightmap</span> <span class="n">ShadowMap</span><span class="p">;</span>
    
    <span class="kt">int</span> <span class="n">LerpX</span><span class="p">,</span> <span class="n">LerpZ</span><span class="p">;</span>
    
    <span class="k">const</span> <span class="kt">int</span> <span class="n">MapWidth</span> <span class="o">=</span> <span class="n">hmap</span><span class="p">.</span><span class="n">GetWidth</span><span class="p">(),</span> <span class="n">MapHeight</span> <span class="o">=</span> <span class="n">hmap</span><span class="p">.</span><span class="n">GetHeight</span><span class="p">();</span>
    
    <span class="c1">//Initialize new shadow map</span>
    <span class="n">ShadowMap</span><span class="p">.</span><span class="n">Create</span><span class="p">(</span><span class="n">MapWidth</span><span class="p">,</span> <span class="n">MapHeight</span><span class="p">);</span>
    
    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"Status = "</span><span class="p">;</span>
    
    <span class="c1">//For every pixel on the map</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">z</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">z</span><span class="o">&lt;</span><span class="n">MapHeight</span><span class="p">;</span> <span class="o">++</span><span class="n">z</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">x</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">x</span><span class="o">&lt;</span><span class="n">MapWidth</span><span class="p">;</span> <span class="o">++</span><span class="n">x</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c1">//Set current position in terrain</span>
            <span class="n">CurrentPos</span><span class="p">.</span><span class="n">Set</span><span class="p">((</span><span class="kt">float</span><span class="p">)</span><span class="n">x</span><span class="p">,</span> <span class="n">hmap</span><span class="p">.</span><span class="n">Get</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">z</span><span class="p">),</span> <span class="p">(</span><span class="kt">float</span><span class="p">)</span><span class="n">z</span><span class="p">);</span>
    
            <span class="c1">//Calc new direction of lightray</span>
            <span class="n">LightDir</span> <span class="o">=</span> <span class="n">Sun</span> <span class="o">-</span> <span class="n">CurrentPos</span><span class="p">;</span>
            <span class="n">LightDir</span><span class="p">.</span><span class="n">Normalize</span><span class="p">();</span>
    
            <span class="n">ShadowMap</span><span class="p">.</span><span class="n">Set</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">z</span><span class="p">,</span> <span class="mi">255</span><span class="p">);</span>
    
            <span class="c1">//Start the test</span>
            <span class="k">while</span> <span class="p">(</span> <span class="n">CurrentPos</span><span class="p">.</span><span class="n">x</span><span class="p">()</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span>
                    <span class="n">CurrentPos</span><span class="p">.</span><span class="n">x</span><span class="p">()</span> <span class="o">&lt;</span> <span class="n">MapWidth</span> <span class="o">&amp;&amp;</span> 
                    <span class="n">CurrentPos</span><span class="p">.</span><span class="n">z</span><span class="p">()</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> 
                    <span class="n">CurrentPos</span><span class="p">.</span><span class="n">z</span><span class="p">()</span> <span class="o">&lt;</span> <span class="n">MapHeight</span> <span class="o">&amp;&amp;</span> 
                    <span class="n">CurrentPos</span> <span class="o">!=</span> <span class="n">Sun</span> <span class="o">&amp;&amp;</span> <span class="n">CurrentPos</span><span class="p">.</span><span class="n">y</span><span class="p">()</span> <span class="o">&lt;</span> <span class="mi">255</span> <span class="p">)</span>
            <span class="p">{</span>
                <span class="n">CurrentPos</span><span class="o">+=</span><span class="n">LightDir</span><span class="p">;</span>
        
                <span class="n">LerpX</span> <span class="o">=</span> <span class="n">round</span><span class="p">(</span><span class="n">CurrentPos</span><span class="p">.</span><span class="n">x</span><span class="p">());</span>
                <span class="n">LerpZ</span> <span class="o">=</span> <span class="n">round</span><span class="p">(</span><span class="n">CurrentPos</span><span class="p">.</span><span class="n">z</span><span class="p">());</span>
    
                <span class="c1">//Hit?</span>
                <span class="k">if</span><span class="p">(</span><span class="n">CurrentPos</span><span class="p">.</span><span class="n">y</span><span class="p">()</span> <span class="o">&lt;=</span> <span class="n">hmap</span><span class="p">.</span><span class="n">Get</span><span class="p">(</span><span class="n">LerpX</span><span class="p">,</span> <span class="n">LerpZ</span><span class="p">))</span>
                <span class="p">{</span> 
                    <span class="n">ShadowMap</span><span class="p">.</span><span class="n">Set</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">z</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
                    <span class="k">break</span><span class="p">;</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">z</span><span class="o">%</span><span class="mi">32</span><span class="p">))</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"0"</span><span class="p">;</span>
    <span class="p">}</span>
    
    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
    
    <span class="n">ShadowMap</span><span class="p">.</span><span class="n">Save</span><span class="p">(</span><span class="s">"result.bmp"</span><span class="p">);</span>
    
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h1 id="final-words">Final Words</h1>

<p>The final result is a shadowmap in respect to the sun's position. There are
two options to use it: Either by multitexturing, or by multiplication with the
terrain texture. This of course is dependent on your texturing method. The
pictures below are an example of a terrain texture multiplied with the
shadowmap.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2003_04_sts_image3.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2003_04_sts_image3.jpg" alt="Figure 3: Multiplied terrain- and shadowmap." title="Figure 3: Multiplied terrain- and shadowmap.">
    
    </a>
    
    
    <figcaption>Figure 3: Multiplied terrain- and shadowmap.</figcaption>
    
</figure>

<p>Another implementation idea would be to fade out the shadow at the edges, but 
in most cases the image will blur out anyway. <a href="https://www.flipcode.com/archives/Advanced_Lightmapping.shtml">For different times of daylight</a>,
one could generate 12 or 24 different lightmaps and change them on the fly
when the skybox changes it's texture (I haven't yet tried this myself).</p>

<h1 id="acknowledgements-and-references">Acknowledgements and References</h1>

<p>Sebastian Wagner for "debugging" this text.</p>

<ol>
  <li>Luke Hodorowicz, <a href="https://www.flipcode.com/archives/Advanced_Lightmapping.shtml">Advanced Lightmapping</a>
</li>
</ol> ]]></description>
            <pubDate>Fri, 18 Apr 2003 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/log/2003/04/18/static-terrain-shadowmapping.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhwRnlnRC9XZDRLUWRXSk8rb1AvaFpVZlVyYQp5b0tub1ZzWVJkMG5rTUFyOEpmS25WRUJBTUxXdGYrUTRPaUxWMVB6cDBIa3dhb3o3d0wrT29aWXluS3BybkIrCjBOME4KPWwvNjEKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>General</category><category>Lightmapping</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
        <item>
            <title>Terrain Texture Generation Redux</title>
            <description><![CDATA[ <h1 id="introduction">Introduction</h1>

<p>A terrain isn't textured by one pattern alone. Depending on the heightmap,
there are a variety of features from sandy beaches to snowtop mountains that make up the range of possible texture combinations. With a set of textures for different landscape attributes at different heights and a heightmap, the only question that remains is: how to mix the textures? This short article will explain how the final texture for the terrain is generated.</p>

<figure>

    
    <a href="https://www.tobias-franke.eu/layout/logcache/2001_04_ttgr_image01.jpg">
    
        <img src="https://www.tobias-franke.eu/layout/logcache/2001_04_ttgr_image01.jpg" alt="Figure 1: Heightmap, texture and rendering" title="Figure 1: Heightmap, texture and rendering">
    
    </a>
    
    
    <figcaption>Figure 1: Heightmap, texture and rendering</figcaption>
    
</figure>

<p>Terrain Texture Generation Redux is a new version of <a href="https://www.flipcode.com/archives/Terrain_Texture_Generation.shtml">my old article on flipCode</a>, but without the horrible english!</p>

<h1 id="mixing-textures">Mixing textures</h1>

<p>Let's assume that the landscape has <em>n</em> different attributes.
For every attribute, there is a corresponding texture, for instance snow or grass. The mechanism works like this: Iterate through all pixels (x/y) in the heightmap and read the height-value at that location. Then, depending on the height, fetch the texture representing the attribute for a height-region and add that color to an output-texture at the same location (x/y). For example, a landscape that has height-values ranging from 0 to 255 could be divided like this:</p>

<ul>
  <li>196-255: Snow</li>
  <li>128-195: Rock</li>
  <li>64-127: Grass</li>
  <li>0-63: Sand</li>
</ul>

<p>The problem with this approach would be the very sharp edges on the output texture when two pixels border two regions. What would be way better is to fade the pixels from one region to another. To do that, there's need for a fading-function.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">float</span> <span class="nf">texfactor</span><span class="p">(</span><span class="kt">float</span> <span class="n">h1</span><span class="p">,</span> <span class="kt">float</span> <span class="n">h2</span><span class="p">,</span> <span class="kt">float</span> <span class="n">w</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">float</span> <span class="n">percent</span><span class="p">;</span>
    <span class="n">percent</span> <span class="o">=</span> <span class="p">(</span><span class="n">w</span> <span class="o">-</span> <span class="n">std</span><span class="o">::</span><span class="n">abs</span><span class="p">(</span><span class="n">h1</span> <span class="o">-</span> <span class="n">h2</span><span class="p">))</span> <span class="o">/</span> <span class="n">w</span><span class="p">;</span>
    
    <span class="k">if</span><span class="p">(</span><span class="n">percent</span> <span class="o">&lt;</span> <span class="mf">0.0f</span><span class="p">)</span> 
        <span class="n">percent</span> <span class="o">=</span> <span class="mf">0.0f</span><span class="p">;</span>
    <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">percent</span> <span class="o">&gt;</span> <span class="mf">1.0f</span><span class="p">)</span> 
        <span class="n">percent</span> <span class="o">=</span> <span class="mf">1.0f</span><span class="p">;</span>

    <span class="k">return</span> <span class="n">percent</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The function shown in the sourcecode is a weighting-function and has two parameters <em>h1</em> and <em>h2</em>, which represent two height-values, and a third parameter <em>w</em> that represents the absolute height of one region. What this function does is to return a percentage value that indicates whether <em>h2</em> is in a region defined by a representative value in that region <em>h1</em>, for instance the upper border. The last parameter is to adjust the height of one region, so in the case of four height-regions that equally divide a range of 256 values, <em>w</em> would be set to 64.</p>

<p>Moving further with the above example: The percentage of visibility for grass for a height value of 200 would yield 0%, since at that height there's only rocks and snow left.</p>

<h1 id="code">Code</h1>

<p>In order to make it all work, some code to read and write image files is also necessary! Assuming that it's already been taken care off, the following code effectively does the magic:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nc">rgb</span>
<span class="p">{</span>
    <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">r</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">b</span><span class="p">;</span>
<span class="p">};</span>

<span class="kt">void</span> <span class="nf">generate_terrain_texture</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">bitmap</span><span class="o">&gt;&amp;</span> <span class="n">attributes</span><span class="p">,</span> 
                              <span class="n">bitmap</span><span class="o">&amp;</span> <span class="n">heightmap</span><span class="p">,</span>
                              <span class="n">bitmap</span><span class="o">&amp;</span> <span class="n">output</span><span class="p">,</span>
                              <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">max_height</span><span class="p">)</span>

<span class="p">{</span>
    <span class="c1">// the height of a region</span>
    <span class="kt">float</span> <span class="n">region_height</span> <span class="o">=</span> <span class="n">max_height</span><span class="o">/</span><span class="n">attributes</span><span class="p">.</span><span class="n">size</span><span class="p">();</span>
    
    <span class="c1">// percentage of visibility for each attribute</span>
    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">float</span><span class="o">&gt;</span> <span class="n">texture_factor</span><span class="p">;</span> 
    
    <span class="c1">// the height at pos (x/y)</span>
    <span class="kt">float</span> <span class="n">hmap_height</span><span class="p">;</span> 

    <span class="c1">// the new rgb values to be written</span>
    <span class="n">rgb</span> <span class="n">color</span><span class="p">;</span>

    <span class="c1">// all rgb values at pos (x/y) for all attributes</span>
    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">rgb</span><span class="o">&gt;</span> <span class="n">old_color</span><span class="p">;</span>

    <span class="c1">// iterate through all pixels</span>
    <span class="k">for</span><span class="p">(</span><span class="kt">size_t</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="n">heightmap</span><span class="p">.</span><span class="n">width</span><span class="p">;</span> <span class="o">++</span><span class="n">y</span><span class="p">)</span> 
    <span class="k">for</span><span class="p">(</span><span class="kt">size_t</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="n">heightmap</span><span class="p">.</span><span class="n">height</span><span class="p">;</span> <span class="o">++</span><span class="n">x</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="c1">// get height at pos (x/y) out of bitmap</span>
        <span class="n">hmap_height</span> <span class="o">=</span> <span class="n">heightmap</span><span class="p">.</span><span class="n">getheight</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">);</span> 
        
        <span class="k">for</span><span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">attributes</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c1">// get percentage for all bitmaps(Regions)</span>
            <span class="n">texture_factor</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span>
                <span class="n">texfactor</span><span class="p">(</span><span class="n">max_height</span> <span class="o">-</span> <span class="n">region_height</span><span class="p">,</span> <span class="n">hmap_height</span><span class="p">)</span>
            <span class="p">);</span>
            
            <span class="c1">// read all texture rgb values</span>
            <span class="n">attributes</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">getcolor</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">color</span><span class="p">);</span>
            <span class="n">old_color</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">color</span><span class="p">);</span>
        <span class="p">}</span>
        
        <span class="n">assert</span> <span class="p">(</span><span class="n">old_color</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">==</span> <span class="n">texture_factor</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&amp;&amp;</span>
        <span class="s">"Error: More colors than factors available or vice versa!"</span><span class="p">);</span>
        
        <span class="n">color</span><span class="p">.</span><span class="n">r</span> <span class="o">=</span> <span class="n">color</span><span class="p">.</span><span class="n">g</span> <span class="o">=</span> <span class="n">color</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        
        <span class="c1">// compose new rgb values for final texture</span>
        <span class="k">for</span><span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">old_color</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
        <span class="p">{</span>        
            <span class="n">color</span><span class="p">.</span><span class="n">r</span> <span class="o">+=</span> <span class="n">old_color</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">r</span> <span class="o">*</span> <span class="n">texture_factor</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
            <span class="n">color</span><span class="p">.</span><span class="n">g</span> <span class="o">+=</span> <span class="n">old_color</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">g</span> <span class="o">*</span> <span class="n">texture_factor</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
            <span class="n">color</span><span class="p">.</span><span class="n">b</span> <span class="o">+=</span> <span class="n">old_color</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">b</span> <span class="o">*</span> <span class="n">texture_factor</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
        <span class="p">}</span>
        
        <span class="c1">// write new color to texture</span>
        <span class="n">output</span><span class="p">.</span><span class="n">setcolor</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">color</span><span class="p">);</span> 
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h1 id="conclusion-and-acknowledgements">Conclusion And Acknowledgements</h1>

<p>Besides mixing various texturesets together into one large image, there are also other methods like texture-splatting. The method described here has the disadvantage that in order to make it look good, the texture itself has to be incredibly large, or else it will just blur out. One can counter that with a detail texture.</p>

<p>This article has been used by Keith Ditchburn, one of the programmers of Emperor: Battle for Dune, for his <a href="https://web.archive.org/web/20101128170648/http://www.toymaker.info/html/texgen.html">T2 Texture Generator</a> (which by now is way more advanced than just generating textures the way it is described in this article).</p>

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

<ol>
  <li>Tobias Alexander Franke, <a href="https://www.flipcode.com/archives/Terrain_Texture_Generation.shtml">Terrain Texture Generation</a>
</li>
  <li>Keith Ditchburn, <a href="https://web.archive.org/web/20101128170648/http://www.toymaker.info/html/texgen.html">T2 Texture Generator</a>
</li>
</ol> ]]></description>
            <pubDate>Mon, 30 Apr 2001 00:00:00 +0200</pubDate>
            <link>https://www.tobias-franke.eu/log/2001/04/30/static-terrain-texture-generation-redux.html</link>
            <guid>LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlKRUVBQllLQURrV0lRU0VrQTY1YTFHMmJ3RDIvWXg0WjdIbzN4ZzBlZ1VDYWt6N294c1VnQUFBQUFBRUFBNXQKWVc1MU1pd3lMalVyTVM0eE1pd3dMRE1BQ2drUWVHZXg2TjhZTkhvV2ZnRUE0a2tZQkxKNjI5Q3NXMkZsMXNZQwpHRUZyS1Qvd24xOHhDZnhtS0VUZ1pmVUErd1pZdnhmRzZ5MjNCeFpxV29hRWhWZ09td1dSN0dsbXFISWhzNjJICmpiNEoKPWNFM3EKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCg==</guid>
            <category>General</category><category>TextureSynthesis</category>
            <comments>https://graphics.social/@thefranke</comments>
        </item>
        
    </channel>
</rss>