<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
<title>Alain Galvan · Ray Tracing Driver Engineer at AMD</title>
<link>https://alain.xyz/</link>
<description>Alain Galvan · Ray Tracing Driver Engineer at AMD. Personal portfolio and blog with articles and projects focused on computer graphics engineering.</description>
<language>en</language>
<lastBuildDate>Wed, 21 Oct 2026 15:23:28 GMT</lastBuildDate>
<generator>foil-rss</generator>
<docs>https://www.rssboard.org/rss-specification</docs>
<category>alain</category><category>galvan</category><category>remote</category><category>west palm beach</category><category>miami</category><category>florida</category><category>new york city</category><category>amd</category><category>marmoset</category><category>gpu</category><category>ai</category><category>vulkan</category><category>opengl</category><category>directx</category><category>metal</category><category>webgpu</category><category>graphics</category><category>programmer</category><category>artist</category><category>indie</category><category>tutorial</category><category>mathematics</category><category>rendering</category><category>demo</category><category>3D</category><category>realtime</category><category>shader</category><category>raytracing</category><category>webgl</category><category>glsl</category><category>research</category>
<item>
<title>Ragnarok Inheritance</title>
<link>https://alain.xyz/blog/ragnarok-inheritance</link>
<description>A story about inheriting a large sum of gold in Ragnarok Online, and the implications that has on meta-gameplay.</description>
<guid isPermaLink="true">https://alain.xyz/blog/ragnarok-inheritance</guid>
<pubDate>Mon, 29 Apr 2024 00:30:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>ragnarok</category><category>online</category><category>zeny</category><category>inheritance</category><category>paladin</category><category>royal guard</category><category>swordsman</category>
<enclosure url="https://alain.xyz/blog/ragnarok-inheritance/assets/cover.png" length="10897" type="image/png"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/ragnarok-inheritance/assets/poring-green-peach-net.gif"/><link rel="preload" as="image" href="https://alain.xyz/blog/ragnarok-inheritance/assets/moc_fild13-ratemyserver.gif"/><p>When I was younger, I played a popular MMORPG which combined detailed 2D isometric pixel art with 3D environments, called <a href="https://renewal.playragnarok.com/">Ragnarok Online</a>.</p>
<p>This was a game that had massive environments and hundreds of thousands of players, PvP and PvE elements, rare items, and a bustling economy of players buying and selling items.</p>
<p><img src="https://alain.xyz/blog/ragnarok-inheritance/assets/poring-green-peach-net.gif" alt="Poring Sprite by The Spriter&#x27;s Resource"/></p>
<p>Despite this being a virtual world, the individual interests and incentive structures of the game aligned themselves in such a way that players collaborated to reduce the cost of travel and improving themselves. Thus, like the real world, a large amount of wealth had concentrated in people that had very valuable items and services, <em>with little stopping the accumulation of wealth and inflation</em>.</p>
<p>The amount of money that could be made from mercantile business was astonishing, where simply performing PvE item sales could net you a few hundred or thousand of the currency, merchandise regularly sold for <strong>100-1000 times</strong> that amount or even more thanks to the economy&#x27;s standing wealth, reminiscent of our own where the multiplicative nature of the finance market leads to more standing wealth.</p>
<p>Similar stories exist in other massively multiplayer games, such as <a href="https://play.runescape.com/">Runescape</a>, <a href="https://www.tibia.com/mmorpg/free-multiplayer-online-role-playing-game.php">Tibia</a>, <a href="https://maplestory.nexon.net/">Maple Story</a>, and of course, <a href="https://worldofwarcraft.blizzard.com/en-us/">World of Warcraft</a>. The value of <em>person to person trade</em> is astonishing and serves as a window to how valuable it is in the real world and how societies progress <em>Maslow&#x27;s hierarchy of needs</em>.</p>
<h2 id="windfall">Windfall</h2>
<p><img src="https://alain.xyz/blog/ragnarok-inheritance/assets/moc_fild13-ratemyserver.gif" alt="Sograt Desert"/></p>
<p>On the close of a typical school day in 5th grade, I ran to my father&#x27;s <em>HP Pavilion a618x</em>, turned on my <code>1280x1024</code> pixel CRT display, and logged onto Ragnarok Online. While exploring the innermost parts of the <a href="http://www.iroclassic.net/db/worldmap.php">map</a> alone, a borderland between the Sograt Desert and Payon Forest, I stumbled across another player that proposed something interesting. They claimed their friend was logging off for good and wanted to transfer in game currency to another player, with the promise that I might receive half.</p>
<p>Looking back on it now, it sounded like they were setting me up as a <em>cash mule</em>. Perhaps the player quitting was actually part of some cash-to-currency exchange or sold duplicated items. Maybe they did something socially terrible to their online friends group, or perhaps their parents just wanted them to quit playing so much. I can&#x27;t say what their motivations were, just that mine were immediately blinded by greed and my 10 year old naivete couldn&#x27;t see the possible red flags.</p>
<p>In hindsight, they were most likely just quitting, as the amount was a paltry sum in the context of the game economy, but very noticeable to me.</p>
<p>I was to take 3 million zeny, split in 2, so <code>1.5M</code> to me, <code>1.5M</code> to the other player. The amount of <em>access</em> this windfall afforded me was astonishing. At the time the most I ever saved was <code>60,000</code> zeny (0.04%), so suddenly having access to <code>1.5M</code> meant the world was open to me. I could <em>buy items</em> sold by players in the <a href="https://ragnarok.fandom.com/wiki/Prontera">capital city of Prontera</a>, build a community via a guild, or <em>travel and explore</em> the greater map quickly through air travel and actually afford the high fare to use <a href="https://ragnarok.fandom.com/wiki/Airship">airships</a>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Eventually I&#x27;d find myself exploring the entire map, becoming a <em>Crusader</em>, then <em>Paladin</em>, then finally a <em>Royal Guard</em>, and without that windfall I wouldn&#x27;t have maintained the motivation or desire to keep going since the calculus of time-to-value was growing too high. Thus it became clear how meta-gameplay of economics and social structures also factored into time savings. For the marginalized, the poor, young and lacking, these moments can resonate with them as they discover their real-life analogues later in life. It makes you wonder how that story might play out in the lives of others, and how they might react if they knew they were born to inherit a silver spoon or not.</p>
<p>Games are meant to teach lessons, and socioeconomic lessons are among the most difficult lessons of all to teach and gamify.</p>
<h3 id="resources">Resources</h3>
<ul>
<li>
<p><a href="https://forums.unrealengine.com/t/how-to-create-2d-characters-in-3d-world-like-ragnarok-online-boomer-shooter/1308742">How to create 2D characters in a 3D World like Ragnarok Online by Fofuxo.</a></p>
</li>
<li>
<p><a href="https://ratemyserver.net/">Rate My Server - Ragnarok Database</a></p>
</li>
</ul>]]></content:encoded>
</item>
<item>
<title>Espresso Research and Engineering</title>
<link>https://alain.xyz/blog/espresso-research-engineering</link>
<description>A comprehensive overview on crafting the perfect espresso drinks, reviewing machine parts, extraction profiles, and types of drinks.</description>
<guid isPermaLink="true">https://alain.xyz/blog/espresso-research-engineering</guid>
<pubDate>Sat, 03 Feb 2024 22:00:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>espresso</category><category>coffee</category><category>ristretto</category><category>machine</category><category>roaster</category><category>parts</category><category>e61</category><category>portafilter</category><category>profiling</category>
<enclosure url="https://alain.xyz/blog/espresso-research-engineering/assets/cover.jpg" length="166216" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/espresso-research-engineering/assets/espresso-machine-with-bops.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/espresso-research-engineering/assets/kenya-onyx2.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/espresso-research-engineering/assets/coffee-fruits.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/espresso-research-engineering/assets/mexican-onyx.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/espresso-research-engineering/assets/espresso-sizes.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/espresso-research-engineering/assets/scale.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/espresso-research-engineering/assets/extraction-graph.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/espresso-research-engineering/assets/latte-sizes.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/espresso-research-engineering/assets/latte-art.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/espresso-research-engineering/assets/e61-grouphead.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/espresso-research-engineering/assets/grinders.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/espresso-research-engineering/assets/roasters.svg"/><p>The art of making the perfect espresso drink is an engaging and interesting problem to solve. From engineering the perfect espresso machine, gathering data such as bean grind size/weight/distribution, water type/volume, and managing the pressure in the group head at different stages in the extraction process; your 1-2 ounces of espresso requires careful consideration to nail the perfect shot. In addition, the dexterous art of pouring the perfect drink is challenging and satisfying when successful. One must make sure to position of the milk pitcher/cup correctly, manage flow velocity, properly homogenize your liquids, and use specific techniques that make pouring feel more like working with a canvas and brush.</p>
<table><thead><tr><th style="text-align:center"></th><th style="text-align:center"></th><th style="text-align:center"></th><th style="text-align:center"></th></tr></thead><tbody><tr><td style="text-align:center"><img src="https://alain.xyz/blog/espresso-research-engineering/assets/espresso-machine-with-bops.jpg" alt="Espresso setup"/></td><td style="text-align:center"><img src="https://alain.xyz/blog/espresso-research-engineering/assets/kenya-onyx2.jpg" alt="Heart latte"/></td><td style="text-align:center"><img src="https://alain.xyz/blog/espresso-research-engineering/assets/coffee-fruits.jpg" alt="Coffee fruits"/></td><td style="text-align:center"><img src="https://alain.xyz/blog/espresso-research-engineering/assets/mexican-onyx.jpg" alt="Mexican onyx latte"/></td></tr></tbody></table>
<p>It wouldn&#x27;t be an exaggeration to say <em>I love espresso drinks</em>. They&#x27;re delicious and everything about them is so pretty, the rich dark crema, the art, the stainless steel machinery and ceramic cups. I&#x27;ve been roasting beans, buying roasts directly from local roasters, making roast blends, brewing espresso, and making hearts, leaves, and rosetta art on single/double shot lattes/cortados multiple times a day, every day for the past few years. In addition, I&#x27;ve been dabbling lightly in homesteading dairy cows and growing coffee plants, though not enough to get a drink yet!</p>
<blockquote>
<p>Do you folks like coffee? Real coffee, from the hills of Colombia? ~ <a href="https://www.youtube.com/watch?v=VFURd_EOIuU">Duncan Hills Coffee Jingle (Deathklok Season 1 Episode 1)</a></p>
</blockquote>
<p>The following are a summary of my notes on crafting espresso drinks, from the engineering to the dexterous art that goes on top. Crafting drinks and engineering the tools to do so is a passion a sizable group of people share, so please take a look at the references and additional resources for more contacts, modifications, and tools.</p>
<h2 id="drinks">Drinks</h2>
<p>A sizable amount of people don&#x27;t know the names of the drinks they want, and cafes are aware of this. Thus when you order a latte, cortado, flat white, cappuccino, etc. you&#x27;ll often get the same exact drink. There are definitions for these drinks such as the <em>Instituto Espresso Italiano (IET)</em> Italian Espresso , however, I&#x27;ll provide a definition based on what most baristas and roasters local to the United States agree on.</p>
<h3 id="espresso">Espresso</h3>
<p><img src="https://alain.xyz/blog/espresso-research-engineering/assets/espresso-sizes.svg" alt="Espresso scale"/></p>
<p>Espresso is normally a <em>single ounce</em> for a <strong>single shot</strong> (or <em>2 ounces</em> in the case of a <strong>double shot</strong>) of concentrated coffee brewed from an espresso machine. That&#x27;s a rough definition however, as it&#x27;s more accurate to craft an espresso based on the weight of the initial dose of grinds. That weight can vary depending on your goals, are you aiming for a stronger more concentrated shot, or one that&#x27;s lighter?</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>ϕ</mi><mo>≈</mo><mn>1</mn><mo>:</mo><mn>2</mn></mrow><annotation encoding="application/x-tex">\phi \approx 1:2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord mathnormal">ϕ</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≈</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">:</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">2</span></span></span></span></span>
<p>There&#x27;s a wide range of flavors that an espresso can have, from sweet and fruity to sour and bitter, and depend heavily on how how the shot is pulled extracted and the beans you choose to use. Generally a shot should be a ratio of <code>1:2</code>, so for <em>every gram</em> of espresso grinds, you extract <em>2 grams</em> of espresso. This also tells you how big your portafilter basket should be, enough to accommodate your intended amount of espresso.</p>
<p><img src="https://alain.xyz/blog/espresso-research-engineering/assets/scale.svg" alt="Espresso scale"/></p>
<p>With coffee beans, the fresher the better, and light to medium roasts tends to have the most floral flavors, and dark roasts tend to have the more caramel and bitter flavors.</p>
<p><img src="https://alain.xyz/blog/espresso-research-engineering/assets/extraction-graph.svg" alt="Extraction graph chart"/></p>
<p><strong>Extraction</strong> refers to the process of making a shot of espresso. When pulling a shot of espresso, your compacted grinds go through the following brewing steps:</p>
<ol>
<li>
<p><strong>Pre-infusion</strong> is when you soak your espresso puck with low pressure water to start your extraction at generally 1-3 bar of pressure. Once the puck is fully immersed in water, we can begin extracting at high pressure.</p>
</li>
<li>
<p><strong>Infusion</strong> is when you&#x27;re pushing high pressure water through the puck. 9 bars of pressure is the standard for espresso, but lever action machines can have a curve of pressure.</p>
</li>
</ol>
<blockquote>
<p>Clive coffee goes into more detail here on pre-infusion <a href="https://clivecoffee.com/blogs/learn/what-is-pre-infusion">here</a>.</p>
</blockquote>
<p>As for types of espresso, these include:</p>
<ul>
<li>
<p><strong>Ristretto</strong> - typically 40 grams or 1.4 oz. A short shot of espresso, made of only the first portion of a shot, and is thus more flavorful.</p>
</li>
<li>
<p><strong>Turboshot</strong> - a fast espresso drink that&#x27;s shorter than the typical 25-35 second extraction.</p>
</li>
<li>
<p><strong>Lungo</strong> - A longer shot where more water passes through a shot.</p>
</li>
</ul>
<h3 id="latte">Latte</h3>
<p><img src="https://alain.xyz/blog/espresso-research-engineering/assets/latte-sizes.svg" alt="Latte sizes"/></p>
<p>A <strong>Latte</strong> can vary greatly depending on what you&#x27;re looking for. The cup its served in generally determines what you get to drink, with <em>12 oz cups being the standard</em>, and 8 oz cups being an alternative size. As much as the cup is advertized as a given size, it&#x27;s real capacity is often more than its size. Some 6 oz cups hold 8.5 oz max. Some 12 oz cups hold exactly 12 oz max. Usually the standard is 12 oz exactly.</p>
<ul>
<li>
<p>A <strong>Flat White</strong> is a drink with very little to no foam, so little to no stretching of the milk, and only frothing/heating it. In spanish communities, a <em>Cafe con Leche</em> is very similar to a flat white, but uses 2-3 oz of coffee instead of an ounce of espresso.</p>
</li>
<li>
<p>A <strong>Cortado</strong> is A 6 oz drink with 1 oz of espresso, and 5 oz of steamed milk. It&#x27;s a concentrated beverage, and essentially half of a 12 oz latte.</p>
</li>
<li>
<p><strong>Cappuccino</strong>s are often interchangeable with a latte, but have significantly more foamy milk, and often have a layer of white foam on top, garnished with powdered chocolate and even decorated.</p>
</li>
</ul>
<h2 id="latte-art">Latte Art</h2>
<p><img src="https://alain.xyz/blog/espresso-research-engineering/assets/latte-art.svg" alt="Latte art design diagrams"/></p>
<p>It&#x27;s possible to draw anything on top of your espresso drink, even going so far as to <a href="https://www.aliexpress.us/item/3256805122112183.html">print art on top of a given drink</a>. You could do use a <a href="https://www.amazon.com/Decorating-Stencils-SENHAI-Cappuccino-Chocolate/dp/B01NCBERTV/ref=sxin_14_pa_sp_search_thematic_sspa">stencil</a> with a <a href="https://www.amazon.com/Electrical-Decoration-Portable-Cappuccino-Professional/dp/B0BW8Y7ZKS/ref=sr_1_6">chocolate powder dispenser</a>, or use a <a href="https://www.amazon.com/Stainless-steel-Coffee-Latte-Art-Pen/dp/B0B2Z6R6KV/ref=sr_1_7">stainless steel latte art pen</a> to adjust your art, but for the most part that&#x27;s very easy. The challenge with latte art is <em>only using steamed milk</em> to do it.</p>
<h3 id="texturing">Texturing</h3>
<p>Getting the perfect milk texture is paramount to successfully doing latte art. Generally you want to let the milk naturally disable the &quot;ripping&quot; sound, by forming a layer of foam at a medium density for lattes, with lower/higher amounts of <em>stretching</em> depending on if you&#x27;re aiming for a flat white or cappuccino. This can vary from machine to machine, and is ultimately driven by your steam wand pressure.</p>
<p>When pouring, maintaining a constant flow and reasonable velocity is very important, that is, approximately 3 fluid oz per second.</p>
<ul>
<li>
<p>Stir your espresso and swirl your steamed milk well to make each more homogenous. <a href="https://www.youtube.com/watch?v=iI09bbH22vQ">You&#x27;ll find the best parts of your espresso at the bottom</a> of the liquid.</p>
</li>
<li>
<p>Pour from above to establish your base. You may want to add some chocolate powder prior to this for some delicious contrast to the white milk coming later.</p>
</li>
<li>
<p>Try resting the spout on the edge of your cup, once you&#x27;ve poured your base. This can offer more control over the pour.</p>
</li>
<li>
<p>Pour closely to establish your white art on top, and form a heart by pouring forward, do pulsing waves for a rosetta, and/or restart the process for tulip layers.</p>
</li>
</ul>
<h2 id="espresso-machines">Espresso Machines</h2>
<p><img src="https://alain.xyz/blog/espresso-research-engineering/assets/e61-grouphead.svg" alt="Espresso E61 grouphead diagram"/></p>
<p>Espresso machines come at a wide range of prices, from very low to very high. The following is a summary of machines at a variety of price ranges under <code>$6000 USD</code>. Realistically, a good price target that have the majority of important features a barista is looking for is around <code>$1500</code> USD, and entry level is about <code>$500</code> USD. Espresso machines can be <em>very expensive</em>, but what matters are specs and performance. A few things to look out for are:</p>
<ol>
<li>
<p><strong>What&#x27;s the heating system?</strong> The cheapest machines use thermoblocks, while more expensive machines use heat exchangers and dedicated boiler(s). <a href="#ref_hoffmann2022">[Hoffmann 2022]</a></p>
</li>
<li>
<p><strong>Is it lever based or feature a flow control system?</strong> There&#x27;s 2 schools of thought with espresso, constant or variable pressure, and generally the variable pressure of a lever machine can result in uniquely flavored espresso.</p>
</li>
<li>
<p><strong>How computerized is the machine?</strong> Does it feature a PID controller or multiple? Does it feature flow controls, timers, scales, etc?</p>
</li>
<li>
<p><strong>What&#x27;s the group head size?</strong> Usually you want a standard E61 group head, as this is the size with the most accessories.</p>
</li>
<li>
<p><strong>What are the components made of?</strong> Plastic, stainless steel, brass, chrome, wood?</p>
</li>
<li>
<p><strong>Is it modable?</strong> Anything can be modded to add sensors, replace parts with wood, but some products have more resources and tutorials than others. The <a href="https://blog.arduino.cc/2022/11/25/enjoy-the-ultimate-espresso-machine-on-a-budget-with-gaggiuino/">Gaggia Classic Evo Pro arduino mod</a> can improve shot profiles tremendously.</p>
</li>
</ol>
<table><thead><tr><th style="text-align:right">Price</th><th style="text-align:left">Name</th><th style="text-align:left">Links</th><th>Details</th></tr></thead><tbody><tr><td style="text-align:right">$2399.00</td><td style="text-align:left">Profitec Pro 600</td><td style="text-align:left"><a href="https://clivecoffee.com/products/profitec-pro-600-espresso-machine?variant=40593294852184">Clive Coffee</a></td><td>E61, Dual Boiler, Shot Timer, PID</td></tr><tr><td style="text-align:right">$2919.00</td><td style="text-align:left">Bezzera DUO MN</td><td style="text-align:left"><a href="https://www.wholelattelove.com/products/bezzera-duo-mn-dual-boiler-espresso-machine-with-flow-control-total-black?variant=40799423823926">Whole Love Latte</a></td><td>Flow Control, PID, Dual Boiler</td></tr><tr><td style="text-align:right">$3399.00</td><td style="text-align:left">ECM Synchronika</td><td style="text-align:left"><a href="https://www.wholelattelove.com/products/ecm-synchronika-espresso-machine-olive-wood">Whole Love Latte</a></td><td>Dual Boiler, PID</td></tr><tr><td style="text-align:right">$2695.00</td><td style="text-align:left">Lucca M58</td><td style="text-align:left"><a href="https://clivecoffee.com/products/lucca-m58-espresso-machine-by-quick-mill">Clive Coffee</a></td><td>Dual Boiler, PID</td></tr><tr><td style="text-align:right">$2395.00</td><td style="text-align:left">Quick Mill QM67 Evo</td><td style="text-align:left"><a href="https://www.chriscoffee.com/products/quick-mill-qm67-evo">Chris Coffee</a></td><td>Dual Boiler, PID</td></tr><tr><td style="text-align:right">$2294.15</td><td style="text-align:left">Bezzera Strega</td><td style="text-align:left"><a href="https://www.wholelattelove.com/products/bezzera-strega-lever-espresso-machine?variant=14409161015355">Whole Love Latte</a></td><td>Lever Action, PID, Single Boiler</td></tr><tr><td style="text-align:right">$1799.95</td><td style="text-align:left">Lelit Mara X</td><td style="text-align:left"><a href="https://www.seattlecoffeegear.com/lelit-bianca-espresso-machine">Seattle Coffee Gear</a></td><td>L58E Group Head, Dual Boiler</td></tr><tr><td style="text-align:right">$1850.00</td><td style="text-align:left">Rocket Espresso Appartamento</td><td style="text-align:left"><a href="https://www.seattlecoffeegear.com/rocket-espresso-appartamento-espresso-machine">Seattle Coffee Gear</a></td><td>Heat Exchanger</td></tr><tr><td style="text-align:right">$1940.00</td><td style="text-align:left">Rancilio Silvia Pro X</td><td style="text-align:left"><a href="https://www.ranciliogroup.com/rancilio/silvia-pro-x/">Rancillo Group</a></td><td>E61 Group Head, Heat Exchanger, PID Controller, 2 L Water Tank</td></tr><tr><td style="text-align:right">$1725.00</td><td style="text-align:left">Ascaso Steel Duo PID</td><td style="text-align:left"><a href="https://ascaso-usa.com/products/steel-duo-pid-programmable-espresso-machine-w-volumetric-controls-dual-thermoblock-120v-white">Ascaso USA</a></td><td>Dual Thermoblock, PID</td></tr><tr><td style="text-align:right">$699.95</td><td style="text-align:left">Breville Barista Express</td><td style="text-align:left"><a href="https://www.amazon.com/Breville-BES870XL-Barista-Express-Espresso/dp/B00CH9QWOU/ref=sr_1_1">Amazon</a></td><td>Thermoblock, built in rotary grinder</td></tr><tr><td style="text-align:right">$549.00</td><td style="text-align:left">Gaggia Classic Evo Pro</td><td style="text-align:left"><a href="https://www.amazon.com/Gaggia-RI9380-50-Classic-Espresso/dp/B086H1W384">Amazon</a></td><td>Thermoblock</td></tr></tbody></table>
<p>There&#x27;s other machines at higher price points for the prosumer/commercial market, while there&#x27;s a very large number of these machines, here&#x27;s a few:</p>
<ul>
<li>
<p><a href="https://tone-swiss.com/">Tone Swiss</a> - Arguably the most interesting technologically, Tone Swiss has been working on highly efficient espresso machines.</p>
</li>
<li>
<p><a href="https://lamarzoccousa.com/">La Marzocco</a> - The <em>Gibson</em> of the espresso machine world, La Marzzoco makes incredible espresso consistently.</p>
</li>
<li>
<p><a href="https://victoriaarduino.com/en/home-en/">Victoria Arduino</a> - This company has been designing espresso machines since <em>1905</em>!</p>
</li>
<li>
<p><a href="https://www.sanremomachines.com/en/products/you/">Sanremo Coffee Machines</a> - Designing high quality commercial espresso machines with smart features.</p>
</li>
</ul>
<p>There&#x27;s also other styles of machines such as manual lever press machines.</p>
<h3 id="components">Components</h3>
<ul>
<li>
<p><strong>Proportional-Integral-Derivative (PID) Controller</strong> - Manages the machine target temperature.</p>
</li>
<li>
<p><strong>Over Pressurization Valve (OPV)</strong> - Manages extraction pressure. Generally low end espresso machines have this set too high, you should aim to have this configured for 9 bars, but this could even be managed by a microcontroller for pressure profiling.</p>
</li>
<li>
<p><strong>Grouphead</strong> - the mechanism your portafilter attaches to, that holds the spout where pressurized water comes out. The <em>E61 grouphead</em> is one of the more common designs, modeled after the Faema E61.</p>
</li>
<li>
<p><strong>Portafilter</strong> - The insert that holds your basket and grinds. Most come in 58mm in diameter, while other like the Breville barista express use 53mm.</p>
</li>
<li>
<p><strong>Portafilter Basket</strong> - The stainless steel basket inside the portafilter. The size and shape depend on the dose you intend to put in it, with double shot baskets resulting in the best extraction.</p>
</li>
<li>
<p><strong>Steam Wand</strong> - The tool to steam milk.</p>
</li>
<li>
<p><strong>Drip Tray</strong> - Where excess water gathers for disposal later.</p>
</li>
</ul>
<h2 id="grinders">Grinders</h2>
<p><img src="https://alain.xyz/blog/espresso-research-engineering/assets/grinders.svg" alt="Grinders diagram"/></p>
<p>Some espresso machines such as the Breville come with a built in grinder, but most do not, so you&#x27;re required to purchase a grinder separately.</p>
<p>For grinders, what&#x27;s important is:</p>
<ol>
<li>
<p><strong>How fine they can it grind?</strong> This is often measured in average particle size. When brewing espresso, the common meme phrase is: <em>&quot;Did you try grinding finer?&quot;</em>, and it&#x27;s true!</p>
</li>
<li>
<p><strong>How quickly can it grind?</strong> as a rule, the bigger the burrs the faster it can grind.</p>
</li>
<li>
<p><strong>What&#x27;s the retention rate of the unit?</strong> Does it feature a plasma generator? Bellow to blow air?</p>
</li>
<li>
<p><strong>How computerized is it?</strong> Does it have a built in scale? Variable grind size profiles?</p>
</li>
</ol>
<table><thead><tr><th style="text-align:right">Price</th><th style="text-align:left">Name</th><th style="text-align:left">Links</th><th>Details</th></tr></thead><tbody><tr><td style="text-align:right">$649.00</td><td style="text-align:left">Turin DF83</td><td style="text-align:left"><a href="https://www.turingrinders.com/products/turin-df83-single-dose-grinder">Turin</a></td><td>83mm flat burrs</td></tr><tr><td style="text-align:right">$700.71</td><td style="text-align:left">Niche Duo</td><td style="text-align:left"><a href="https://www.nichecoffee.co.uk/products/niche-duo">Niche</a></td><td>83mm flat burrs</td></tr><tr><td style="text-align:right">$989.00</td><td style="text-align:left">Eureka Oro Mignon Single Dose Espresso Grinder</td><td style="text-align:left"><a href="https://clivecoffee.com/products/eureka-oro-mignon-single-dose-espresso-grinder?variant=39749971083352">Clive Coffee</a></td><td>65mm flat steel burrs, single dose design</td></tr><tr><td style="text-align:right">$959.04</td><td style="text-align:left">Barsetto E6 Professional Coffee Grinder</td><td style="text-align:left"><a href="https://www.aliexpress.us/item/3256805270580273.html">Aliexpress</a></td><td>64mm titanium burrs, built in scale</td></tr><tr><td style="text-align:right">$1299.00</td><td style="text-align:left">La Marzocco Mazzer Lux D Espresso Grinder</td><td style="text-align:left"><a href="https://www.voltagerestaurantsupply.com/products/la-marzocco-lux-d">Voltage Restaurant Supply</a></td><td>61mm burrs</td></tr><tr><td style="text-align:right">$2299.00</td><td style="text-align:left">Mahlkonig E65S Espresso Grinder</td><td style="text-align:left"><a href="https://prima-coffee.com/equipment/mahlkonig/e65s-mahlk-pp">Prima Coffee</a></td><td>65mm flat steel burrs, programmable, 2.6 lb hopper</td></tr><tr><td style="text-align:right">$2139.00</td><td style="text-align:left">Weber Workshops The Key</td><td style="text-align:left"><a href="https://weberworkshops.com/products/key-grinder">Weber Workshops</a></td><td>83mm flat burrs, all metal build</td></tr><tr><td style="text-align:right">$299.90</td><td style="text-align:left">Varia VS3 Grinder</td><td style="text-align:left"><a href="https://www.variabrewing.com/products/varia-vs3-grinder">Varia</a></td><td>48m conical steel burrs</td></tr><tr><td style="text-align:right">$385.00</td><td style="text-align:left">Femobook A5</td><td style="text-align:left"><a href="https://femobook.myshopify.com/products/a5-electric-coffee-grinder">Femobook</a></td><td>53mm flat burrs, Compact, Battery Operated</td></tr></tbody></table>
<h2 id="roasters">Roasters</h2>
<p>Roasting beans yourself can be an extermely effective way of producing amazing coffee. Here&#x27;s a few high tech roasters:</p>
<ul>
<li>
<p><a href="https://kaleidoroasters.com/products/220v-m10-dual-1-2kg-coffee-roaster-220v-artisan-system-kaleido-tablet">Kaleido</a></p>
</li>
<li>
<p><a href="http://www.stronghold-technology.com/product/s7pro.sq">Stronghold</a></p>
</li>
</ul>
<h3 id="accessories">Accessories</h3>
<p>For espresso machine accessories, it&#x27;s important to bear in mind sizes. Check the size of your group head and portafilter prior to getting any accessory. Generally machines have 58mm group heads, but cheaper Breville/Sage machines uses 53mm.</p>
<ul>
<li>
<p><strong>Portafilter</strong> - Can be bottomless, or with different spouts.</p>
</li>
<li>
<p><strong>Dosing Ring</strong> - A ring used to help keep your portafilter clean.</p>
</li>
<li>
<p><strong>Dosing Cup</strong> - A cup that can be used to ensure doing weights when serving espresso and keep your grinds clean.</p>
</li>
<li>
<p><strong>Filter Paper</strong> - a paper that goes on the bottom of your espresso basket.</p>
</li>
<li>
<p><strong>Puck Screen</strong> - A screen that goes on top of your tamped coffee. Helps keep the group head clean.</p>
</li>
<li>
<p><strong>Refractometer</strong> - Measure your extraction yield. Generally you should aim for a 20-25% yield.</p>
</li>
</ul>
<p>A few vendors of accessories include:</p>
<ul>
<li>
<p><a href="https://www.amazon.com/stores/NORMCORECOFFEETOOLS/page/0EFB4AD4-4390-4B58-BEEA-826A37195A80">Normcore</a></p>
</li>
<li>
<p><a href="https://www.mhw3bomber.com/en/">MHW-3BOMBER</a></p>
</li>
<li>
<p><a href="https://www.notneutral.com/">notNeutral</a></p>
</li>
<li>
<p><a href="https://www.amazon.com/stores/Coffeezone/page/5C7DD300-8AB9-4A9A-920C-AC493CB8F239">Coffeezone</a></p>
</li>
<li>
<p><a href="https://subminimal.com/products/flick">Subminimal</a></p>
</li>
<li>
<p><a href="https://prima-coffee.com">Prima Coffee</a></p>
</li>
<li>
<p><a href="https://www.timemore.com/">Timemore</a></p>
</li>
</ul>
<h2 id="roasters-1">Roasters</h2>
<p><img src="https://alain.xyz/blog/espresso-research-engineering/assets/roasters.svg" alt="Roaster box art"/></p>
<p>There&#x27;s a variety of commercial roasters that ship quality roasted coffee to your doorstep, among which are:</p>
<ul>
<li>
<p><a href="https://onyxcoffeelab.com/">Onyx Coffee Lab</a></p>
</li>
<li>
<p><a href="https://www.blackwhiteroasters.com/">Black White Roasters</a></p>
</li>
<li>
<p><a href="https://theboyandthebear.com/">The Boy and the Bear Roasters</a></p>
</li>
<li>
<p><a href="https://www.seycoffee.com/">Sey Coffee</a></p>
</li>
<li>
<p><a href="https://hscoffeeroasters.com/">HS Coffee Roasters</a></p>
</li>
<li>
<p><a href="https://sweetbloomcoffee.com/">Sweet Bloom Coffee</a></p>
</li>
<li>
<p><a href="https://prestacoffee.com/">Presta</a></p>
</li>
<li>
<p><a href="https://www.bigfacebrand.com/collections/bf-gifting/products/bf-iof-02">Bigface</a></p>
</li>
<li>
<p><a href="https://kurasu.kyoto/collections/single-origin/products/brazil-marciano-tomazini-light-roast-1">Kurasu</a></p>
</li>
</ul>
<p>There&#x27;s large and medium sized roasters such as:</p>
<ul>
<li>
<p><a href="https://counterculturecoffee.com/">Counter Culture</a></p>
</li>
<li>
<p><a href="https://www.stumptowncoffee.com/">Stumptown</a></p>
</li>
<li>
<p><a href="https://www.citygirlcoffee.com/">City Girl Coffee</a></p>
</li>
</ul>
<p>And depending on your area, you may find great local roasters. Here in Florida there&#x27;s:</p>
<ul>
<li>
<p><a href="https://www.buddybrew.com/">Buddy Brew</a></p>
</li>
<li>
<p><a href="https://www.foxtailcoffee.com/">Foxtail Coffee</a></p>
</li>
<li>
<p><a href="https://blazingbean.com/">Blazing Bean Roasters</a></p>
</li>
</ul>
<p>There&#x27;s certainly more roasters out there, so look for local roasters in your area.</p>
<h2 id="additional-resources">Additional Resources</h2>
<ul>
<li>
<p><a href="https://espressoaf.com/">Espresso Aficionados</a></p>
<ul>
<li><a href="https://discord.gg/espresso">Discord</a></li>
</ul>
</li>
<li>
<p><a href="https://www.baristahustle.com/">Barista Hustle</a></p>
</li>
<li>
<p><a href="https://www.milancoffeefestival.com/?lang=en-GB">The Milan Coffee Festival</a></p>
</li>
<li>
<p><a href="https://www.coffeeexpo.org/">Specialty Coffee Expo</a></p>
</li>
<li>
<p><a href="https://www.espressoparts.com/">Espresso Parts</a></p>
</li>
<li>
<p><a href="https://www.cropster.com/">Cropster</a></p>
</li>
<li>
<p><a href="https://mtpak.coffee/">Mtpack</a></p>
</li>
<li>
<p><a href="https://www.roestcoffee.com/roestblog/rate-of-rise">Roest</a></p>
</li>
<li>
<p><a href="https://espressosetupbuilder.com/">Espresso Setup Builder</a></p>
</li>
</ul>
<h3 id="video">Video</h3>
<ul>
<li>
<p><a href="https://www.youtube.com/channel/UCR2_8uHyWcTSV6iIw6BN4rg">Lance Hedrick</a></p>
</li>
<li>
<p><a href="https://www.youtube.com/channel/UCN6R2iBmZ4N30ruHAhG34Kw">Emilee Bryant</a></p>
</li>
<li>
<p><a href="https://www.youtube.com/@jameshoffmann">James Hoffmann</a></p>
</li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_hoffmann2022"><span class="markademic-citename">[Hoffmann 2022]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">How to Make the Best Coffee at Home</span></strong></em><br/><span class="markademic-authors"><span><span>James Hoffmann</span></span></span><br/><span><span class="markademic-publisher"> Firefly Books 2022</span><br/></span><span><a href="http://www.theworldatlasofcoffee.com/">theworldatlasofcoffee.com</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Guitar Pedal Circuitry</title>
<link>https://alain.xyz/blog/guitar-pedal-circuitry</link>
<description>Pedal circuit components make a big impact in the overall output of a guitar or electric instrument. How do components and their many circuit permutations create common sounds?</description>
<guid isPermaLink="true">https://alain.xyz/blog/guitar-pedal-circuitry</guid>
<pubDate>Sat, 10 Jun 2023 00:30:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>notes</category><category>guitar</category><category>circuit</category><category>electrical</category><category>engineering</category>
<enclosure url="https://alain.xyz/blog/guitar-pedal-circuitry/assets/cover.svg" length="6318" type="application/octet-stream"/>
<content:encoded><![CDATA[<p>Finding the perfect guitar tone might seem like an <em>abstract goal</em> that&#x27;s hard to quantify, but there is a science to it that for the most part has been solved, to the point where companies like Fender and Gibson have transitioned from solving musical engineering problems to largely <a href="https://www.latimes.com/entertainment/music/la-et-ms-gibson-brands-guitar-henry-juszkiewicz-20170618-htmlstory.html">labeling themselves as a &#x27;lifestyle&#x27; brand</a>. Thus following in the footsteps of clothing companies by consistently raise prices while lowering quality. The circuits behind the most popular guitars, speakers, amplifiers, pedals, etc. are public knowledge thanks to reverse engineering and just basic intuition, and understanding how the entire sound stack works is a fascinating subject in and of itself.</p>
<p>While tone might seem impossible to quantify, there&#x27;s no such thing as <a href="https://forum.pedalpcb.com/threads/klon-kliche-diodes.8732/">&quot;magic diodes&quot;</a>, tone is a signal, and what you hear is primarily influenced by how it&#x27;s compressed, filtered, and combined, both prior to the speaker, and especially after as arguably the biggest impact on your sound is your cabinet and the speaker cones that make it up.</p>
<h2 id="humbuckers">Humbuckers</h2>
<p>EMG 81s use copper coils that connect to different parts of the preamp stage built into them to produce their sound, and use a smaller humbucker internally, about the size of a firebird pickup, with a solid magnet piece across. This results in a pickup that is less sensitive to interference and has a lower noise floor. This <a href="https://electrosmash.mas-effects.com/emg81.html">article</a> goes into the circuit design in more detail.</p>
<p>Fishmans use <a href="https://www.guitarcenter.com/riffs/buying-guides/accessories/fishman-fluence-pickups">stacked PCBs</a> for their pickups, which lets them control which parts of the pickup they can turn on/off to get a single coil/alnico sound with the different voice options. While this is a departure from traditional wound copper coils, the result is consistency and ease of variability. Tosin Abasi for instance detailed how he designed his signature Fishmans in this <a href="https://www.youtube.com/watch?v=pKXd8PAl2dc">video</a>.</p>
<p>Currently the state of the art in commercial pickups looks to be Fishmans as they&#x27;re consistent and have high variability.</p>
<h2 id="pedal-components">Pedal Components</h2>
<p>Pedals aren&#x27;t magic, they&#x27;re intentional designs meant to solve specific musical problems, such as introducing clipping, adding fuzz, or denoising a signal. To understand these effects, we can examine the fundamental building blocks used in their design. While this list isn&#x27;t exhaustive, it covers the most common components found in pedal motherboards. For a detailed look at how these components interact, you can refer to a this <a href="https://electrosmash.mas-effects.com/tube-screamer-analysis.html">Tube Screamer Circuit Analysis</a>.</p>
<h3 id="resistors-and-capacitors">Resistors and Capacitors</h3>
<p>Resistors and capacitors form an <strong>RC (resistor-capacitor) filter</strong>, where the capacitor acts as a frequency-dependent gate and the resistor controls how strongly the capacitor can divert signal to ground, setting the <a href="https://freecircuitsim.com/guides/rc-filter-guide.html">cutoff frequency</a>.</p>
<ul>
<li>
<p><strong>Low Pass Filters</strong>: the capacitor shunts high frequencies to ground as signal frequency rises, darkening or &quot;warming&quot; the tone and cutting fizz from a distorted signal.</p>
</li>
<li>
<p><strong>High Pass Filters</strong>: the capacitor blocks low frequencies while passing high ones, brightening the tone and removing muddy lows.</p>
</li>
</ul>
<p>A guitar&#x27;s <a href="https://www.amplifiedparts.com/tech-articles/basic-electric-guitar-circuits-2-pots-tone-capacitors">tone pot</a> uses an RC circuit. As you roll it down, the resistance to ground drops, letting the tone capacitor bleed more high frequencies away and gradually darkening the sound. Larger capacitors bleed more highs for a warmer result, while smaller ones preserve the natural treble of the instrument.</p>
<h3 id="transistors">Transistors</h3>
<p>Transistors are the primary way to generate a classic fuzz sound, often by providing hard clipping to the input signal. The type of transistor used significantly impacts your sound:</p>
<ul>
<li>
<p><strong>BJT (Bipolar Junction Transistor)</strong>: Classic fuzz and distortion, like for the Big Muff Pi. They are known for high gain and the ability to produce aggressive, saturated clipping. The Big Muff Pi, for instance, uses <a href="https://diyschematics.com/schematics/action.php?id=5360&amp;part=e">4 transistors</a> in cascaded stages to achieve its signature, thick, &quot;synth-like&quot; sound.</p>
</li>
<li>
<p><strong>JFET (Junction Field-Effect Transistor)</strong>: Often used in boutique pedals to emulate the &quot;warmth&quot; of vacuum tubes. They typically offer much higher input impedance, making them gentler on guitar pickups and ideal for cleaner, more expressive overdrive sounds.</p>
</li>
<li>
<p><strong>MOSFET (Metal-Oxide-Semiconductor Field-Effect Transistor)</strong>: Modern gain. They are often used in high-gain pedals or clean boosts where maintaining signal clarity is important.</p>
</li>
</ul>
<h3 id="diodes">Diodes</h3>
<p>Diodes limit the signal at various peaks, which serve to help soft-clip your signal, perfect for overdrive and metal circuits.</p>
<p>The number of diodes heavily influences the sound, and how they&#x27;re laid out.</p>
<ul>
<li>
<p>Asymmetric clipping - Used by the BOSS OD-1, leads to an interesting sound where only one side of a frequency is clipped.</p>
</li>
<li>
<p>Symmetric clipping - Typical overdrive sound, where both sides are clipped.</p>
</li>
</ul>
<p>The Axial Schottky diode (DO-41) is one of the more famous diodes, and is ideal for soft clipping and achieving &quot;warmer&quot; tones.</p>
<h3 id="operational-amplifiers-op-amps">Operational Amplifiers (Op Amps)</h3>
<p>These are the most common ICs used to shape gain and clipping characteristics in pedals.</p>
<ul>
<li>
<p><strong>TL072</strong> - Dual JFET-input op amp (<a href="https://www.ti.com/product/TL072">Datasheet</a>) - Used in the <strong>Tube Screamer</strong>.</p>
</li>
<li>
<p><strong>TL074</strong> - Quad JFET Operational Amplifier - Used in <strong>Amptweaker Fat Metal</strong>.</p>
</li>
<li>
<p><strong>TL082</strong> - Dual JFET Op Amp - Used in <strong>Earthquaker Plumes</strong>.</p>
</li>
<li>
<p><strong>OP275</strong> - Dual Bipolar/JFET Audio Amplifier - Used in <strong>Mad Professor Sweet Honey Pedal</strong>.</p>
</li>
<li>
<p><strong>JRC4580D</strong> - Dual op-amp, DIP8 (<a href="https://www.jrc-ic.co.jp/en/">Nisshinbo Micro Devices</a>) - A common alternative in the <strong>King of Tone</strong>.</p>
</li>
</ul>
<h3 id="voltage-regulators">Voltage Regulators</h3>
<p>Often used to increase or stabilize the voltage of a given circuit, which can affect the headroom and output tone.</p>
<ul>
<li>
<p><strong>LM108</strong> - LDO Voltage regulator IC - Used in the <strong>RAT</strong>.</p>
</li>
<li>
<p><strong>TC1044SCPA</strong> - Charge-pump Switching Voltage Regulator. Often used in fuzz and bass overdrive pedals.</p>
</li>
</ul>
<h2 id="interesting-pedals-and-brands">Interesting Pedals and Brands</h2>
<ul>
<li>
<p><a href="https://www.electronicaudioexperiments.com/pedals/eldritch-blast">Eldritch Blast</a></p>
</li>
<li>
<p><a href="https://www.oldbloodnoise.com/pedals/black-fountain-v3-w-tap-tempo-blue">Black Fountain</a></p>
</li>
<li>
<p><a href="https://www.acornamps.com/gear/adhd-synth-fuzz">Acorn Amplifiers ADHD Synth Fuzz</a></p>
</li>
<li>
<p><a href="https://kernom.com/en-us">Kernom Ridge</a> - the addition of controllable clipping, pre/post EQ, and mixing means this pedal can be a Klon, a Tube Screamer, and everything in between.</p>
</li>
<li>
<p><a href="https://www.jhspedals.com/products/notaklon">JHS Notaklon</a> - A DIY kit that offers a hands-on way to recreate the legendary Klon Centaur sound.</p>
</li>
<li>
<p><a href="https://lichtlaermaudio.com/shop">Lichtlaerm Gehenna</a></p>
</li>
<li>
<p><a href="https://amptweaker.com/product/tight-metal-pro-ii/">Amptweaker Tight Metal</a></p>
</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>Ultimately, understanding the circuitry just the first step, it will take actually building a circuit to really understand where tone resides. Once you understand the role of each part, you move from simply buying a sound to designing one.</p>
<h3 id="more-resources">More Resources</h3>
<ul>
<li>
<p>PCB design communities such as:</p>
<ul>
<li>
<p><a href="https://www.pedalpcb.com">Pedal PCB</a> - A large catalog of PCB layouts for clones of a variety of pedals, from Klons to Tube Screamers, Delays, and everything in between.</p>
</li>
<li>
<p><a href="https://aionfx.com">Aion FX</a> - Similar catalog</p>
</li>
<li>
<p><a href="https://pcbguitarmania.com/">PCB Guitar Mania</a></p>
</li>
<li>
<p><a href="https://www.deadendfx.com/">Deadend FX</a></p>
</li>
<li>
<p><a href="https://www.kicad.org/">KiKad</a></p>
</li>
</ul>
</li>
<li>
<p>Wampler Pedals - <a href="https://www.youtube.com/watch?v=F-WHBgIowmU">How To Design An Overdrive Pedal Circuit</a></p>
</li>
<li>
<p><a href="https://www.sweetwater.com/insync/boost-overdrive-distortion-fuzz-pedals-whats-the-difference/">Sweetwater Boost, Overdrive, Distortion &amp; Fuzz Pedals</a></p>
</li>
<li>
<p>JHS Pedals <a href="https://www.youtube.com/watch?v=ao1aMtB6kAI">How to Find the Fuzz Pedal You Need</a></p>
</li>
<li>
<p><a href="https://www.youtube.com/watch?v=UcMtN8y2Hes&amp;t=1440s">Understand Guitar Overdrive Pedals</a></p>
</li>
<li>
<p><a href="https://basicpitch.spotify.com/">BasicPitch Audio to MIDI converter</a></p>
</li>
<li>
<p><a href="https://www.youtube.com/@SpectreSoundStudios">Spectre Sound Studios is a YouTube channel that compares expensive gear and reasonably priced gear.</a></p>
</li>
</ul>]]></content:encoded>
</item>
<item>
<title>GPGPU Compute Concepts</title>
<link>https://alain.xyz/blog/gpgpu-compute-concepts</link>
<description>A comprehensive overview of general purpose graphics processing unit (GPGPU) computing, how modern GPU hardware execute compute shaders, and best practices for applications and games.</description>
<guid isPermaLink="true">https://alain.xyz/blog/gpgpu-compute-concepts</guid>
<pubDate>Thu, 19 Jan 2023 22:00:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>gpgpu</category><category>rdna2</category><category>gpu</category><category>shader</category><category>kernel</category>
<enclosure url="https://alain.xyz/blog/gpgpu-compute-concepts/assets/cover.jpg" length="138954" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/gpgpu-compute-concepts/assets/rdna-wave.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/gpgpu-compute-concepts/assets/post-processing-dispatch.svg"/><p><em>General Purpose Graphics Programming Unit</em> (GPGPU) computing is an effective way of using all of the computational resources your computer has to offer by offloading work to the GPU, making it an easy way to access heterogeneous parallel computing. Even though the <em>&quot;G&quot;</em> in GPU stands for graphics, it&#x27;s more accurate to call modern GPUs highly parallel <em>general</em> processors capable of processing large quantities of data quickly thanks to having a large number of simple microprocessor cores, called <em>Stream Processors</em> (AMD) or <em>Compute Unified Device Architecture</em> (CUDA) cores (NVIDIA), or just GPU cores in other architectures like the Apple M series or Qualcomm Snapdragon. These microprocessor cores are designed to not perform well with latency or branching, but instead focus on throughput <a href="#ref_huw2022">[Hwu et al. 2022]</a>. This architecture is designed around processing large amounts of data with a single instruction, otherwise known as a <em>Single Instruction Multiple Data</em> (SIMD) or more accurately <em>Single Instruction Multiple Thread</em> (SIMT) architecture, where a group of threads process a single instruction in lock-step.</p>
<p>GPUs can be asked to perform local arithmetic operations, transcendental operations that cost 4 cycles such as square roots, cosine/sine, load/store operations, hardware accelerated ray tracing traversal or matrix multiplications for executing machine learning (ML) models, making them both general and application specific depending on the workload.</p>
<p><strong>Compute shaders</strong> (or <em>kernels</em>) are programs that execute computations on the GPU, and are useful for a variety of tasks within and outside of computer graphics:</p>
<ul>
<li>
<p><strong>Executing and Training ML Models</strong> - From Leaky ReLUs to convolutions, machine learning models can be written as shader code and execute quickly and easily.</p>
</li>
<li>
<p><strong>Geometric Processing</strong> - every aspect of ray tracing, from the routines to trace rays, build bounding volume hierarchies (BVHs), and updating them, can be done by the GPU, which can process large quantities of triangle data in real time. Physics simulations such as rigid bodies can be simulated by processing vertices.</p>
</li>
<li>
<p><strong>Encoding/Compression</strong> - Video encoding, compressing mesh data, compressing frame buffer attachments such as 3D normals to 2D view space normals.</p>
</li>
<li>
<p><strong>Postprocessing</strong> - traditional fragment based post-processing effects can be easily rewritten as compute shaders and executed more efficiently and even at the same time.</p>
</li>
<li>
<p><strong>Materials and Lighting</strong> - Some games opt to use compute shaders in tandem with lookup structured buffers to add a level of abstraction from rasterization, with Nanite using software rendering in combination with meshlets; Mortal Kombat 11 indexed material IDs from a given fragment for its lighting passes.</p>
</li>
</ul>
<p>When processing a compute shader, every scheduled microprocessor (96 Compute Units (CUs) with 64 <em>shading units</em> each for a maximum of 6144 stream processors on a 7900 XTX) receives the same instructions, with the only differences being what the current thread ID is for that a single thread, which waves are on which instruction, and what data is accessible between threads of the same threadgroup:</p>
<div><pre><code class="language-asm"><span class="hljs-comment">; API shader hash:                  0x00000000000001009731533EAB7158CE</span>
<span class="hljs-comment">; API PSO hash:                     0xB4116117903F261D</span>
<span class="hljs-comment">; Driver internal pipeline hash:    0x9337B876E9519DBAD84A08F55EB136FC</span>
<span class="hljs-comment">;</span>
<span class="hljs-comment">; Vector registers:                 56 (64 allocated)</span>
<span class="hljs-comment">; Scalar registers:                 52 (128 allocated)</span>
<span class="hljs-symbol">
_amdgpu_cs_main:</span>
 s_version <span class="hljs-number">0x4004</span>                 <span class="hljs-comment">; 000000000000: B0804004</span>
 s_inst_prefetch <span class="hljs-number">0x3</span>              <span class="hljs-comment">; 000000000004: BFA00003</span>
 s_getpc_b64 s[<span class="hljs-number">0</span>:<span class="hljs-number">1</span>]               <span class="hljs-comment">; 000000000008: BE801F80</span>
 s_mov_b32 s4, s2                 <span class="hljs-comment">; 00000000000C: BE840302</span>
 s_mov_b32 s5, s1                 <span class="hljs-comment">; 000000000010: BE850301</span>
 s_mov_b32 s7, s1                 <span class="hljs-comment">; 000000000014: BE870301</span>
 s_movk_i32 s10, <span class="hljs-number">0x1000</span>           <span class="hljs-comment">; 000000000018: B00A1000</span>
    <span class="hljs-comment">; ...</span></code></pre></div>
<p><img src="https://alain.xyz/blog/gpgpu-compute-concepts/assets/rdna-wave.svg" alt="Waves in RDNA3"/></p>
<p>Groups of threads are further grouped together in hardware in what&#x27;s called a Wave (DirectX), Subgroup (Vulkan), Wavefront (AMD), SIMD Group (Apple), or Warp (NVIDIA CUDA). On both recent AMD and NVIDIA hardware, waves have 32 lanes, with each lane executing one thread. Older AMD hardware traditionally uses 64 threads per wave.</p>
<blockquote>
<p>Since these terms are different for every hardware vendor and graphics API, we&#x27;ll stick with the word <em>threadgroup</em> for all general mentions of groups threads, and <em>wave</em> for mentions of hardware groups of 32-64 threads, unless working in a specific graphics API.</p>
</blockquote>
<hr/>
<p>Let&#x27;s review GPGPU computing, the terminology, and techniques involved in writing GPGPU programs in different graphics and compute APIs, with a focus on WebGPU and DirectX 12. We&#x27;ll cover how to write compute kernels to do the following:</p>
<ol>
<li>
<p>Convolution - a basic operation that is useful for blurring, sharpening, and executing a machine learning model.</p>
</li>
<li>
<p>Histogram - Form a summary of your input data that shows how many times a given value appears within a range of values.</p>
</li>
<li>
<p>Sorting - Sort a list of values from smallest to greatest with radix and merge sort kernels.</p>
</li>
</ol>
<h2 id="dispatch">Dispatch</h2>
<p><img src="https://alain.xyz/blog/gpgpu-compute-concepts/assets/post-processing-dispatch.svg" alt="Dispatch Diagram"/></p>
<div><ul style="display:flex;align-items:left;padding:0"><li style="list-style:none;background:rgba(16, 19, 24, .5);border-radius:.5em"><a style="text-decoration:none;display:block;padding:0.75em 1em">WGSL</a></li><li style="list-style:none;background:transparent;border-radius:.5em"><a style="text-decoration:none;display:block;padding:0.75em 1em">HLSL</a></li></ul><div style="display:block"><div><pre title="Dispatch in WebGPU"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// 🚄 Dispatch Compute Work</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">let</span> width = <span class="hljs-number">1920</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"><span class="hljs-keyword">let</span> height = <span class="hljs-number">1080</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-keyword">let</span> workgroupSize = { <span class="hljs-attr">x</span>: <span class="hljs-number">8</span>, <span class="hljs-attr">y</span>: <span class="hljs-number">4</span> };</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"><span class="hljs-keyword">let</span> workgroupCount = {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6">  <span class="hljs-attr">x</span>: <span class="hljs-title">Math</span>.<span class="hljs-title">ciel</span>(width / workgroupSize.<span class="hljs-property">x</span>),</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">  <span class="hljs-attr">y</span>: <span class="hljs-title">Math</span>.<span class="hljs-title">ciel</span>(height / workgroupSize.<span class="hljs-property">y</span>),</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">  <span class="hljs-attr">z</span>: <span class="hljs-number">1</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">passEncoder.<span class="hljs-title">dispatchWorkgroups</span>(</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">  workgroupCount.<span class="hljs-property">x</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12">  workgroupCount.<span class="hljs-property">y</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">  workgroupCount.<span class="hljs-property">z</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14">);</td></tr></tbody></table></code></pre></div></div><div style="display:none"><div><pre title="Dispatch in C++ DirectX 12"><code class="language-cpp"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// 🚄 Dispatch Compute Work</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-type">unsigned</span> width = <span class="hljs-number">1920</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"><span class="hljs-type">unsigned</span> height = <span class="hljs-number">1080</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-type">unsigned</span> numThreadsX = <span class="hljs-number">8</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"><span class="hljs-type">unsigned</span> numThreadsY = <span class="hljs-number">4</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"><span class="hljs-type">unsigned</span> threadgroupX = <span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">unsigned</span>&gt;(<span class="hljs-built_in">ceil</span>(<span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">float</span>&gt;(width) / <span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">float</span>&gt;(numThreadsX)));</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7"><span class="hljs-type">unsigned</span> threadgroupY = <span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">unsigned</span>&gt;(<span class="hljs-built_in">ceil</span>(<span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">float</span>&gt;(height) / <span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">float</span>&gt;(numThreadsY)));</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8"><span class="hljs-type">unsigned</span> threadgroupZ = <span class="hljs-number">1</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">commandList-&gt;<span class="hljs-built_in">Dispatch</span>(threadgroupX, threadgroupY, threadgroupZ);</td></tr></tbody></table></code></pre></div></div></div>

<p>A <code>dispatch</code> call in a given graphics API can process groups of work (aptly called <em>workgroups</em> or <em>threadgroups</em>), normally a set of threads that can number anywhere between 8-256, though generally you&#x27;ll want to stick with groups of 32 or 64 threads, the size of a wave on most hardware, unless your workload would benefit from more data locality over large groups such as a radix sort. In most graphics APIs, the shader itself describes the thread counts for each of these thread groups, Metal is the only API that doesn&#x27;t have this requirement and lets you set the number of threads per group at dispatch time. Other APIs <em>could do the same</em> however with preprocessor definitions.</p>

<p>A typical application will dispatch a given task across a large workload, then another dispatch that takes the results of that previous pass. For instance, you may need to first sort a list of numbers such as morton codes, then use that sorted list in a separate pass. This is important to note, if a pass requires another to be finished, you must either execute a separate dispatch, or use clever task synchronization methods across an entire dispatch such as using an <em>atomic task counter</em>.</p>
<h2 id="compute-shader">Compute Shader</h2>
<p>A compute shader consists of a dispatch to a shader kernel that executes groups of threads, each of which has the following basic intrinsics:</p>
<div><ul style="display:flex;align-items:left;padding:0"><li style="list-style:none;background:rgba(16, 19, 24, .5);border-radius:.5em"><a style="text-decoration:none;display:block;padding:0.75em 1em">WGSL</a></li><li style="list-style:none;background:transparent;border-radius:.5em"><a style="text-decoration:none;display:block;padding:0.75em 1em">HLSL</a></li><li style="list-style:none;background:transparent;border-radius:.5em"><a style="text-decoration:none;display:block;padding:0.75em 1em">GLSL</a></li><li style="list-style:none;background:transparent;border-radius:.5em"><a style="text-decoration:none;display:block;padding:0.75em 1em">MSL</a></li><li style="list-style:none;background:transparent;border-radius:.5em"><a style="text-decoration:none;display:block;padding:0.75em 1em">CUDA</a></li></ul><div style="display:block"><div><pre><code class="language-wgsl"><span class="hljs-comment">// 🫂 Local Group Thread ID</span>
<span class="hljs-meta-keyword">@builtin</span>(local_invocation_id) localInvocationID: <span class="hljs-type">vec3</span>&lt;<span class="hljs-type">u32</span>&gt;

<span class="hljs-comment">// 👶 Group ID</span>
<span class="hljs-meta-keyword">@builtin</span>(workgroup_id) workgroupID: <span class="hljs-type">vec3</span>

<span class="hljs-comment">// 🖼️ Dispatch Thread ID</span>
<span class="hljs-meta-keyword">@builtin</span>(global_invocation_id) globalInvocationID: <span class="hljs-type">vec3</span>&lt;<span class="hljs-type">u32</span>&gt;

<span class="hljs-comment">// 🗂️ Group Index</span>
<span class="hljs-meta-keyword">@builtin</span>(local_invocation_index) localInvocationIndex: <span class="hljs-type">u32</span>

<span class="hljs-comment">// 🔢 Number of Thread Groups</span>
<span class="hljs-meta-keyword">@builtin</span>(num_workgroups) numWorkgroups: <span class="hljs-type">vec3</span>&lt;<span class="hljs-type">u32</span>&gt;
</code></pre></div></div><div style="display:none"><div><pre><code class="language-hlsl"><span class="hljs-comment">// 🫂 Local Group Thread ID</span>
<span class="hljs-type">uint3</span> groupThreadID : <span class="hljs-built_in">SV_GroupThreadID</span>

<span class="hljs-comment">// 👶 Group ID</span>
<span class="hljs-type">uint3</span> groupID : <span class="hljs-built_in">SV_GroupID</span>

<span class="hljs-comment">// 🖼️ Dispatch Thread ID</span>
<span class="hljs-type">uint3</span> dispatchThreadID: <span class="hljs-built_in">SV_DispatchThreadID</span>

<span class="hljs-comment">// 🗂️ Group Index</span>
<span class="hljs-type">uint</span> groupIndex : <span class="hljs-built_in">SV_GroupIndex</span>

<span class="hljs-comment">// 🔢 Number of Thread Groups</span>
<span class="hljs-comment">// HLSL Has no builtin for the number of workgroups.</span></code></pre></div></div><div style="display:none"><div><pre><code class="language-glsl"><span class="hljs-comment">// 🫂 Local Group Thread ID</span>
<span class="hljs-built_in">gl_LocalInvocationID</span>

<span class="hljs-comment">// 👶 Group ID</span>
<span class="hljs-built_in">gl_WorkGroupID</span>

<span class="hljs-comment">// 🖼️ Dispatch Thread ID</span>
<span class="hljs-built_in">gl_GlobalInvocationID</span>

<span class="hljs-comment">// 🗂️ Group Index</span>
<span class="hljs-built_in">gl_LocalInvocationIndex</span>

<span class="hljs-comment">// 🔢 Number of Thread Groups</span>
<span class="hljs-built_in">gl_NumWorkGroups</span></code></pre></div></div><div style="display:none"><div><pre><code class="language-msl"><span class="hljs-comment">// 🫂 Local Group Thread ID</span>
<span class="hljs-type">uint3</span> threadPositionInThreadgroup [[<span class="hljs-keyword">thread_position_in_threadgroup</span>]]

<span class="hljs-comment">// 👶 Group ID</span>
<span class="hljs-type">uint3</span> threadPositionInGrid [[<span class="hljs-keyword">threadgroup_position_in_grid</span>]]

<span class="hljs-comment">// 🖼️ Dispatch Thread ID</span>
<span class="hljs-type">uint3</span> dispatchThreadID [[<span class="hljs-keyword">thread_position_in_grid</span>]]

<span class="hljs-comment">// 🗂️ Group Index</span>
<span class="hljs-type">uint</span> threadIndex [[<span class="hljs-keyword">thread_index_in_threadgroup</span>]]

<span class="hljs-comment">// 🔢 Number of Thread Groups</span>
<span class="hljs-type">uint3</span> tpg [[<span class="hljs-keyword">threads_per_grid</span>]]</code></pre></div></div><div style="display:none"><div><pre><code class="language-c"><span class="hljs-comment">// 🫂 Local Group Thread ID</span>
uint3 threadIdx;

<span class="hljs-comment">// 👶 Group ID</span>
uint3 blockIdx;

<span class="hljs-comment">// 🖼️ Dispatch Thread ID (You must compute this yourself):</span>
uint3 dispatchThreadIdx = blockIdx * blockDim + threadIdx;

<span class="hljs-comment">// 🗂️ Group Index (You must compute this yourself):</span>

<span class="hljs-comment">// 🔢 Number of Thread Groups</span>

<span class="hljs-comment">// 🎡 Group Size</span>
uint3 blockDim;</code></pre></div></div></div>
<ul>
<li>
<p><strong>Group Thread ID</strong> - What thread you&#x27;re currently working on within a workgroup.</p>
</li>
<li>
<p><strong>Group ID</strong> - What workgroup you&#x27;re currently exectuing in, useful for designating certain workgroups for the final parts of an algorithm such as prefix sum.</p>
</li>
<li>
<p><strong>Dispatch Thread ID</strong> - The actual thread that&#x27;s currently being executed by the hardware, the &quot;pixel&quot; in the case of a screen space dispatch.</p>
</li>
<li>
<p><strong>Group Index</strong> - a linear representation of the current thread index.</p>
</li>
<li>
<p><strong>Number of Thread Groups</strong> - What&#x27;s passed as arguments to your dispatch, the number of thread groups currently executing for this shader kernel.</p>
</li>
</ul>
<p>In addition, you can take advantage of shared memory across a given workgroup</p>
<h3 id="group-local-data">Group Local Data</h3>
<div><ul style="display:flex;align-items:left;padding:0"><li style="list-style:none;background:rgba(16, 19, 24, .5);border-radius:.5em"><a style="text-decoration:none;display:block;padding:0.75em 1em">WGSL</a></li><li style="list-style:none;background:transparent;border-radius:.5em"><a style="text-decoration:none;display:block;padding:0.75em 1em">HLSL</a></li></ul><div style="display:block"><div><pre><code class="language-wgsl"><span class="hljs-type">var</span>&lt;workgroup&gt; local_data: array&lt;i32,<span class="hljs-number">32</span>*<span class="hljs-number">16</span>&gt;;</code></pre></div></div><div style="display:none"><div><pre><code class="language-hlsl"><span class="hljs-keyword">groupshared</span> <span class="hljs-type">uint</span> localData[<span class="hljs-number">32</span>*<span class="hljs-number">16</span>];</code></pre></div></div></div>
<p>Each workgroup can allocate memory within that group that can be shared between threads of that group. This can be used as a local cache across the whole threadgroup to do privatization of the final resultant data structure, enable scan algorithms like Kogge-Stone and Brent-Kung, prefix sum of a given value in every thread, radix binning, sweeps, scatter, and compaction. These primitives can then be used to do more advanced workloads like sorting algorithms, building hierarchical structures like BVHs, neural network model execution, and much more.</p>
<p>In the RDNA3 hardware this looks nearly identical to the shader itself, with that data shared within a given compute unit&#x27;s L1 cache, known as the local data share (LDS). Level cache rules apply to GPUs as well, the more local your data the faster your algorithm can execute, so try to keep data as close to a thread as possible. This means using local variables (which become scalar or vector registers in hardware) more often than global memory such as your strctured buffers, textures, etc.</p>
<h3 id="atomic-operations">Atomic Operations</h3>
<p>Atomic operations allow for threads to read/write to the same location without worrying about race conditions with data. These operations are significantly slower than standard read/writes, as an atomic operation that is performed by a trailing thread cannot be started until the atomic operation of a leading thread completes. This effectively serializes the atomic operations that are being performed on a memory location, slowing execution down by a factor of 10x <a href="#ref_huw2022">[Hwu et al. 2022]</a>.</p>
<div><pre><code class="language-hlsl"><span class="hljs-keyword">void</span> <span class="hljs-built_in">InterlockedAdd</span>(<span class="hljs-keyword">in</span> R dest, <span class="hljs-keyword">in</span> T value, <span class="hljs-keyword">out</span> T original_value);</code></pre></div>
<div><pre><code class="language-wgsl"><span class="hljs-comment">// https://www.w3.org/TR/WGSL/#atomic-builtin-functions</span>
<span class="hljs-keyword">fn</span> <span class="hljs-title class_">atomicLoad</span>(atomic_ptr: ptr&lt;AS, atomic&lt;T&gt;, read_write&gt;) -&gt; <span class="hljs-title class_">T</span>
<span class="hljs-keyword">fn</span> <span class="hljs-title class_">atomicStore</span>(atomic_ptr: ptr&lt;AS, atomic&lt;T&gt;, read_write&gt;, v: T)

<span class="hljs-keyword">fn</span> <span class="hljs-title class_">atomicAdd</span>(atomic_ptr: ptr&lt;AS, atomic&lt;T&gt;, read_write&gt;, v: T) -&gt; <span class="hljs-title class_">T</span>
<span class="hljs-keyword">fn</span> <span class="hljs-title class_">atomicSub</span>(atomic_ptr: ptr&lt;AS, atomic&lt;T&gt;, read_write&gt;, v: T) -&gt; <span class="hljs-title class_">T</span>
<span class="hljs-keyword">fn</span> <span class="hljs-title class_">atomicMax</span>(atomic_ptr: ptr&lt;AS, atomic&lt;T&gt;, read_write&gt;, v: T) -&gt; <span class="hljs-title class_">T</span>
<span class="hljs-keyword">fn</span> <span class="hljs-title class_">atomicMin</span>(atomic_ptr: ptr&lt;AS, atomic&lt;T&gt;, read_write&gt;, v: T) -&gt; <span class="hljs-title class_">T</span>
<span class="hljs-keyword">fn</span> <span class="hljs-title class_">atomicAnd</span>(atomic_ptr: ptr&lt;AS, atomic&lt;T&gt;, read_write&gt;, v: T) -&gt; <span class="hljs-title class_">T</span>
<span class="hljs-keyword">fn</span> <span class="hljs-title class_">atomicOr</span>(atomic_ptr: ptr&lt;AS, atomic&lt;T&gt;, read_write&gt;, v: T) -&gt; <span class="hljs-title class_">T</span>
<span class="hljs-keyword">fn</span> <span class="hljs-title class_">atomicXor</span>(atomic_ptr: ptr&lt;AS, atomic&lt;T&gt;, read_write&gt;, v: T) -&gt; <span class="hljs-title class_">T</span>

<span class="hljs-keyword">fn</span> <span class="hljs-title class_">atomicExchange</span>(atomic_ptr: ptr&lt;AS, atomic&lt;T&gt;, read_write&gt;, v: T) -&gt; <span class="hljs-title class_">T</span>
<span class="hljs-keyword">fn</span> <span class="hljs-title class_">atomicCompareExchangeWeak</span>(atomic_ptr: ptr&lt;AS, atomic&lt;T&gt;, read_write&gt;, cmp: T, v: T) -&gt; <span class="hljs-title class_">__atomic_compare_exchange_result</span>&lt;T&gt;
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">__atomic_compare_exchange_result</span>&lt;T&gt; {
  old_value : T; <span class="hljs-comment">// old value stored in the atomic</span>
  exchanged : <span class="hljs-type">bool</span>; <span class="hljs-comment">// true if the exchange was done</span>
}</code></pre></div>
<p>Atomic operations allow for threads to perform logic on a large dataset and write the result to either group shared or global memory. This is useful for performing a variety of tasks such as <code>atomicAdd</code>s for prefix sums, atomic min/max for finding the range of points, or <code>atomicXor</code>s for compression.</p>
<blockquote>
<p>WebGPU is unique in that atomics are strictly typed, whereas with HLSL atomic operations can work on <a href="https://raphlinus.github.io/gpu/2021/11/17/prefix-sum-portable.html">regular memory locations</a>.</p>
</blockquote>
<h3 id="shader-memory-barriers">Shader Memory Barriers</h3>
<p>Barriers allow for a you to guarantee that group shared memory or device memory accesses are finished within a threadgroup.</p>
<div><pre><code class="language-hlsl"><span class="hljs-keyword">void</span> <span class="hljs-built_in">GroupMemoryBarrier</span>(<span class="hljs-keyword">void</span>);
<span class="hljs-keyword">void</span> <span class="hljs-built_in">GroupMemoryBarrierWithGroupSync</span>(<span class="hljs-keyword">void</span>);</code></pre></div>
<h3 id="wave-intrinsics">Wave Intrinsics</h3>
<p>Shader Model 6.0 introduces <a href="https://github.com/Microsoft/DirectXShaderCompiler/wiki/Wave-Intrinsics">wave intrinsics</a> to HLSL, though this is exclusive to both HLSL and GLSL as <em>subgroup intrinsics</em> at the moment. There is a proposal to bring this to WGSL as well.</p>
<div><pre><code class="language-hlsl"><span class="hljs-keyword">if</span> (localId == <span class="hljs-number">0</span>)
{
 <span class="hljs-comment">// Your code block...</span>
}

<span class="hljs-comment">// Vs.</span>

<span class="hljs-type">bool</span> WaveIsFirstLane(<span class="hljs-keyword">void</span>);
<span class="hljs-keyword">if</span> (WaveIsFirstLane())
{
    <span class="hljs-comment">// Your code block...</span>
}</code></pre></div>
<h2 id="conclusion">Conclusion</h2>
<p>GPGPU computing is an effective way to solve problems with large datasets, which is why you find it most often used in computer graphics, machine learning, and simulations. The SIMT architecture of the GPU requires that you conceptualize the solution to a problem in a different way, mapping the problem to a large number of groups of threads.</p>
<h3 id="more-resources">More Resources</h3>
<ul>
<li>
<p><a href="https://www.amd.com/system/files/documents/rdna-whitepaper.pdf">RDNA Whitepaper</a></p>
</li>
<li>
<p><a href="https://developer.amd.com/wp-content/resources/RDNA3_Shader_ISA_December2022.pdf">RDNA3 Instruction Set Architecture</a></p>
</li>
<li>
<p><a href="https://images.nvidia.com/content/volta-architecture/pdf/volta-architecture-whitepaper.pdf">NVIDIA Volta Whitepaper</a></p>
</li>
<li>
<p><a href="https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html">NVIDIA CUDA C++ Programming Guide</a></p>
</li>
<li>
<p><a href="https://developer.nvidia.com/gpugems/gpugems2/part-iv-general-purpose-computation-gpus-primer">GPU Gems 2 - GPGPU Primer</a></p>
</li>
<li>
<p><a href="https://gpu-primitives-course.github.io/">GPU Programming Primitives for Computer Graphics - Siggraph Asia 2024</a></p>
</li>
<li>
<p>Lou Kramer (Technology Developer Engineer @ AMD) released a talk titled <a href="https://www.youtube.com/watch?v=eDLilzy2mq0">Compute Shaders</a>.</p>
</li>
<li>
<p><a href="https://ispc.github.io/">Intel Implicit SPMD Program Compiler</a></p>
</li>
<li>
<p><a href="https://github.com/microsoft/DirectXShaderCompiler/wiki/Wave-Intrinsics">HLSL Wave Intrinsics</a></p>
</li>
<li>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/HLSL_ShaderModel6_5.html">HLSL Shader Model 6.5</a></p>
</li>
<li>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/HLSL_ShaderModel6_6.html">HLSL Shader Model 6.6</a></p>
</li>
<li>
<p><a href="https://microsoft.github.io/DirectX-Specs/d3d/HLSL_ShaderModel6_8.html">HLSL Shader Model 6.8</a></p>
</li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_huw2022"><span class="markademic-citename">[Hwu et al. 2022]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Programming Massively Parallel Processors: A Hands-on Approach, Edition 4</span></strong></em><br/><span class="markademic-authors"><span><span>Wen-mei Hwu</span></span><span>, <span>David B. Kirk</span></span><span> and <span>Izzat El Hajj</span></span></span><br/><span><span class="markademic-publisher">Morgan Kaufmann 2022</span><br/></span><span><a href="https://link.springer.com/book/10.1007/978-3-031-10968-3">link.springer.com</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Ray Tracing Acceleration Structures</title>
<link>https://alain.xyz/blog/ray-tracing-acceleration-structures</link>
<description>An overview of ray tracing acceleration structures, reviewing bounding volume hierarchies, KD-trees, morton codes, and recent papers on the subject.</description>
<guid isPermaLink="true">https://alain.xyz/blog/ray-tracing-acceleration-structures</guid>
<pubDate>Sun, 24 Apr 2022 22:00:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>aabb</category><category>intersection</category><category>acceleration</category><category>ray tracing</category><category>bounding volume hierarchy</category>
<enclosure url="https://alain.xyz/blog/ray-tracing-acceleration-structures/assets/cover.jpg" length="53326" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-acceleration-structures/assets/kd-vs-bvh.svg"/><link rel="preload" as="image" href="https://github.com/morgan3d.png"/><link rel="preload" as="image" href="https://github.com/mmp.png"/><link rel="preload" as="image" href="https://github.com/wjakob.png"/><link rel="preload" as="image" href="https://github.com/inversepixel.png"/><link rel="preload" as="image" href="https://github.com/ingowald.png"/><link rel="preload" as="image" href="https://github.com/meistdan.png"/><link rel="preload" as="image" href="https://github.com/shinjiogaki.png"/><p><strong>Acceleration structures</strong> as the name implies are <em>spatial data structures that speed up searching for triangles, distance fields, etc. in a given scene</em>. Ray intersection tests for certain shapes like <strong>axis aligned bounding boxes</strong> (AABBs) are extremely cheap and easy to implement, so by traversing a hierarchical structure of AABBs, you can determine if a triangle is intersected, and use a triangle-ray intersection test to get the necessary data such as barycentrics, interpolated attributes such as normals <a href="#ref_mcguire2018">[McGuire 2018]</a> to perform any number of ray traced effects such as accurate shadows, ambient occlusion, global illumination, reflections, or even a full path tracer <a href="#ref_kajiya1986">[Kajiya 1986]</a> <a href="#ref_pharr2016">[Pharr et al. 2016]</a>.</p>
<div style="touch-action:none;user-select:none;-webkit-user-select:none"><canvas style="touch-action:none;user-select:none;-webkit-user-select:none;width:100%;height:20em"></canvas><p style="text-align:center"><strong>Controls: </strong><em>Hover over</em> the AABB or triangle to do a ray hit test. <em>Drag</em> horizontally to rotate it.</p></div>
<div><pre title="AABB and Triangle Intersection Tests"><code class="language-hlsl"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// ✅ Boolean tests for AABB and Triangle-Ray intersection.</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-comment">// You may need more information such as baricentric coordinates,</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"><span class="hljs-comment">// Ray distance, etc.</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-comment">//</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"><span class="hljs-comment">// Adapted from David Eberly&#x27;s Geometric Tools</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"><span class="hljs-comment">// Copyright (c) 1998-2022 | Boost License, v1.0.</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7"><span class="hljs-comment">// https://www.geometrictools.com/GTE/Mathematics/IntrRay3AlignedBox3.h</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8"><span class="hljs-comment">// https://www.geometrictools.com/GTE/Mathematics/IntrRay3Triangle3.h</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10"><span class="hljs-keyword">struct</span> <span class="hljs-title">AABB</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">{</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12">    <span class="hljs-type">float3</span> center;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">    <span class="hljs-type">float3</span> extent;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16"><span class="hljs-type">bool</span> intersectAABB(<span class="hljs-type">float3</span> origin, <span class="hljs-type">float3</span> direction, AABB box)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17">{</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18">    <span class="hljs-type">float3</span> boxCenter = box.center;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19">    <span class="hljs-type">float3</span> boxExtent = box.extent;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="20"><div class="hljs-ln-n" data-line-number="20"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="20"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="21"><div class="hljs-ln-n" data-line-number="21"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="21">    <span class="hljs-comment">// Transform the ray to the aligned-box coordinate system.</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="22"><div class="hljs-ln-n" data-line-number="22"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="22">    <span class="hljs-type">float3</span> rayOrigin = origin - boxCenter;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="23"><div class="hljs-ln-n" data-line-number="23"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="23"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="24"><div class="hljs-ln-n" data-line-number="24"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="24">    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">any</span>(<span class="hljs-built_in">abs</span>(rayOrigin) &gt; boxExtent &amp;&amp; rayOrigin * direction &gt;= <span class="hljs-number">0.0</span>))</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="25"><div class="hljs-ln-n" data-line-number="25"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="25">    {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="26"><div class="hljs-ln-n" data-line-number="26"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="26">        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="27"><div class="hljs-ln-n" data-line-number="27"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="27">    }</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="28"><div class="hljs-ln-n" data-line-number="28"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="28"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="29"><div class="hljs-ln-n" data-line-number="29"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="29">    <span class="hljs-comment">// Perform AABB-line test</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="30"><div class="hljs-ln-n" data-line-number="30"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="30">    <span class="hljs-type">float3</span> WxD = <span class="hljs-built_in">cross</span>(direction, rayOrigin);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="31"><div class="hljs-ln-n" data-line-number="31"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="31">    <span class="hljs-type">float3</span> absWdU = <span class="hljs-built_in">abs</span>(direction);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="32"><div class="hljs-ln-n" data-line-number="32"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="32">    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">any</span>(WxD &gt; <span class="hljs-type">float3</span>(<span class="hljs-built_in">dot</span>(boxExtent.yz, absWdU.zy),</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="33"><div class="hljs-ln-n" data-line-number="33"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="33">                         <span class="hljs-built_in">dot</span>(boxExtent.xz, absWdU.zx),</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="34"><div class="hljs-ln-n" data-line-number="34"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="34">                         <span class="hljs-built_in">dot</span>(boxExtent.xy, absWdU.yx))))</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="35"><div class="hljs-ln-n" data-line-number="35"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="35">    {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="36"><div class="hljs-ln-n" data-line-number="36"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="36">        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="37"><div class="hljs-ln-n" data-line-number="37"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="37">    }</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="38"><div class="hljs-ln-n" data-line-number="38"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="38"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="39"><div class="hljs-ln-n" data-line-number="39"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="39">    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="40"><div class="hljs-ln-n" data-line-number="40"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="40">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="41"><div class="hljs-ln-n" data-line-number="41"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="41"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="42"><div class="hljs-ln-n" data-line-number="42"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="42"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="43"><div class="hljs-ln-n" data-line-number="43"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="43"><span class="hljs-keyword">struct</span> <span class="hljs-title">Triangle</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="44"><div class="hljs-ln-n" data-line-number="44"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="44">{</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="45"><div class="hljs-ln-n" data-line-number="45"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="45">    <span class="hljs-type">float3</span> v[<span class="hljs-number">3</span>];</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="46"><div class="hljs-ln-n" data-line-number="46"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="46">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="47"><div class="hljs-ln-n" data-line-number="47"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="47"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="48"><div class="hljs-ln-n" data-line-number="48"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="48"><span class="hljs-type">bool</span> intersectTriangle(<span class="hljs-type">float3</span> origin, <span class="hljs-type">float3</span> direction, Triangle <span class="hljs-keyword">triangle</span>)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="49"><div class="hljs-ln-n" data-line-number="49"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="49">{</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="50"><div class="hljs-ln-n" data-line-number="50"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="50">    <span class="hljs-type">float3</span> diff = origin - <span class="hljs-keyword">triangle</span>.v[<span class="hljs-number">0</span>];</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="51"><div class="hljs-ln-n" data-line-number="51"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="51">    <span class="hljs-type">float3</span> edge1 = <span class="hljs-keyword">triangle</span>.v[<span class="hljs-number">1</span>] - <span class="hljs-keyword">triangle</span>.v[<span class="hljs-number">0</span>];</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="52"><div class="hljs-ln-n" data-line-number="52"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="52">    <span class="hljs-type">float3</span> edge2 = <span class="hljs-keyword">triangle</span>.v[<span class="hljs-number">2</span>] - <span class="hljs-keyword">triangle</span>.v[<span class="hljs-number">0</span>];</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="53"><div class="hljs-ln-n" data-line-number="53"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="53">    <span class="hljs-type">float3</span> normal = <span class="hljs-built_in">cross</span>(edge1, edge2);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="54"><div class="hljs-ln-n" data-line-number="54"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="54"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="55"><div class="hljs-ln-n" data-line-number="55"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="55">    <span class="hljs-type">float</span> DdN = <span class="hljs-built_in">dot</span>(direction, normal);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="56"><div class="hljs-ln-n" data-line-number="56"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="56">    <span class="hljs-type">float</span> <span class="hljs-built_in">sign</span> = <span class="hljs-number">0.0</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="57"><div class="hljs-ln-n" data-line-number="57"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="57">    <span class="hljs-keyword">if</span> (DdN &gt; <span class="hljs-number">0.0</span>)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="58"><div class="hljs-ln-n" data-line-number="58"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="58">    {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="59"><div class="hljs-ln-n" data-line-number="59"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="59">        <span class="hljs-built_in">sign</span> = <span class="hljs-number">1.0</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="60"><div class="hljs-ln-n" data-line-number="60"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="60">    }</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="61"><div class="hljs-ln-n" data-line-number="61"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="61">    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (DdN &lt; <span class="hljs-number">0</span>)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="62"><div class="hljs-ln-n" data-line-number="62"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="62">    {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="63"><div class="hljs-ln-n" data-line-number="63"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="63">        <span class="hljs-built_in">sign</span> = <span class="hljs-number">-1.0</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="64"><div class="hljs-ln-n" data-line-number="64"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="64">        DdN = -DdN;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="65"><div class="hljs-ln-n" data-line-number="65"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="65">    }</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="66"><div class="hljs-ln-n" data-line-number="66"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="66">    <span class="hljs-keyword">else</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="67"><div class="hljs-ln-n" data-line-number="67"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="67">    {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="68"><div class="hljs-ln-n" data-line-number="68"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="68">        <span class="hljs-comment">// Ray and triangle are parallel.</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="69"><div class="hljs-ln-n" data-line-number="69"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="69">        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="70"><div class="hljs-ln-n" data-line-number="70"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="70">    }</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="71"><div class="hljs-ln-n" data-line-number="71"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="71"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="72"><div class="hljs-ln-n" data-line-number="72"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="72">    <span class="hljs-type">float</span> DdQxE2 = <span class="hljs-built_in">sign</span> * <span class="hljs-built_in">dot</span>(direction, <span class="hljs-built_in">cross</span>(diff, edge2));</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="73"><div class="hljs-ln-n" data-line-number="73"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="73">    <span class="hljs-keyword">if</span> (DdQxE2 &gt;= <span class="hljs-number">0.0</span>)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="74"><div class="hljs-ln-n" data-line-number="74"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="74">    {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="75"><div class="hljs-ln-n" data-line-number="75"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="75">        <span class="hljs-type">float</span> DdE1xQ = <span class="hljs-built_in">sign</span> * <span class="hljs-built_in">dot</span>(direction, <span class="hljs-built_in">cross</span>(edge1, diff));</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="76"><div class="hljs-ln-n" data-line-number="76"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="76">        <span class="hljs-keyword">if</span> (DdE1xQ &gt;= <span class="hljs-number">0.0</span>)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="77"><div class="hljs-ln-n" data-line-number="77"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="77">        {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="78"><div class="hljs-ln-n" data-line-number="78"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="78">            <span class="hljs-keyword">if</span> (DdQxE2 + DdE1xQ &lt;= DdN)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="79"><div class="hljs-ln-n" data-line-number="79"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="79">            {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="80"><div class="hljs-ln-n" data-line-number="80"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="80">                <span class="hljs-comment">// Line intersects triangle, check whether ray does.</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="81"><div class="hljs-ln-n" data-line-number="81"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="81">                <span class="hljs-type">float</span> QdN = -<span class="hljs-built_in">sign</span> * <span class="hljs-built_in">dot</span>(diff, normal);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="82"><div class="hljs-ln-n" data-line-number="82"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="82">                <span class="hljs-keyword">if</span> (QdN &gt;= <span class="hljs-number">0.0</span>)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="83"><div class="hljs-ln-n" data-line-number="83"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="83">                {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="84"><div class="hljs-ln-n" data-line-number="84"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="84">                    <span class="hljs-comment">// Ray intersects triangle.</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="85"><div class="hljs-ln-n" data-line-number="85"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="85">                    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="86"><div class="hljs-ln-n" data-line-number="86"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="86">                }</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="87"><div class="hljs-ln-n" data-line-number="87"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="87">            }</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="88"><div class="hljs-ln-n" data-line-number="88"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="88">        }</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="89"><div class="hljs-ln-n" data-line-number="89"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="89">    }</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="90"><div class="hljs-ln-n" data-line-number="90"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="90"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="91"><div class="hljs-ln-n" data-line-number="91"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="91">    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="92"><div class="hljs-ln-n" data-line-number="92"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="92">}</td></tr></tbody></table></code></pre></div>
<p>A ray intersection test for AABBs or triangles can easily be represented in only <em>a few DirectX Intermediate Language (DXIL) instructions alone</em>, and manufacturers can take that concept even further by converting those instructions to <em>application specific integrated curcuits (ASICs)</em>, or <em>even converting other aspects of a ray tracer</em> such as acceleration structure builds <a href="#ref_doyle2012">[J. Doyle et al. 2012]</a> or traversal <a href="#ref_liktor2016">[Liktor et al. 2016]</a> <a href="#ref_vaidyanathan2016">[Vaidyanathan et al. 2016]</a> to ASIC hardware.</p>
<p>Hardware accelerated intersection tests is what AMD did with the RDNA2 Architecture. When you executed your <a href="https://docs.microsoft.com/en-us/windows/win32/direct3d12/traceray-function">DirectX 12 Ray Tracing</a> or Vulkan Ray Tracing shaders:</p>
<div><pre><code class="language-hlsl">template&lt;typename payload_t&gt;
<span class="hljs-keyword">void</span> <span class="hljs-built_in">TraceRay</span>(<span class="hljs-keyword">RaytracingAccelerationStructure</span> AccelerationStructure,
              <span class="hljs-type">uint</span> RayFlags,
              <span class="hljs-type">uint</span> InstanceInclusionMask,
              <span class="hljs-type">uint</span> RayContributionToHitGroupIndex,
              <span class="hljs-type">uint</span> MultiplierForGeometryContributionToHitGroupIndex,
              <span class="hljs-type">uint</span> MissShaderIndex,
              <span class="hljs-keyword">RayDesc</span> Ray,
              <span class="hljs-keyword">inout</span> payload_t Payload);</code></pre></div>
<p>All those calls to <code>TraceRay</code> become DXIL which eventually lead to this AMDIL instruction:</p>
<blockquote>
<p>These instructions receive ray data from the Vector General Purpose Registers (VGPRs) and fetch Bounding Volume Hierarchy (BVH) data from memory. <a href="#ref_rdna2020">[AMD 2020]</a></p>
<ul>
<li>Box BVH nodes perform 4x Ray/Box intersection, sorts the 4 children based on intersection distance and returns the child pointers and hit status.</li>
<li>Triangle nodes perform 1 Ray/Triangle intersection test and returns the intersection point and triangle ID.</li>
</ul>
<div><pre><code class="language-asm"><span class="hljs-comment">; AMDIL for RDNA2</span>
image_bvh_intersect_ray vgpr_d[<span class="hljs-number">4</span>], vgpr_a[<span class="hljs-number">11</span>], sgpr_r[<span class="hljs-number">4</span>]
image_bvh_intersect_ray vgpr_d[<span class="hljs-number">4</span>], vgpr_a[<span class="hljs-number">8</span>], sgpr_r[<span class="hljs-number">4</span>] A16=<span class="hljs-number">1</span>
image_bvh64_intersect_ray vgpr_d[<span class="hljs-number">4</span>], vgpr_a[<span class="hljs-number">12</span>], sgpr_r[<span class="hljs-number">4</span>]
image_bvh64_intersect_ray vgpr_d[<span class="hljs-number">4</span>], vgpr_a[<span class="hljs-number">9</span>], sgpr_r[<span class="hljs-number">4</span>] A16=<span class="hljs-number">1</span></code></pre></div>
</blockquote>
<p>Despite the fact that in the application layer this is handled by a given <em>graphics API</em>, it&#x27;s worth understanding how acceleration structures work, either for CPU based collision tests, or to better futureproof one&#x27;s understanding of the subject. Let&#x27;s review acceleration structures, how they can be represented, how they can be built/updated, and how they can be traversed to query for intersections.</p>
<h2 id="terminology">Terminology</h2>
<p>A <strong>Bounding Volume Hierarchy (BVH)</strong> is a <em>hierarchical spatial structure of nodes</em>, and can be represented in a variety of ways. You could use axis aligned bounding boxes (AABBs), maintain a tree of 2 leafs per node, a variable number of leafs per node (an example of a <em>wide BVH</em> <a href="#ref_vaidyanathan2019">[Vaidyanathan et al. 2019]</a>), attempt to split the tree into smaller boxes to improve performance <a href="#ref_benthin2017">[Benthin et al. 2017]</a>, refit the bounds of the BVH nodes according to changes to the mesh, reorient the tree to avoid paths with many nodes or to prioritize nodes that are visited often, and much more. There&#x27;s a vast swath of research in the area of tree based data structures that you can take advantage of as well. <a href="#ref_karras2020">[Karras 2020]</a></p>
<div style="touch-action:none;user-select:none;-webkit-user-select:none"><div style="touch-action:none;user-select:none;-webkit-user-select:none;background-image:url(/blog/ray-tracing-acceleration-structures/assets/ref-8192-spp.png);background-size:contain;background-position:center;background-repeat:no-repeat"><canvas style="touch-action:none;user-select:none;-webkit-user-select:none;width:100%;height:31.5em"></canvas></div><p style="text-align:center"><strong>Controls: </strong><em>Hover over</em> a triangle in this Cornell Box to do a ray hit test, showing the triangle.</p></div>
<p>Bounding Boxes can be easily represented with lightweight nodes, for example, this struct can represent a node in as little as 64 bytes (and smaller if you take on compression schemes, use spare bits for cached information like the <code>splitPlane</code>), and you can encode extra data to that payload such as the AABB itself and an address to a triangle, parent, etc. <a href="#ref_meister2021">[Meister et al. 2021]</a></p>
<div><pre title="Bounding Volume Hierarchy Node"><code class="language-hlsl"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-keyword">struct</span> <span class="hljs-title">BVHNode</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2">{</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3">    <span class="hljs-comment">// 📤 Left AABB</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4">    <span class="hljs-type">float3</span> leftMin;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5">    <span class="hljs-type">float3</span> leftMax;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6">    <span class="hljs-comment">// 📥 Right AABB</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">    <span class="hljs-type">float3</span> rightMin;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">    <span class="hljs-type">float3</span> rightMax;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">    <span class="hljs-comment">// *️ Pointers</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">    <span class="hljs-type">uint</span> leftPointer;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">    <span class="hljs-type">uint</span> rightPointer;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12">};</td></tr></tbody></table></code></pre></div>
<p>Building a BVH can vary depending on your requirements, with the rule generally being a higher quality BVH is slower to build but can be traversed faster. BVH builders have been categorized as <em>top-down</em>, <em>bottom-up</em> and <em>insertion-based</em>. In general real time builders use fast techniques such as bottom up builders <a href="#ref_benthin2022">[Benthin et al. 2022]</a> do well due to the highly parallel nature of the GPU.</p>
<p><img src="https://alain.xyz/blog/ray-tracing-acceleration-structures/assets/kd-vs-bvh.svg" alt="KD Trees vs. BVH Diagram"/></p>
<p>Contrast a BVH with a <strong>KD-Tree</strong>, a simpler tree of data which is faster to construct and contains less information per node, but is slower to traverse. <a href="#ref_vinkler2014">[Vinkler et al. 2014]</a></p>
<div><pre title="K-Dimensional Tree Node"><code class="language-hlsl"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-keyword">struct</span> <span class="hljs-title">KDNode</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2">{</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3">    <span class="hljs-type">uint</span> parent;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4">    <span class="hljs-type">uint</span> flags;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5">    <span class="hljs-type">uint</span> leftPointer;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6">    <span class="hljs-type">uint</span> rightPointer;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">};</td></tr></tbody></table></code></pre></div>
<p>Notice that both of these examples include pointers, this could be to the triangle itself, but ideally should be to the triangle index stored in a <em>separate list</em>, that way the triangles can be rearanged in case the BVH needs to change due to animations, transformations, procedural geometry, etc. <a href="#ref_bikker2022">[Bikker 2022]</a></p>
<p>The <strong>Surface Area Heuristic</strong> (SAH) function can be used to estimate the efficiency of a BVH. In recent real time builders such as <a href="https://github.com/meistdan/ploc/blob/9ad7aa66d9adb5ef77bf88608a58318d3b9bdd47/gpu-ray-traversal/src/rt/bvh/SplitBVHBuilder.cpp#L145">PLOC</a>, this Heuristic is used to better perform splits when building a BVH.</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>P</mi><mo stretchy="false">(</mo><msub><mi>N</mi><mi>c</mi></msub><mi mathvariant="normal">∣</mi><mi>N</mi><msup><mo stretchy="false">)</mo><mrow><mi>S</mi><mi>A</mi><mi>H</mi></mrow></msup><mo>=</mo><mfrac><mrow><mi>S</mi><mi>A</mi><mo stretchy="false">(</mo><msub><mi>N</mi><mi>c</mi></msub><mo stretchy="false">)</mo></mrow><mrow><mi>S</mi><mi>A</mi><mo stretchy="false">(</mo><mi>N</mi><mo stretchy="false">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">P(N_c | N)^{SAH} = \frac{SA(N_c)}{SA(N)}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1413em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.1389em">P</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.109em">N</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.109em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">c</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.109em">N</span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8913em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0576em">S</span><span class="mord mathnormal mtight">A</span><span class="mord mathnormal mtight" style="margin-right:0.0813em">H</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.363em;vertical-align:-0.936em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.427em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0576em">S</span><span class="mord mathnormal">A</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.109em">N</span><span class="mclose">)</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0576em">S</span><span class="mord mathnormal">A</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.109em">N</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.109em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">c</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span>
<p>Where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi><mi>A</mi></mrow><annotation encoding="application/x-tex">SA</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0576em">S</span><span class="mord mathnormal">A</span></span></span></span> is the surface area function, and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi></mrow><annotation encoding="application/x-tex">N</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.109em">N</span></span></span></span> represents a node. <a href="#ref_pharr2016">[Pharr et al. 2016]</a></p>
<p>Thanks to the SAH, we can better estimate the probability that we hit the sub-nodes of a given BVH node through the ratio surface area of their bounding boxes and their parent, which can be useful for optimization passes to the BVH, selecting split planes for building BVHs, optimzing the BVH much more.</p>
<div><pre><code class="language-hlsl"><span class="hljs-comment">// XYZ XYZ XYZ</span>
<span class="hljs-type">uint</span> morton = <span class="hljs-number">0</span>b001002003;</code></pre></div>
<p><strong>Morton codes</strong> are a small hash value that can encode a 3D position in 32 bits, so in BVH implementations one can use this to easily index a given location (or <em>morton curve space</em>) in the structure when building the hierarchy. Each bit can represent a location in the hierarchy and groups of bits can be used for locations in XYZ space.</p>
<p>A builder can encode the position of box nodes of a BVH as a morton code normalized by the scale of the scene, and easily store and query for that value when building a BVH. This is done by <a href="https://github.com/meistdan/ploc/blob/9ad7aa66d9adb5ef77bf88608a58318d3b9bdd47/gpu-ray-traversal/src/rt/ploc/PLOCBuilderKernels.cu#L82">PLOC</a>.</p>
<ul>
<li><strong>C++ Library</strong>: <a href="https://github.com/Forceflow/libmorton">Libmorton (MIT)</a></li>
</ul>
<h2 id="ray-tracing-apis">Ray Tracing APIs</h2>
<p>DirectX 12 and Vulkan both support hardware accelerated raytracing and the interface for these APIs is the same. Hardware accelerated ray tracing uses a 2-level BVH, one level for the scene and one for individual meshes.</p>
<p><strong>Building</strong> acceleration structures with graphics APIs involves allocating <em>scratch buffers</em> for API and vendor specific data, providing the API with <em>Bottom Level Acceleration Structure (BLAS)</em> data in the form of your <em>index</em> and <em>vertex position buffer</em>, and <em>top level acceleration structures (TLAS)</em> built from your scene hierarchy with their transforms, and the instance of the BLAS the node correspond to.</p>
<div><pre title="Basic DirectX 12 Ray Tracing Example"><code class="language-cpp"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// 👋 Declare Handles</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2">ID3D12Device5* device;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3">ID3D12Resource* vertexBuffer;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4">ID3D12Resource* indexBuffer;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5">ID3D12GraphicsCommandList4* commandList;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7"><span class="hljs-comment">// Declare Outputs</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">ID3D12Resource* asBuffer;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">ID3D12Resource* asScratchBuffer;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">ID3D12Resource* instanceDescs;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">HRESULT hr;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13"><span class="hljs-comment">// 🌳 Top Level Acceleration Structure</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14">D3D12_RAYTRACING_GEOMETRY_DESC geomDescs[<span class="hljs-number">1</span>];</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15">geomDescs[<span class="hljs-number">0</span>].Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16">geomDescs[<span class="hljs-number">0</span>].Triangles.IndexBuffer = indexBuffer-&gt;<span class="hljs-built_in">GetGPUVirtualAddress</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17">geomDescs[<span class="hljs-number">0</span>].Triangles.IndexCount =</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18">    <span class="hljs-built_in">static_cast</span>&lt;UINT&gt;(indexBuffer-&gt;<span class="hljs-built_in">GetDesc</span>().Width) / <span class="hljs-built_in">sizeof</span>(UINT16);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19">geomDescs[<span class="hljs-number">0</span>].Triangles.IndexFormat = DXGI_FORMAT_R16_UINT;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="20"><div class="hljs-ln-n" data-line-number="20"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="20">geomDescs[<span class="hljs-number">0</span>].Triangles.Transform3x4 = <span class="hljs-number">0</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="21"><div class="hljs-ln-n" data-line-number="21"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="21">geomDescs[<span class="hljs-number">0</span>].Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="22"><div class="hljs-ln-n" data-line-number="22"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="22">geomDescs[<span class="hljs-number">0</span>].Triangles.VertexCount =</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="23"><div class="hljs-ln-n" data-line-number="23"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="23">    <span class="hljs-built_in">static_cast</span>&lt;UINT&gt;(vertexBuffer-&gt;<span class="hljs-built_in">GetDesc</span>().Width) / <span class="hljs-built_in">sizeof</span>(Vertex);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="24"><div class="hljs-ln-n" data-line-number="24"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="24">geomDescs[<span class="hljs-number">0</span>].Triangles.VertexBuffer.StartAddress =</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="25"><div class="hljs-ln-n" data-line-number="25"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="25">    vertexBuffer-&gt;<span class="hljs-built_in">GetGPUVirtualAddress</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="26"><div class="hljs-ln-n" data-line-number="26"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="26">geomDescs[<span class="hljs-number">0</span>].Triangles.VertexBuffer.StrideInBytes = <span class="hljs-built_in">sizeof</span>(Vertex);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="27"><div class="hljs-ln-n" data-line-number="27"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="27"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="28"><div class="hljs-ln-n" data-line-number="28"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="28"><span class="hljs-comment">// 🧱 Get prebuild info</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="29"><div class="hljs-ln-n" data-line-number="29"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="29">D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS topLevelInputs[<span class="hljs-number">1</span>];</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="30"><div class="hljs-ln-n" data-line-number="30"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="30">topLevelInputs[<span class="hljs-number">0</span>].DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="31"><div class="hljs-ln-n" data-line-number="31"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="31">topLevelInputs[<span class="hljs-number">0</span>].Flags =</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="32"><div class="hljs-ln-n" data-line-number="32"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="32">    D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PREFER_FAST_TRACE;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="33"><div class="hljs-ln-n" data-line-number="33"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="33">topLevelInputs[<span class="hljs-number">0</span>].NumDescs = <span class="hljs-number">1</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="34"><div class="hljs-ln-n" data-line-number="34"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="34">topLevelInputs[<span class="hljs-number">0</span>].Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="35"><div class="hljs-ln-n" data-line-number="35"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="35">D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO topLevelPrebuildInfo = {};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="36"><div class="hljs-ln-n" data-line-number="36"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="36">device-&gt;<span class="hljs-built_in">GetRaytracingAccelerationStructurePrebuildInfo</span>(topLevelInputs,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="37"><div class="hljs-ln-n" data-line-number="37"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="37">                                                       topLevelPrebuildInfo);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="38"><div class="hljs-ln-n" data-line-number="38"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="38"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="39"><div class="hljs-ln-n" data-line-number="39"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="39">D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS bottomLevelInputs[<span class="hljs-number">1</span>];</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="40"><div class="hljs-ln-n" data-line-number="40"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="40">bottomLevelInputs[<span class="hljs-number">0</span>] = topLevelInputs[<span class="hljs-number">0</span>];</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="41"><div class="hljs-ln-n" data-line-number="41"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="41">bottomLevelInputs.Type =</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="42"><div class="hljs-ln-n" data-line-number="42"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="42">    D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="43"><div class="hljs-ln-n" data-line-number="43"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="43">bottomLevelInputs.pGeometryDescs = geomDescs;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="44"><div class="hljs-ln-n" data-line-number="44"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="44">D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO bottomLevelPrebuildInfo =</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="45"><div class="hljs-ln-n" data-line-number="45"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="45">    {};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="46"><div class="hljs-ln-n" data-line-number="46"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="46">device-&gt;<span class="hljs-built_in">GetRaytracingAccelerationStructurePrebuildInfo</span>(</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="47"><div class="hljs-ln-n" data-line-number="47"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="47">    &amp;bottomLevelInputs, &amp;bottomLevelPrebuildInfo);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="48"><div class="hljs-ln-n" data-line-number="48"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="48"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="49"><div class="hljs-ln-n" data-line-number="49"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="49"><span class="hljs-comment">// 🍱 Create Scratch Buffer</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="50"><div class="hljs-ln-n" data-line-number="50"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="50">D3D12_RESOURCE_DESC asResourceDesc;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="51"><div class="hljs-ln-n" data-line-number="51"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="51">asResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="52"><div class="hljs-ln-n" data-line-number="52"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="52">asResourceDesc.Alignment = <span class="hljs-number">0</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="53"><div class="hljs-ln-n" data-line-number="53"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="53">asResourceDesc.Width = <span class="hljs-built_in">max</span>(topLevelPrebuildInfo.ScratchDataSizeInBytes,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="54"><div class="hljs-ln-n" data-line-number="54"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="54">                           bottomLevelPrebuildInfo.ScratchDataSizeInBytes);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="55"><div class="hljs-ln-n" data-line-number="55"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="55">asResourceDesc.Height = <span class="hljs-number">1</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="56"><div class="hljs-ln-n" data-line-number="56"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="56">asResourceDesc.DepthOrArraySize = <span class="hljs-number">1</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="57"><div class="hljs-ln-n" data-line-number="57"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="57">asResourceDesc.MipLevels = <span class="hljs-number">1</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="58"><div class="hljs-ln-n" data-line-number="58"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="58">asResourceDesc.Format = DXGI_FORMAT_UNKNOWN;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="59"><div class="hljs-ln-n" data-line-number="59"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="59">asResourceDesc.SampleDesc.Count = <span class="hljs-number">1</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="60"><div class="hljs-ln-n" data-line-number="60"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="60">asResourceDesc.SampleDesc.Quality = <span class="hljs-number">0</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="61"><div class="hljs-ln-n" data-line-number="61"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="61">asResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="62"><div class="hljs-ln-n" data-line-number="62"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="62">asResourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="63"><div class="hljs-ln-n" data-line-number="63"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="63"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="64"><div class="hljs-ln-n" data-line-number="64"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="64"><span class="hljs-comment">// 🍱 Create AS Scratch Buffer</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="65"><div class="hljs-ln-n" data-line-number="65"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="65">hr = device-&gt;<span class="hljs-built_in">CreateCommittedResource</span>(</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="66"><div class="hljs-ln-n" data-line-number="66"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="66">    &amp;heapProps, D3D12_HEAP_FLAG_NONE, &amp;asResourceDesc,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="67"><div class="hljs-ln-n" data-line-number="67"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="67">    D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE, <span class="hljs-literal">nullptr</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="68"><div class="hljs-ln-n" data-line-number="68"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="68">    <span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;asScratchBuffer));</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="69"><div class="hljs-ln-n" data-line-number="69"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="69"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="70"><div class="hljs-ln-n" data-line-number="70"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="70"><span class="hljs-comment">// 🍱 Create TLAS Buffer</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="71"><div class="hljs-ln-n" data-line-number="71"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="71">asResourceDesc.width = topLevelPrebuildInfo.ResultDataMaxSizeInBytes;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="72"><div class="hljs-ln-n" data-line-number="72"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="72">hr = device-&gt;<span class="hljs-built_in">CreateCommittedResource</span>(</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="73"><div class="hljs-ln-n" data-line-number="73"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="73">    &amp;heapProps, D3D12_HEAP_FLAG_NONE, &amp;asResourceDesc,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="74"><div class="hljs-ln-n" data-line-number="74"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="74">    D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE, <span class="hljs-literal">nullptr</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="75"><div class="hljs-ln-n" data-line-number="75"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="75">    <span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;tlasBuffer));</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="76"><div class="hljs-ln-n" data-line-number="76"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="76"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="77"><div class="hljs-ln-n" data-line-number="77"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="77"><span class="hljs-comment">// 🍱 Create BLAS Buffer</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="78"><div class="hljs-ln-n" data-line-number="78"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="78">asResourceDesc.width = bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="79"><div class="hljs-ln-n" data-line-number="79"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="79">hr = device-&gt;<span class="hljs-built_in">CreateCommittedResource</span>(</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="80"><div class="hljs-ln-n" data-line-number="80"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="80">    &amp;heapProps, D3D12_HEAP_FLAG_NONE, &amp;asResourceDesc,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="81"><div class="hljs-ln-n" data-line-number="81"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="81">    D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE, <span class="hljs-literal">nullptr</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="82"><div class="hljs-ln-n" data-line-number="82"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="82">    <span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;blasBuffer));</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="83"><div class="hljs-ln-n" data-line-number="83"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="83"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="84"><div class="hljs-ln-n" data-line-number="84"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="84">D3D12_RAYTRACING_INSTANCE_DESC instanceDesc = {};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="85"><div class="hljs-ln-n" data-line-number="85"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="85">instanceDesc.Transform[<span class="hljs-number">0</span>][<span class="hljs-number">0</span>] = instanceDesc.Transform[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>] =</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="86"><div class="hljs-ln-n" data-line-number="86"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="86">    instanceDesc.Transform[<span class="hljs-number">2</span>][<span class="hljs-number">2</span>] = <span class="hljs-number">1</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="87"><div class="hljs-ln-n" data-line-number="87"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="87">instanceDesc.InstanceMask = <span class="hljs-number">1</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="88"><div class="hljs-ln-n" data-line-number="88"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="88">instanceDesc.AccelerationStructure = blasBuffer-&gt;<span class="hljs-built_in">GetGPUVirtualAddress</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="89"><div class="hljs-ln-n" data-line-number="89"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="89">D3D12_HEAP_PROPERTIES uploadHeapProperties = {};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="90"><div class="hljs-ln-n" data-line-number="90"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="90">uploadHeapProperties.Type = D3D12_HEAP_TYPE_UPLOAD;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="91"><div class="hljs-ln-n" data-line-number="91"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="91">uploadHeapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="92"><div class="hljs-ln-n" data-line-number="92"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="92">uploadHeapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="93"><div class="hljs-ln-n" data-line-number="93"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="93">uploadHeapProperties.CreationNodeMask = <span class="hljs-number">1</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="94"><div class="hljs-ln-n" data-line-number="94"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="94">uploadHeapProperties.VisibleNodeMask = <span class="hljs-number">1</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="95"><div class="hljs-ln-n" data-line-number="95"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="95"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="96"><div class="hljs-ln-n" data-line-number="96"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="96">D3D12_RESOURCE_DESC bufferDesc = {};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="97"><div class="hljs-ln-n" data-line-number="97"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="97">bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="98"><div class="hljs-ln-n" data-line-number="98"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="98">bufferDesc.Alignment = <span class="hljs-number">0</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="99"><div class="hljs-ln-n" data-line-number="99"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="99">bufferDesc.Width = <span class="hljs-built_in">sizeof</span>(instanceDesc);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="100"><div class="hljs-ln-n" data-line-number="100"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="100">bufferDesc.Height = <span class="hljs-number">1</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="101"><div class="hljs-ln-n" data-line-number="101"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="101">bufferDesc.DepthOrArraySize = <span class="hljs-number">1</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="102"><div class="hljs-ln-n" data-line-number="102"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="102">bufferDesc.MipLevels = <span class="hljs-number">1</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="103"><div class="hljs-ln-n" data-line-number="103"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="103">bufferDesc.Format = DXGI_FORMAT_UNKNOWN;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="104"><div class="hljs-ln-n" data-line-number="104"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="104">bufferDesc.SampleDesc.Count = <span class="hljs-number">1</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="105"><div class="hljs-ln-n" data-line-number="105"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="105">bufferDesc.SampleDesc.Quality = <span class="hljs-number">0</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="106"><div class="hljs-ln-n" data-line-number="106"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="106">bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="107"><div class="hljs-ln-n" data-line-number="107"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="107">bufferDesc.Flags = D3D12_RESOURCE_FLAG_NONE;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="108"><div class="hljs-ln-n" data-line-number="108"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="108">hr = device-&gt;<span class="hljs-built_in">CreateCommittedResource</span>(</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="109"><div class="hljs-ln-n" data-line-number="109"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="109">    &amp;uploadHeapProperties, D3D12_HEAP_FLAG_NONE, &amp;bufferDesc,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="110"><div class="hljs-ln-n" data-line-number="110"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="110">    D3D12_RESOURCE_STATE_GENERIC_READ, <span class="hljs-literal">nullptr</span>, <span class="hljs-built_in">IID_PPV_ARGS</span>(instanceDescs));</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="111"><div class="hljs-ln-n" data-line-number="111"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="111"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="112"><div class="hljs-ln-n" data-line-number="112"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="112"><span class="hljs-type">void</span>* pMappedData;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="113"><div class="hljs-ln-n" data-line-number="113"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="113">(*instanceDescs)-&gt;<span class="hljs-built_in">Map</span>(<span class="hljs-number">0</span>, <span class="hljs-literal">nullptr</span>, &amp;pMappedData);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="114"><div class="hljs-ln-n" data-line-number="114"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="114"><span class="hljs-built_in">memcpy</span>(pMappedData, &amp;instanceDesc, <span class="hljs-built_in">sizeof</span>(instanceDesc));</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="115"><div class="hljs-ln-n" data-line-number="115"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="115">(*instanceDescs)-&gt;<span class="hljs-built_in">Unmap</span>(<span class="hljs-number">0</span>, <span class="hljs-literal">nullptr</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="116"><div class="hljs-ln-n" data-line-number="116"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="116"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="117"><div class="hljs-ln-n" data-line-number="117"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="117"><span class="hljs-comment">// 🏗️ BLAS build command</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="118"><div class="hljs-ln-n" data-line-number="118"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="118">D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC bottomLevelBuildDesc = {};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="119"><div class="hljs-ln-n" data-line-number="119"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="119"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="120"><div class="hljs-ln-n" data-line-number="120"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="120">bottomLevelBuildDesc.Inputs = bottomLevelInputs;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="121"><div class="hljs-ln-n" data-line-number="121"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="121">bottomLevelBuildDesc.ScratchAccelerationStructureData =</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="122"><div class="hljs-ln-n" data-line-number="122"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="122">    scratchResource-&gt;<span class="hljs-built_in">GetGPUVirtualAddress</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="123"><div class="hljs-ln-n" data-line-number="123"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="123">bottomLevelBuildDesc.DestAccelerationStructureData =</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="124"><div class="hljs-ln-n" data-line-number="124"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="124">    blasBuffer-&gt;<span class="hljs-built_in">GetGPUVirtualAddress</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="125"><div class="hljs-ln-n" data-line-number="125"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="125"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="126"><div class="hljs-ln-n" data-line-number="126"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="126"><span class="hljs-comment">// 🏗️ TLAS build command</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="127"><div class="hljs-ln-n" data-line-number="127"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="127">D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC topLevelBuildDesc = {};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="128"><div class="hljs-ln-n" data-line-number="128"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="128"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="129"><div class="hljs-ln-n" data-line-number="129"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="129">topLevelInputs.InstanceDescs = instanceDescs-&gt;<span class="hljs-built_in">GetGPUVirtualAddress</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="130"><div class="hljs-ln-n" data-line-number="130"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="130">topLevelBuildDesc.Inputs = topLevelInputs;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="131"><div class="hljs-ln-n" data-line-number="131"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="131">topLevelBuildDesc.DestAccelerationStructureData =</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="132"><div class="hljs-ln-n" data-line-number="132"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="132">    tlasBuffer-&gt;<span class="hljs-built_in">GetGPUVirtualAddress</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="133"><div class="hljs-ln-n" data-line-number="133"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="133">topLevelBuildDesc.ScratchAccelerationStructureData =</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="134"><div class="hljs-ln-n" data-line-number="134"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="134">    scratchResource-&gt;<span class="hljs-built_in">GetGPUVirtualAddress</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="135"><div class="hljs-ln-n" data-line-number="135"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="135"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="136"><div class="hljs-ln-n" data-line-number="136"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="136"><span class="hljs-comment">// 🏢 Execute build</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="137"><div class="hljs-ln-n" data-line-number="137"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="137">commandList-&gt;<span class="hljs-built_in">BuildRaytracingAccelerationStructure</span>(&amp;bottomLevelBuildDesc, <span class="hljs-number">0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="138"><div class="hljs-ln-n" data-line-number="138"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="138">                                                  <span class="hljs-literal">nullptr</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="139"><div class="hljs-ln-n" data-line-number="139"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="139"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="140"><div class="hljs-ln-n" data-line-number="140"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="140">D3D12_RESOURCE_BARRIER barrier = {};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="141"><div class="hljs-ln-n" data-line-number="141"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="141">barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="142"><div class="hljs-ln-n" data-line-number="142"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="142">barrier.UAV.pResource = blasBuffer;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="143"><div class="hljs-ln-n" data-line-number="143"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="143">commandList-&gt;<span class="hljs-built_in">ResourceBarrier</span>(<span class="hljs-number">1</span>, &amp;barrier);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="144"><div class="hljs-ln-n" data-line-number="144"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="144">commandList-&gt;<span class="hljs-built_in">BuildRaytracingAccelerationStructure</span>(&amp;topLevelBuildDesc, <span class="hljs-number">0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="145"><div class="hljs-ln-n" data-line-number="145"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="145">                                                  <span class="hljs-literal">nullptr</span>);</td></tr></tbody></table></code></pre></div>
<p><strong>Traversal</strong> occurs by executing a ray tracing pipeline with a given graphics API, and further in ray tracing programmable shader steps when calling the builtin <code>TraceRay</code> function.</p>
<div><pre title="Ray Tracing HLSL Shader"><code class="language-hlsl"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// 🌎 Global Resources</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">RaytracingAccelerationStructure</span> Scene : <span class="hljs-keyword">register</span>(t0, space0);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"><span class="hljs-keyword">RWTexture2D</span>&lt;<span class="hljs-type">float4</span>&gt; tOutput : <span class="hljs-keyword">register</span>(u0);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-keyword">cbuffer</span> cb : <span class="hljs-keyword">register</span>(b0)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5">{</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6">    <span class="hljs-keyword">row_major</span> <span class="hljs-type">float4x4</span> projectionMatrix : <span class="hljs-keyword">packoffset</span>(c0);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">    <span class="hljs-keyword">row_major</span> <span class="hljs-type">float4x4</span> viewMatrix : <span class="hljs-keyword">packoffset</span>(c4);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">    <span class="hljs-type">float3</span> origin : <span class="hljs-keyword">packoffset</span>(c8);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">    <span class="hljs-type">float</span> near : <span class="hljs-keyword">packoffset</span>(c9.x);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">    <span class="hljs-type">float</span> far : <span class="hljs-keyword">packoffset</span>(c9.y);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">[shader(&quot;raygeneration&quot;)]</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14"><span class="hljs-keyword">void</span> raygen()</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15">{</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16">    <span class="hljs-type">float2</span> screenCoords =</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17">        (<span class="hljs-type">float2</span>)DispatchRaysIndex() / (<span class="hljs-type">float2</span>)DispatchRaysDimensions();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18">    <span class="hljs-type">float2</span> ndc = screenCoords * <span class="hljs-number">2.0</span> - <span class="hljs-type">float2</span>(<span class="hljs-number">1.0</span>, <span class="hljs-number">1.0</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="20"><div class="hljs-ln-n" data-line-number="20"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="20">    <span class="hljs-type">float</span> near = <span class="hljs-number">.1</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="21"><div class="hljs-ln-n" data-line-number="21"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="21">    <span class="hljs-type">float</span> far = <span class="hljs-number">300.0</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="22"><div class="hljs-ln-n" data-line-number="22"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="22"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="23"><div class="hljs-ln-n" data-line-number="23"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="23">    <span class="hljs-type">float3</span> rayDir =</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="24"><div class="hljs-ln-n" data-line-number="24"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="24">        <span class="hljs-built_in">normalize</span>(<span class="hljs-built_in">mul</span>(<span class="hljs-built_in">mul</span>(viewMatrix, projectionMatrix),</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="25"><div class="hljs-ln-n" data-line-number="25"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="25">                      <span class="hljs-type">float4</span>(ndc * (far - near), far + near, far - near)))</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="26"><div class="hljs-ln-n" data-line-number="26"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="26">            .xyz;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="27"><div class="hljs-ln-n" data-line-number="27"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="27"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="28"><div class="hljs-ln-n" data-line-number="28"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="28">    <span class="hljs-keyword">RayDesc</span> ray;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="29"><div class="hljs-ln-n" data-line-number="29"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="29">    ray.Origin = origin.xyz;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="30"><div class="hljs-ln-n" data-line-number="30"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="30">    ray.Direction = rayDir;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="31"><div class="hljs-ln-n" data-line-number="31"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="31">    ray.TMin = <span class="hljs-number">0.1</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="32"><div class="hljs-ln-n" data-line-number="32"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="32">    ray.TMax = <span class="hljs-number">300.0</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="33"><div class="hljs-ln-n" data-line-number="33"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="33">    RayPayload payload = {<span class="hljs-type">float4</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.5</span>, <span class="hljs-number">0</span>)};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="34"><div class="hljs-ln-n" data-line-number="34"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="34">    <span class="hljs-built_in">TraceRay</span>(Scene, RAY_FLAG_NONE, ~<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, ray, payload);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="35"><div class="hljs-ln-n" data-line-number="35"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="35"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="36"><div class="hljs-ln-n" data-line-number="36"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="36">    <span class="hljs-comment">// Write the raytraced color to the output texture.</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="37"><div class="hljs-ln-n" data-line-number="37"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="37">    tOutput[DispatchRaysIndex().xy] = payload.color;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="38"><div class="hljs-ln-n" data-line-number="38"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="38">}</td></tr></tbody></table></code></pre></div>
<h2 id="more-resources">More Resources</h2>
<ul>
<li>
<p>Tero Karras (NVIDIA) released a <a href="https://developer.nvidia.com/blog/thinking-parallel-part-i-collision-detection-gpu/">3 part series describing collision detection, tree traversal, and tree construction on the GPU</a>.</p>
</li>
<li>
<p>Jacco Bikker (<a href="https://twitter.com/j_bikker">@j_bikker</a>) wrote a series of blog posts <a href="https://jacco.ompf2.com/2022/04/13/how-to-build-a-bvh-part-1-basics/">on BVH construction here</a>.</p>
</li>
<li>
<p><a href="https://alain.xyz/books/raw-directx12/ch4-ray-tracing">My Raw DirectX 12 Book - Chapter 4</a> focuses on ray tracing with DirectX 12 and provides more details on how to execute ray tracing in DirectX 12.</p>
</li>
<li>
<p><a href="https://www.geometrictools.com/Source/Mathematics.html">Geometric Tools for Computer Graphics</a> by Philip Schneider and David H. Eberly has a resources page that provides C++ header files for intersection tests for axis aligned bounding boxes as well as other volumes.</p>
</li>
<li>
<p>There&#x27;s a video presentation on <a href="https://www.youtube.com/watch?v=4tO0KTngVAw&amp;t=600s">a Survey on Bounding Volume Hierarchies for Ray Tracing</a> provided by High Performance Graphics.</p>
</li>
<li>
<p>Computer Graphics at TU Wien gave a lecture on <a href="https://www.youtube.com/watch?v=MzUxOe5x24w">spatial acceleration structures here</a>.</p>
</li>
<li>
<p><a href="https://raytracing.github.io/books/RayTracingTheNextWeek.html#boundingvolumehierarchies">Ray Tracing: The Next Week</a> reviews these concepts as well.</p>
</li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_mcguire2018"><span class="markademic-citename">[McGuire 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">The Graphics Codex</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/morgan3d.png" alt="Morgan McGuire&#x27;s picture"/><a href="https://casual-effects.com/">Morgan McGuire</a> (<a href="https://twitter.com/CasualEffects">@CasualEffects</a>)</span></span><br/><span><span class="markademic-publisher">Casual Effects 2018</span><br/></span><span><a href="https://graphicscodex.courses.nvidia.com/app.html">graphicscodex.courses.nvidia.com</a></span></div></td></tr><tr><td id="ref_kajiya1986"><span class="markademic-citename">[Kajiya 1986]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">The Rendering Equation</span></strong></em><br/><span class="markademic-authors"><span><span>James Kajiya</span></span></span><br/><span><span class="markademic-publisher">ACM, SIGGRAPH Computer Graphics 1986</span><br/></span><span><a href="http://doi.acm.org/10.1145/15886.15902">doi.acm.org</a></span></div></td></tr><tr><td id="ref_pharr2016"><span class="markademic-citename">[Pharr et al. 2016]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Physically based rendering: From theory to implementation</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/mmp.png" alt="Matt Pharr&#x27;s picture"/><a href="https://pharr.org/matt/">Matt Pharr</a></span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/wjakob.png" alt="Wenzel Jakob&#x27;s picture"/><a href="https://rgl.epfl.ch/people/wjakob">Wenzel Jakob</a></span><span> and <span>Greg Humphreys</span></span></span><br/><span><span class="markademic-publisher">Morgan Kaufmann 2016</span><br/></span><span><a href="https://www.pbr-book.org/">pbr-book.org</a></span></div></td></tr><tr><td id="ref_doyle2012"><span class="markademic-citename">[J. Doyle et al. 2012]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Hardware Accelerated Construction of SAH-Based Bounding Volume Hierarchies for Interactive Ray Tracing</span></strong></em><br/><span class="markademic-authors"><span><span>Michael J. Doyle</span></span><span>, <span>Colin Fowler</span></span><span> and <span>Michael Manzke</span></span></span><br/><span><span class="markademic-publisher">ACM 2012</span><br/></span><span><a href="https://doi.org/10.1145/2159616.2159655">doi.org</a></span></div></td></tr><tr><td id="ref_liktor2016"><span class="markademic-citename">[Liktor et al. 2016]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Bandwidth-Efficient BVH Layout for Incremental Hardware Traversal</span></strong></em><br/><span class="markademic-authors"><span><span>Gabor Liktor</span></span><span> and <span>Karthik Vaidyanathan</span></span></span><br/><span><span class="markademic-publisher">Eurographics 2016</span><br/></span><span><a href="https://www.intel.com/content/www/us/en/developer/articles/technical/bandwidth-efficient-bvh-layout-for-incremental-hardware-traversal.html">intel.com</a></span></div></td></tr><tr><td id="ref_vaidyanathan2016"><span class="markademic-citename">[Vaidyanathan et al. 2016]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Watertight Ray Traversal with Reduced Precision</span></strong></em><br/><span class="markademic-authors"><span><span>Karthik Vaidyanathan</span></span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/inversepixel.png" alt="Tomas Akenine-Möller&#x27;s picture"/><a href="http://cs.lth.se/tomas-akenine-moller/">Tomas Akenine-Möller</a> (<a href="https://twitter.com/inversepixel">@inversepixel</a>)</span><span> and <span>Marco Salvi</span> (<a href="https://twitter.com/marcosalvi">@marcosalvi</a>)</span></span><br/><span><span class="markademic-publisher">Eurographics 2016</span><br/></span><span><a href="https://www.intel.com/content/www/us/en/developer/articles/technical/watertight-ray-traversal-with-reduced-precision.html">intel.com</a></span></div></td></tr><tr><td id="ref_rdna2020"><span class="markademic-citename">[AMD 2020]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">RDNA 2 Instruction Set Architecture</span></strong></em><br/><span><span class="markademic-publisher">AMD 2020</span><br/></span><span><a href="https://developer.amd.com/wp-content/resources/RDNA2_Shader_ISA_November2020.pdf">developer.amd.com</a></span></div></td></tr><tr><td id="ref_vaidyanathan2019"><span class="markademic-citename">[Vaidyanathan et al. 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Wide BVH Traversal with a Short Stack</span></strong></em><br/><span class="markademic-authors"><span><span>Karthik Vaidyanathan</span></span><span>, <span>Sven Woop</span></span><span> and <span>Carsten Benthin</span></span></span><br/><span><span class="markademic-publisher">Eurographics 2019</span><br/></span><span><a href="https://www.intel.com/content/www/us/en/developer/articles/technical/wide-bvh-traversal-with-a-short-stack.html">intel.com</a></span></div></td></tr><tr><td id="ref_benthin2017"><span class="markademic-citename">[Benthin et al. 2017]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Improved Two-Level BVHs Using Partial Re-Braiding</span></strong></em><br/><span class="markademic-authors"><span><span>Carsten Benthin</span></span><span>, <a href="http://www.sven-woop.de/">Sven Woop</a></span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/ingowald.png" alt="Ingo Wald&#x27;s picture"/><a href="https://github.com/ingowald">Ingo Wald</a> (<a href="https://twitter.com/IngoWald">@IngoWald</a>)</span><span> and <span>Attila T. Afra</span></span></span><br/><span><span class="markademic-publisher">ACM 2017</span><br/></span><span><a href="http://www.sven-woop.de/papers/2017-HPG-openmerge.pdf">sven-woop.de</a></span></div></td></tr><tr><td id="ref_karras2020"><span class="markademic-citename">[Karras 2020]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Thinking parallel, part III: Tree construction on the GPU</span></strong></em><br/><span class="markademic-authors"><span><a href="https://research.nvidia.com/person/tero-karras">Tero Karras</a></span></span><br/><span><span class="markademic-publisher">NVIDIA 2020</span><br/></span><span><a href="https://developer.nvidia.com/blog/thinking-parallel-part-iii-tree-construction-gpu/">developer.nvidia.com</a></span></div></td></tr><tr><td id="ref_meister2021"><span class="markademic-citename">[Meister et al. 2021]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">A Survey on Bounding Volume Hierarchies for Ray Tracing</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/meistdan.png" alt="Daniel Meister&#x27;s picture"/><a href="https://meistdan.github.io/">Daniel Meister</a></span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/shinjiogaki.png" alt="Shinji Ogaki&#x27;s picture"/><a href="https://shinjiogaki.github.io/">Shinji Ogaki</a> (<a href="https://twitter.com/ShinjiOgaki">@ShinjiOgaki</a>)</span><span>, <span>Carsten Benthin</span></span><span>, <span>Michael J. Doyle</span> (<a href="https://twitter.com/mjdoyle0">@mjdoyle0</a>)</span><span>, <span>Michael Guthe</span></span><span> and <a href="https://dcgi.fel.cvut.cz/home/bittner/">Jiří Bittner</a></span></span><br/><span><span class="markademic-publisher">Computer Graphics Forum 2021</span><br/></span><span><a href="https://meistdan.github.io/publications/bvh_star/paper.pdf">meistdan.github.io</a></span></div></td></tr><tr><td id="ref_benthin2022"><span class="markademic-citename">[Benthin et al. 2022]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">PLOC++: Parallel Locally-Ordered Clustering for Bounding Volume Hierarchy Construction Revisited</span></strong></em><br/><span class="markademic-authors"><span><span>Carsten Benthin</span></span><span>, <span>Radoslaw Drabinski</span></span><span>, <span>Lorenzo Tessari</span></span><span> and <span>Addis Dittebrandt</span></span></span><br/><span><span class="markademic-publisher">ACM 2022</span><br/></span><span><a href="https://www.intel.com/content/www/us/en/developer/articles/technical/ploc-for-bounding-volume.html">intel.com</a></span></div></td></tr><tr><td id="ref_vinkler2014"><span class="markademic-citename">[Vinkler et al. 2014]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Bounding Volume Hierarchies versus Kd-Trees on Contemporary Many-Core Architectures</span></strong></em><br/><span class="markademic-authors"><span><span>Marek Vinkler</span></span><span>, <span>Vlastimil Havran</span></span><span> and <a href="https://dcgi.fel.cvut.cz/home/bittner/">Jiří Bittner</a></span></span><br/><span><span class="markademic-publisher">ACM 2014</span><br/></span><span><a href="https://doi.org/10.1145/2643188.2643196">doi.org</a></span></div></td></tr><tr><td id="ref_bikker2022"><span class="markademic-citename">[Bikker 2022]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">How to build a BVH – Part 1: Basics</span></strong></em><br/><span class="markademic-authors"><span><a href="https://jacco.ompf2.com">Jacco Bikker</a> (<a href="https://twitter.com/j_bikker">@j_bikker</a>)</span></span><br/><span><span class="markademic-publisher">2022</span><br/></span><span><a href="https://jacco.ompf2.com/2022/04/13/how-to-build-a-bvh-part-1-basics/">jacco.ompf2.com</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>A Review of Shader Languages</title>
<link>https://alain.xyz/blog/a-review-of-shader-languages</link>
<description>A review of the state of the art in real time graphics shading languages and compilers in both graphics and compute. What are some of the differences between HLSL, GLSL, MSL, and WGSL? What are some ways to transpile shaders?</description>
<guid isPermaLink="true">https://alain.xyz/blog/a-review-of-shader-languages</guid>
<pubDate>Sat, 12 Feb 2022 23:30:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>shader</category><category>hlsl</category><category>glsl</category><category>msl</category><category>vulkan</category><category>directx</category><category>opengl</category><category>spir-v</category><category>metal</category>
<enclosure url="https://alain.xyz/blog/a-review-of-shader-languages/assets/cover.jpg" length="120081" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/a-review-of-shader-languages/assets/pipelines.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/a-review-of-shader-languages/assets/directx12-logo.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/a-review-of-shader-languages/assets/opengl-vulkan-logo.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/a-review-of-shader-languages/assets/metal-logo.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/a-review-of-shader-languages/assets/webgpu-logo.svg"/><link rel="preload" as="image" href="https://github.com/litherum.png"/><p>Shaders are a set of instructions (so a <em>program</em> or <em>kernel</em>) that execute on programmable steps of a given GPU pipeline, be it a raster pipeline, compute, ray tracing, etc.</p>
<p><img src="https://alain.xyz/blog/a-review-of-shader-languages/assets/pipelines.svg" alt="Programmable Steps Diagram"/></p>
<p>They could be used to <em>transform vertices</em>, <em>shade pixels in raster based rendering</em>, <em>handle intersections</em> of primitives in ray tracing, <em>machine learning</em>, <em>perform general computations</em>, and much more. Shaders execute on a Single-Instruction-Multiple-Data (SIMD) architecture, where groups of processing cores execute the same instruction across large swaths of data.</p>
<p>Depending on the vendor this can mean different things. For instance, on AMD hardware a kernel executes across groups of 32 or 64 <em>work-items</em> (or <em>dispatch threads</em>) called <em>wavefronts</em> (or <em>thread groups</em>). These work-items execute on a small processor that handles scalar logic, vector logic, and scalar/vector memory stored in the local cache of the group or in GPU memory. <a href="#ref_rdna2020">[AMD 2020]</a></p>
<p>There are a number of shader languages used in real-time rendering:</p>
<table><thead><tr><th>Abbreviation</th><th>Language</th><th>Official Graphics APIs</th><th>Transpiling</th></tr></thead><tbody><tr><td>HLSL</td><td>High Level Shading Language</td><td>DirectX</td><td>Both <code>dxc</code> and <code>glslang</code> support HLSL → SPIR-V</td></tr><tr><td>GLSL</td><td>OpenGL Shading Language</td><td>Vulkan / OpenGL</td><td>GLSL → SPIR-V → HLSL</td></tr><tr><td>MSL</td><td>Metal Shading Language</td><td>Metal</td><td>SPIR-V → MSL via SPIR-V Cross</td></tr><tr><td>WGSL</td><td>WebGPU Shading Language</td><td>WebGPU</td><td><code>naga</code> or <code>tint</code> → SPIR-V</td></tr></tbody></table>
<p>With the addition of other platform specific languages such as <em>PlayStation Shading Language (PSL)</em>, experimental languages such as <a href="https://github.com/gfx-rs/rspirv">Rust to SPIR-V</a>, and the older C for Graphics (Cg) used in older Unity 3D code <a href="#ref_doppioslash2018">[Doppioslash 2018]</a>. Depending on which platform/API you&#x27;re targeting, you&#x27;ll be stuck with one of these languages.</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi><munder><mo><mrow><mi mathvariant="bold">S</mi><mi mathvariant="bold">h</mi><mi mathvariant="bold">a</mi><mi mathvariant="bold">d</mi><mi mathvariant="bold">e</mi><mi mathvariant="bold">r</mi></mrow></mo><mrow><mi>A</mi><mi>P</mi><mi>I</mi></mrow></munder></mi><mo>⇒</mo><mi><munder><mo><mrow><mi mathvariant="bold">I</mi><mi mathvariant="bold">R</mi></mrow></mo><mrow><mi>A</mi><mi>P</mi><mi>I</mi></mrow></munder></mi><mo>⇒</mo><mi><munder><mo><mrow><mi mathvariant="bold">I</mi><mi mathvariant="bold">R</mi></mrow></mo><mrow><mi>V</mi><mi>e</mi><mi>n</mi><mi>d</mi><mi>o</mi><mi>r</mi></mrow></munder></mi></mrow><annotation encoding="application/x-tex">\underset{API}{\mathbf{Shader}} \Rightarrow \underset{API}{\mathbf{IR}} \Rightarrow \underset{Vendor}{\mathbf{IR}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.4388em;vertical-align:-0.7443em"></span><span class="mord"><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-2.3557em;margin-left:0em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">A</span><span class="mord mathnormal mtight" style="margin-right:0.1389em">P</span><span class="mord mathnormal mtight" style="margin-right:0.0785em">I</span></span></span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span><span class="mop"><span class="mord"><span class="mord mathbf">Shader</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.7443em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">⇒</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.4304em;vertical-align:-0.7443em"></span><span class="mord"><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6861em"><span style="top:-2.3557em;margin-left:0em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">A</span><span class="mord mathnormal mtight" style="margin-right:0.1389em">P</span><span class="mord mathnormal mtight" style="margin-right:0.0785em">I</span></span></span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span><span class="mop"><span class="mord"><span class="mord mathbf">IR</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.7443em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">⇒</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.4382em;vertical-align:-0.7521em"></span><span class="mord"><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6861em"><span style="top:-2.3479em;margin-left:0em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.2222em">V</span><span class="mord mathnormal mtight">e</span><span class="mord mathnormal mtight">n</span><span class="mord mathnormal mtight">d</span><span class="mord mathnormal mtight" style="margin-right:0.0278em">or</span></span></span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span><span class="mop"><span class="mord"><span class="mord mathbf">IR</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.7521em"><span></span></span></span></span></span></span></span></span></span></span>
<p>The shader language world is <em>fragmented by design</em> given there&#x27;s so many vendors and APIs, but every shading language is similar enough to C that one can easily switch between them. Depending on the language, there could be an API specific intermediary representation (IR) of the shader code, and each vendor (AMD, NVIDIA, Intel, Qualcomm, etc.) converts that IR to their machine code at the driver level.</p>
<table><thead><tr><th>IR Abbreviation</th><th>Description</th><th>Official Graphics APIs</th></tr></thead><tbody><tr><td>DXIL</td><td>DirectX Intermediate Language</td><td>DirectX 12</td></tr><tr><td>DXBC</td><td>DirectX Bytecode</td><td>DirectX 11 / 12</td></tr><tr><td>SPIR-V</td><td>Standard Portable Intermediate Representation</td><td>Vulkan / OpenGL</td></tr><tr><td>RDNA ISA</td><td>AMD 5-7000 Series Instruction Set Architecture</td><td>N/A</td></tr><tr><td>PTX</td><td>NVIDIA Parallel Thread Execution VM and ISA</td><td>N/A</td></tr><tr><td>SASS</td><td>NVIDIA Bytecode</td><td>N/A</td></tr></tbody></table>
<p>Let&#x27;s review some of the similarities and differences between shader languages, their features, how they execute, and how to approach writing shaders for your applications.</p>
<p>We&#x27;ll review a <strong>vertex shader</strong> in HLSL, GLSL, MSL, and WGSL where we:</p>
<ul>
<li>
<p>Access data received from the <em>Input Assembly</em> step of the graphics pipeline.</p>
</li>
<li>
<p>Pass data to the next step of the graphics pipeline.</p>
</li>
<li>
<p>Receive data through <em>uniforms</em> or <em>constant buffer views</em>.</p>
</li>
</ul>
<p>In addition, we&#x27;ll review a <strong>compute shader</strong> in each of those languages where we:</p>
<ul>
<li>
<p>Read and write to buffers.</p>
</li>
<li>
<p>Perform atomic operations.</p>
</li>
<li>
<p>Share data across thread groups.</p>
</li>
</ul>
<p>From there we&#x27;ll go over how to approach compiling those shaders and include them in your applications.</p>
<h2 id="language-features">Language Features</h2>
<p>Nearly all shading languages are very similar to C, and have all the usual features associated with C.</p>
<p>In addition, shading languages feature a standard library of intrinsic functions available in the global namespace for performing common math operations. These standard functions include:</p>
<ul>
<li>
<p>Vector Math (matrix multiplication, vector addition, dot products). Some languages overload <code>operator*</code>, others use a function <code>mul</code>.</p>
</li>
<li>
<p>Transcendentals (Sin, Cos, Log, etc.) Some languages distinguish <code>tan(v)</code> and <code>tan2(a, b)</code>.</p>
</li>
<li>
<p>Neighbor Operations (derivatives) with minor differences in function names.</p>
</li>
<li>
<p>Logic (checking all members of a boolean vector with <code>all()</code>, etc.)</p>
</li>
<li>
<p>GPGPU memory synchronization such as <code>atomicAdd</code>.</p>
</li>
</ul>
<p>Most of these functions are even named the same, but some are slightly different (HLSL&#x27;s <code>frac</code> vs GLSL&#x27;s <code>fract</code>) or so simple that they&#x27;re not included in other languages like HLSL&#x27;s <code>saturate()</code> vs <code>clamp(val, 0.0, 1.0)</code>.</p>
<ul>
<li>DirectX Shader Compiler (DXC) features a page in their <a href="https://github.com/microsoft/DirectXShaderCompiler/blob/master/docs/SPIR-V.rst#using-glsl-extended-instructions">documentation that showcases HLSL vs. GLSL intrinsic functions</a>.</li>
</ul>
<h3 id="hlsl">HLSL</h3>
<p><img src="https://alain.xyz/blog/a-review-of-shader-languages/assets/directx12-logo.svg" alt="DirectX 12 Logo"/></p>
<p><strong>High-Level Shading Language (HLSL)</strong> is arguably the best language for writing shaders, as it&#x27;s very well supported, separates samplers and textures (unlike GLSL), and has common language features like:</p>
<ul>
<li>namespaces</li>
<li>template generics</li>
<li>overloadable operators</li>
<li><code>#include</code></li>
</ul>
<p>and much more <a href="https://devblogs.microsoft.com/directx/announcing-hlsl-2021/">as of HLSL 2021</a>.</p>
<p>For more details on the HLSL language, review the following:</p>
<ul>
<li>
<p><a href="https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-data-types">Built-in Types</a></p>
</li>
<li>
<p><a href="https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics">Semantic Decorators</a></p>
</li>
<li>
<p><a href="https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-intrinsic-functions">Intrinsic Functions</a></p>
</li>
</ul>
<div><pre><code class="language-hlsl"><span class="hljs-comment">// ❎ HLSL Vertex Shader</span>

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">Constants</span>
{
  <span class="hljs-keyword">row_major</span> <span class="hljs-type">float4x4</span> modelViewProjection : <span class="hljs-keyword">packoffset</span>(c0);
  <span class="hljs-type">float4x4</span> inverseTransposeModel : <span class="hljs-keyword">packoffset</span>(c4);
  <span class="hljs-type">float4x4</span> view : <span class="hljs-keyword">packoffset</span>(c8);
};

<span class="hljs-keyword">ConstantBuffer</span>&lt;Constants&gt; constants : <span class="hljs-keyword">register</span>(b0);

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">VertexInput</span>
{
    <span class="hljs-type">float3</span> position : <span class="hljs-built_in">POSITION</span>;
    <span class="hljs-type">float3</span> normal : <span class="hljs-built_in">NORMAL</span>;
    <span class="hljs-type">float2</span> texCoord : <span class="hljs-built_in">TEXCOORD</span>;
};

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">VertexOutput</span>
{
    <span class="hljs-type">float4</span> position : <span class="hljs-built_in">SV_Position</span>;
    <span class="hljs-type">float3</span> normal : <span class="hljs-built_in">NORMAL</span>;
    <span class="hljs-type">float2</span> texCoord : <span class="hljs-built_in">TEXCOORD</span>;

};

VertexOutput main(VertexInput vertexInput)
{
    <span class="hljs-type">float4</span> position = <span class="hljs-built_in">mul</span>(<span class="hljs-type">float4</span>(vertexInput.position, <span class="hljs-number">1.0f</span>), constants.modelViewProjection);
    <span class="hljs-type">float3</span> outNormal = <span class="hljs-built_in">mul</span>(<span class="hljs-type">float4</span>(vertexInput.normal, <span class="hljs-number">1.0</span>), constants.inverseTransposeModel).xyz;
    <span class="hljs-type">float2</span> outTexCoord = vertexInput.texCoord;

    VertexOutput output;
    output.position = position;
    output.normal = outNormal;
    output.texCoord = outTexCoord;

    <span class="hljs-keyword">return</span> output;
}</code></pre></div>
<p>If you want globally accessible shader constants (like how uniforms worked in older APIs), you can use the keyword <code>cbuffer</code>.</p>
<div><pre><code class="language-hlsl"><span class="hljs-keyword">cbuffer</span> cb : <span class="hljs-keyword">register</span>(b0)
{
    <span class="hljs-keyword">row_major</span> <span class="hljs-type">float4x4</span> modelViewProjection : <span class="hljs-keyword">packoffset</span>(c0);
    <span class="hljs-type">float3</span> origin : <span class="hljs-keyword">packoffset</span>(c4);
    <span class="hljs-type">float</span> time : <span class="hljs-keyword">packoffset</span>(c5);
};

<span class="hljs-comment">// Later...</span>

<span class="hljs-type">float4</span> position = <span class="hljs-built_in">mul</span>(<span class="hljs-type">float4</span>(vertexInput.position, <span class="hljs-number">1.0f</span>), modelViewProjection);</code></pre></div>
<p>HLSL is unique in that it describes attribute inputs with <a href="https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics"><em>semantics</em></a> (for example, <code>float3 position : POSITION</code>), though you can and <em>should</em> opt to avoid these and instead use the more generic <code>ATTRIB</code>. You can also use this syntax to describe other things about your variable similar to a bitfield, such as alignment or what buffer/texture/sampler registers correspond to a given variable.</p>
<p>HLSL shaders are very intuitive, they have an input, and output. They can have global constants or constants contained in a constant buffer.</p>
<p>Now let&#x27;s look at a compute shader:</p>
<div><pre><code class="language-hlsl"><span class="hljs-comment">// ❎ HLSL Compute Shader</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">Constants</span>
{
  <span class="hljs-type">float</span> time : <span class="hljs-keyword">packoffset</span>(c0);
};

<span class="hljs-keyword">ConstantBuffer</span>&lt;Constants&gt; constants : <span class="hljs-keyword">register</span>(b0);
<span class="hljs-type">Texture2D</span>&lt;<span class="hljs-type">float4</span>&gt; albedoTex : <span class="hljs-keyword">register</span>(t1);
<span class="hljs-type">Texture2D</span>&lt;<span class="hljs-type">float4</span>&gt; pbrLutTex : <span class="hljs-keyword">register</span>(t2);
<span class="hljs-type">SamplerState</span> gSampler : <span class="hljs-keyword">register</span>(s0);

<span class="hljs-keyword">RWTexture2D</span>&lt;<span class="hljs-type">float4</span>&gt; tOutput : <span class="hljs-keyword">register</span>(u0);

<span class="hljs-keyword">groupshared</span> <span class="hljs-type">float</span> groupData[<span class="hljs-number">4</span>];

<span class="hljs-keyword">namespace</span> random
{
    <span class="hljs-type">float</span> foo()
    {
        <span class="hljs-keyword">return</span> <span class="hljs-number">16.0</span>;
    }
}

[<span class="hljs-keyword">numthreads</span>(<span class="hljs-number">8</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>)]
<span class="hljs-keyword">void</span> main(<span class="hljs-type">uint3</span> groupThreadID : <span class="hljs-built_in">SV_GroupThreadID</span>,
         <span class="hljs-type">uint3</span> groupID : <span class="hljs-built_in">SV_GroupID</span>,
         <span class="hljs-type">uint</span>  groupIndex : <span class="hljs-built_in">SV_GroupIndex</span>,
         <span class="hljs-type">uint3</span> dispatchThreadID: <span class="hljs-built_in">SV_DispatchThreadID</span>)
{
    tOutput[dispatchThreadID.xy] = <span class="hljs-type">float4</span>( <span class="hljs-type">float</span>(groupThreadID.x) / random::foo(), <span class="hljs-type">float</span>(groupThreadID.y) / <span class="hljs-number">16.0</span>, dispatchThreadID.x / <span class="hljs-number">1280.0</span>, <span class="hljs-number">1.0</span>);
}</code></pre></div>
<p>You can easily define compute shader intrinsic values as arguments of the main function and expect those to be passed in, and name them whatever you might like. Otherwise this is practically the same as a vertex shader and as we&#x27;ll see, the same across languages.</p>
<h3 id="glsl">GLSL</h3>
<p><img src="https://alain.xyz/blog/a-review-of-shader-languages/assets/opengl-vulkan-logo.svg" alt="OpenGL and Vulkan Logos"/></p>
<p><strong>OpenGL Shading Language (GLSL)</strong> is the standard shader programming language for Khronos APIs such as Vulkan, OpenGL 4.x through optional extensions for different versions of GLSL, and WebGL. There&#x27;s a few fundamental differences between GLSL and other languages:</p>
<ul>
<li>
<p>Vector types use the prefix <code>vec</code> (for example, <code>vec4</code> instead of <code>float4</code> and <code>mat4</code> instead of <code>float4x4</code>)</p>
</li>
<li>
<p>Samplers and texture bindings are tied together. This is a big difference and area of contention, as this isn&#x27;t necessary and other languages separate these two data structures.</p>
</li>
</ul>
<p>Feature wise however GLSL is similar to HLSL, with support for <code>#include</code> through optional extensions and similar intrinsic functions.</p>
<p>The <strong>GLSL</strong> code begins with layout declarations that define exactly how each struct and object is laid out in memory. Each <code>location</code> corresponds to an address pointing to a maximum of 4 single precision floats. The keyword <code>in</code> denotes that this data is coming from the <strong>Input Assembly</strong> step of the graphics pipeline, and the keyword <code>out</code> denotes that this is data being passed to the next step of the graphics pipeline (<code>in</code> could be described as <em>passing a reference</em>).</p>
<p>The uniforms in this shader are struct based, and are bound to sets and binding indexes. Uniforms can be <em>Uniform Buffer Objects (UBOs)</em> where the last parameter can be an n-dimensional matrix if needed, or the could be <em>Samplers</em>. Note you don&#x27;t need to denote the namespace of the parameters of the UBOs, each name is unique and accessible in the global namespace (<code>model.transform == transform</code>).</p>
<blockquote>
<p>It&#x27;s worth noting that there&#x27;s a wide range of supported versions of GLSL and OpenGL, with <em>GLSL ES 1.0</em> being the version used for WebGL, and <em>GLSL ES 3.0</em> used for WebGL 2.</p>
</blockquote>
<p>In the <code>main</code> function we perform our shader logic and write our output position and varying variables. Note that the use of the keyword <code>main</code> can be set to anything you would like.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🌋 GLSL Vertex Shader</span>
<span class="hljs-meta">#version 450</span>

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">VertexInput</span>
{
    <span class="hljs-type">vec3</span> position;
    <span class="hljs-type">vec3</span> normal;
    <span class="hljs-type">vec2</span> texCoord;
};

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">VertexOutput</span>
{
    <span class="hljs-type">vec4</span> position;
    <span class="hljs-type">vec3</span> normal;
    <span class="hljs-type">vec2</span> texCoord;
};

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">Constants</span>
{
    <span class="hljs-type">mat4</span> modelViewProjection;
    <span class="hljs-type">mat4</span> inverseTransposeModel;
    <span class="hljs-type">mat4</span> view;
};

<span class="hljs-keyword">layout</span>(set = <span class="hljs-number">0</span>) <span class="hljs-keyword">uniform</span> Constants constants;

<span class="hljs-keyword">layout</span> (<span class="hljs-keyword">location</span> = <span class="hljs-number">0</span>) <span class="hljs-keyword">in</span> <span class="hljs-type">vec3</span> position;
<span class="hljs-keyword">layout</span> (<span class="hljs-keyword">location</span> = <span class="hljs-number">1</span>) <span class="hljs-keyword">in</span> <span class="hljs-type">vec3</span> normal;
<span class="hljs-keyword">layout</span> (<span class="hljs-keyword">location</span> = <span class="hljs-number">2</span>) <span class="hljs-keyword">in</span> <span class="hljs-type">vec2</span> texCoord;

<span class="hljs-keyword">layout</span> (<span class="hljs-keyword">location</span> = <span class="hljs-number">0</span>) <span class="hljs-keyword">out</span> <span class="hljs-type">vec3</span> outNormal;
<span class="hljs-keyword">layout</span> (<span class="hljs-keyword">location</span> = <span class="hljs-number">1</span>) <span class="hljs-keyword">out</span> <span class="hljs-type">vec2</span> outTexCoord;

<span class="hljs-type">void</span> main()
{
    <span class="hljs-type">vec4</span> position =  constants.modelViewProjection * <span class="hljs-type">vec4</span>(position, <span class="hljs-number">1.0</span>f);
    outNormal = (constants.inverseTransposeModel * <span class="hljs-type">vec4</span>(normal, <span class="hljs-number">1.0</span>)).xyz;
    outTexCoord = texCoord;
    <span class="hljs-built_in">gl_Position</span> = position;
}</code></pre></div>
<p>Compute shaders are generally the same as HLSL, though each built-in value passed in HLSL is just a global in GLSL. Writing data to an image is done through a function instead of the array syntax. There&#x27;s also no need to annotate the main function to specify what kind of shader it is, since that&#x27;s written out at compile time to SPIR-V.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🌋 GLSL Compute Shader</span>
<span class="hljs-meta">#version 450</span>
<span class="hljs-keyword">layout</span>(<span class="hljs-keyword">local_size_x</span> = <span class="hljs-number">8</span>, <span class="hljs-keyword">local_size_y</span> = <span class="hljs-number">4</span>, <span class="hljs-keyword">local_size_z</span> = <span class="hljs-number">1</span>) <span class="hljs-keyword">in</span>;

<span class="hljs-keyword">layout</span>(<span class="hljs-keyword">binding</span> = <span class="hljs-number">0</span>, <span class="hljs-keyword">std140</span>) <span class="hljs-keyword">uniform</span> Constants
{
    <span class="hljs-type">float</span> time;
} constants;

<span class="hljs-keyword">layout</span>(<span class="hljs-keyword">binding</span> = <span class="hljs-number">0</span>, <span class="hljs-keyword">rgba32f</span>) <span class="hljs-keyword">uniform</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">writeonly</span> <span class="hljs-type">image2D</span> tOutput;

<span class="hljs-keyword">shared</span> <span class="hljs-type">float</span> groupData[<span class="hljs-number">4</span>];

<span class="hljs-type">float</span> foo()
{
    <span class="hljs-keyword">return</span> <span class="hljs-number">16.0</span>;
}

<span class="hljs-type">void</span> main()
{
    <span class="hljs-built_in">imageStore</span>(tOutput, <span class="hljs-built_in">gl_GlobalInvocationID</span>.xy,
               <span class="hljs-type">vec4</span>(<span class="hljs-type">float</span>(gl_SubgroupInvocationID.x) / foo(),
                    <span class="hljs-type">float</span>(gl_SubgroupInvocationID.y) / <span class="hljs-number">16.0</span>,
                    <span class="hljs-built_in">gl_GlobalInvocationID</span>.x / <span class="hljs-number">1280.0</span>, <span class="hljs-number">1.0</span>));
}</code></pre></div>
<h3 id="msl">MSL</h3>
<p><img src="https://alain.xyz/blog/a-review-of-shader-languages/assets/metal-logo.svg" alt="Metal Logo"/></p>
<p>In <strong>Metal Shading Language (MSL)</strong> there is no <code>uniform</code> keyword, everything that is a buffer could be a uniform. Similar to HLSL, attributes are passed as arguments in the main function, with directives to denote where that data is coming from. Unlike HLSL or GLSL, data to be passed to later stages of the pipeline is returned by the main function.</p>
<p>Metal vector types are named the same as HLSL, and the intrinsic functions are nearly all the same as well.</p>
<div><pre><code class="language-msl"><span class="hljs-comment">// 🤖 Metal Vertex Shader</span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;metal_stdlib&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;simd/simd.h&gt;</span></span>

<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> metal;

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">Constants</span>
{
    <span class="hljs-type">float4x4</span> modelViewProjection;
    <span class="hljs-type">float4x4</span> inverseTransposeModel;
    <span class="hljs-type">float4x4</span> view;
};

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">VertexOutput</span>
{
    <span class="hljs-type">float3</span> normal [[<span class="hljs-built_in">user</span>(locn0)]];
    <span class="hljs-type">float2</span> texCoord [[<span class="hljs-built_in">user</span>(locn1)]];
    <span class="hljs-type">float4</span> position [[position]];
};

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">VertexInput</span>
{
    <span class="hljs-type">float3</span> position [[<span class="hljs-built_in">attribute</span>(<span class="hljs-number">0</span>)]];
    <span class="hljs-type">float3</span> normal [[<span class="hljs-built_in">attribute</span>(<span class="hljs-number">1</span>)]];
    <span class="hljs-type">float2</span> texCoord [[<span class="hljs-built_in">attribute</span>(<span class="hljs-number">2</span>)]];
};

<span class="hljs-function"><span class="hljs-keyword">vertex</span> VertexOutput <span class="hljs-title">main</span><span class="hljs-params">(VertexInput in [[<span class="hljs-keyword">stage_in</span>]],
                         <span class="hljs-type">constant</span> Constants&amp; constants [[<span class="hljs-keyword">buffer</span>(<span class="hljs-number">0</span>)]],
                         <span class="hljs-type">uint</span> gl_VertexID [[<span class="hljs-keyword">vertex_id</span>]],
                         <span class="hljs-type">uint</span> gl_InstanceID [[<span class="hljs-keyword">instance_id</span>]])</span>
</span>{
    VertexOutput out = {};
    <span class="hljs-type">float4</span> position = constants.modelViewProjection * <span class="hljs-built_in">float4</span>(in.position, <span class="hljs-number">1.0</span>);
    out.normal = (constants.inverseTransposeModel * <span class="hljs-built_in">float4</span>(in.normal, <span class="hljs-number">1.0</span>)).xyz;
    out.texCoord = in.texCoord;
    out.position = position;
    <span class="hljs-keyword">return</span> out;
}</code></pre></div>
<p>Metal&#x27;s input/output model is much more clear than in other languages, where the main function shows those bindings, and this applies to their compute shaders as well (denoted with the keyword <code>kernel</code>).</p>
<div><pre><code class="language-msl"><span class="hljs-comment">// 🤖 Metal Compute Shader</span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;metal_stdlib&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;simd/simd.h&gt;</span></span>

<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> metal;

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">Constants</span>
{
    <span class="hljs-type">float</span> time;
};

<span class="hljs-function"><span class="hljs-type">float</span> <span class="hljs-title">foo</span><span class="hljs-params">()</span>
</span>{
    <span class="hljs-keyword">return</span> <span class="hljs-number">16.0</span>;
}

<span class="hljs-function"><span class="hljs-keyword">kernel</span> <span class="hljs-type">void</span> <span class="hljs-title">main</span><span class="hljs-params">(
    <span class="hljs-type">uint3</span> groupThreadID [[ <span class="hljs-keyword">thread_position_in_threadgroup</span> ]],
    <span class="hljs-type">uint3</span> groupID [[<span class="hljs-keyword">threadgroup_position_in_grid</span>]],
    <span class="hljs-type">uint</span> groupIndex [[<span class="hljs-keyword">thread_index_in_threadgroup</span>]],
    <span class="hljs-type">uint3</span> dispatchThreadID [[<span class="hljs-keyword">thread_position_in_grid</span>]],
    <span class="hljs-type">constant</span> Constants&amp; constants [[<span class="hljs-keyword">buffer</span>(<span class="hljs-number">0</span>)]],
    <span class="hljs-keyword">texture2d</span>&lt;<span class="hljs-type">float</span>, access::read_write&gt; tOutput [[<span class="hljs-keyword">texture</span>(<span class="hljs-number">0</span>)]])</span>
</span>{
    <span class="hljs-type">threadgroup</span> <span class="hljs-type">float</span> groupData[<span class="hljs-number">4</span>];
    tOutput[dispatchThreadID.xy] = <span class="hljs-built_in">float4</span>( <span class="hljs-built_in">float</span>(groupThreadID.x) / <span class="hljs-built_in">foo</span>(), <span class="hljs-built_in">float</span>(groupThreadID.y) / <span class="hljs-number">16.0</span>, dispatchThreadID.x / <span class="hljs-number">1280.0</span>, <span class="hljs-number">1.0</span>);
}</code></pre></div>
<h3 id="wgsl">WGSL</h3>
<p><img src="https://alain.xyz/blog/a-review-of-shader-languages/assets/webgpu-logo.svg" alt="WebGPU Logo"/></p>
<p><strong>WebGPU Shading Language (WGSL)</strong> is pretty unique, with a syntax that&#x27;s a bit like JavaScript, Rust, C, and Apple Metal. It&#x27;s evolved a lot over its development, it used to be much more verbose <a href="#ref_maxfield2018">[Maxfield 2018]</a> but has since become a blend of Metal Shader Language and HLSL.</p>
<p>Vector types are unique in that they are similar to both HLSL and GLSL, but have a more clearly defined size for each type.</p>
<div><pre><code class="language-wgsl"><span class="hljs-comment">// 🌐 WGSL Vertex Shader</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">Constants</span>
{
  modelViewProjection: <span class="hljs-type">mat4x4</span>&lt;<span class="hljs-type">f32</span>&gt;,
  inverseTransposeModel: <span class="hljs-type">mat4x4</span>&lt;<span class="hljs-type">f32</span>&gt;,
  view: <span class="hljs-type">mat4x4</span>&lt;<span class="hljs-type">f32</span>&gt;
};

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">VertexOutput</span>
{
    <span class="hljs-meta-keyword">@builtin</span>(position) position: <span class="hljs-type">vec4</span>&lt;<span class="hljs-type">f32</span>&gt;,
    <span class="hljs-meta-keyword">@location</span>(<span class="hljs-number">0</span>) normal: <span class="hljs-type">vec3</span>&lt;<span class="hljs-type">f32</span>&gt;,
    <span class="hljs-meta-keyword">@location</span>(<span class="hljs-number">1</span>) uv: <span class="hljs-type">vec2</span>&lt;<span class="hljs-type">f32</span>&gt;
};

<span class="hljs-meta-keyword">@group</span>(<span class="hljs-number">0</span>) <span class="hljs-meta-keyword">@binding</span>(<span class="hljs-number">0</span>) <span class="hljs-type">var</span>&lt;<span class="hljs-keyword">uniform</span>&gt; constants : Constants;

<span class="hljs-meta-keyword">@vertex</span>
<span class="hljs-keyword">fn</span> <span class="hljs-title class_">main</span>(<span class="hljs-meta-keyword">@location</span>(<span class="hljs-number">0</span>) position: <span class="hljs-type">vec4</span>&lt;<span class="hljs-type">f32</span>&gt;,
        <span class="hljs-meta-keyword">@location</span>(<span class="hljs-number">1</span>) uv: <span class="hljs-type">vec2</span>&lt;<span class="hljs-type">f32</span>&gt;) -&gt; <span class="hljs-title class_">VertexOutput</span> {
    <span class="hljs-type">var</span> vsOut: VertexOutput;
    vsOut.position = constants.modelViewProjection * <span class="hljs-type">vec4</span>&lt;<span class="hljs-type">f32</span>&gt;(position, <span class="hljs-number">1.0</span>);
    vsOut.position = constants.inverseTransposeModel * <span class="hljs-type">vec4</span>&lt;<span class="hljs-type">f32</span>&gt;(normal, <span class="hljs-number">1.0</span>);
    vsOut.uv = uv;
    <span class="hljs-keyword">return</span> vsOut;
}</code></pre></div>
<p>Compute shaders are similar to GLSL, with similar names for built-in values such as the global invocation ID, etc:</p>
<div><pre><code class="language-wgsl"><span class="hljs-comment">// 🌐 WGSL Compute Shader</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">Constants</span>
{
    time: <span class="hljs-type">f32</span>
};

<span class="hljs-meta-keyword">@group</span>(<span class="hljs-number">0</span>) <span class="hljs-meta-keyword">@binding</span>(<span class="hljs-number">0</span>) <span class="hljs-type">var</span>&lt;<span class="hljs-keyword">uniform</span>&gt; constants: Constants;
<span class="hljs-meta-keyword">@group</span>(<span class="hljs-number">0</span>) <span class="hljs-meta-keyword">@binding</span>(<span class="hljs-number">1</span>) <span class="hljs-type">var</span> tOutput: texture_storage_2d&lt;rgba8unorm,write&gt;;

<span class="hljs-keyword">fn</span> <span class="hljs-title class_">foo</span>() -&gt; <span class="hljs-title class_">f32</span>
{
    <span class="hljs-keyword">return</span> <span class="hljs-number">4.0</span>;
}

<span class="hljs-meta-keyword">@compute</span> <span class="hljs-meta-keyword">@workgroup_size</span>(<span class="hljs-number">8</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>)
<span class="hljs-keyword">fn</span> <span class="hljs-title class_">main</span>(<span class="hljs-meta-keyword">@builtin</span>(local_invocation_id) localInvocationID: <span class="hljs-type">vec3</span>&lt;<span class="hljs-type">u32</span>&gt;,
        <span class="hljs-meta-keyword">@builtin</span>(workgroup_id) workgroupID: <span class="hljs-type">vec3</span>&lt;<span class="hljs-type">u32</span>&gt;,
        <span class="hljs-meta-keyword">@builtin</span>(local_invocation_index) localInvocationIndex: <span class="hljs-type">u32</span>,
        <span class="hljs-meta-keyword">@builtin</span>(global_invocation_id) globalInvocationID: <span class="hljs-type">vec3</span>&lt;<span class="hljs-type">u32</span>&gt;) {

    <span class="hljs-type">var</span> color = <span class="hljs-type">vec4</span>&lt;<span class="hljs-type">f32</span>&gt;(
        (<span class="hljs-type">f32</span>(localInvocationID.x) / <span class="hljs-number">8.0</span>) + <span class="hljs-built_in">sin</span>((<span class="hljs-type">f32</span>(globalInvocationID.x) / <span class="hljs-number">160.0</span>) + (constants.time * <span class="hljs-number">0.01</span>)),
        <span class="hljs-type">f32</span>(localInvocationID.y) / foo(),
        <span class="hljs-type">f32</span>(globalInvocationID.y) / <span class="hljs-number">360.0</span>,
        <span class="hljs-number">255</span>
    );

    <span class="hljs-built_in">textureStore</span>(tOutput, <span class="hljs-type">vec2</span>&lt;i32&gt;(globalInvocationID.xy), color);
}</code></pre></div>
<h3 id="opencl">OpenCL</h3>
<p><strong>OpenCL</strong> looks incredibly similar to both Apple Metal and OpenGL. While the idea of a graphics pipeline doesn&#x27;t exist in OpenCL, a compute kernel in other APIs is essentially the same thing.</p>
<div><pre><code class="language-cpp"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Constants</span>
{
    <span class="hljs-type">float</span> time;
    uint width;
    uint height;
};

<span class="hljs-function"><span class="hljs-type">float</span> <span class="hljs-title">foo</span><span class="hljs-params">()</span>
</span>{
    <span class="hljs-keyword">return</span> <span class="hljs-number">16.0</span>;
}

<span class="hljs-function">__kernel <span class="hljs-type">void</span> <span class="hljs-title">main</span>
<span class="hljs-params">(

    __constant Constants constants,
    __global float4* tOutput
)</span>
</span>{
    uint localId = <span class="hljs-built_in">get_local_id</span>(<span class="hljs-number">0</span>);
    uint workgroupId = <span class="hljs-built_in">get_group_id</span>(<span class="hljs-number">0</span>);
    uint globalId = <span class="hljs-built_in">get_global_id</span>(<span class="hljs-number">0</span>);

    <span class="hljs-type">int</span> x = globalId % constants.width;
    <span class="hljs-type">int</span> y = globalId / constants.width;

    <span class="hljs-keyword">if</span> (x &gt;= width || y &gt;= constants.height)
    {
        <span class="hljs-keyword">return</span>;
    }
    tOutput[pixel_idx] = (float4)((<span class="hljs-type">float</span>)(x) / <span class="hljs-built_in">foo</span>(), (<span class="hljs-type">float</span>)(y) / <span class="hljs-number">16.0</span>, (<span class="hljs-type">float</span>)(x) / <span class="hljs-number">1280.0</span>, <span class="hljs-number">1.0</span>);
}</code></pre></div>
<h2 id="shader-compilation">Shader Compilation</h2>
<h3 id="offline">Offline</h3>
<blockquote>
<p>Yak-shave: When I press &quot;F5&quot; now in this side project, cmake actually detects if I&#x27;ve changed shaders, compiles them to SPIR-V, converts that to hex in a .c file, compiles those c files into a lib, and then links it to the exe so I can reference the bytecode without file reads. ~ <a href="https://twitter.com/m_ninepoints/status/1494917159985713152">Jeremy Ong (@m_ninepoints)</a></p>
</blockquote>
<p>You should <em>always aim to compile your shaders in advance</em> to your target graphics API&#x27;s IR. This will result in a noticeable improvement in performance, since you no longer have to wait for shaders to compile before submitting commands. Some languages however encourage you <em>not to compile shaders to an IR</em> such as GLSL with older versions of OpenGL (though you can now use SPIR-V thanks to extensions) or WebGPU Shading Language (WGSL), and for those you can minify/obfuscate them similar to JavaScript for production.</p>
<h4 id="hlsl-1">HLSL</h4>
<div><pre><code class="language-bash"><span class="hljs-comment"># ❎ DirectX</span>
dxc.exe -T lib_6_3 -Fo assets/triangle.vert.dxil assets/triangle.vert.hlsl

<span class="hljs-comment"># 🌋 Vulkan / OpenGL</span>
dxc.exe -spirv -Fo assets/triangle.vert.spv assets/triangle.vert.hlsl</code></pre></div>
<p>HLSL Shaders can be compiled offline to DXIL with <a href="https://github.com/microsoft/DirectXShaderCompiler">DirectX Shader Compiler</a>.</p>
<p>If you want to output SPIR-V, you will need to pass the <code>-spirv</code> flag as well as <a href="https://github.com/microsoft/DirectXShaderCompiler/blob/master/docs/SPIR-V.rst#specialization-constants">annotate your HLSL with SPIR-V decorators</a>.</p>
<h4 id="glsl-1">GLSL</h4>
<div><pre><code class="language-bash"><span class="hljs-comment"># 🌋 Vulkan / OpenGL</span>
glslangValidator -V triangle.vert -o triangle.vert.spv</code></pre></div>
<p><a href="https://github.com/KhronosGroup/glslang">GLSLang Validator</a> is Khronos&#x27; reference front end for GLSL, and can compile it to SPIR-V.</p>
<h4 id="metal">Metal</h4>
<div><pre><code class="language-bash"><span class="hljs-comment"># 🤖 Metal</span>
metallib triangle.vert.msl triangle.vert.mbc</code></pre></div>
<p>The <code>metallib</code> compiler is available in the command line in any recent MacOS installation.</p>
<h3 id="online">Online</h3>
<div><pre><code class="language-cpp"><span class="hljs-comment">// Vulkan (glslang.h)</span>

<span class="hljs-function">SH_IMPORT_EXPORT <span class="hljs-type">int</span> <span class="hljs-title">ShCompile</span><span class="hljs-params">(
    <span class="hljs-type">const</span> <span class="hljs-type">void</span>* shHandle,
    <span class="hljs-type">const</span> <span class="hljs-type">char</span>* <span class="hljs-type">const</span> shaderStrings[],
    <span class="hljs-type">const</span> <span class="hljs-type">int</span> numStrings,
    <span class="hljs-type">const</span> <span class="hljs-type">int</span>* lengths,
    <span class="hljs-type">const</span> EShOptimizationLevel,
    <span class="hljs-type">const</span> TBuiltInResource *resources,
    <span class="hljs-type">int</span> debugOptions,
    <span class="hljs-type">int</span> defaultVersion = <span class="hljs-number">110</span>,            <span class="hljs-comment">// use 100 for ES environment, overridden by #version in shader</span>
    <span class="hljs-type">bool</span> forwardCompatible = <span class="hljs-literal">false</span>,      <span class="hljs-comment">// give errors for use of deprecated features</span>
    EShMessages messages = EShMsgDefault <span class="hljs-comment">// warnings and errors</span>
    )</span></span>;</code></pre></div>
<p>Vulkan introduces offline shader compilation, so to compile you&#x27;ll need to either write a compiler to SPIR-V or use the official compiler. GLSLang even supports compiling HLSL to SPIR-V, though this feature is still experimental.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// OpenGL</span>

GLuint shader = <span class="hljs-built_in">glCreateShader</span>(VETEX_SHADER);

<span class="hljs-built_in">glShaderSource</span>(shader, srcStr);

<span class="hljs-built_in">glCompileShader</span>(shader);

GLint isCompiled = <span class="hljs-number">0</span>;
<span class="hljs-built_in">glGetShaderiv</span>(shader, GL_COMPILE_STATUS, &amp;isCompiled);
<span class="hljs-keyword">if</span>(isCompiled == GL_FALSE)
{
  GLint maxLength = <span class="hljs-number">0</span>;
  <span class="hljs-built_in">glGetShaderiv</span>(shader, GL_INFO_LOG_LENGTH, &amp;maxLength);
  <span class="hljs-function">std::vector&lt;GLchar&gt; <span class="hljs-title">errorLog</span><span class="hljs-params">(maxLength)</span></span>;
  <span class="hljs-built_in">glGetShaderInfoLog</span>(shader, maxLength, &amp;maxLength, &amp;errorLog[<span class="hljs-number">0</span>]);
}</code></pre></div>
<p>OpenGL is pretty straight forward, you send it the string and check if it&#x27;s valid. Alternatively, newer versions of OpenGL let you load SPIR-V directly.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// DirectX</span>

<span class="hljs-function">HRESULT WINAPI <span class="hljs-title">D3DCompile</span><span class="hljs-params">(
  in      LPCVOID pSrcData,
  in      SIZE_T SrcDataSize,
  in_opt  LPCSTR pSourceName,
  in_opt  <span class="hljs-type">const</span> D3D_SHADER_MACRO pDefines,
  in_opt  ID3DInclude pInclude,
  in_opt  LPCSTR pEntrypoint,
  in      LPCSTR pTarget,
  in      UINT Flags1,
  in      UINT Flags2,
  out     ID3DBlob ppCode,
  out_opt ID3DBlob ppErrorMsgs
)</span></span>;</code></pre></div>
<p>DirectX like OpenGL supports compiling shaders at runtime, however both now support loading both bytecode or shader strings.</p>
<div><pre><code class="language-mm"><span class="hljs-type">id</span>&lt;<span class="hljs-built_in">MTLLibrary</span>&gt; library = [<span class="hljs-keyword">self</span>.device newDefaultLibrary];
<span class="hljs-type">id</span>&lt;<span class="hljs-built_in">MTLFunction</span>&gt; vertexFunc = [library newFunctionWithName:<span class="hljs-string">@&quot;vertex_main&quot;</span>];
<span class="hljs-type">id</span>&lt;<span class="hljs-built_in">MTLFunction</span>&gt; fragmentFunc = [library newFunctionWithName:<span class="hljs-string">@&quot;fragment_main&quot;</span>];</code></pre></div>
<p>Rather than managing shader modules yourself, in Metal, you can create libraries of shader functions and load those as needed. If you need to grab the main of a given library&#x27;s function list, you can use the <code>newFunctionWithName</code> method.</p>
<h3 id="transpiling">Transpiling</h3>
<p>So long as a shader is written in valid GLSL or HLSL, transpiling it to either GLSL, HLSL, or MSL is possible with <a href="https://github.com/KhronosGroup/SPIRV-Cross">SPIRV-Cross</a>. You may need to annotate some parts of your shader though with API specific information.</p>
<div><pre><code class="language-bash"><span class="hljs-comment"># 🌋 Compile shaders to SPIR-V binary</span>
glslangValidator -V triangle.vert -o triangle.vert.spv
glslangValidator -V triangle.frag -o triangle.frag.spv

<span class="hljs-comment"># ❎ HLSL</span>
spirv-cross triangle.vert.spv --hlsl --shader-model 50 --set-hlsl-vertex-input-semantic 0 POSITION --set-hlsl-vertex-input-semantic 1 COLOR --output triangle.vert.hlsl
spirv-cross triangle.frag.spv --hlsl --shader-model 50 --set-hlsl-vertex-input-semantic 0 COLOR --output triangle.frag.hlsl

<span class="hljs-comment"># ⚪ OpenGL ES 3.1</span>
spirv-cross triangle.vert.spv --version 310 --es --output triangle.vert.glsl
spirv-cross triangle.frag.spv --version 310 --es --output triangle.frag.glsl

<span class="hljs-comment"># 🤖 Metal</span>
spirv-cross triangle.vert.spv --msl --output triangle.vert.msl
spirv-cross triangle.frag.spv --msl --output triangle.frag.msl</code></pre></div>
<p>In addition, WGSL compilers can also transpile directly:</p>
<div><pre><code class="language-bash"><span class="hljs-comment"># 🦊 Mozilla Naga</span>
<span class="hljs-comment"># https://github.com/gfx-rs/naga</span>

<span class="hljs-comment"># ⚪ Convert the WGSL to GLSL vertex stage under ES 3.20 profile</span>
cargo run my_shader.wgsl my_shader.vert --profile es310

<span class="hljs-comment"># 🤖 Convert the SPV to Metal</span>
cargo run my_shader.spv my_shader.metal</code></pre></div>
<h2 id="conclusion">Conclusion</h2>
<p>Shading languages are similar to each other, with minor differences in keywords, but different underlying designs philosophies such as the union of samplers and textures in GLSL, or the buffer attribute model of Metal. It&#x27;s not too difficult to work around these differences though, and bridge the gap between HLSL, GLSL, MSL, and WGSL.</p>
<p>There&#x27;s a few areas that we didn&#x27;t go over that are worth reviewing such as:</p>
<ul>
<li>
<p>Ray Tracing Shaders - DirectX 12 and Vulkan share the same type of ray tracing shaders, but Metal takes a more unique approach.</p>
</li>
<li>
<p>Meshlet Shaders - These can be reviewed in their own posts, but are currently limited to DirectX 12 and Vulkan.</p>
</li>
</ul>
<h3 id="resources">Resources</h3>
<p>Each shader language has documentation pages that go over their nuances in more detail:</p>
<ul>
<li>
<p>The <a href="https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-intrinsic-functions">HLSL Reference</a> can be reviewed in from their documentation page.</p>
</li>
<li>
<p>The <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl4/index.php">GLSL Reference</a> is combined with OpenGL&#x27;s reference, it&#x27;s a bit tough to parse through, but anything with <code>gl_</code> or without a <code>glCamelCase</code> is part of GLSL.</p>
</li>
<li>
<p>The <a href="https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf#page=138">MSL Reference</a> details their intrinsic functions, keywords, etc.</p>
</li>
<li>
<p>The <a href="https://gpuweb.github.io/gpuweb/wgsl/#builtin-functions">WGSL Reference</a> features everything about the language from intrinsic functions to decorators.</p>
</li>
<li>
<p>The <a href="https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_C.html">OpenCL C Reference</a> showcases the kernel language used for OpenCL. Even though the language is for compute only, it&#x27;s extremely similar to GLSL and worth a look.</p>
</li>
</ul>
<p>It&#x27;s also worth reviewing vendor ISAs to better understand what instructions your shader will eventually become:</p>
<ul>
<li>
<p>The AMD <a href="https://developer.amd.com/wp-content/resources/RDNA2_Shader_ISA_November2020.pdf">RDNA2 ISA</a> reference guide will help you better architect your shaders by showing what instructions DXIL and SPIR-V ultimately become in AMD hardware.</p>
</li>
<li>
<p>NVIDIA&#x27;s <a href="https://docs.nvidia.com/cuda/parallel-thread-execution/index.html">Parallel Thread Execution (PTX) ISA</a> shows what your shader IR will eventually become on NVIDIA hardware, and describes the instructions CUDA cores can execute.</p>
</li>
</ul>
<p>It&#x27;s also worth reviewing the test suite for the various compilers for these languages:</p>
<ul>
<li>
<p><a href="https://github.com/microsoft/DirectXShaderCompiler">DirectX Shader Compiler</a> can also output SPIR-V code, and would serve as a better tool from translating HLSL to GLSL than say, SPIR-V Cross.</p>
</li>
<li>
<p><a href="https://github.com/KhronosGroup/glslang">GLSLang</a> is Khronos&#x27; official GLSL compiler, and besides compile SPRIR-V it can also reflect GLSL and even compile some HLSL.</p>
</li>
<li>
<p><a href="https://github.com/gfx-rs/naga">Mozilla Naga</a> is the Firefox WGSL compiler written in Rust.</p>
</li>
<li>
<p><a href="https://dawn.googlesource.com/tint/">Google Tint</a> is Chromium&#x27;s WGSL compiler.</p>
</li>
<li>
<p><a href="https://github.com/KhronosGroup/SPIRV-Cross">SPIRV-Cross</a> can convert SPIR-V to GLSL, HLSL, and MSL easily.</p>
</li>
<li>
<p>Tim Jones (<a href="https://twitter.com/_tim_jones_">@<em>tim_jones</em></a>) released a VS Code Plugin called <a href="https://marketplace.visualstudio.com/items?itemName=TimGJones.hlsltools">HLSL Tools</a> that lets you lint your shaders more easily.</p>
</li>
</ul>
<p>There&#x27;s also a number of videos, blog posts, and projects that focus on shaders:</p>
<ul>
<li>
<p><a href="https://www.youtube.com/watch?v=uhhwREjtMY0&amp;t=1681s">The State of Shader Tooling in Vulkan</a> by David Neto (<a href="https://twitter.com/dneto1969">@dneto1969</a>) contrasts different shader IRs and their compilers. You can find the <a href="https://www.khronos.org/assets/uploads/developers/library/2018-gdc-webgl-and-gltf/1-Vulkan-Whats-New-GDC_Mar18.pdf">slides here</a>. (Thanks for sharing this and the WGSL compilers on Twitter David!)</p>
</li>
<li>
<p><a href="https://github.com/shader-slang/slang">Slang</a> is an new shading language that compiles to GLSL, HLSL and SPIR-V, a spiritual successor of Cg.</p>
</li>
<li>
<p>AMD released a talk titled <a href="https://www.youtube.com/watch?v=_ilAL-1-moA&amp;list=PL-ZzkbB7kcKjoE3-q_-6eL49XRqqcWZeJ&amp;index=19">Trip Down the Shader Compiler Pipeline</a>, which reviews how shaders go from source code to their vendor specific IR.</p>
</li>
<li>
<p>Matthäus G. Chajdas (<a href="https://twitter.com/NIV_Anteru">@NIV_Anteru</a>) released a blog post <a href="https://anteru.net/blog/2016/mapping-between-HLSL-and-GLSL/">detailing how HLSL and GLSL map to each other</a>.</p>
</li>
<li>
<p><a href="http://kvark.github.io">Dzmitry Malyshau</a> <a href="http://kvark.github.io/naga/shader/2022/02/17/shader-translation-benchmark.html">benchmarked the Rust WGSL shader compiler Naga with different compilers</a>.</p>
</li>
<li>
<p><a href="https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/hlsl-shader-model-6-0-features-for-direct3d-12">Shader Model 6.0</a> adds new features to HLSL that are worth reviewing here, with <a href="https://devblogs.microsoft.com/directx/in-the-works-hlsl-shader-model-6-7/">Shader Model 6.7</a> providing new Gather operations.</p>
</li>
<li>
<p>The <a href="https://shader-playground.timjones.io/">Shader Playground</a> by <a href="http://timjones.io/">Tim Jones</a> (<a href="https://twitter.com/_tim_jones_">@<em>tim_jones</em></a>) provides an easy interface to see what your shaders compile to in various ISAs for different vendors/APIs.</p>
</li>
<li>
<p>Adam Sawicki (<a href="https://twitter.com/Reg__">@Reg__</a>) released an <a href="https://asawicki.info/news_1719_two_shader_compilers_of_direct3d_12">article detailing the difference between the two shader compilers of DirectX 12 </a>. (Thanks @raphlinus from Hacker News for this and all the feedback!)</p>
</li>
</ul>
<p>I wrote a variety of blog posts that may be worth reviewing as well:</p>
<ul>
<li>
<p>If you&#x27;re curious about shader debugging, visit this article <a href="https://alain.xyz/blog/graphics-debugging">comparing different graphics API debugging tools</a>.</p>
</li>
<li>
<p>Visit my blog post <a href="https://alain.xyz/blog/comparison-of-modern-graphics-apis">comparing different modern graphics APIs</a> to get more insights into how shaders and their host APIs differ in design.</p>
</li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_rdna2020"><span class="markademic-citename">[AMD 2020]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">RDNA 2 Instruction Set Architecture</span></strong></em><br/><span><span class="markademic-publisher">AMD 2020</span><br/></span><span><a href="https://developer.amd.com/wp-content/resources/RDNA2_Shader_ISA_November2020.pdf">developer.amd.com</a></span></div></td></tr><tr><td id="ref_doppioslash2018"><span class="markademic-citename">[Doppioslash 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Physically based Shader Development for unity 2017: Develop Custom Lighting Systems</span></strong></em><br/><span class="markademic-authors"><span><a href="http://www.doppioslash.com/">Claudia Doppioslash</a> (<a href="https://twitter.com/doppioslash">@doppioslash</a>)</span></span><br/><span><span class="markademic-publisher">Apress 2018</span><br/></span><span><a href="https://link.springer.com/book/10.1007/978-1-4842-3309-2">link.springer.com</a></span></div></td></tr><tr><td id="ref_maxfield2018"><span class="markademic-citename">[Maxfield 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Web High Level Shading Language</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/litherum.png" alt="Myles Maxfield&#x27;s picture"/><a href="https://github.com/litherum">Myles Maxfield</a> (<a href="https://twitter.com/Litherum">@Litherum</a>)</span></span><br/><span><span class="markademic-publisher">2018</span><br/></span><span><a href="https://webkit.org/blog/8482/web-high-level-shading-language/">webkit.org</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Raw DirectX 12</title>
<link>https://alain.xyz/blog/raw-directx12</link>
<description>An introduction to writing a simple Hello Triangle DirectX 12 application. DirectX 12 is the latest low level graphics API for writing Windows applications, supporting ray tracing, compute, rasterization, and much more.</description>
<guid isPermaLink="true">https://alain.xyz/blog/raw-directx12</guid>
<pubDate>Sun, 24 Oct 2021 00:36:34 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>opengl</category><category>triangle</category><category>hello</category><category>theory</category><category>diagrams</category><category>directx</category><category>vulkan</category>
<enclosure url="https://alain.xyz/blog/raw-directx12/assets/cover.jpg" length="131073" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/directx12-logo.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/directx12-users.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/win32-window.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/factory.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/adapter.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/device.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/queue.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/command-allocator.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/semaphore-fence.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/swapchain.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/descriptor-heap.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/root-signature.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/heap.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/vertex-buffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/index-buffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/constant-buffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/vertex-shader.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/pixel-shader.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/raster-pipeline.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/command-list.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx12/assets/triangle-raster.gif"/><div style="text-align:center"><a href="https://github.com/alaingalvan/directx12-seed" style="text-decoration:none;color:#fff;border-color:#28a745;background-color:#28a745" class="btn"><p>Github Repo</p></a> <a href="https://github.com/alaingalvan/directx12-seed/archive/master.zip" style="text-decoration:none;color:#fff;border-color:rgb(105, 69, 191);background-color:rgb(105, 69, 191)" class="btn"><p>Download Sample</p></a> <a href="https://alain.xyz/books/raw-directx12/" style="text-decoration:none;color:#fff;border-color:rgb(69, 88, 191);background-color:rgb(69, 88, 191)" class="btn"><p>Book</p></a></div>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/directx12-logo.svg" alt="DirectX 12 Logo"/></p>
<p>DirectX 12 is the latest iteration of Microsoft&#x27;s proprietary computer graphics API used for Windows and Xbox platforms. DirectX 12, like newer graphics APIs such as <a href="https://alain.xyz/blog/raw-vulkan">Vulkan</a>, <a href="https://alain.xyz/blog/raw-metal">Apple Metal</a>, or <a href="https://alain.xyz/blog/raw-webgpu">WebGPU</a>, was designed to map closer to the GPU&#x27;s underlying architecture, resulting in a much less complex and faster driver. This means the application developer is responsible for the creation of pipeline states directly, specifying which commands get executed with queues, and now has the option to build these data structures with multiple threads.</p>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/directx12-users.jpg" alt="DirectX Users"/></p>
<p>DirectX focuses on real time rendering, thus is designed for <strong>Game Developers</strong> and <strong>Computer Aided Design (CAD) Software Engineers</strong>. As the industry standard in Computer Graphics APIs, you can expect nearly all compliant hardware to have robust support for the API, making it the standard for <em>commercial projects</em>.</p>
<p>From 3D/image authoring software like <a href="https://marmoset.co">Marmoset Toolbag</a>, <a href="https://adobe.com/photoshop">Adobe PhotoShop</a>, <a href="https://www.unrealengine.com">Unreal Engine</a>, to commercial games such as Activision Blizzard&#x27;s <a href="https://overwatch.com">OverWatch</a>, <a href="https://www.callofduty.com/">Call of Duty</a>, Epic&#x27;s <a href="https://www.epicgames.com/fortnite/play-now">Fortnite</a>, the vast majority of titles on Valve&#x27;s <a href="https://store.steampowered.com">Steam</a> and much more, this graphics API is the <strong>most popular</strong> and ubiquitous one of them all, <em>despite</em> its platform exclusivity.</p>
<p>That&#x27;s not to say there aren&#x27;t uses for it outside of 3D rendering. Google Chrome renders <a href="https://skia.org/docs/user/special/angle/">HTML and SVG elements with DirectX</a>, Tensorflow features a <a href="https://github.com/microsoft/tensorflow-directml">DirectX 12 backend for machine learning execution via DirectML</a>, and GPGPU computations with the compute pipeline can execute all manner of workloads, from physics simulations, BVH construction, and much more.</p>
<p>DirectX 12 is currently supported on:</p>
<ul>
<li>
<p>🗔 Windows 7 - 11</p>
</li>
<li>
<p>✖️ Xbox One - Xbox Series X/S</p>
</li>
</ul>
<p>With Windows 7 having <em>partial support</em> for it via Microsoft&#x27;s <a href="https://github.com/microsoft/DirectX-Graphics-Samples/tree/develop/Samples/Desktop/D3D12On7">D3D12 On 7</a>.</p>
<p>While the official languages might be C and C++, many languages support DirectX 12:</p>
<ul>
<li>
<p><strong>C</strong></p>
</li>
<li>
<p><strong>C++</strong></p>
</li>
<li>
<p><strong>C#</strong></p>
</li>
<li>
<p><strong>Rust</strong> (through <a href="https://crates.io/crates/directx-sys">dx-sys</a>)</p>
</li>
</ul>
<hr/>
<p>I&#x27;ve prepared a <a href="http://github.com/alaingalvan/directx12-seed">Github Repo</a> with everything we need to get started. We&#x27;re going to walk through a <em>Hello Triangle</em> app in C++, a program that creates a triangle with the raster graphics pipeline and renders it onto the screen.</p>
<h2 id="setup">Setup</h2>
<p>First install:</p>
<ul>
<li>
<p><a href="https://git-scm.com/">Git</a></p>
</li>
<li>
<p><a href="https://cmake.org">CMake</a></p>
</li>
<li>
<p>An IDE such as <a href="https://visualstudio.microsoft.com/downloads/">Visual Studio</a>, <a href="https://developer.apple.com/xcode/">XCode</a>, or a compiler such as <a href="https://gcc.gnu.org/">GCC</a>.</p>
</li>
</ul>
<p>Then type the following in any terminal such as <a href="https://code.visualstudio.com/docs/editor/integrated-terminal">VS Code&#x27;s Integrated Terminal</a>.</p>
<div><pre><code class="language-bash"><span class="hljs-comment"># 🐑 Clone the repo</span>
git <span class="hljs-built_in">clone</span> https://github.com/alaingalvan/directx12-seed --recurse-submodules

<span class="hljs-comment"># 💿 go inside the folder</span>
<span class="hljs-built_in">cd</span> directx12-seed

<span class="hljs-comment"># 👯 If you forget to `recurse-submodules` you can always run:</span>
git submodule update --init

<span class="hljs-comment"># 👷 Make a ./build/ folder and place your project files inside:</span>

<span class="hljs-comment"># 🖼️ To build your Visual Studio solution on Windows x64</span>
cmake . -B build -A x64

<span class="hljs-comment"># 🔨 Build on any platform:</span>
cmake --build build</code></pre></div>
<h2 id="overview">Overview</h2>
<p>DirectX 12&#x27;s documentation recommends the use of <a href="https://docs.microsoft.com/en-us/windows/win32/learnwin32/com-coding-practices"><code>ComPtr&lt;T&gt;</code></a> as an alternative to <code>std::shared_ptr&lt;T&gt;</code>, with the benefit of better debugging and easier initialization of DirectX 12 data structures.</p>
<p>Regardless of whether or not you choose to use <code>ComPtr&lt;T&gt;</code>, the steps to rendering raster graphics with DirectX 12 are pretty similar to <a href="https://alain.xyz/blog/comparison-of-modern-graphics-apis">other modern graphics APIs</a>:</p>
<ol>
<li>
<p><strong>Initialize the API</strong> - Create your <code>IDXGIFactory</code>, <code>IDXGIAdapter</code>, <code>ID3D12Device</code>, <code>ID3D12CommandQueue</code>, <code>ID3D12CommandAllocator</code>, <code>ID3D12GraphicsCommandList</code>.</p>
</li>
<li>
<p><strong>Setup Frame Backings</strong> - Create your <code>IDXGISwapChain</code>,<code>ID3D12DescriptorHeap</code> for your back buffers, your back buffer <code>ID3D12Resource</code> Render Target Views, a <code>ID3D12Fence</code> to detect when a frame is finished rendering.</p>
</li>
<li>
<p><strong>Initialize Resources</strong> - Create your Triangle Data such as your <code>ID3D12Resource</code> Vertex Buffers, <code>ID3D12Resource</code> Index Buffer, <code>ID3D12Fence</code>s to detect when uploads to GPU memory are complete. Load your shaders <code>ID3DBlob</code>s, your constant buffer <code>ID3D12Resource</code>s and their <code>ID3D12DescriptorHeap</code>, describe what resources will be accessible with a <code>ID3D12RootSignature</code>, and build your <code>ID3D12PipelineState</code>.</p>
</li>
<li>
<p><strong>Encode Commands</strong> - Write commands to your <code>ID3D12GraphicsCommandList</code> with what pipeline commands you intend to execute, making sure to put <code>ResourceBarrier</code>s where appropriate.</p>
</li>
<li>
<p><strong>Render</strong> - Update your GPU constant buffer data (Uniforms), submit commands to the <code>ID3D12CommandQueue</code> with <code>ExecuteCommandLists</code>, <code>Present</code> your swapchain, and await the next frame.</p>
</li>
<li>
<p><strong>Destroy</strong> - Destroy any data structures you&#x27;re done using with <code>Release()</code> or rely on <code>ComPtr&lt;T&gt;</code> deallocating for you.</p>
</li>
</ol>
<p>The following will explain snippets that can be found in the <a href="https://github.com/alaingalvan/directx12-seed">Github repo</a>, with certain parts omitted, and <em>member variables (<code>mMemberVariable</code>) declared inline without the <code>m</code> prefix so their type is easier to see and the examples here can work on their own</em>.</p>
<h2 id="window-creation">Window Creation</h2>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/win32-window.svg" alt="Win32 Window"/></p>
<p>We&#x27;re using <a href="https://alain.xyz/libraries/CrossWindow">CrossWindow</a> to handle cross platform window creation, so creating a Win32 window and updating it is very easy:</p>
<div><pre><code class="language-cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&quot;CrossWindow/CrossWindow.h&quot;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&quot;Renderer.h&quot;</span></span>

<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;iostream&gt;</span></span>

<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">xmain</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">const</span> <span class="hljs-type">char</span>** argv)</span>
</span>{
  <span class="hljs-comment">// 🖼️ Create Window</span>
  xwin::WindowDesc wdesc;
  wdesc.title = <span class="hljs-string">&quot;DirectX 12 Seed&quot;</span>;
  wdesc.name = <span class="hljs-string">&quot;MainWindow&quot;</span>;
  wdesc.visible = <span class="hljs-literal">true</span>;
  wdesc.width = <span class="hljs-number">640</span>;
  wdesc.height = <span class="hljs-number">640</span>;
  wdesc.fullscreen = <span class="hljs-literal">false</span>;

  xwin::Window window;
  xwin::EventQueue eventQueue;

  <span class="hljs-keyword">if</span> (!window.<span class="hljs-built_in">create</span>(wdesc, eventQueue))
  { <span class="hljs-keyword">return</span>; };

  <span class="hljs-comment">// 🎨 Create a renderer</span>
  <span class="hljs-function">Renderer <span class="hljs-title">renderer</span><span class="hljs-params">(window)</span></span>;

  <span class="hljs-comment">// 🏁 Engine loop</span>
  <span class="hljs-type">bool</span> isRunning = <span class="hljs-literal">true</span>;
  <span class="hljs-keyword">while</span> (isRunning)
  {
    <span class="hljs-type">bool</span> shouldRender = <span class="hljs-literal">true</span>;

    <span class="hljs-comment">// ♻️ Update the event queue</span>
    eventQueue.<span class="hljs-built_in">update</span>();

    <span class="hljs-comment">// 🎈 Iterate through that queue:</span>
    <span class="hljs-keyword">while</span> (!eventQueue.<span class="hljs-built_in">empty</span>())
    {
      <span class="hljs-comment">//Update Events</span>
      <span class="hljs-type">const</span> xwin::Event&amp; event = eventQueue.<span class="hljs-built_in">front</span>();

      <span class="hljs-comment">// 💗 On Resize:</span>
      <span class="hljs-keyword">if</span> (event.type == xwin::EventType::Resize)
      {
        <span class="hljs-type">const</span> xwin::ResizeData data = event.data.resize;
        renderer.<span class="hljs-built_in">resize</span>(data.width, data.height);
        shouldRender = <span class="hljs-literal">false</span>;
      }

      <span class="hljs-comment">// ❌ On Close:</span>
      <span class="hljs-keyword">if</span> (event.type == xwin::EventType::Close)
      {
        window.<span class="hljs-built_in">close</span>();
        shouldRender = <span class="hljs-literal">false</span>;
        isRunning = <span class="hljs-literal">false</span>;
      }

      eventQueue.<span class="hljs-built_in">pop</span>();
    }

    <span class="hljs-comment">// ✨ Update Visuals</span>
    <span class="hljs-keyword">if</span> (shouldRender)
    {
      renderer.<span class="hljs-built_in">render</span>();
    }
  }
}</code></pre></div>
<p>As an alternative to CrossWindow, you could use another library like GLFW, SFML, SDL, QT, or just interface directly with the Win32 or UWP APIs.</p>
<h2 id="initialize-api">Initialize API</h2>
<h3 id="factory">Factory</h3>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/factory.svg" alt="Factory Diagram"/></p>
<p><strong>Factories</strong> are the entry point to the DirectX 12 API, and will allow you to find adapters that you can use to execute DirectX 12 commands.</p>
<p>You can also create a <strong>Debug Controller</strong> alongside the factory which can enable API usage validation, though this should only be used in <em>debug</em> builds.</p>
<ul>
<li>Reference: <a href="https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_6/nn-dxgi1_6-idxgifactory7">IDXGIFactory7</a></li>
</ul>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare DirectX 12 Handles</span>
IDXGIFactory4* factory;
ID3D12Debug1* debugController;

<span class="hljs-comment">// 🏭 Create Factory</span>
UINT dxgiFactoryFlags = <span class="hljs-number">0</span>;

<span class="hljs-meta">#<span class="hljs-keyword">if</span> defined(_DEBUG)</span>
<span class="hljs-comment">// 🐛 Create a Debug Controller to track errors</span>
ID3D12Debug* dc;
<span class="hljs-built_in">ThrowIfFailed</span>(<span class="hljs-built_in">D3D12GetDebugInterface</span>(<span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;dc)));
<span class="hljs-built_in">ThrowIfFailed</span>(dc-&gt;<span class="hljs-built_in">QueryInterface</span>(<span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;debugController)));
debugController-&gt;<span class="hljs-built_in">EnableDebugLayer</span>();
debugController-&gt;<span class="hljs-built_in">SetEnableGPUBasedValidation</span>(<span class="hljs-literal">true</span>);

dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;

dc-&gt;<span class="hljs-built_in">Release</span>();
dc = <span class="hljs-literal">nullptr</span>;
<span class="hljs-meta">#<span class="hljs-keyword">endif</span></span>

HRESULT result = <span class="hljs-built_in">CreateDXGIFactory2</span>(dxgiFactoryFlags, <span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;factory));</code></pre></div>
<h3 id="adapter">Adapter</h3>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/adapter.svg" alt="Adapter Diagram"/></p>
<p>An <strong>Adapter</strong> provides information on the physical properties of a given DirectX device. You can query your current GPU&#x27;s name, manufacturer, how much memory it has, <a href="https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_2/ns-dxgi1_2-dxgi_adapter_desc2">and much more</a>.</p>
<p>There&#x27;s 2 categories for adapters, <em>software</em> and <em>hardware</em> adapters. Microsoft Windows always includes a software based DirectX implementation that can be used in the event of there being no dedicated hardware such as a discrete or integrated GPU.</p>
<ul>
<li>Reference: <a href="https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_6/nn-dxgi1_6-idxgiadapter4">IDXGIAdapter4</a></li>
</ul>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare Handles</span>
IDXGIAdapter1* adapter;

<span class="hljs-comment">// 🔌 Create Adapter</span>
<span class="hljs-keyword">for</span> (UINT adapterIndex = <span class="hljs-number">0</span>;
     DXGI_ERROR_NOT_FOUND != factory-&gt;<span class="hljs-built_in">EnumAdapters1</span>(adapterIndex, &amp;adapter);
     ++adapterIndex)
{
    DXGI_ADAPTER_DESC1 desc;
    adapter-&gt;<span class="hljs-built_in">GetDesc1</span>(&amp;desc);

    <span class="hljs-comment">// ❌ Don&#x27;t select the Basic Render Driver adapter.</span>
    <span class="hljs-keyword">if</span> (desc.Flags &amp; DXGI_ADAPTER_FLAG_SOFTWARE)
    {
        <span class="hljs-keyword">continue</span>;
    }

    <span class="hljs-comment">// ✔️ Check if the adapter supports Direct3D 12, and use that for the rest</span>
    <span class="hljs-comment">// of the application</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">SUCCEEDED</span>(<span class="hljs-built_in">D3D12CreateDevice</span>(adapter, D3D_FEATURE_LEVEL_12_0,
                                    _uuidof(ID3D12Device), <span class="hljs-literal">nullptr</span>)))
    {
        <span class="hljs-keyword">break</span>;
    }

    <span class="hljs-comment">// ❌ Else we won&#x27;t use this iteration&#x27;s adapter, so release it</span>
    adapter-&gt;<span class="hljs-built_in">Release</span>();
}</code></pre></div>
<h3 id="device">Device</h3>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/device.svg" alt="Device Diagram"/></p>
<p>A <strong>Device</strong> is your primary entry point to the DirectX 12 API, giving you access to the inner parts of the API. This is key to accessing important data structures and functions such as pipelines, shader blobs, render state, resource barriers, etc.</p>
<ul>
<li>Reference: <a href="https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_5/nn-dxgi1_5-idxgidevice4">IDXGIDevice4</a></li>
</ul>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare Handles</span>
ID3D12Device* device;

<span class="hljs-comment">// 💻 Create Device</span>
<span class="hljs-built_in">ThrowIfFailed</span>(<span class="hljs-built_in">D3D12CreateDevice</span>(adapter, D3D_FEATURE_LEVEL_12_0,
                                <span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;device)));</code></pre></div>
<p>A <strong>Debug Device</strong> allows you to rely on DirectX 12&#x27;s debug mode. It can be difficult to keep track of data structures created with DirectX. With this you&#x27;ll be able to prevent data leaks or verify if you&#x27;re creating or using the API correctly.</p>
<ul>
<li>Reference: <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12sdklayers/nn-d3d12sdklayers-id3d12debugdevice">ID3D12DebugDevice</a></li>
</ul>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare Handles</span>
ID3D12DebugDevice* debugDevice;

<span class="hljs-meta">#<span class="hljs-keyword">if</span> defined(_DEBUG)</span>
<span class="hljs-comment">// 💻 Get debug device</span>
<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">QueryInterface</span>(&amp;debugDevice));
<span class="hljs-meta">#<span class="hljs-keyword">endif</span></span></code></pre></div>
<h3 id="command-queue">Command Queue</h3>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/queue.svg" alt="Command Queue Diagram"/></p>
<p>A <strong>Command Queue</strong> allows you to submit groups of draw calls, known as <em>command lists</em>, together to execute in order, thus allowing a GPU to stay busy and optimize its execution speed.</p>
<ul>
<li>Reference: <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nn-d3d12-id3d12commandqueue">ID3D12CommandQueue</a></li>
</ul>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare Handles</span>
ID3D12CommandQueue* commandQueue;

<span class="hljs-comment">// 📦 Create Command Queue</span>
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;

<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateCommandQueue</span>(&amp;queueDesc,
                                         <span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;commandQueue)));</code></pre></div>
<h3 id="command-allocator">Command Allocator</h3>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/command-allocator.svg" alt="Command Allocator Diagram"/></p>
<p>A <strong>Command Allocator</strong> allows you to create <em>command lists</em> where you can define the functions you want the GPU to execute.</p>
<ul>
<li>Reference: <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nn-d3d12-id3d12commandallocator">ID3D12CommandAllocator</a></li>
</ul>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare Handles</span>
ID3D12CommandAllocator* commandAllocator;

<span class="hljs-comment">// 🎅 Create Command Allocator</span>
<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateCommandAllocator</span>(D3D12_COMMAND_LIST_TYPE_DIRECT,
                                             <span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;commandAllocator)));</code></pre></div>
<h3 id="synchronization">Synchronization</h3>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/semaphore-fence.svg" alt="Semaphore and Fence Diagram"/></p>
<p>DirectX 12 features a number of synchronization primitives that can help the driver know how resources will be used in the future, know when tasks have been completed by the GPU, etc.</p>
<p>A <strong>Fence</strong> lets your program know when certain tasks have been executed by the GPU, be it uploads to GPU exclusive memory, or when you&#x27;ve finished presenting to the screen.</p>
<ul>
<li>Reference: <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nn-d3d12-id3d12fence1">ID3D12Fence1</a></li>
</ul>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
UINT frameIndex;
HANDLE fenceEvent;
ID3D12Fence* fence;
UINT64 fenceValue;

<span class="hljs-comment">// 🚧 Create fence</span>
<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateFence</span>(<span class="hljs-number">0</span>, D3D12_FENCE_FLAG_NONE,
                                  <span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;fence)));</code></pre></div>
<p>A <strong>Barrier</strong> lets the driver know how a resource should be used in upcoming commands. This can be useful if say, you&#x27;re writing to a texture, and you want to copy that texture to another texture (such as the swapchain&#x27;s render attachment).</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
ID3D12GraphicsCommandList* commandList;

<span class="hljs-comment">// 🔮 Create Barrier</span>
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource = texResource;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;

commandList-&gt;<span class="hljs-built_in">ResourceBarrier</span>(<span class="hljs-number">1</span>, &amp;barrier);</code></pre></div>
<h3 id="swapchain">Swapchain</h3>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/swapchain.svg" alt="Swapchain Diagram"/></p>
<p><strong>Swapchain</strong>s handle swapping and allocating back buffers to display what you&#x27;re rendering to a given window.</p>
<ul>
<li>Reference: <a href="https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_5/nn-dxgi1_5-idxgiswapchain4">IDXGISwapChain4</a></li>
</ul>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 💾 Declare Data</span>
<span class="hljs-type">unsigned</span> width = <span class="hljs-number">640</span>;
<span class="hljs-type">unsigned</span> height = <span class="hljs-number">640</span>;

<span class="hljs-comment">// 👋 Declare Handles</span>
<span class="hljs-type">static</span> <span class="hljs-type">const</span> UINT backbufferCount = <span class="hljs-number">2</span>;
UINT currentBuffer;
ID3D12DescriptorHeap* renderTargetViewHeap;
ID3D12Resource* renderTargets[backbufferCount];
UINT rtvDescriptorSize;

<span class="hljs-comment">// ⛓️ Swapchain</span>
IDXGISwapChain3* swapchain;
D3D12_VIEWPORT viewport;
D3D12_RECT surfaceSize;

surfaceSize.left = <span class="hljs-number">0</span>;
surfaceSize.top = <span class="hljs-number">0</span>;
surfaceSize.right = <span class="hljs-built_in">static_cast</span>&lt;LONG&gt;(width);
surfaceSize.bottom = <span class="hljs-built_in">static_cast</span>&lt;LONG&gt;(height);

viewport.TopLeftX = <span class="hljs-number">0.0f</span>;
viewport.TopLeftY = <span class="hljs-number">0.0f</span>;
viewport.Width = <span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">float</span>&gt;(width);
viewport.Height = <span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">float</span>&gt;(height);
viewport.MinDepth = <span class="hljs-number">.1</span>f;
viewport.MaxDepth = <span class="hljs-number">1000.f</span>;

<span class="hljs-keyword">if</span> (swapchain != <span class="hljs-literal">nullptr</span>)
{
    <span class="hljs-comment">// Create Render Target Attachments from swapchain</span>
    swapchain-&gt;<span class="hljs-built_in">ResizeBuffers</span>(backbufferCount, width, height,
                             DXGI_FORMAT_R8G8B8A8_UNORM, <span class="hljs-number">0</span>);
}
<span class="hljs-keyword">else</span>
{
    <span class="hljs-comment">// ⛓️ Create swapchain</span>
    DXGI_SWAP_CHAIN_DESC1 swapchainDesc = {};
    swapchainDesc.BufferCount = backbufferCount;
    swapchainDesc.Width = width;
    swapchainDesc.Height = height;
    swapchainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    swapchainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapchainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
    swapchainDesc.SampleDesc.Count = <span class="hljs-number">1</span>;

    IDXGISwapChain1* newSwapchain =
        xgfx::<span class="hljs-built_in">createSwapchain</span>(window, factory, commandQueue, &amp;swapchainDesc);
    HRESULT swapchainSupport = swapchain-&gt;<span class="hljs-built_in">QueryInterface</span>(
        __uuidof(IDXGISwapChain3), (<span class="hljs-type">void</span>**)&amp;newSwapchain);
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">SUCCEEDED</span>(swapchainSupport))
    {
        swapchain = (IDXGISwapChain3*)newSwapchain;
    }
}

frameIndex = swapchain-&gt;<span class="hljs-built_in">GetCurrentBackBufferIndex</span>();

<span class="hljs-comment">// Describe and create a render target view (RTV) descriptor heap.</span>
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
rtvHeapDesc.NumDescriptors = backbufferCount;
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateDescriptorHeap</span>(
    &amp;rtvHeapDesc, <span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;renderTargetViewHeap)));

rtvDescriptorSize =
    device-&gt;<span class="hljs-built_in">GetDescriptorHandleIncrementSize</span>(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);

<span class="hljs-comment">// 🎞️ Create frame resources</span>

<span class="hljs-function">D3D12_CPU_DESCRIPTOR_HANDLE
    <span class="hljs-title">rtvHandle</span><span class="hljs-params">(renderTargetViewHeap-&gt;GetCPUDescriptorHandleForHeapStart())</span></span>;

<span class="hljs-comment">// Create a RTV for each frame.</span>
<span class="hljs-keyword">for</span> (UINT n = <span class="hljs-number">0</span>; n &lt; backbufferCount; n++)
{
    <span class="hljs-built_in">ThrowIfFailed</span>(swapchain-&gt;<span class="hljs-built_in">GetBuffer</span>(n, <span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;renderTargets[n])));
    device-&gt;<span class="hljs-built_in">CreateRenderTargetView</span>(renderTargets[n], <span class="hljs-literal">nullptr</span>, rtvHandle);
    rtvHandle.ptr += (<span class="hljs-number">1</span> * rtvDescriptorSize);
}</code></pre></div>
<h2 id="initialize-resources">Initialize Resources</h2>
<h3 id="descriptor-heaps">Descriptor Heaps</h3>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/descriptor-heap.svg" alt="Descriptor Heap Diagram"/></p>
<p><strong>Descriptor heaps</strong> are objects that handle memory allocation required for storing the descriptions of objects that shaders reference.</p>
<ul>
<li>Reference: <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nn-d3d12-id3d12descriptorheap">ID3D12DescriptorHeap</a></li>
</ul>
<div><pre><code class="language-cpp">ID3D12DescriptorHeap* renderTargetViewHeap;

D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
rtvHeapDesc.NumDescriptors = backbufferCount;
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateDescriptorHeap</span>(
    &amp;rtvHeapDesc, <span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;renderTargetViewHeap)));</code></pre></div>
<h3 id="root-signature">Root Signature</h3>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/root-signature.svg" alt="Root Signature Diagram"/></p>
<p><strong>Root Signatures</strong> are objects that define what type of resources are accessible to your shaders, be it constant buffers, structured buffers, samplers, textures, structured buffers, etc.</p>
<ul>
<li>Reference: <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nn-d3d12-id3d12rootsignature">ID3D12RootSignature</a></li>
</ul>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare Handles</span>
ID3D12RootSignature* rootSignature;

<span class="hljs-comment">// 🔎 Determine if we can get Root Signature Version 1.1:</span>
D3D12_FEATURE_DATA_ROOT_SIGNATURE featureData = {};
featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_1;

<span class="hljs-keyword">if</span> (<span class="hljs-built_in">FAILED</span>(device-&gt;<span class="hljs-built_in">CheckFeatureSupport</span>(D3D12_FEATURE_ROOT_SIGNATURE,
                                       &amp;featureData, <span class="hljs-built_in">sizeof</span>(featureData))))
{
    featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_0;
}

<span class="hljs-comment">// 📂 Individual GPU Resources</span>
D3D12_DESCRIPTOR_RANGE1 ranges[<span class="hljs-number">1</span>];
ranges[<span class="hljs-number">0</span>].BaseShaderRegister = <span class="hljs-number">0</span>;
ranges[<span class="hljs-number">0</span>].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
ranges[<span class="hljs-number">0</span>].NumDescriptors = <span class="hljs-number">1</span>;
ranges[<span class="hljs-number">0</span>].RegisterSpace = <span class="hljs-number">0</span>;
ranges[<span class="hljs-number">0</span>].OffsetInDescriptorsFromTableStart = <span class="hljs-number">0</span>;
ranges[<span class="hljs-number">0</span>].Flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE;

<span class="hljs-comment">//🗄️ Groups of GPU Resources</span>
D3D12_ROOT_PARAMETER1 rootParameters[<span class="hljs-number">1</span>];
rootParameters[<span class="hljs-number">0</span>].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
rootParameters[<span class="hljs-number">0</span>].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;

rootParameters[<span class="hljs-number">0</span>].DescriptorTable.NumDescriptorRanges = <span class="hljs-number">1</span>;
rootParameters[<span class="hljs-number">0</span>].DescriptorTable.pDescriptorRanges = ranges;

<span class="hljs-comment">// 🏢  Overall Layout</span>
D3D12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1;
rootSignatureDesc.Desc_1_1.Flags =
    D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
rootSignatureDesc.Desc_1_1.NumParameters = <span class="hljs-number">1</span>;
rootSignatureDesc.Desc_1_1.pParameters = rootParameters;
rootSignatureDesc.Desc_1_1.NumStaticSamplers = <span class="hljs-number">0</span>;
rootSignatureDesc.Desc_1_1.pStaticSamplers = <span class="hljs-literal">nullptr</span>;

ID3DBlob* signature;
ID3DBlob* error;
<span class="hljs-keyword">try</span>
{
    <span class="hljs-comment">// 🌱 Create the root signature</span>
    <span class="hljs-built_in">ThrowIfFailed</span>(<span class="hljs-built_in">D3D12SerializeVersionedRootSignature</span>(&amp;rootSignatureDesc,
                                                       &amp;signature, &amp;error));
    <span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateRootSignature</span>(<span class="hljs-number">0</span>, signature-&gt;<span class="hljs-built_in">GetBufferPointer</span>(),
                                              signature-&gt;<span class="hljs-built_in">GetBufferSize</span>(),
                                              <span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;rootSignature)));
    rootSignature-&gt;<span class="hljs-built_in">SetName</span>(<span class="hljs-string">L&quot;Hello Triangle Root Signature&quot;</span>);
}
<span class="hljs-built_in">catch</span> (std::exception e)
{
    <span class="hljs-type">const</span> <span class="hljs-type">char</span>* errStr = (<span class="hljs-type">const</span> <span class="hljs-type">char</span>*)error-&gt;<span class="hljs-built_in">GetBufferPointer</span>();
    std::cout &lt;&lt; errStr;
    error-&gt;<span class="hljs-built_in">Release</span>();
    error = <span class="hljs-literal">nullptr</span>;
}

<span class="hljs-keyword">if</span> (signature)
{
    signature-&gt;<span class="hljs-built_in">Release</span>();
    signature = <span class="hljs-literal">nullptr</span>;
}</code></pre></div>
<blockquote>
<p>While these work well enough, using bindless resources is significantly more easy, <a href="https://therealmjp.github.io/">Matt Pettineo</a> (<a href="https://twitter.com/mynameismjp">@MyNameIsMJP</a>) wrote a <a href="https://therealmjp.github.io/posts/rtg2-bindless/">blog post and chapter in Ray Tracing Gems 2 about this</a>.</p>
</blockquote>
<h3 id="heaps">Heaps</h3>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/heap.svg" alt="Heap Diagram"/></p>
<p><strong>Heaps</strong> are objects that encompass GPU memory. They can be used to upload resources like vertex buffers or textures to GPU exclusive memory.</p>
<ul>
<li>Reference: <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nn-d3d12-id3d12resource">ID3D12Resource</a></li>
</ul>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 🔼 Upload:</span>

<span class="hljs-comment">// 👋 Declare Handles</span>
ID3D12Resource* uploadBuffer;
std::vector&lt;<span class="hljs-type">unsigned</span> <span class="hljs-type">char</span>&gt; sourceData;

D3D12_HEAP_PROPERTIES uploadHeapProps = {D3D12_HEAP_TYPE_UPLOAD,
                                         D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
                                         D3D12_MEMORY_POOL_UNKNOWN, <span class="hljs-number">1u</span>, <span class="hljs-number">1u</span>};
D3D12_RESOURCE_DESC uploadBufferDesc = {D3D12_RESOURCE_DIMENSION_BUFFER,
                                        <span class="hljs-number">65536ull</span>,
                                        <span class="hljs-number">65536ull</span>,
                                        <span class="hljs-number">1u</span>,
                                        <span class="hljs-number">1</span>,
                                        <span class="hljs-number">1</span>,
                                        DXGI_FORMAT_UNKNOWN,
                                        {<span class="hljs-number">1u</span>, <span class="hljs-number">0u</span>},
                                        D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
                                        D3D12_RESOURCE_FLAG_NONE};
result = device-&gt;<span class="hljs-built_in">CreateCommittedResource</span>(
    &amp;uploadHeapProps, D3D12_HEAP_FLAG_NONE, &amp;uploadBufferDesc,
    D3D12_RESOURCE_STATE_GENERIC_READ, <span class="hljs-literal">nullptr</span>, __uuidof(ID3D12Resource),
    ((<span class="hljs-type">void</span>**)&amp;uploadBuffer));

<span class="hljs-type">uint8_t</span>* data = <span class="hljs-literal">nullptr</span>;
D3D12_RANGE range{<span class="hljs-number">0</span>, <span class="hljs-built_in">SIZE_T</span>(chunkSize)};
<span class="hljs-keyword">auto</span> hr = spStaging -&gt; <span class="hljs-built_in">Map</span>(<span class="hljs-number">0</span>, &amp;range, <span class="hljs-built_in">reinterpret_cast</span>&lt;<span class="hljs-type">void</span>**&gt;(&amp;data));
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">FAILED</span>(hr))
{
    std::cout &lt;&lt; <span class="hljs-string">&quot;Could not map resource&quot;</span>;
}

<span class="hljs-comment">// Copy data</span>
<span class="hljs-keyword">if</span> (resourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
{
    <span class="hljs-built_in">memcpy</span>(data, sourceData.<span class="hljs-built_in">data</span>(), sourceData.<span class="hljs-built_in">size</span>());
}

<span class="hljs-comment">// 📖 Readback:</span>

<span class="hljs-comment">// 👋 Declare Handles</span>
ID3D12Resource* readbackBuffer;

D3D12_HEAP_PROPERTIES heapPropsRead = {D3D12_HEAP_TYPE_READBACK,
                                       D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
                                       D3D12_MEMORY_POOL_UNKNOWN, <span class="hljs-number">1u</span>, <span class="hljs-number">1u</span>};
D3D12_RESOURCE_DESC resourceDescDimBuffer = {
    D3D12_RESOURCE_DIMENSION_BUFFER,
    D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
    <span class="hljs-number">2725888ull</span>,
    <span class="hljs-number">1u</span>,
    <span class="hljs-number">1</span>,
    <span class="hljs-number">1</span>,
    DXGI_FORMAT_UNKNOWN,
    {<span class="hljs-number">1u</span>, <span class="hljs-number">0u</span>},
    D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
    D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE};

result = device-&gt;<span class="hljs-built_in">CreateCommittedResource</span>(
    &amp;heapPropsRead, D3D12_HEAP_FLAG_NONE, &amp;resourceDescDimBuffer,
    D3D12_RESOURCE_STATE_COPY_DEST, <span class="hljs-literal">nullptr</span>, __uuidof(ID3D12Resource),
    ((<span class="hljs-type">void</span>**)&amp;readbackBuffer));</code></pre></div>
<p>You can be even more granular with your memory management by <a href="https://docs.microsoft.com/en-us/windows/win32/direct3d12/memory-management">creating your own heaps</a>. It can be a handful to manage heaps though, so you could use a memory allocation library instead.</p>
<h3 id="vertex-buffer">Vertex Buffer</h3>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/vertex-buffer.svg" alt="Vertex Buffer Diagram"/></p>
<p>A <strong>Vertex Buffer</strong> stores the per vertex information available as attributes in your Vertex Shader. All buffers are <code>ID3D12Resource</code> objects in DirectX 12, be it Vertex Buffers, Index Buffers, Constant Buffers, etc.</p>
<ul>
<li>Reference: <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_vertex_buffer_view">D3D12_VERTEX_BUFFER_VIEW</a></li>
</ul>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 💾 Declare Data</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">Vertex</span>
{
    <span class="hljs-type">float</span> position[<span class="hljs-number">3</span>];
    <span class="hljs-type">float</span> color[<span class="hljs-number">3</span>];
};

Vertex vertexBufferData[<span class="hljs-number">3</span>] = {{{<span class="hljs-number">1.0f</span>, <span class="hljs-number">-1.0f</span>, <span class="hljs-number">0.0f</span>}, {<span class="hljs-number">1.0f</span>, <span class="hljs-number">0.0f</span>, <span class="hljs-number">0.0f</span>}},
                              {{<span class="hljs-number">-1.0f</span>, <span class="hljs-number">-1.0f</span>, <span class="hljs-number">0.0f</span>}, {<span class="hljs-number">0.0f</span>, <span class="hljs-number">1.0f</span>, <span class="hljs-number">0.0f</span>}},
                              {{<span class="hljs-number">0.0f</span>, <span class="hljs-number">1.0f</span>, <span class="hljs-number">0.0f</span>}, {<span class="hljs-number">0.0f</span>, <span class="hljs-number">0.0f</span>, <span class="hljs-number">1.0f</span>}}};

<span class="hljs-comment">// 👋 Declare Handles</span>
ID3D12Resource* vertexBuffer;
D3D12_VERTEX_BUFFER_VIEW vertexBufferView;

<span class="hljs-type">const</span> UINT vertexBufferSize = <span class="hljs-built_in">sizeof</span>(vertexBufferData);

D3D12_HEAP_PROPERTIES heapProps;
heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
heapProps.CreationNodeMask = <span class="hljs-number">1</span>;
heapProps.VisibleNodeMask = <span class="hljs-number">1</span>;

D3D12_RESOURCE_DESC vertexBufferResourceDesc;
vertexBufferResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
vertexBufferResourceDesc.Alignment = <span class="hljs-number">0</span>;
vertexBufferResourceDesc.Width = vertexBufferSize;
vertexBufferResourceDesc.Height = <span class="hljs-number">1</span>;
vertexBufferResourceDesc.DepthOrArraySize = <span class="hljs-number">1</span>;
vertexBufferResourceDesc.MipLevels = <span class="hljs-number">1</span>;
vertexBufferResourceDesc.Format = DXGI_FORMAT_UNKNOWN;
vertexBufferResourceDesc.SampleDesc.Count = <span class="hljs-number">1</span>;
vertexBufferResourceDesc.SampleDesc.Quality = <span class="hljs-number">0</span>;
vertexBufferResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
vertexBufferResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;

<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateCommittedResource</span>(
    &amp;heapProps, D3D12_HEAP_FLAG_NONE, &amp;vertexBufferResourceDesc,
    D3D12_RESOURCE_STATE_GENERIC_READ, <span class="hljs-literal">nullptr</span>, <span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;vertexBuffer)));

<span class="hljs-comment">// 📄 Copy the triangle data to the vertex buffer.</span>
UINT8* pVertexDataBegin;

<span class="hljs-comment">// 🙈 We do not intend to read from this resource on the CPU.</span>
D3D12_RANGE readRange;
readRange.Begin = <span class="hljs-number">0</span>;
readRange.End = <span class="hljs-number">0</span>;

<span class="hljs-built_in">ThrowIfFailed</span>(vertexBuffer-&gt;<span class="hljs-built_in">Map</span>(<span class="hljs-number">0</span>, &amp;readRange,
                                <span class="hljs-built_in">reinterpret_cast</span>&lt;<span class="hljs-type">void</span>**&gt;(&amp;pVertexDataBegin)));
<span class="hljs-built_in">memcpy</span>(pVertexDataBegin, vertexBufferData, <span class="hljs-built_in">sizeof</span>(vertexBufferData));
vertexBuffer-&gt;<span class="hljs-built_in">Unmap</span>(<span class="hljs-number">0</span>, <span class="hljs-literal">nullptr</span>);

<span class="hljs-comment">// 👀 Initialize the vertex buffer view.</span>
vertexBufferView.BufferLocation = vertexBuffer-&gt;<span class="hljs-built_in">GetGPUVirtualAddress</span>();
vertexBufferView.StrideInBytes = <span class="hljs-built_in">sizeof</span>(Vertex);
vertexBufferView.SizeInBytes = vertexBufferSize;</code></pre></div>
<h3 id="index-buffer">Index Buffer</h3>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/index-buffer.svg" alt="Index Buffer Diagram"/></p>
<p>An <strong>Index Buffer</strong> contains the individual indices of each triangle/line/point that you intend to draw.</p>
<ul>
<li>Reference: <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_index_buffer_view">D3D12_INDEX_BUFFER_VIEW</a></li>
</ul>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 💾 Declare Data</span>
<span class="hljs-type">uint32_t</span> indexBufferData[<span class="hljs-number">3</span>] = {<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>};

<span class="hljs-comment">// 👋 Declare Handles</span>
ID3D12Resource* indexBuffer;
D3D12_INDEX_BUFFER_VIEW indexBufferView;

<span class="hljs-type">const</span> UINT indexBufferSize = <span class="hljs-built_in">sizeof</span>(indexBufferData);

D3D12_HEAP_PROPERTIES heapProps;
heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
heapProps.CreationNodeMask = <span class="hljs-number">1</span>;
heapProps.VisibleNodeMask = <span class="hljs-number">1</span>;

D3D12_RESOURCE_DESC vertexBufferResourceDesc;
vertexBufferResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
vertexBufferResourceDesc.Alignment = <span class="hljs-number">0</span>;
vertexBufferResourceDesc.Width = indexBufferSize;
vertexBufferResourceDesc.Height = <span class="hljs-number">1</span>;
vertexBufferResourceDesc.DepthOrArraySize = <span class="hljs-number">1</span>;
vertexBufferResourceDesc.MipLevels = <span class="hljs-number">1</span>;
vertexBufferResourceDesc.Format = DXGI_FORMAT_UNKNOWN;
vertexBufferResourceDesc.SampleDesc.Count = <span class="hljs-number">1</span>;
vertexBufferResourceDesc.SampleDesc.Quality = <span class="hljs-number">0</span>;
vertexBufferResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
vertexBufferResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;

<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateCommittedResource</span>(
    &amp;heapProps, D3D12_HEAP_FLAG_NONE, &amp;vertexBufferResourceDesc,
    D3D12_RESOURCE_STATE_GENERIC_READ, <span class="hljs-literal">nullptr</span>, <span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;indexBuffer)));

<span class="hljs-comment">// 📄 Copy data to DirectX 12 driver memory:</span>
UINT8* pVertexDataBegin;

<span class="hljs-comment">// 🙈 We do not intend to read from this resource on the CPU.</span>
D3D12_RANGE readRange;
readRange.Begin = <span class="hljs-number">0</span>;
readRange.End = <span class="hljs-number">0</span>;

<span class="hljs-built_in">ThrowIfFailed</span>(indexBuffer-&gt;<span class="hljs-built_in">Map</span>(<span class="hljs-number">0</span>, &amp;readRange,
                               <span class="hljs-built_in">reinterpret_cast</span>&lt;<span class="hljs-type">void</span>**&gt;(&amp;pVertexDataBegin)));
<span class="hljs-built_in">memcpy</span>(pVertexDataBegin, indexBufferData, <span class="hljs-built_in">sizeof</span>(indexBufferData));
indexBuffer-&gt;<span class="hljs-built_in">Unmap</span>(<span class="hljs-number">0</span>, <span class="hljs-literal">nullptr</span>);

<span class="hljs-comment">// 👀 Initialize the index buffer view.</span>
indexBufferView.BufferLocation = indexBuffer-&gt;<span class="hljs-built_in">GetGPUVirtualAddress</span>();
indexBufferView.Format = DXGI_FORMAT_R32_UINT;
indexBufferView.SizeInBytes = indexBufferSize;</code></pre></div>
<h3 id="constant-buffer">Constant Buffer</h3>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/constant-buffer.svg" alt="Constant Buffer Diagram"/></p>
<p>A <strong>Constant Buffer</strong> describes data that we&#x27;ll be sending to shader stages when drawing. Normally you would put Model View Projection Matrices or any specific variable data like colors, sliders, etc. here.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 💾 Declare Data</span>
<span class="hljs-keyword">struct</span>
{
    glm::mat4 projectionMatrix;
    glm::mat4 modelMatrix;
    glm::mat4 viewMatrix;
} cbVS;

<span class="hljs-comment">// 👋 Declare Handles</span>
ID3D12Resource* constantBuffer;
ID3D12DescriptorHeap* constantBufferHeap;
UINT8* mappedConstantBuffer;

<span class="hljs-comment">// 🧊 Create the Constant Buffer</span>

D3D12_HEAP_PROPERTIES heapProps;
heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
heapProps.CreationNodeMask = <span class="hljs-number">1</span>;
heapProps.VisibleNodeMask = <span class="hljs-number">1</span>;

D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.NumDescriptors = <span class="hljs-number">1</span>;
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateDescriptorHeap</span>(&amp;heapDesc,
                                           <span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;constantBufferHeap)));

D3D12_RESOURCE_DESC cbResourceDesc;
cbResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
cbResourceDesc.Alignment = <span class="hljs-number">0</span>;
cbResourceDesc.Width = (<span class="hljs-built_in">sizeof</span>(cbVS) + <span class="hljs-number">255</span>) &amp; ~<span class="hljs-number">255</span>;
cbResourceDesc.Height = <span class="hljs-number">1</span>;
cbResourceDesc.DepthOrArraySize = <span class="hljs-number">1</span>;
cbResourceDesc.MipLevels = <span class="hljs-number">1</span>;
cbResourceDesc.Format = DXGI_FORMAT_UNKNOWN;
cbResourceDesc.SampleDesc.Count = <span class="hljs-number">1</span>;
cbResourceDesc.SampleDesc.Quality = <span class="hljs-number">0</span>;
cbResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
cbResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;

<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateCommittedResource</span>(
    &amp;heapProps, D3D12_HEAP_FLAG_NONE, &amp;cbResourceDesc,
    D3D12_RESOURCE_STATE_GENERIC_READ, <span class="hljs-literal">nullptr</span>, <span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;constantBuffer)));
constantBufferHeap-&gt;<span class="hljs-built_in">SetName</span>(<span class="hljs-string">L&quot;Constant Buffer Upload Resource Heap&quot;</span>);

<span class="hljs-comment">// 👓 Create our Constant Buffer View</span>
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {};
cbvDesc.BufferLocation = constantBuffer-&gt;<span class="hljs-built_in">GetGPUVirtualAddress</span>();
cbvDesc.SizeInBytes =
    (<span class="hljs-built_in">sizeof</span>(cbVS) + <span class="hljs-number">255</span>) &amp; ~<span class="hljs-number">255</span>; <span class="hljs-comment">// CB size is required to be 256-byte aligned.</span>

<span class="hljs-function">D3D12_CPU_DESCRIPTOR_HANDLE
    <span class="hljs-title">cbvHandle</span><span class="hljs-params">(constantBufferHeap-&gt;GetCPUDescriptorHandleForHeapStart())</span></span>;
cbvHandle.ptr = cbvHandle.ptr + device-&gt;<span class="hljs-built_in">GetDescriptorHandleIncrementSize</span>(
                                    D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV) *
                                    <span class="hljs-number">0</span>;

device-&gt;<span class="hljs-built_in">CreateConstantBufferView</span>(&amp;cbvDesc, cbvHandle);

<span class="hljs-comment">// 🙈 We do not intend to read from this resource on the CPU.</span>
D3D12_RANGE readRange;
readRange.Begin = <span class="hljs-number">0</span>;
readRange.End = <span class="hljs-number">0</span>;

<span class="hljs-built_in">ThrowIfFailed</span>(constantBuffer-&gt;<span class="hljs-built_in">Map</span>(
    <span class="hljs-number">0</span>, &amp;readRange, <span class="hljs-built_in">reinterpret_cast</span>&lt;<span class="hljs-type">void</span>**&gt;(&amp;mappedConstantBuffer)));
<span class="hljs-built_in">memcpy</span>(mappedConstantBuffer, &amp;cbVS, <span class="hljs-built_in">sizeof</span>(cbVS));
constantBuffer-&gt;<span class="hljs-built_in">Unmap</span>(<span class="hljs-number">0</span>, &amp;readRange);</code></pre></div>
<h3 id="vertex-shader">Vertex Shader</h3>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/vertex-shader.svg" alt="Vertex Shader Diagram"/></p>
<p><strong>Vertex Shaders</strong> execute per vertex, and are perfect for transforming a given object, performing per vertex animations with blend shapes, GPU skinning, etc.</p>
<div><pre><code class="language-hlsl"><span class="hljs-keyword">cbuffer</span> cb : <span class="hljs-keyword">register</span>(b0)
{
    <span class="hljs-keyword">row_major</span> <span class="hljs-type">float4x4</span> projectionMatrix : <span class="hljs-keyword">packoffset</span>(c0);
    <span class="hljs-keyword">row_major</span> <span class="hljs-type">float4x4</span> modelMatrix : <span class="hljs-keyword">packoffset</span>(c4);
    <span class="hljs-keyword">row_major</span> <span class="hljs-type">float4x4</span> viewMatrix : <span class="hljs-keyword">packoffset</span>(c8);
};

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">VertexInput</span>
{
    <span class="hljs-type">float3</span> inPos : <span class="hljs-built_in">POSITION</span>;
    <span class="hljs-type">float3</span> inColor : <span class="hljs-built_in">COLOR</span>;
};

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">VertexOutput</span>
{
    <span class="hljs-type">float3</span> color : <span class="hljs-built_in">COLOR</span>;
    <span class="hljs-type">float4</span> position : <span class="hljs-built_in">SV_Position</span>;
};

VertexOutput main(VertexInput vertexInput)
{
    <span class="hljs-type">float3</span> inColor = vertexInput.inColor;
    <span class="hljs-type">float3</span> inPos = vertexInput.inPos;
    <span class="hljs-type">float3</span> outColor = inColor;
    <span class="hljs-type">float4</span> position = <span class="hljs-built_in">mul</span>(<span class="hljs-type">float4</span>(inPos, <span class="hljs-number">1.0f</span>), <span class="hljs-built_in">mul</span>(modelMatrix, <span class="hljs-built_in">mul</span>(viewMatrix, projectionMatrix)));

    VertexOutput output;
    output.position = position;
    output.color = outColor;
    <span class="hljs-keyword">return</span> output;
}</code></pre></div>
<p>You could compile shaders using the legacy DirectX Shader Compiler bundled with the DirectX 11/12 API, <a href="https://github.com/microsoft/DirectXShaderCompiler">but it&#x27;s best to use the newer official compiler</a>.</p>
<div><pre><code class="language-bash">dxc.exe -T lib_6_3 -Fo assets/triangle.vert.dxil assets/triangle.vert.hlsl</code></pre></div>
<p>Then you could load the shader as a binary file:</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// Helper to read a binary file:</span>
<span class="hljs-function"><span class="hljs-keyword">inline</span> std::vector&lt;<span class="hljs-type">char</span>&gt; <span class="hljs-title">readFile</span><span class="hljs-params">(<span class="hljs-type">const</span> std::string&amp; filename)</span>
</span>{
    <span class="hljs-function">std::ifstream <span class="hljs-title">file</span><span class="hljs-params">(filename, std::ios::ate | std::ios::binary)</span></span>;
    <span class="hljs-type">bool</span> exists = (<span class="hljs-type">bool</span>)file;

    <span class="hljs-keyword">if</span> (!exists || !file.<span class="hljs-built_in">is_open</span>())
    {
        <span class="hljs-keyword">throw</span> std::<span class="hljs-built_in">runtime_error</span>(<span class="hljs-string">&quot;failed to open file!&quot;</span>);
    }

    <span class="hljs-type">size_t</span> fileSize = (<span class="hljs-type">size_t</span>)file.<span class="hljs-built_in">tellg</span>();
    <span class="hljs-function">std::vector&lt;<span class="hljs-type">char</span>&gt; <span class="hljs-title">buffer</span><span class="hljs-params">(fileSize)</span></span>;

    file.<span class="hljs-built_in">seekg</span>(<span class="hljs-number">0</span>);
    file.<span class="hljs-built_in">read</span>(buffer.<span class="hljs-built_in">data</span>(), fileSize);

    file.<span class="hljs-built_in">close</span>();

    <span class="hljs-keyword">return</span> buffer;
};

<span class="hljs-comment">// 👋 Declare handles</span>
D3D12_SHADER_BYTECODE vsBytecode;
std::string compiledPath;

std::vector&lt;<span class="hljs-type">char</span>&gt; vsBytecodeData = <span class="hljs-built_in">readFile</span>(compCompiledPath);
vsBytecode.pShaderBytecode = vsBytecodeData.<span class="hljs-built_in">data</span>();
vsBytecode.BytecodeLength = vsBytecodeData.<span class="hljs-built_in">size</span>();</code></pre></div>
<h3 id="pixel-shader">Pixel Shader</h3>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/pixel-shader.svg" alt="Pixel Shader Diagram"/></p>
<p><strong>Pixel Shaders</strong> execute per each pixel of your output, including the other attachments that correspond to that pixel coordinate.</p>
<div><pre><code class="language-hlsl"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">PixelInput</span>
{
    <span class="hljs-type">float3</span> color : <span class="hljs-built_in">COLOR</span>;
};

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">PixelOutput</span>
{
    <span class="hljs-type">float4</span> attachment0 : <span class="hljs-built_in">SV_Target0</span>;
};

PixelOutput main(PixelInput pixelInput)
{
    <span class="hljs-type">float3</span> inColor = pixelInput.color;
    PixelOutput output;
    output.attachment0 = <span class="hljs-type">float4</span>(inColor, <span class="hljs-number">1.0f</span>);
    <span class="hljs-keyword">return</span> output;
}</code></pre></div>
<h3 id="pipeline-state">Pipeline State</h3>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/raster-pipeline.svg" alt="Raster Pipeline Diagram"/></p>
<p>The <strong>Pipeline State</strong> describes everything necessary to execute a given raster based draw call.</p>
<ul>
<li>Reference: <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nn-d3d12-id3d12graphicscommandlist5">ID3D12GraphicsCommandList5</a></li>
</ul>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
ID3D12PipelineState* pipelineState;

<span class="hljs-comment">// ⚗️ Define the Graphics Pipeline</span>
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};

<span class="hljs-comment">// 🔣 Input Assembly</span>
D3D12_INPUT_ELEMENT_DESC inputElementDescs[] = {
    {<span class="hljs-string">&quot;POSITION&quot;</span>, <span class="hljs-number">0</span>, DXGI_FORMAT_R32G32B32_FLOAT, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>,
     D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, <span class="hljs-number">0</span>},
    {<span class="hljs-string">&quot;COLOR&quot;</span>, <span class="hljs-number">0</span>, DXGI_FORMAT_R32G32B32_FLOAT, <span class="hljs-number">0</span>, <span class="hljs-number">12</span>,
     D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, <span class="hljs-number">0</span>}};
psoDesc.InputLayout = {inputElementDescs, _countof(inputElementDescs)};

<span class="hljs-comment">// 🦄 Resources</span>
psoDesc.pRootSignature = rootSignature;

<span class="hljs-comment">// 🔺 Vertex Shader</span>
D3D12_SHADER_BYTECODE vsBytecode;
vsBytecode.pShaderBytecode = vertexShaderBlob-&gt;<span class="hljs-built_in">GetBufferPointer</span>();
vsBytecode.BytecodeLength = vertexShaderBlob-&gt;<span class="hljs-built_in">GetBufferSize</span>();
psoDesc.VS = vsBytecode;

<span class="hljs-comment">// 🖌️ Pixel Shader</span>
D3D12_SHADER_BYTECODE psBytecode;
psBytecode.pShaderBytecode = pixelShaderBlob-&gt;<span class="hljs-built_in">GetBufferPointer</span>();
psBytecode.BytecodeLength = pixelShaderBlob-&gt;<span class="hljs-built_in">GetBufferSize</span>();
psoDesc.PS = psBytecode;

<span class="hljs-comment">// 🟨 Rasterization</span>
D3D12_RASTERIZER_DESC rasterDesc;
rasterDesc.FillMode = D3D12_FILL_MODE_SOLID;
rasterDesc.CullMode = D3D12_CULL_MODE_NONE;
rasterDesc.FrontCounterClockwise = FALSE;
rasterDesc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
rasterDesc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
rasterDesc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
rasterDesc.DepthClipEnable = TRUE;
rasterDesc.MultisampleEnable = FALSE;
rasterDesc.AntialiasedLineEnable = FALSE;
rasterDesc.ForcedSampleCount = <span class="hljs-number">0</span>;
rasterDesc.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
psoDesc.RasterizerState = rasterDesc;
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;

<span class="hljs-comment">// 🌀 Color/Blend</span>
D3D12_BLEND_DESC blendDesc;
blendDesc.AlphaToCoverageEnable = FALSE;
blendDesc.IndependentBlendEnable = FALSE;
<span class="hljs-type">const</span> D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = {
    FALSE,
    FALSE,
    D3D12_BLEND_ONE,
    D3D12_BLEND_ZERO,
    D3D12_BLEND_OP_ADD,
    D3D12_BLEND_ONE,
    D3D12_BLEND_ZERO,
    D3D12_BLEND_OP_ADD,
    D3D12_LOGIC_OP_NOOP,
    D3D12_COLOR_WRITE_ENABLE_ALL,
};
<span class="hljs-keyword">for</span> (UINT i = <span class="hljs-number">0</span>; i &lt; D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
    blendDesc.RenderTarget[i] = defaultRenderTargetBlendDesc;
psoDesc.BlendState = blendDesc;

<span class="hljs-comment">// 🌑 Depth/Stencil State</span>
psoDesc.DepthStencilState.DepthEnable = FALSE;
psoDesc.DepthStencilState.StencilEnable = FALSE;
psoDesc.SampleMask = UINT_MAX;

<span class="hljs-comment">// 🖼️ Output</span>
psoDesc.NumRenderTargets = <span class="hljs-number">1</span>;
psoDesc.RTVFormats[<span class="hljs-number">0</span>] = DXGI_FORMAT_R8G8B8A8_UNORM;
psoDesc.SampleDesc.Count = <span class="hljs-number">1</span>;

<span class="hljs-comment">// 🌟 Create the raster pipeline state</span>
<span class="hljs-keyword">try</span>
{
    <span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateGraphicsPipelineState</span>(
        &amp;psoDesc, <span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;pipelineState)));
}
<span class="hljs-built_in">catch</span> (std::exception e)
{
    std::cout &lt;&lt; <span class="hljs-string">&quot;Failed to create Graphics Pipeline!&quot;</span>;
}</code></pre></div>
<h2 id="encoding-commands">Encoding Commands</h2>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/command-list.svg" alt="Encoding Commands Diagram"/></p>
<p>In order to execute draw calls, you&#x27;re going to need a place to write commands. A <strong>Command List</strong> is an object that can encode a number of commands to be executed by the GPU, be it configuring barriers, setting root signatures, etc.</p>
<ul>
<li>Reference: <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nn-d3d12-id3d12graphicscommandlist5">ID3D12GraphicsCommandList5</a></li>
</ul>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
ID3D12CommandAllocator* commandAllocator;
ID3D12PipelineState* initialPipelineState;
ID3D12GraphicsCommandList* commandList;

<span class="hljs-comment">// 📃 Create the command list.</span>
<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateCommandList</span>(<span class="hljs-number">0</span>, D3D12_COMMAND_LIST_TYPE_DIRECT,
                                        commandAllocator, initialPipelineState,
                                        <span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;commandList)));</code></pre></div>
<p>Later, encode those commands and submit them:</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 🚿 Reset the command list and add new commands.</span>
<span class="hljs-built_in">ThrowIfFailed</span>(commandAllocator-&gt;<span class="hljs-built_in">Reset</span>());

<span class="hljs-comment">// 🖌️ Begin using the Raster Graphics Pipeline</span>
<span class="hljs-built_in">ThrowIfFailed</span>(commandList-&gt;<span class="hljs-built_in">Reset</span>(commandAllocator, pipelineState));

<span class="hljs-comment">// 🔳 Setup Resources</span>
commandList-&gt;<span class="hljs-built_in">SetGraphicsRootSignature</span>(rootSignature);
ID3D12DescriptorHeap* pDescriptorHeaps[] = {constantBufferHeap};
commandList-&gt;<span class="hljs-built_in">SetDescriptorHeaps</span>(_countof(pDescriptorHeaps), pDescriptorHeaps);
<span class="hljs-function">D3D12_GPU_DESCRIPTOR_HANDLE
    <span class="hljs-title">cbvHandle</span><span class="hljs-params">(constantBufferHeap-&gt;GetGPUDescriptorHandleForHeapStart())</span></span>;
commandList-&gt;<span class="hljs-built_in">SetGraphicsRootDescriptorTable</span>(<span class="hljs-number">0</span>, cbvHandle);

<span class="hljs-comment">// 🖼️ Indicate that the back buffer will be used as a render target.</span>
D3D12_RESOURCE_BARRIER renderTargetBarrier;
renderTargetBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
renderTargetBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
renderTargetBarrier.Transition.pResource = renderTargets[frameIndex];
renderTargetBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
renderTargetBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
renderTargetBarrier.Transition.Subresource =
    D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
commandList-&gt;<span class="hljs-built_in">ResourceBarrier</span>(<span class="hljs-number">1</span>, &amp;renderTargetBarrier);

<span class="hljs-function">D3D12_CPU_DESCRIPTOR_HANDLE
    <span class="hljs-title">rtvHandle</span><span class="hljs-params">(rtvHeap-&gt;GetCPUDescriptorHandleForHeapStart())</span></span>;
rtvHandle.ptr = rtvHandle.ptr + (frameIndex * rtvDescriptorSize);
commandList-&gt;<span class="hljs-built_in">OMSetRenderTargets</span>(<span class="hljs-number">1</span>, &amp;rtvHandle, FALSE, <span class="hljs-literal">nullptr</span>);

<span class="hljs-comment">// 🎥 Record raster commands.</span>
<span class="hljs-type">const</span> <span class="hljs-type">float</span> clearColor[] = {<span class="hljs-number">0.2f</span>, <span class="hljs-number">0.2f</span>, <span class="hljs-number">0.2f</span>, <span class="hljs-number">1.0f</span>};
commandList-&gt;<span class="hljs-built_in">RSSetViewports</span>(<span class="hljs-number">1</span>, &amp;viewport);
commandList-&gt;<span class="hljs-built_in">RSSetScissorRects</span>(<span class="hljs-number">1</span>, &amp;surfaceSize);
commandList-&gt;<span class="hljs-built_in">ClearRenderTargetView</span>(rtvHandle, clearColor, <span class="hljs-number">0</span>, <span class="hljs-literal">nullptr</span>);
commandList-&gt;<span class="hljs-built_in">IASetPrimitiveTopology</span>(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
commandList-&gt;<span class="hljs-built_in">IASetVertexBuffers</span>(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, &amp;vertexBufferView);
commandList-&gt;<span class="hljs-built_in">IASetIndexBuffer</span>(&amp;indexBufferView);

commandList-&gt;<span class="hljs-built_in">DrawIndexedInstanced</span>(<span class="hljs-number">3</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

<span class="hljs-comment">// 🖼️ Indicate that the back buffer will now be used to present.</span>
D3D12_RESOURCE_BARRIER presentBarrier;
presentBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
presentBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
presentBarrier.Transition.pResource = renderTargets[frameIndex];
presentBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
presentBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
presentBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;

commandList-&gt;<span class="hljs-built_in">ResourceBarrier</span>(<span class="hljs-number">1</span>, &amp;presentBarrier);

<span class="hljs-built_in">ThrowIfFailed</span>(commandList-&gt;<span class="hljs-built_in">Close</span>());</code></pre></div>
<h2 id="render">Render</h2>
<p><img src="https://alain.xyz/blog/raw-directx12/assets/triangle-raster.gif" alt="Triangle Raster Gif"/></p>
<p><strong>Rendering</strong> in DirectX 12 is a simple matter of changing any constant buffer data you intend to update, submitting your command lists to be executed, presenting the swapchain so your Win32 or UWP window updates, and signaling your application that you&#x27;ve finished rendering.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 declare handles</span>
std::chrono::time_point&lt;std::chrono::steady_clock&gt; tStart, tEnd;
<span class="hljs-type">float</span> elapsedTime = <span class="hljs-number">0.0f</span>;

<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">render</span><span class="hljs-params">()</span>
</span>{
    <span class="hljs-comment">// ⌚ Frame limit set to 60 fps</span>
    tEnd = std::chrono::high_resolution_clock::<span class="hljs-built_in">now</span>();
    <span class="hljs-type">float</span> time =
        std::chrono::<span class="hljs-built_in">duration</span>&lt;<span class="hljs-type">float</span>, std::milli&gt;(tEnd - tStart).<span class="hljs-built_in">count</span>();
    <span class="hljs-keyword">if</span> (time &lt; (<span class="hljs-number">1000.0f</span> / <span class="hljs-number">60.0f</span>))
    {
        <span class="hljs-keyword">return</span>;
    }
    tStart = std::chrono::high_resolution_clock::<span class="hljs-built_in">now</span>();

    <span class="hljs-comment">// 🎛️ Update Uniforms</span>
    elapsedTime += <span class="hljs-number">0.001f</span> * time;
    elapsedTime = <span class="hljs-built_in">fmodf</span>(elapsedTime, <span class="hljs-number">6.283185307179586f</span>);
    cbVS.modelMatrix = Matrix4::<span class="hljs-built_in">rotationY</span>(elapsedTime);

    D3D12_RANGE readRange;
    readRange.Begin = <span class="hljs-number">0</span>;
    readRange.End = <span class="hljs-number">0</span>;

    <span class="hljs-built_in">ThrowIfFailed</span>(constantBuffer-&gt;<span class="hljs-built_in">Map</span>(
        <span class="hljs-number">0</span>, &amp;readRange, <span class="hljs-built_in">reinterpret_cast</span>&lt;<span class="hljs-type">void</span>**&gt;(&amp;mappedConstantBuffer)));
    <span class="hljs-built_in">memcpy</span>(mappedConstantBuffer, &amp;cbVS, <span class="hljs-built_in">sizeof</span>(cbVS));
    constantBuffer-&gt;<span class="hljs-built_in">Unmap</span>(<span class="hljs-number">0</span>, &amp;readRange);

    <span class="hljs-built_in">setupCommands</span>();

    ID3D12CommandList* ppCommandLists[] = {commandList};
    commandQueue-&gt;<span class="hljs-built_in">ExecuteCommandLists</span>(_countof(ppCommandLists), ppCommandLists);

    <span class="hljs-comment">// 🎥 Present, then wait till finished to continue execution</span>
    swapchain-&gt;<span class="hljs-built_in">Present</span>(<span class="hljs-number">1</span>, <span class="hljs-number">0</span>);

    <span class="hljs-type">const</span> UINT64 fence = fenceValue;
    <span class="hljs-built_in">ThrowIfFailed</span>(commandQueue-&gt;<span class="hljs-built_in">Signal</span>(fence, fence));
    fenceValue++;

    <span class="hljs-keyword">if</span> (fence-&gt;<span class="hljs-built_in">GetCompletedValue</span>() &lt; fence)
    {
        <span class="hljs-built_in">ThrowIfFailed</span>(fence-&gt;<span class="hljs-built_in">SetEventOnCompletion</span>(fence, fenceEvent));
        <span class="hljs-built_in">WaitForSingleObject</span>(fenceEvent, INFINITE);
    }

    frameIndex = swapchain-&gt;<span class="hljs-built_in">GetCurrentBackBufferIndex</span>();
}</code></pre></div>
<h2 id="destroy-handles">Destroy Handles</h2>
<p>If you&#x27;re using <code>ComPtr&lt;T&gt;</code> data structures, then just like with shared pointers, you don&#x27;t need to worry about destroying any handles you create. If you aren&#x27;t, you can call the <code>Release()</code> function built into every DirectX data structure.</p>
<h2 id="conclusion">Conclusion</h2>
<p>DirectX 12 is a feature rich and robust computer graphics API, perfect for commercial projects. The API is similar to <a href="https://alain.xyz/blog/comparison-of-modern-graphics-apis">modern graphics APIs in its design</a>, while also being the primary API maintained by both hardware driver engineers and engineers of commercial projects. This post reviews raster based drawing, however there are more aspects of DirectX not discussed here worth reviewing, such as:</p>
<ul>
<li>
<p><a href="https://docs.microsoft.com/en-us/windows/win32/direct3d12/dml-intro">DirectML</a> - Hardware accelerated machine learning model execution.</p>
</li>
<li>
<p><a href="https://alain.xyz/blog/real-time-raytracing">DirectX Raytracing</a> - Hardware accelerated ray tracing and scene traversal.</p>
</li>
<li>
<p>Compute Shaders - GPGPU based execution of arbitrary tasks, such as image processing, physics, etc.</p>
</li>
<li>
<p>Mesh Shaders - an alternative way of building primitives from traditional raster based rendering techniques.</p>
</li>
<li>
<p>Variable Rate Shading - More fine tuned control over the shading rate of a given set of commands.</p>
</li>
</ul>
<p>I also wrote more about DirectX:</p>
<ul>
<li>
<p>The <a href="https://alain.xyz/books/raw-directx12/">Raw DirectX 12 book</a> goes over the compute pipeline, ray tracing pipeline, and much more.</p>
</li>
<li>
<p>The <a href="https://alain.xyz/blog/raw-directx11">Raw DirectX 11 blog post</a> reviews the previous version of DirectX 11 and could serve as a great start to learning DirectX since many of the concepts transfer over to DirectX 12.</p>
</li>
</ul>
<h3 id="more-resources">More Resources</h3>
<p>Be sure to check out some of the following blog posts, tools, and projects:</p>
<h4 id="articles">Articles</h4>
<ul>
<li>
<p><a href="https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/d3d10-graphics-reference-dxgi-interfaces">Microsoft&#x27;s documentation on DirectX 12 features a page with all it&#x27;s data structures for initialization here</a>.</p>
</li>
<li>
<p><a href="https://microsoft.github.io/DirectX-Specs/">Microsoft maintains a specification document for D3D12 and 11 here</a>.</p>
</li>
<li>
<p><a href="https://www.jendrikillner.com/">Jendrik Illner</a> (<a href="https://twitter.com/jendrikillner">@jendrikillner</a>) wrote <a href="https://www.jendrikillner.com/post/d3d12-learning-plan/">DirectX 12 learning plan here</a>.</p>
</li>
<li>
<p><a href="https://www.braynzarsoft.net/viewtutorial/q16390-directx-12-textures-from-file">Braynzarsoft, a community of DirectX tutorials</a>.</p>
</li>
<li>
<p><a href="http://rikoophorst.com/">Riko Ophorst</a>&#x27;s <a href="https://github.com/RikoOphorst/dxr-path-tracing">DirectX 12 Raytracing Thesis</a>.</p>
</li>
<li>
<p><a href="https://logins.github.io/graphics/2020/10/31/D3D12ComputeShaders.html">Riccardo Loggini&#x27;s D3D12 blog posts</a>.</p>
</li>
<li>
<p><a href="https://www.intel.com/content/www/us/en/developer/articles/technical/understanding-directx-multithreaded-rendering-performance-by-experiments.html">Intel maintains a library of D3D12 blog posts here</a>.</p>
</li>
<li>
<p><a href="https://developer.nvidia.com/dx12-dos-and-donts">NVIDIA maintains a number of blog posts on D3D12 and similar topics here</a>.</p>
</li>
<li>
<p>Diligent Graphics (<a href="https://twitter.com/diligentengine">@diligentengine</a>) wrote <a href="http://diligentgraphics.com/diligent-engine/architecture/d3d12/">articles on DirectX 12 here</a>.</p>
</li>
<li>
<p>Jeremiah van Oosten&#x27;s <a href="https://www.3dgep.com/learning-directx-12-2/">DirectX 12.2 Tutorial Series</a>.</p>
</li>
<li>
<p><a href="https://alextardif.com">Alex Tardif</a> (<a href="https://twitter.com/longbool">@longbool</a>)&#x27;s <a href="https://alextardif.com/DX12Tutorial.html">A Gentle Introduction to D3D12</a>.</p>
</li>
</ul>
<h4 id="samples">Samples</h4>
<ul>
<li>
<p><a href="https://github.com/Microsoft/DirectX-Graphics-Samples">Microsoft&#x27;s official repo of DirectX 12 Samples</a></p>
</li>
<li>
<p><a href="https://github.com/GameTechDev">Intel&#x27;s official GitHub Organization Game Tech Dev</a>.</p>
</li>
<li>
<p><a href="https://github.com/NVIDIAGameWorks">NVIDIA&#x27;s official GitHub Organization GameWorks</a>.</p>
</li>
<li>
<p>Matthäus G. Chajdas (<a href="https://twitter.com/NIV_Anteru">@NIV_Anteru</a>) released <a href="https://github.com/GPUOpen-LibrariesAndSDKs/HelloD3D12">HelloD3D12, a part of AMD&#x27;s GPUOpen Libraries &amp; SDKs</a>.</p>
</li>
</ul>
<h4 id="tools">Tools</h4>
<ul>
<li>
<p>The <a href="https://github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator">D3D12 Memory Allocator</a> by Adam Sawicki (<a href="https://twitter.com/Reg__">@Reg__</a>), the same author behind the Vulkan Memory Allocator.</p>
</li>
<li>
<p>Tim Jones (<a href="https://twitter.com/_tim_jones_">@<em>tim_jones</em></a>) released a VS Code Plugin called <a href="https://marketplace.visualstudio.com/items?itemName=TimGJones.hlsltools">HLSL Tools</a> that lets you lint your shaders more easily.</p>
</li>
</ul>
<p>You&#x27;ll find all the source code described in this post in the <a href="https://github.com/alaingalvan/directx12-seed">Github repo</a> here.</p>]]></content:encoded>
</item>
<item>
<title>Raw DirectX 11</title>
<link>https://alain.xyz/blog/raw-directx11</link>
<description>An introduction to writing a simple Hello Triangle DirectX 11 application. DirectX 11 is the previous version of the standard low level graphics API for writing Windows applications, supporting compute, rasterization, and much more.</description>
<guid isPermaLink="true">https://alain.xyz/blog/raw-directx11</guid>
<pubDate>Sat, 23 Oct 2021 00:36:34 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>opengl</category><category>triangle</category><category>hello</category><category>theory</category><category>diagrams</category><category>directx</category><category>vulkan</category>
<enclosure url="https://alain.xyz/blog/raw-directx11/assets/cover.jpg" length="130218" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx11/assets/win32-window.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx11/assets/factory.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx11/assets/adapter.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx11/assets/device.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx11/assets/swapchain.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx11/assets/render-targets.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx11/assets/vertex-buffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx11/assets/index-buffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx11/assets/constant-buffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx11/assets/vertex-shader.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx11/assets/pixel-shader.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx11/assets/raster-pipeline.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-directx11/assets/triangle-raster.gif"/><p>DirectX 11 is the previous version of Microsoft&#x27;s proprietary graphics API, designed to be simple to work with, while at the same time capable of high performance rendering. While DirectX 11 is an older API, it&#x27;s still possible to use DirectX 11 with 12 through the library <a href="https://docs.microsoft.com/en-us/windows/win32/direct3d12/direct3d-11-on-12">DirectX 11 on 12</a> or directly interfacing with the API.</p>
<p>That being said, given that there&#x27;s a newer version of DirectX available to engineers, it may be best to start any greenfield projects with DirectX 12, though 11 is much simpler and can serve as a good entry point to graphics programming.</p>
<blockquote>
<p>Visit my <a href="https://alain.xyz/blog/raw-directx12">blog post on DirectX 12</a> to learn more about the latest version of the DirectX API.</p>
</blockquote>
<h2 id="overview">Overview</h2>
<p>DirectX&#x27;s documentation recommends the use of <a href="https://docs.microsoft.com/en-us/windows/win32/learnwin32/com-coding-practices"><code>ComPtr&lt;T&gt;</code></a> as an alternative to <code>std::shared_ptr&lt;T&gt;</code>, with the benefit of better debugging and easier initialization of DirectX 11 data structures.</p>
<p>Regardless of whether or not you choose to use <code>ComPtr&lt;T&gt;</code>, the steps to rendering raster graphics with DirectX 11 are pretty similar to <a href="https://alain.xyz/blog/comparison-of-modern-graphics-apis">other modern graphics APIs</a>:</p>
<ol>
<li>
<p><strong>Initialize the API</strong> - Create your <code>IDXGIFactory</code>, <code>IDXGIAdapter</code>, <code>ID3D11Device</code>, and <code>ID3D11DeviceContext</code>.</p>
</li>
<li>
<p><strong>Setup Frame Backings</strong> - Create your <code>IDXGIOutput</code> to determine what your back buffer color space/size should be, <code>IDXGISwapChain</code> for your back buffers, get your back buffer <code>ID3D11Texture2D</code> and create <code>ID3D11RenderTargetView</code>s for those back buffers.</p>
</li>
<li>
<p><strong>Initialize Resources</strong> - Create your Triangle Data such as your <code>ID3D11Buffer</code> Vertex Buffers, <code>ID3D11Buffer</code> Index Buffer, <code>ID3D11Buffer</code> Constant Buffer. Compile or load your <code>ID3D11VertexShader</code> and <code>ID3D11PixelShader</code>, your constant buffer <code>ID3D11Buffer</code>s and and define your <strong>Raster Pipeline State</strong> with <code>ID3D11DepthStencilState</code>, <code>ID3D11RasterizerState</code>, etc.</p>
</li>
<li>
<p><strong>Render</strong> - Update your GPU constant buffer data (Uniforms), write commands to your <code>ID3D11DeviceContext</code>, and <code>Present</code> your swapchain.</p>
</li>
<li>
<p><strong>Destroy</strong> - Destroy any data structures you&#x27;re done using with <code>Release()</code> or rely on <code>ComPtr&lt;T&gt;</code> deallocating for you.</p>
</li>
</ol>
<p>The following will explain snippets of a DirectX 11 application, with certain parts omitted, and <em>member variables (<code>mMemberVariable</code>) declared inline without the <code>m</code> prefix so their type is easier to see and the examples here can work on their own</em>.</p>
<h2 id="window-creation">Window Creation</h2>
<p><img src="https://alain.xyz/blog/raw-directx11/assets/win32-window.svg" alt="Win32 Window"/></p>
<p>We&#x27;re using <a href="https://github.com/alaingalvan/CrossWindow">CrossWindow</a> to handle cross platform window creation, so creating a Win32 window and updating it is very easy:</p>
<div><pre><code class="language-cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&quot;CrossWindow/CrossWindow.h&quot;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&quot;Renderer.h&quot;</span></span>

<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;iostream&gt;</span></span>

<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">xmain</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">const</span> <span class="hljs-type">char</span>** argv)</span>
</span>{
  <span class="hljs-comment">// 🖼️ Create Window</span>
  xwin::WindowDesc wdesc;
  wdesc.title = <span class="hljs-string">&quot;DirectX 11 Seed&quot;</span>;
  wdesc.name = <span class="hljs-string">&quot;MainWindow&quot;</span>;
  wdesc.visible = <span class="hljs-literal">true</span>;
  wdesc.width = <span class="hljs-number">640</span>;
  wdesc.height = <span class="hljs-number">640</span>;
  wdesc.fullscreen = <span class="hljs-literal">false</span>;

  xwin::Window window;
  xwin::EventQueue eventQueue;

  <span class="hljs-keyword">if</span> (!window.<span class="hljs-built_in">create</span>(wdesc, eventQueue))
  { <span class="hljs-keyword">return</span>; };

  <span class="hljs-comment">// 🎨 Create a renderer</span>
  <span class="hljs-function">Renderer <span class="hljs-title">renderer</span><span class="hljs-params">(window)</span></span>;

  <span class="hljs-comment">// 🏁 Engine loop</span>
  <span class="hljs-type">bool</span> isRunning = <span class="hljs-literal">true</span>;
  <span class="hljs-keyword">while</span> (isRunning)
  {
    <span class="hljs-type">bool</span> shouldRender = <span class="hljs-literal">true</span>;

    <span class="hljs-comment">// ♻️ Update the event queue</span>
    eventQueue.<span class="hljs-built_in">update</span>();

    <span class="hljs-comment">// 🎈 Iterate through that queue:</span>
    <span class="hljs-keyword">while</span> (!eventQueue.<span class="hljs-built_in">empty</span>())
    {
      <span class="hljs-comment">//Update Events</span>
      <span class="hljs-type">const</span> xwin::Event&amp; event = eventQueue.<span class="hljs-built_in">front</span>();

      <span class="hljs-comment">// 💗 On Resize:</span>
      <span class="hljs-keyword">if</span> (event.type == xwin::EventType::Resize)
      {
        <span class="hljs-type">const</span> xwin::ResizeData data = event.data.resize;
        renderer.<span class="hljs-built_in">resize</span>(data.width, data.height);
        shouldRender = <span class="hljs-literal">false</span>;
      }

      <span class="hljs-comment">// ❌ On Close:</span>
      <span class="hljs-keyword">if</span> (event.type == xwin::EventType::Close)
      {
        window.<span class="hljs-built_in">close</span>();
        shouldRender = <span class="hljs-literal">false</span>;
        isRunning = <span class="hljs-literal">false</span>;
      }

      eventQueue.<span class="hljs-built_in">pop</span>();
    }

    <span class="hljs-comment">// ✨ Update Visuals</span>
    <span class="hljs-keyword">if</span> (shouldRender)
    {
      renderer.<span class="hljs-built_in">render</span>();
    }
  }
}</code></pre></div>
<p>As an alternative to CrossWindow, you could use another library like GLFW, SFML, SDL, QT, or just interface directly with the Win32 or UWP APIs.</p>
<h2 id="initialize-api">Initialize API</h2>
<h3 id="factory">Factory</h3>
<p><img src="https://alain.xyz/blog/raw-directx11/assets/factory.svg" alt="Factory Diagram"/></p>
<p><strong>Factories</strong> are the entry point to the DirectX 11 API, and will allow you to find adapters that you can use to execute DirectX 11 commands.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 declare handles</span>
IDXGIFactory* factory;

<span class="hljs-built_in">ThrowIfFailed</span>(<span class="hljs-built_in">CreateDXGIFactory</span>(<span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;factory)));</code></pre></div>
<h3 id="adapter">Adapter</h3>
<p><img src="https://alain.xyz/blog/raw-directx11/assets/adapter.svg" alt="Adapter Diagram"/></p>
<p>An <strong>Adapter</strong> provides information on the physical properties of a given DirectX device. You can query your current GPU, how much memory it has, etc.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 declare handles</span>
IDXGIAdapter* adapter;

<span class="hljs-built_in">ThrowIfFailed</span>(factory-&gt;<span class="hljs-built_in">EnumAdapters</span>(<span class="hljs-number">0</span>, &amp;adapter));</code></pre></div>
<h3 id="adapter-output">Adapter Output</h3>
<p>An <strong>Adapter Output</strong> allows you to query display information for their color space and refresh rate.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 declare handles</span>
IDXGIOutput* adapterOutput;
<span class="hljs-type">unsigned</span> numerator = <span class="hljs-number">0</span>;
<span class="hljs-type">unsigned</span> denominator = <span class="hljs-number">1</span>;

<span class="hljs-built_in">ThrowIfFailed</span>(adapter-&gt;<span class="hljs-built_in">EnumOutputs</span>(<span class="hljs-number">0</span>, &amp;adapterOutput));

<span class="hljs-comment">// Get the number of modes that fit the DXGI_FORMAT_R8G8B8A8_UNORM display</span>
<span class="hljs-comment">// format for the adapter output (monitor).</span>
<span class="hljs-type">unsigned</span> <span class="hljs-type">int</span> numModes;
DXGI_MODE_DESC* displayModeList;
<span class="hljs-built_in">ThrowIfFailed</span>(adapterOutput-&gt;<span class="hljs-built_in">GetDisplayModeList</span>(DXGI_FORMAT_R8G8B8A8_UNORM,
                                                DXGI_ENUM_MODES_INTERLACED,
                                                &amp;numModes, <span class="hljs-literal">NULL</span>));

<span class="hljs-comment">// Create a list to hold all the possible display modes for this monitor/video</span>
<span class="hljs-comment">// card combination.</span>
displayModeList = <span class="hljs-keyword">new</span> DXGI_MODE_DESC[numModes];
<span class="hljs-built_in">ThrowIfFailed</span>(adapterOutput-&gt;<span class="hljs-built_in">GetDisplayModeList</span>(DXGI_FORMAT_R8G8B8A8_UNORM,
                                                DXGI_ENUM_MODES_INTERLACED,
                                                &amp;numModes, displayModeList));

<span class="hljs-comment">// Now go through all the display modes and find the one that matches the screen</span>
<span class="hljs-comment">// width and height. When a match is found store the numerator and denominator</span>
<span class="hljs-comment">// of the refresh rate for that monitor.</span>
<span class="hljs-keyword">for</span> (<span class="hljs-type">size_t</span> i = <span class="hljs-number">0</span>; i &lt; numModes; i++)
{
    <span class="hljs-keyword">if</span> (displayModeList[i].Width = width &amp;&amp; displayModeList[i].Height == height)
    {
        numerator = displayModeList[i].RefreshRate.Numerator;
        denominator = displayModeList[i].RefreshRate.Denominator;
        <span class="hljs-keyword">break</span>;
    }
}

<span class="hljs-comment">// Release the display mode list.</span>
<span class="hljs-keyword">delete</span>[] displayModeList;
displayModeList = <span class="hljs-literal">nullptr</span>;</code></pre></div>
<h3 id="device">Device</h3>
<p><img src="https://alain.xyz/blog/raw-directx11/assets/device.svg" alt="Device Diagram"/></p>
<p>A <strong>Device</strong> is your primary entry point to the DirectX 11 API, giving you the ability to create data structures such as buffers, textures, views, pipeline states, etc.</p>
<p>The <strong>Device Context</strong> is where you can execute commands, be it setting the raster pipeline, drawing triangles, or setting frame buffer attachments.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 declare handles</span>
ID3D11Device* device;
ID3D11DeviceContext* deviceContext;

D3D_FEATURE_LEVEL featureLevelInputs[<span class="hljs-number">7</span>] = {
    D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1,
    D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3,  D3D_FEATURE_LEVEL_9_2,
    D3D_FEATURE_LEVEL_9_1};

D3D_FEATURE_LEVEL featureLevelOutputs = D3D_FEATURE_LEVEL_11_1;

<span class="hljs-built_in">ThrowIfFailed</span>(<span class="hljs-built_in">D3D11CreateDevice</span>(adapter, D3D_DRIVER_TYPE_UNKNOWN, <span class="hljs-number">0</span>,
                                D3D11_CREATE_DEVICE_SINGLETHREADED |
                                    D3D11_CREATE_DEVICE_DEBUG,
                                featureLevelInputs, <span class="hljs-number">7u</span>, D3D11_SDK_VERSION,
                                &amp;device, &amp;featureLevelOutputs, &amp;deviceContext));</code></pre></div>
<p>You can optionally enable the <strong>Debug Controller</strong> to validate your commands on runtime.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 declare handles</span>
ID3D11Debug* debugController;

<span class="hljs-meta">#<span class="hljs-keyword">if</span> defined(_DEBUG)</span>
<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">QueryInterface</span>(<span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;debugController)));
<span class="hljs-meta">#<span class="hljs-keyword">endif</span></span></code></pre></div>
<h2 id="frame-backing">Frame Backing</h2>
<h3 id="swapchain">Swapchain</h3>
<p><img src="https://alain.xyz/blog/raw-directx11/assets/swapchain.svg" alt="Swapchain Diagram"/></p>
<p><strong>Swapchain</strong>s handle swapping and allocating back buffers to display what you&#x27;re rendering to a given window. <a href="https://github.com/alaingalvan/CrossWindow-Graphics">CrossWindow-Graphics</a> makes creating a swapchain for a window very easy:</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 declare handles</span>
IDXGISwapChain* swapchain;
<span class="hljs-type">bool</span> vsync = <span class="hljs-literal">false</span>;
<span class="hljs-type">unsigned</span> width = <span class="hljs-number">640</span>;
<span class="hljs-type">unsigned</span> height = <span class="hljs-number">640</span>;

DXGI_SWAP_CHAIN_DESC swapchainDesc;
<span class="hljs-built_in">ZeroMemory</span>(&amp;swapchainDesc, <span class="hljs-built_in">sizeof</span>(DXGI_SWAP_CHAIN_DESC));
swapchainDesc.BufferCount = <span class="hljs-number">1</span>;
swapchainDesc.BufferDesc.Width = width;
swapchainDesc.BufferDesc.Height = height;
swapchainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;

<span class="hljs-comment">// 🏃‍♀️ Set the refresh rate of the back buffer.</span>
<span class="hljs-keyword">if</span> (vsync)
{
    swapchainDesc.BufferDesc.RefreshRate.Numerator = numerator;
    swapchainDesc.BufferDesc.RefreshRate.Denominator = denominator;
}
<span class="hljs-keyword">else</span>
{
    swapchainDesc.BufferDesc.RefreshRate.Numerator = <span class="hljs-number">0</span>;
    swapchainDesc.BufferDesc.RefreshRate.Denominator = <span class="hljs-number">1</span>;
}
<span class="hljs-comment">// Set the usage of the back buffer.</span>
swapchainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
<span class="hljs-comment">// Turn multi-sampling off.</span>
swapchainDesc.SampleDesc.Count = <span class="hljs-number">1</span>;
swapchainDesc.SampleDesc.Quality = <span class="hljs-number">0</span>;
<span class="hljs-comment">// Set the scan line ordering and scaling to unspecified.</span>
swapchainDesc.BufferDesc.ScanlineOrdering =
    DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
swapchainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
<span class="hljs-comment">// Discard the back buffer contents after presenting.</span>
swapchainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
<span class="hljs-comment">// Don&#x27;t set the advanced flags.</span>
swapchainDesc.Flags = <span class="hljs-number">0</span>;

swapchain = xgfx::<span class="hljs-built_in">createSwapchain</span>(window, factory, device, &amp;swapchainDesc);</code></pre></div>
<h3 id="render-targets">Render Targets</h3>
<p><img src="https://alain.xyz/blog/raw-directx11/assets/render-targets.svg" alt="Render Target Diagram"/></p>
<p><strong>Render Targets</strong> are textures that will be written to during raster based rendering.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 declare handles</span>
ID3D11RenderTargetView* renderTargetView;
ID3D11Texture2D* backbufferTex;

xwin::WindowDesc desc = window-&gt;<span class="hljs-built_in">getDesc</span>();

<span class="hljs-comment">// Get the back buffer texture.</span>
<span class="hljs-built_in">ThrowIfFailed</span>(swapchain-&gt;<span class="hljs-built_in">GetBuffer</span>(<span class="hljs-number">0</span>, <span class="hljs-built_in">IID_PPV_ARGS</span>(&amp;backBufferTex)));
<span class="hljs-comment">// Create the render target view with the back buffer pointer.</span>
<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateRenderTargetView</span>(backBufferTex, <span class="hljs-literal">NULL</span>,
                                             &amp;renderTargetView));

<span class="hljs-comment">// Create the texture for the depth buffer.</span>
D3D11_TEXTURE2D_DESC depthBufferDesc;
<span class="hljs-built_in">ZeroMemory</span>(&amp;depthBufferDesc, <span class="hljs-built_in">sizeof</span>(depthBufferDesc));
depthBufferDesc.Width = width;
depthBufferDesc.Height = height;
depthBufferDesc.MipLevels = <span class="hljs-number">1</span>;
depthBufferDesc.ArraySize = <span class="hljs-number">1</span>;
depthBufferDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthBufferDesc.SampleDesc.Count = <span class="hljs-number">1</span>;
depthBufferDesc.SampleDesc.Quality = <span class="hljs-number">0</span>;
depthBufferDesc.Usage = D3D11_USAGE_DEFAULT;
depthBufferDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
depthBufferDesc.CPUAccessFlags = <span class="hljs-number">0</span>;
depthBufferDesc.MiscFlags = <span class="hljs-number">0</span>;
<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateTexture2D</span>(&amp;depthBufferDesc, <span class="hljs-literal">NULL</span>,
                                      &amp;depthStencilBuffer));

<span class="hljs-comment">// Create the depth stencil view.</span>
D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
<span class="hljs-built_in">ZeroMemory</span>(&amp;depthStencilViewDesc, <span class="hljs-built_in">sizeof</span>(depthStencilViewDesc));
depthStencilViewDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
depthStencilViewDesc.Texture2D.MipSlice = <span class="hljs-number">0</span>;
<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateDepthStencilView</span>(depthStencilBuffer,
                                             &amp;depthStencilViewDesc,
                                             &amp;depthStencilView));</code></pre></div>
<h2 id="resources">Resources</h2>
<h3 id="vertex-buffer">Vertex Buffer</h3>
<p><img src="https://alain.xyz/blog/raw-directx11/assets/vertex-buffer.svg" alt="Vertex Buffer Diagram"/></p>
<p>A <strong>Vertex Buffer</strong> stores the per vertex information available as attributes in your Vertex Shader. All buffers are <code>ID3D11Buffer</code> objects in DirectX 11, be it Vertex Buffers, Index Buffers, Constant Buffers, etc.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 💾 Declare Data</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">Vertex</span>
{
    <span class="hljs-type">float</span> position[<span class="hljs-number">3</span>];
    <span class="hljs-type">float</span> color[<span class="hljs-number">3</span>];
};

Vertex vertexBufferData[<span class="hljs-number">3</span>] = {{{<span class="hljs-number">1.0f</span>, <span class="hljs-number">1.0f</span>, <span class="hljs-number">0.0f</span>}, {<span class="hljs-number">1.0f</span>, <span class="hljs-number">0.0f</span>, <span class="hljs-number">0.0f</span>}},
                              {{<span class="hljs-number">-1.0f</span>, <span class="hljs-number">1.0f</span>, <span class="hljs-number">0.0f</span>}, {<span class="hljs-number">0.0f</span>, <span class="hljs-number">1.0f</span>, <span class="hljs-number">0.0f</span>}},
                              {{<span class="hljs-number">0.0f</span>, <span class="hljs-number">-1.0f</span>, <span class="hljs-number">0.0f</span>}, {<span class="hljs-number">0.0f</span>, <span class="hljs-number">0.0f</span>, <span class="hljs-number">1.0f</span>}}};

<span class="hljs-comment">// 👋 declare handles</span>
ID3D11Buffer* vertexBuffer;

<span class="hljs-comment">// Set up the description of the static vertex buffer.</span>
D3D11_BUFFER_DESC vertexBufferDesc;
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth = <span class="hljs-built_in">sizeof</span>(Vertex) * <span class="hljs-number">3</span>;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = <span class="hljs-number">0</span>;
vertexBufferDesc.MiscFlags = <span class="hljs-number">0</span>;
vertexBufferDesc.StructureByteStride = <span class="hljs-number">0</span>;

<span class="hljs-comment">// Give the subresource structure a pointer to the vertex data.</span>
D3D11_SUBRESOURCE_DATA vertexData;
vertexData.pSysMem = vertexBufferData;
vertexData.SysMemPitch = <span class="hljs-number">0</span>;
vertexData.SysMemSlicePitch = <span class="hljs-number">0</span>;

<span class="hljs-comment">// Now create the vertex buffer.</span>
<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateBuffer</span>(&amp;vertexBufferDesc, &amp;vertexData,
                                   &amp;vertexBuffer));</code></pre></div>
<h3 id="index-buffer">Index Buffer</h3>
<p><img src="https://alain.xyz/blog/raw-directx11/assets/index-buffer.svg" alt="Index Buffer Diagram"/></p>
<p>An <strong>Index Buffer</strong> contains the individual indices of each triangle/line/point that you intend to draw.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 💾 Declare Data</span>
<span class="hljs-type">uint32_t</span> indexBufferData[<span class="hljs-number">3</span>] = {<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>};

<span class="hljs-comment">// 👋 declare handles</span>
ID3D11Buffer* indexBuffer;

<span class="hljs-comment">// Set up the description of the static index buffer.</span>
D3D11_BUFFER_DESC indexBufferDesc;
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.ByteWidth = <span class="hljs-built_in">sizeof</span>(<span class="hljs-type">unsigned</span>) * <span class="hljs-number">3</span>;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = <span class="hljs-number">0</span>;
indexBufferDesc.MiscFlags = <span class="hljs-number">0</span>;
indexBufferDesc.StructureByteStride = <span class="hljs-number">0</span>;

<span class="hljs-comment">// Give the subresource structure a pointer to the index data.</span>
D3D11_SUBRESOURCE_DATA indexData;
indexData.pSysMem = indexBufferData;
indexData.SysMemPitch = <span class="hljs-number">0</span>;
indexData.SysMemSlicePitch = <span class="hljs-number">0</span>;

<span class="hljs-comment">// Create the index buffer.</span>
<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateBuffer</span>(&amp;indexBufferDesc, &amp;indexData,
                                   &amp;mIndexBuffer));</code></pre></div>
<h3 id="constant-buffer">Constant Buffer</h3>
<p><img src="https://alain.xyz/blog/raw-directx11/assets/constant-buffer.svg" alt="Constant Buffer Diagram"/></p>
<p>A <strong>Constant Buffer</strong> describes data that we&#x27;ll be sending to shader stages when drawing. Normally you would put Model View Projection Matrices or any specific variable data like colors, sliders, etc. here.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 💾 Declare Data</span>
<span class="hljs-keyword">struct</span>
{
    glm::mat4 projectionMatrix;
    glm::mat4 modelMatrix;
    glm::mat4 viewMatrix;
} cbVS;

<span class="hljs-comment">// 👋 declare handles</span>
ID3D11Buffer* constantBuffer;

<span class="hljs-comment">// Setup the description of the dynamic matrix constant buffer that is in the</span>
<span class="hljs-comment">// vertex shader.</span>
D3D11_BUFFER_DESC constantBufferDesc;
uniformBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
uniformBufferDesc.ByteWidth = <span class="hljs-built_in">sizeof</span>(cboVS);
uniformBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
uniformBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
uniformBufferDesc.MiscFlags = <span class="hljs-number">0</span>;
uniformBufferDesc.StructureByteStride = <span class="hljs-number">0</span>;

<span class="hljs-comment">// Create the constant buffer pointer so we can access the vertex shader</span>
<span class="hljs-comment">// constant buffer from within this class.</span>
<span class="hljs-built_in">ThrowIfFailed</span>(mDevice-&gt;<span class="hljs-built_in">CreateBuffer</span>(&amp;constantBufferDesc, <span class="hljs-literal">NULL</span>,
                                    &amp;constantBuffer));</code></pre></div>
<h3 id="vertex-shader">Vertex Shader</h3>
<p><img src="https://alain.xyz/blog/raw-directx11/assets/vertex-shader.svg" alt="Vertex Shader Diagram"/></p>
<p><strong>Vertex Shaders</strong> execute per vertex, and are perfect for transforming a given object, performing per vertex animations with blend shapes, GPU skinning, etc.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
ID3D11VertexShader* vertexShader;

ID3DBlob* vertexShaderBlob = <span class="hljs-literal">nullptr</span>;
ID3DBlob* errors = <span class="hljs-literal">nullptr</span>;

<span class="hljs-meta">#<span class="hljs-keyword">if</span> defined(_DEBUG)</span>
<span class="hljs-comment">// Enable better shader debugging with the graphics debugging tools.</span>
UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
<span class="hljs-meta">#<span class="hljs-keyword">else</span></span>
UINT compileFlags = <span class="hljs-number">0</span>;
<span class="hljs-meta">#<span class="hljs-keyword">endif</span></span>
std::string path = <span class="hljs-string">&quot;&quot;</span>;
<span class="hljs-type">char</span> pBuf[<span class="hljs-number">1024</span>];

_getcwd(pBuf, <span class="hljs-number">1024</span>);
path = pBuf;
path += <span class="hljs-string">&quot;\\&quot;</span>;
std::wstring wpath = std::<span class="hljs-built_in">wstring</span>(path.<span class="hljs-built_in">begin</span>(), path.<span class="hljs-built_in">end</span>());

std::wstring vertPath = wpath + <span class="hljs-string">L&quot;assets/shaders/triangle.vert.hlsl&quot;</span>;

<span class="hljs-keyword">try</span>
{
    <span class="hljs-built_in">ThrowIfFailed</span>(<span class="hljs-built_in">D3DCompileFromFile</span>(vertPath.<span class="hljs-built_in">c_str</span>(), <span class="hljs-literal">nullptr</span>, <span class="hljs-literal">nullptr</span>, <span class="hljs-string">&quot;main&quot;</span>,
                                     <span class="hljs-string">&quot;vs_5_0&quot;</span>, compileFlags, <span class="hljs-number">0</span>, &amp;vertexShader,
                                     &amp;errors));
}
<span class="hljs-built_in">catch</span> (std::exception e)
{
    <span class="hljs-type">const</span> <span class="hljs-type">char</span>* errStr = (<span class="hljs-type">const</span> <span class="hljs-type">char</span>*)errors-&gt;<span class="hljs-built_in">GetBufferPointer</span>();
    std::cout &lt;&lt; errStr;
}

<span class="hljs-comment">// Create the vertex shader from the buffer.</span>
<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateVertexShader</span>(vertexShaderBlob-&gt;<span class="hljs-built_in">GetBufferPointer</span>(),
                                         vertexShaderBlob-&gt;<span class="hljs-built_in">GetBufferSize</span>(),
                                         <span class="hljs-literal">NULL</span>, &amp;vertexShader));

<span class="hljs-comment">// Create the pixel shader from the buffer.</span>
<span class="hljs-built_in">ThrowIfFailed</span>(mDevice-&gt;<span class="hljs-built_in">CreatePixelShader</span>(pixelShader-&gt;<span class="hljs-built_in">GetBufferPointer</span>(),
                                         pixelShader-&gt;<span class="hljs-built_in">GetBufferSize</span>(), <span class="hljs-literal">NULL</span>,
                                         &amp;mPixelShader));
</code></pre></div>
<p>Here&#x27;s the vertex shader:</p>
<div><pre><code class="language-hlsl"><span class="hljs-keyword">cbuffer</span> cb : <span class="hljs-keyword">register</span>(b0)
{
    <span class="hljs-keyword">row_major</span> <span class="hljs-type">float4x4</span> projectionMatrix : <span class="hljs-keyword">packoffset</span>(c0);
    <span class="hljs-keyword">row_major</span> <span class="hljs-type">float4x4</span> modelMatrix : <span class="hljs-keyword">packoffset</span>(c4);
    <span class="hljs-keyword">row_major</span> <span class="hljs-type">float4x4</span> viewMatrix : <span class="hljs-keyword">packoffset</span>(c8);
};

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">VertexInput</span>
{
    <span class="hljs-type">float3</span> inPos : <span class="hljs-built_in">POSITION</span>;
    <span class="hljs-type">float3</span> inColor : <span class="hljs-built_in">COLOR</span>;
};

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">VertexOutput</span>
{
    <span class="hljs-type">float3</span> color : <span class="hljs-built_in">COLOR</span>;
    <span class="hljs-type">float4</span> position : <span class="hljs-built_in">SV_Position</span>;
};

VertexOutput main(VertexInput vertexInput)
{
    <span class="hljs-type">float3</span> inColor = vertexInput.inColor;
    <span class="hljs-type">float3</span> inPos = vertexInput.inPos;
    <span class="hljs-type">float3</span> outColor = inColor;
    <span class="hljs-type">float4</span> position = <span class="hljs-built_in">mul</span>(<span class="hljs-type">float4</span>(inPos, <span class="hljs-number">1.0</span>), <span class="hljs-built_in">mul</span>(modelMatrix, <span class="hljs-built_in">mul</span>(viewMatrix, projectionMatrix)));

    VertexOutput output;
    output.position = position;
    output.color = outColor;
    <span class="hljs-keyword">return</span> output;
}</code></pre></div>
<h3 id="pixel-shader">Pixel Shader</h3>
<p><img src="https://alain.xyz/blog/raw-directx11/assets/pixel-shader.svg" alt="Pixel Shader Diagram"/></p>
<p><strong>Pixel Shaders</strong> execute per each pixel of your output, including the other attachments that correspond to that pixel coordinate.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
ID3D11PixelShader* pixelShader;

ID3DBlob* pixelShader = <span class="hljs-literal">nullptr</span>;
ID3DBlob* errors = <span class="hljs-literal">nullptr</span>;

<span class="hljs-meta">#<span class="hljs-keyword">if</span> defined(_DEBUG)</span>
<span class="hljs-comment">// Enable better shader debugging with the graphics debugging tools.</span>
UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
<span class="hljs-meta">#<span class="hljs-keyword">else</span></span>
UINT compileFlags = <span class="hljs-number">0</span>;
<span class="hljs-meta">#<span class="hljs-keyword">endif</span></span>

std::string path = <span class="hljs-string">&quot;&quot;</span>;
<span class="hljs-type">char</span> pBuf[<span class="hljs-number">1024</span>];

_getcwd(pBuf, <span class="hljs-number">1024</span>);
path = pBuf;
path += <span class="hljs-string">&quot;\\&quot;</span>;
std::wstring wpath = std::<span class="hljs-built_in">wstring</span>(path.<span class="hljs-built_in">begin</span>(), path.<span class="hljs-built_in">end</span>());

std::wstring fragPath = wpath + <span class="hljs-string">L&quot;assets/shaders/triangle.frag.hlsl&quot;</span>;

<span class="hljs-keyword">try</span>
{
    <span class="hljs-built_in">ThrowIfFailed</span>(<span class="hljs-built_in">D3DCompileFromFile</span>(fragPath.<span class="hljs-built_in">c_str</span>(), <span class="hljs-literal">nullptr</span>, <span class="hljs-literal">nullptr</span>, <span class="hljs-string">&quot;main&quot;</span>,
                                     <span class="hljs-string">&quot;ps_5_0&quot;</span>, compileFlags, <span class="hljs-number">0</span>, &amp;pixelShader,
                                     &amp;errors));
}
<span class="hljs-built_in">catch</span> (std::exception e)
{
    <span class="hljs-type">const</span> <span class="hljs-type">char</span>* errStr = (<span class="hljs-type">const</span> <span class="hljs-type">char</span>*)errors-&gt;<span class="hljs-built_in">GetBufferPointer</span>();
    std::cout &lt;&lt; errStr;
}

<span class="hljs-comment">// Create the pixel shader from the buffer.</span>
<span class="hljs-built_in">ThrowIfFailed</span>(mDevice-&gt;<span class="hljs-built_in">CreatePixelShader</span>(pixelShader-&gt;<span class="hljs-built_in">GetBufferPointer</span>(),
                                         pixelShader-&gt;<span class="hljs-built_in">GetBufferSize</span>(), <span class="hljs-literal">NULL</span>,
                                         &amp;mPixelShader));</code></pre></div>
<p>And here&#x27;s the pixel shader:</p>
<div><pre><code class="language-hlsl"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">PixelInput</span>
{
    <span class="hljs-type">float3</span> color : <span class="hljs-built_in">COLOR</span>;
};

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">PixelOutput</span>
{
    <span class="hljs-type">float4</span> attachment0 : <span class="hljs-built_in">SV_Target0</span>;
};

PixelOutput main(PixelInput pixelInput)
{
    <span class="hljs-type">float3</span> inColor = pixelInput.color;
    PixelOutput output;
    output.attachment0 = <span class="hljs-type">float4</span>(inColor, <span class="hljs-number">1.0f</span>);
    <span class="hljs-keyword">return</span> output;
}</code></pre></div>
<h2 id="rendering">Rendering</h2>
<h3 id="pipeline-state">Pipeline State</h3>
<p><img src="https://alain.xyz/blog/raw-directx11/assets/raster-pipeline.svg" alt="Raster Pipeline Diagram"/></p>
<p>The <strong>Pipeline State</strong> describes everything necessary to execute a given raster based draw call. DirectX 11 separates each of these individual states into easy to setup up sub-states.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
ID3D11InputLayout* mLayout;
ID3D11DepthStencilState* depthStencilState;
ID3D11RasterizerState* rasterState;

<span class="hljs-comment">// ⚗️ Define the Graphics Pipeline</span>

<span class="hljs-comment">// 🔣 Input Assembly</span>
D3D11_INPUT_ELEMENT_DESC polygonLayout[<span class="hljs-number">2</span>];

polygonLayout[<span class="hljs-number">0</span>].SemanticName = <span class="hljs-string">&quot;POSITION&quot;</span>;
polygonLayout[<span class="hljs-number">0</span>].SemanticIndex = <span class="hljs-number">0</span>;
polygonLayout[<span class="hljs-number">0</span>].Format = DXGI_FORMAT_R32G32B32_FLOAT;
polygonLayout[<span class="hljs-number">0</span>].InputSlot = <span class="hljs-number">0</span>;
polygonLayout[<span class="hljs-number">0</span>].AlignedByteOffset = <span class="hljs-number">0</span>;
polygonLayout[<span class="hljs-number">0</span>].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
polygonLayout[<span class="hljs-number">0</span>].InstanceDataStepRate = <span class="hljs-number">0</span>;

polygonLayout[<span class="hljs-number">1</span>].SemanticName = <span class="hljs-string">&quot;COLOR&quot;</span>;
polygonLayout[<span class="hljs-number">1</span>].SemanticIndex = <span class="hljs-number">0</span>;
polygonLayout[<span class="hljs-number">1</span>].Format = DXGI_FORMAT_R32G32B32_FLOAT;
polygonLayout[<span class="hljs-number">1</span>].InputSlot = <span class="hljs-number">0</span>;
polygonLayout[<span class="hljs-number">1</span>].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
polygonLayout[<span class="hljs-number">1</span>].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
polygonLayout[<span class="hljs-number">1</span>].InstanceDataStepRate = <span class="hljs-number">0</span>;

<span class="hljs-comment">// Get a count of the elements in the layout.</span>
<span class="hljs-type">unsigned</span> numElements = <span class="hljs-built_in">sizeof</span>(polygonLayout) / <span class="hljs-built_in">sizeof</span>(polygonLayout[<span class="hljs-number">0</span>]);

<span class="hljs-comment">// Create the vertex input layout.</span>
<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateInputLayout</span>(polygonLayout, numElements,
                                        vertexShaderBlob-&gt;<span class="hljs-built_in">GetBufferPointer</span>(),
                                        vertexShaderBlob-&gt;<span class="hljs-built_in">GetBufferSize</span>(),
                                        &amp;layout));

<span class="hljs-comment">// 🌑 Depth/Stencil</span>
D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
<span class="hljs-built_in">ZeroMemory</span>(&amp;depthStencilDesc, <span class="hljs-built_in">sizeof</span>(depthStencilDesc));
depthStencilDesc.DepthEnable = <span class="hljs-literal">true</span>;
depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
depthStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;
depthStencilDesc.StencilEnable = <span class="hljs-literal">true</span>;
depthStencilDesc.StencilReadMask = <span class="hljs-number">0xFF</span>;
depthStencilDesc.StencilWriteMask = <span class="hljs-number">0xFF</span>;
<span class="hljs-comment">// Stencil operations if pixel is front-facing.</span>
depthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
depthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
<span class="hljs-comment">// Stencil operations if pixel is back-facing.</span>
depthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
depthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

<span class="hljs-comment">// Create the depth stencil state.</span>
<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateDepthStencilState</span>(&amp;depthStencilDesc,
                                              &amp;depthStencilState));

<span class="hljs-comment">// Set the depth stencil state.</span>
deviceContext-&gt;<span class="hljs-built_in">OMSetDepthStencilState</span>(depthStencilState, <span class="hljs-number">1</span>);

<span class="hljs-comment">// 🟨 Rasterization</span>
D3D11_RASTERIZER_DESC rasterDesc;
rasterDesc.AntialiasedLineEnable = <span class="hljs-literal">false</span>;
rasterDesc.CullMode = D3D11_CULL_NONE;
rasterDesc.DepthBias = <span class="hljs-number">0</span>;
rasterDesc.DepthBiasClamp = <span class="hljs-number">0.0f</span>;
rasterDesc.DepthClipEnable = <span class="hljs-literal">true</span>;
rasterDesc.FillMode = D3D11_FILL_SOLID;
rasterDesc.FrontCounterClockwise = <span class="hljs-literal">false</span>;
rasterDesc.MultisampleEnable = <span class="hljs-literal">false</span>;
rasterDesc.ScissorEnable = <span class="hljs-literal">false</span>;
rasterDesc.SlopeScaledDepthBias = <span class="hljs-number">0.0f</span>;

<span class="hljs-comment">// Create the rasterizer state from the description we just filled out.</span>
<span class="hljs-built_in">ThrowIfFailed</span>(device-&gt;<span class="hljs-built_in">CreateRasterizerState</span>(&amp;rasterDesc, &amp;rasterState));

<span class="hljs-comment">// Now set the rasterizer state.</span>
deviceContext-&gt;<span class="hljs-built_in">RSSetState</span>(rasterState);</code></pre></div>
<h3 id="draw-calls">Draw Calls</h3>
<p><img src="https://alain.xyz/blog/raw-directx11/assets/triangle-raster.gif" alt="Triangle Raster Gif"/></p>
<p>DirectX 11 has a more direct approach to rendering, where you tell the device context exactly what you want to do and it handles queuing up those commands to be executed.</p>
<div><pre><code class="language-cpp"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">render</span><span class="hljs-params">()</span>
</span>{
    <span class="hljs-comment">// Framelimit set to 60 fps</span>
    tEnd = std::chrono::high_resolution_clock::<span class="hljs-built_in">now</span>();
    <span class="hljs-type">float</span> time =
        std::chrono::<span class="hljs-built_in">duration</span>&lt;<span class="hljs-type">float</span>, std::milli&gt;(tEnd - tStart).<span class="hljs-built_in">count</span>();
    <span class="hljs-keyword">if</span> (time &lt; (<span class="hljs-number">1000.0f</span> / <span class="hljs-number">60.0f</span>))
    {
        <span class="hljs-keyword">return</span>;
    }
    tStart = std::chrono::high_resolution_clock::<span class="hljs-built_in">now</span>();

    <span class="hljs-comment">// Update Uniforms</span>
    elapsedTime += <span class="hljs-number">0.001f</span> * time;
    elapsedTime = <span class="hljs-built_in">fmodf</span>(elapsedTime, <span class="hljs-number">6.283185307179586f</span>);
    cbVS.modelMatrix = Matrix4::<span class="hljs-built_in">rotationY</span>(mElapsedTime);

    <span class="hljs-comment">// Set the position of the constant buffer in the vertex shader.</span>
    <span class="hljs-type">unsigned</span> <span class="hljs-type">int</span> bufferNumber = <span class="hljs-number">0</span>;

    <span class="hljs-comment">// Lock the constant buffer so it can be written to.</span>
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    <span class="hljs-built_in">ThrowIfFailed</span>(deviceContext-&gt;<span class="hljs-built_in">Map</span>(constantBuffer, <span class="hljs-number">0</span>, D3D11_MAP_WRITE_DISCARD,
                                     <span class="hljs-number">0</span>, &amp;mappedResource));

    <span class="hljs-comment">// Get a pointer to the data in the constant buffer.</span>
    <span class="hljs-built_in">memcpy</span>(mappedResource.pData, &amp;cbVS, <span class="hljs-built_in">sizeof</span>(cbVS));

    <span class="hljs-comment">// Unlock the constant buffer.</span>
    deviceContext-&gt;<span class="hljs-built_in">Unmap</span>(constantBuffer, <span class="hljs-number">0</span>);

    <span class="hljs-comment">// Finally set the constant buffer in the vertex shader with the updated</span>
    <span class="hljs-comment">// values.</span>
    deviceContext-&gt;<span class="hljs-built_in">VSSetConstantBuffers</span>(bufferNumber, <span class="hljs-number">1</span>, &amp;constantBuffer);

    <span class="hljs-comment">// Bind the render target view and depth stencil buffer to the output render</span>
    <span class="hljs-comment">// pipeline.</span>
    deviceContext-&gt;<span class="hljs-built_in">OMSetRenderTargets</span>(<span class="hljs-number">1</span>, &amp;renderTargetView, depthStencilView);

    deviceContext-&gt;<span class="hljs-built_in">RSSetViewports</span>(<span class="hljs-number">1</span>, &amp;viewport);

    <span class="hljs-comment">// Clear textures</span>
    <span class="hljs-type">float</span> color[<span class="hljs-number">4</span>] = {<span class="hljs-number">0.2f</span>, <span class="hljs-number">0.2f</span>, <span class="hljs-number">0.2f</span>, <span class="hljs-number">1.0f</span>};
    deviceContext-&gt;<span class="hljs-built_in">ClearRenderTargetView</span>(renderTargetView, color);

    <span class="hljs-comment">// Clear the depth buffer.</span>
    deviceContext-&gt;<span class="hljs-built_in">ClearDepthStencilView</span>(depthStencilView, D3D11_CLEAR_DEPTH,
                                         <span class="hljs-number">1.0f</span>, <span class="hljs-number">0</span>);

    <span class="hljs-comment">// Set the vertex input layout.</span>
    deviceContext-&gt;<span class="hljs-built_in">IASetInputLayout</span>(inputLayout);

    <span class="hljs-comment">// Set the vertex and pixel shaders that will be used to render this</span>
    <span class="hljs-comment">// triangle.</span>
    deviceContext-&gt;<span class="hljs-built_in">VSSetShader</span>(vertexShader, <span class="hljs-literal">NULL</span>, <span class="hljs-number">0</span>);
    deviceContext-&gt;<span class="hljs-built_in">PSSetShader</span>(pixelShader, <span class="hljs-literal">NULL</span>, <span class="hljs-number">0</span>);

    <span class="hljs-comment">// Set the vertex buffer to active in the input assembler so it can be</span>
    <span class="hljs-comment">// rendered.</span>
    <span class="hljs-type">unsigned</span> stride = <span class="hljs-built_in">sizeof</span>(Vertex);
    <span class="hljs-type">unsigned</span> offset = <span class="hljs-number">0</span>;
    deviceContext-&gt;<span class="hljs-built_in">IASetVertexBuffers</span>(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, &amp;vertexBuffer, &amp;stride, &amp;offset);

    <span class="hljs-comment">// Set the index buffer to active in the input assembler so it can be</span>
    <span class="hljs-comment">// rendered.</span>
    deviceContext-&gt;<span class="hljs-built_in">IASetIndexBuffer</span>(indexBuffer, DXGI_FORMAT_R32_UINT, <span class="hljs-number">0</span>);

    <span class="hljs-comment">// Set the type of primitive that should be rendered from this vertex</span>
    <span class="hljs-comment">// buffer, in this case triangles.</span>
    deviceContext-&gt;<span class="hljs-built_in">IASetPrimitiveTopology</span>(
        D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    <span class="hljs-comment">// Render the triangle.</span>
    deviceContext-&gt;<span class="hljs-built_in">DrawIndexed</span>(<span class="hljs-number">3</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

    <span class="hljs-keyword">if</span> (vsync)
    {
        swapchain-&gt;<span class="hljs-built_in">Present</span>(<span class="hljs-number">1</span>, <span class="hljs-number">0</span>);
    }
    <span class="hljs-keyword">else</span>
    {
        swapchain-&gt;<span class="hljs-built_in">Present</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
    }
}</code></pre></div>
<h2 id="conclusion">Conclusion</h2>
<p>DirectX 11 is a much easier to use graphics API than other modern APIs like DirecX 12, Vulkan, Metal, or WebGPU. That ease of use comes at the cost of some performance, but it&#x27;s a great start to learning how to engineer graphics applications.</p>
<h3 id="more-resources">More Resources</h3>
<ul>
<li>
<p><a href="https://docs.microsoft.com/en-us/windows/win32/direct3d11/atoc-dx-graphics-direct3d-11">The DirectX 11 Programming Guide</a></p>
</li>
<li>
<p><a href="https://github.com/microsoft/DirectXTK">The DirectX Toolkit</a>, a set of tools to help with loading textures, gamepad input, memory allocation, and much more.</p>
</li>
<li>
<p>I wrote a <a href="https://alain.xyz/blog/raw-directx12">blog post on DirectX 12</a> as well which is worth reviewing even if you plan on developing your project for D3D11.</p>
</li>
</ul>]]></content:encoded>
</item>
<item>
<title>Fintech Data Engineering</title>
<link>https://alain.xyz/blog/fintech-data-engineering</link>
<description>A discussion on the state of financial markets and techniques to for building data driven portfolio management systems; detailing stocks, forex, crypto, and agriculture markets and their nuances.</description>
<guid isPermaLink="true">https://alain.xyz/blog/fintech-data-engineering</guid>
<pubDate>Wed, 11 Aug 2021 00:30:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>fintech</category><category>stocks</category><category>crypto</category>
<enclosure url="https://alain.xyz/blog/fintech-data-engineering/assets/cover.jpg" length="78742" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/fintech-data-engineering/assets/amd-2017-2020.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/fintech-data-engineering/assets/doge-2021.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/fintech-data-engineering/assets/redfin-median.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/fintech-data-engineering/assets/florida-cattle.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/fintech-data-engineering/assets/level2-hood.svg"/><blockquote>
<p>This is not financial advice. I am not a professional financial advisor. This is my own opinion and observations.</p>
</blockquote>
<p>Financial market forces can appear chaotic and unpredictable. It&#x27;s as though the eyes of the public are obscured, and with more perfect information on purchases, buyers, sellers, and existing portfolios, we can better predict global markets. There are however some visible parts to this metaphorical blindfold.</p>
<p><em>Index funds</em> can give an overview of the health of a given industry, <em>Level 2 data</em> gives individual traders access to current market asks/bids, CEOs and government officials must <em>disclose their purchases and sales</em> and their assets are public knowledge, and quarterly earnings reports provide a top level view of the company&#x27;s finances.</p>
<p>Every bank is reinvesting the money deposited in your account in the form of assets such as <em>securities (stocks/bonds)</em>, <em>foreign exchange (Forex)</em>, <em>mortgages</em>, <em>loans</em>, <em>leases</em>, and <em>credit</em>, and making significant returns on the money you deposit.</p>
<p>Government monetary policy drives inflation, with interest rate control being so powerful it was able to reverse a <a href="https://en.wikipedia.org/wiki/2020_stock_market_crash">potential crash in the stock market in March 2020</a>. Governments can align their monetary policy towards an end goal, such as China&#x27;s interest in <a href="https://www.bloomberg.com/news/newsletters/2024-02-03/bloomberg-new-economy-the-promise-and-peril-of-china-s-strong-yuan-policy">maintaining the renminbi pegged against the US dollar</a> to better compete in the international export market.</p>
<p><img src="https://alain.xyz/blog/fintech-data-engineering/assets/amd-2017-2020.svg" alt="AMD Stock Chart by Google Finance"/></p>
<p>Market forces and monetary policy come together to bring impressive market trends such as <code>$AMD</code> multiplying its value 10 fold between 2017 and 2021, with the value of tech stocks such as <code>$NVDA</code> and <code>$MSFT</code> following similarly.</p>
<p><img src="https://alain.xyz/blog/fintech-data-engineering/assets/doge-2021.svg" alt="DOGE Chart by Coinbase"/></p>
<p>Digital assets such as the cryptocurrency DogeCoin <code>$DOGE</code> have gone from <code>$0.005</code> to as high as <code>$0.69</code> over the period of 5 months multiplied in value <strong>138 times</strong> at the start of 2021. Tesla <code>$TSLA</code>had a <em>bull run</em> at the start of May 2019 where the value skyrocketed from a low of<code>$160</code>, to over <code>$1000</code>, then had a 4-1 split and followed a similar trend, with the value hitting as high as <code>$760</code> in April 2021, multiplying in value <strong>17 times</strong>. Option trading has seen a boom over the course of the past few years, and publicly available data on currently traded puts and calls allowed for the famous <em>short squeeze</em> on GameStop <code>$GME</code> in late 2020, which caused the price to skyrocket from <code>$4</code> all the way to <code>$325</code>, multiplying in value <strong>81 times</strong>.</p>
<p><img src="https://alain.xyz/blog/fintech-data-engineering/assets/redfin-median.svg" alt="US Median House by Redfin"/></p>
<p>Stock market forces echoes into other markets, with the <a href="https://www.tradingview.com/symbols/NASDAQ-NQUSB35/">Nasdaq US Benchmark Real Estate Index</a> showing a spike following the COVID 19&#x27; pandemic, and the <a href="https://www.tradingview.com/symbols/ECONOMICS-USHPI/">United States House Price Index</a> showing only 1 direction for prices, up. The value of <em>construction assets</em> is increasing, and the <em>price of building a house</em> reaching an average <code>$120</code> a square foot in 2021. <em>Land prices</em> in the South Florida area increased suddenly, with a 10,000 square foot lot averaging <code>$40,000</code> last year <strong>doubling</strong> in price in 2021, with this price remaining stable to 2024. The increase in online sales of housing has shifted the role of the traditional real estate agent becoming less necessary for some markets, leaving open a margin of 3-10% of the price of the sale to the seller. Real Estate mortgages had seen a drop in interest rates, with 3% rates becoming commonplace, though recently they&#x27;ve stablized to around 5-6% as the federal reserve adjusted their rate to combat inflation.</p>
<p><img src="https://alain.xyz/blog/fintech-data-engineering/assets/florida-cattle.svg" alt="Cattle Auction Chart by Cattle.com / USDA"/></p>
<p>These echos can also be found in <em>agricultural markets</em>. The average price per hundred pounds per slaughter and feeder cattle has also seen an upward trend. The value of investing in cattle to grow (feeder cattle) in the Okeechobee Cattle Auction has increased over the past year, benefiting farmers with pregnant cattle. Recently, steers of feeder cattle averaged 370 pounds and cost <code>$168</code> per hundred pounds (<code>$622</code>). Breaker cattle have seen an average price of <code>$60</code> per hundred pounds and a weight average of 1400 pounds (<code>$840</code>). Milk cows have become much more valuable based on their genetic score in professional dairy operations, with sales reaching as high as <code>$200K</code> in private auctions hosted by the <a href="">University of Michigan</a>.</p>
<p>These increases in asset value make sense given the federal response to the global pandemic made <em>loaning money incredibly cheap and easy</em>. Small businesses receiving federal loans of hundreds of thousands at low interest rates is <em>a recipe for an asset bull run</em>. Anecdotally there have been rumors of widespread fraud in employee income assistance programs and in requesting investment loans from the US government, which only exacerbated this problem as individuals <a href="https://www.justice.gov/usao-sdfl/pr/three-miami-residents-charged-covid-19-pandemic-relief-fraud">waste this money on sports cars and more property</a>.</p>
<p><img src="https://alain.xyz/blog/fintech-data-engineering/assets/level2-hood.svg" alt="Level 2 Robinhood Data by Robinhood / NYSE"/></p>
<p>The <em>democratization of data</em> makes it possible for retail investors that have a mind towards software engineering to build tools to solve financial problems and make more educated financial decisions. Publicly available data on floating ask/bids in real time through <strong>Level 2 Data</strong> provides a <a href="https://blog.robinhood.com/news/2019/6/17/nasdaq-level-2-market-data-is-here">real time view of puts and calls for stocks traded in the New York Stock Exchange (NYSE)</a>, and
can enable a rough prediction on the short term behavior of a given asset. A stock&#x27;s <strong>Price to Earnings ratio (P/E ratio)</strong>, price history, trading volume, ask/bid history, derivatives behavior (options such as <strong>Calls</strong> and <strong>Puts</strong>), and earnings reports can help with determining long and short positions on a stock. and Real estate data such as property sales prices and regional market trends can allow buyers to better predict the price of their asset in the future, estimate the return on investment (ROI) with rental investments in agriculture/residential/commercial markets, and be aware of the overall history of a given region.</p>
<p>There&#x27;s a scary underside to all this data. <em>Public ownership records</em> from real estate appraisals make it possible to estimate the net worth of an individual or business, information that could be used in cruel ways like offering less to new employees or estimating credit scores more harshly. Cryptocurrency public addresses associated with companies like Tesla make it possible to see exactly how much money they made by switching 10% of their cash to Bitcoin and communities like Twitter make it easier than ever for individuals to make big impacts on markets by simply tweeting about <code>$DOGE</code> and <code>$BTC</code> such as certain tech billionaires:</p>
<p>Let&#x27;s discuss financial engineering in asset trading in brief detail.</p>
<h2 id="algorithmic-trading">Algorithmic Trading</h2>
<p>There are public algorithmic trading tools that allow for you to <em>simulate positions</em> with a given trading bot or outright use those positions in a retail trading API.</p>
<p><strong>Back Testing</strong> is using prior asset market trends to test portfolio positions/changes and predict where those positions would be today. You can use a strategy tester or a basic asset portfolio tester to determine what your wealth would have been had you made a financial decision years ago.</p>
<h2 id="crypto-trading">Crypto Trading</h2>
<p>There&#x27;s a lot of good things that cryptocurrencies can enable, suddenly transferring thousands of dollars instantly and securely (at least as long as (P \neq NP)) means you can easily transfer money to anyone for any sort of transaction, be it payroll, asset purchases, or simply buying common household goods.</p>
<p>Computational cryptocurrencies such as Etherium <code>$ETH</code> allow for programmable contracts that can allow unique identification for ownership of a given asset non-fungible tokens (NFTs), programmable loans, and more.</p>
<p>That freedom also enables <em>bad behavior</em>, from the wasted electricity that the global power grid now needs to support due to mining, <a href="https://www.youtube.com/watch?v=WqD-ATqw3js">paying ransomware</a> or for illegal items, money laundering (With some NFT art sales serving that purpose), or gambling.</p>
<h2 id="more-resources">More Resources</h2>
<p>Here&#x27;s a few sites that focus on stock information and metric data:</p>
<ul>
<li>
<p><a href="https://www.quiverquant.com/sources/senatetrading">Quiver Quantitative</a> exposes public information such as senator stock trades, Robinhood user behavior, r/wallstreetbets trends, etc. in an easy to read interface.</p>
</li>
<li>
<p><a href="https://www.portfoliovisualizer.com/backtest-portfolio">Portfolio Visualizer</a> let&#x27;s you backtest a portfolio of stocks, in other words, test if a portfolio would have done well if you bought it X number of years ago.</p>
</li>
<li>
<p><a href="https://www.tradingview.com">TradingView</a> - Asset visualizations, backtesting strategies, and every indicator you can imagine.</p>
</li>
<li>
<p><a href="https://dividend.watch/tracker">Dividend.Watch</a> - Track portfolios, score the dividend performance of given stocks.</p>
</li>
<li>
<p><a href="https://Hawksight.co">HawkSight</a> offers an easy way to try back testing. It&#x27;s a bit like a game, have fun imagining the possibilities.</p>
</li>
<li>
<p><a href="https://financialmodelingprep.com">Financial Modeling Prep</a> is more of a back testing + machine learning training community.</p>
</li>
<li>
<p><a href="https://unusualwhales.com/">Unusual Whales</a> provides a history of puts/calls and open interest.</p>
</li>
</ul>
<p>For algorithmic trading there&#x27;s a number of exchanges and apis with a wide range of assets to trade/study:</p>
<ul>
<li>
<p><a href="https://polygon.io/">Polygon</a> - Real time stock data.</p>
</li>
<li>
<p><a href="https://developer.webull.com/api-doc/">Webull&#x27;s Quantitative OpenAPI</a></p>
</li>
<li>
<p><a href="https://alpaca.markets/">Alpaca Markets</a> - An alternative API for algorithmic trading.</p>
</li>
<li>
<p><a href="https://www.quantconnect.com/">QuantConnect</a> - Open source algorithmic trading.</p>
</li>
<li>
<p><a href="https://www.interactivebrokers.com/en/trading/ib-api.php">Interactive Brokers API</a> - A solid API for algorithmic trading.</p>
</li>
<li>
<p><a href="https://docs.kraken.com/rest/">Kraken API</a></p>
</li>
<li>
<p><a href="https://developers.coinbase.com/docs/exchange/">Coinbase API</a></p>
</li>
</ul>
<p>In addition, there&#x27;s a few miscellaneous sites and articles:</p>
<ul>
<li>
<p><a href="https://www.marketwatch.com">MarketWatch</a></p>
</li>
<li>
<p><a href="https://blog.demofox.org/2024/03/17/what-i-wish-i-knew-about-espp-and-rsus-sooner-company-stock-benefits-2024-usa-california/">What I wish I knew about ESPP and RSUs sooner (company stock benefits. 2024, USA, California)</a></p>
</li>
<li>
<p><a href="https://web3isgoinggreat.com/">Web3 is going just great</a> is a website by Molly White (<a href="https://twitter.com/molly0xFFF">@molly0xFFF</a>) that covers the failures of cryptocurrencies.</p>
</li>
<li>
<p><a href="https://github.com/KonishchevDmitry/investments">Investments</a> - A CLI pet project for managing stock portfolios. While not too useful here in the US, it does serve as a good reference for fintech engineering.</p>
</li>
<li>
<p><a href="https://github.com/maybe-finance/maybe">Maybe</a> - A financial tracking agrigator tool to manage portfolios and expenses.</p>
</li>
<li>
<p><a href="https://www.wealthfront.com/">Wealthfront</a> is an automated portfolio manager for US and foreign stocks.</p>
</li>
<li>
<p><a href="https://investor.vanguard.com/">Vanguard</a> is a popular investment manager transitioning to automation.</p>
</li>
<li>
<p><a href="https://play.google.com/store/books/details?pcampaignid=books_read_action&amp;id=x5Q308_4S3kC">Boomerang: Travels in the New Third World</a> covers the history behind financial markets and their subsequent crashes throughout the turn of the 2nd millenium.</p>
</li>
</ul>]]></content:encoded>
</item>
<item>
<title>Frame Analysis - Control</title>
<link>https://alain.xyz/blog/frame-analysis-control</link>
<description>A review of the rendering architecture of Control, reviewing their ray tracer, rendering architecture, and technical art direction.</description>
<guid isPermaLink="true">https://alain.xyz/blog/frame-analysis-control</guid>
<pubDate>Fri, 11 Jun 2021 19:00:34 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>game engine</category><category>frame</category><category>analysis</category><category>render</category><category>pbr</category><category>prepass</category><category>control</category>
<enclosure url="https://alain.xyz/blog/frame-analysis-control/assets/cover.jpg" length="1114338" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-control/assets/shadow-depth.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-control/assets/view-space-normals-noedge.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-control/assets/roughness.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-control/assets/subsurface-scattering.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-control/assets/metalness.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-control/assets/view-space-normals.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-control/assets/velocity.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-control/assets/bent-normals.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-control/assets/reflection-1spp.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-control/assets/gi-1spp.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-control/assets/shadows-1spp.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-control/assets/fluid-velocity.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-control/assets/fluid-pressure.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-control/assets/fluid-divergence.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-control/assets/dlss.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-control/assets/ui.jpg"/><link rel="preload" as="image" href="https://github.com/cschied.png"/><iframe width="100%" height="512" src="https://www.youtube.com/embed/hxPQ2F96F9E" frameBorder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowFullScreen=""></iframe>
<p>Control is a metaphysical 3rd person shooter by <a href="https://www.remedygames.com/games/control/">Remedy Entertainment</a>, creators of <a href="https://www.remedygames.com/games/quantumbreak/">Quantum Break</a> and <a href="https://www.remedygames.com/games/alan-wake/">Alan Wake</a>. You play as <strong>Ms. Jesse Faden</strong>, who finds herself in the <em>Federal Bureau of Control</em>, and upon discovering an odd gun and apparent suicide of the previous director, assumes the role of new Director of the FBC by <em>the Board</em>, a god-like group reminiscent of the employers from titles like <a href="https://www.half-life.com/en/alyx/">Valve&#x27;s Half Life</a>. She&#x27;s tasked to ridding the FBC from a metaphysical force she called the Hiss (which appears to be a force similar to the Dark Presence in Alan Wake) to both please <em>the Board</em> and further her own goals.</p>
<p>Control&#x27;s gameplay loop is similar to that of <a href="https://www.playstation.com/en-us/games/bloodborne/">From Software&#x27;s Bloodbore</a> or <a href="https://bethesda.net/game/doom">Id Software&#x27;s Doom Eternal</a>, with a strong emphasis on moving to dodge your enemies attacks and using abilities granted to you by <em>objects of power</em> (or OoPs) to both improve your combat abilities and navigate the world like a <em>Metroidvania</em>. Possessed enemies have similar combat abilities that they use to attack and lock off sections of the world.</p>
<p>Visually, the game looks stunning, with <em>real time ray traced shadows</em>, <em>ray traced global illumination</em>, <em>ray traced reflections</em>, <em>Deep Learning Super Sampling</em>, volumetric effects, and carefully tuned environmental effects like broken glass, and office debris. Let&#x27;s analyze exactly what&#x27;s happening in a single frame of Control, at its highest settings at <code>4K</code> with Deep Learning Super Sampling enabled.</p>
<h2 id="frame-analysis">Frame Analysis</h2>
<blockquote>
<p>⚠️ <strong>Note</strong>: This is not an official analysis of the <strong>Northlight Engine</strong> renderer. Please support <em>Control Ultimate Edition</em> by grabbing a copy of it via Steam, Playstation Store, Xbox Store, or the Nintendo Store, thanks!</p>
</blockquote>
<p>We&#x27;ll be doing a high level analysis of a somewhat still frame mid combat in an area relatively early in the game, with plenty of lights, reflections, and global illumination.</p>
<h3 id="frame-setup">Frame Setup</h3>
<p>Each frame starts with setting up various data structures. Some early Draw calls build <em>texture atlases</em> for various UI elements, generate <strong>Acceleration Data Structures</strong> for the scene and Bounding Volume Hierarchies (BVHs) for lights.</p>
<h3 id="shadow-depth-pass">Shadow Depth Pass</h3>
<p><img src="https://alain.xyz/blog/frame-analysis-control/assets/shadow-depth.jpg" alt="Shadow Depth Screenshot"/></p>
<p>A <strong>light shadow depth pass</strong> renders to a large <code>16384x2048</code> <em>texture atlas</em>. Each segment of the atlas is relatively small, about <code>512x512</code> with the smallest being <code>256x256</code>. While it might seem odd that this is here given that ray traced shadows are enabled, both ray traced shadows <strong>and</strong> raster based shadows are used to resolve shadows in Control, with ray traced shadows used for select lights as a means of better resolving contact shadows.</p>
<h3 id="prepass">Prepass</h3>
<table><thead><tr><th style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-control/assets/view-space-normals-noedge.jpg" alt="Prepass 0 RG channels Screenshot"/></th><th style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-control/assets/roughness.jpg" alt="Prepass 0 B channels Screenshot"/></th></tr></thead><tbody><tr><td style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-control/assets/subsurface-scattering.jpg" alt="Prepass 1 R channels Screenshot"/></td><td style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-control/assets/metalness.jpg" alt="Prepass 1 G channels Screenshot"/></td></tr></tbody></table>
<p>The <strong>Prepass</strong> for Control renders <em>view space normals</em> with the <code>b</code> channel being used for edge softening/contact edges for corners, and a second render attachment encodes <em>subsurface data</em> in the <code>r</code> channel and <em>roughness</em> in <code>g</code>.</p>
<p><img src="https://alain.xyz/blog/frame-analysis-control/assets/view-space-normals.jpg" alt="Prepass RG channels Screenshot"/></p>
<p>There&#x27;s a second shadow and prepass step as well for smaller objects as well and to apply beveled edge effects to objects in the environment.</p>
<p><img src="https://alain.xyz/blog/frame-analysis-control/assets/velocity.jpg" alt="Velocity Screenshot"/></p>
<p>Velocity is encoded in its own separate pass afterwards. This appears to be NDC based velocity, encoded to an <code>R16G16_FLOAT</code> texture.</p>
<h3 id="lighting">Lighting</h3>
<p><img src="https://alain.xyz/blog/frame-analysis-control/assets/bent-normals.jpg" alt="Bent Normals Screenshot"/></p>
<p>The <strong>lighting passes</strong> begins with a few <code>Dispatch</code> calls to calculate bent normals, then renders reflections, global illumination, and shadows with <code>DispatchRays</code>.</p>
<p><img src="https://alain.xyz/blog/frame-analysis-control/assets/reflection-1spp.jpg" alt="Reflection Screenshot"/></p>
<p><strong>Ray traced reflections</strong> renders at full resolution with samples denoised shortly after rendering. Reflected rays are based on the GGX specular bidirectional reflectance distribution function (BRDF). Control takes advantage of some approximation effects to help reduce the cost of evaluation, by only using reflected rays based on how far they are from the current surface - and that surface&#x27;s roughness. <a href="#ref_mars2021">[Sjöholm et al. 2021]</a></p>
<p><img src="https://alain.xyz/blog/frame-analysis-control/assets/gi-1spp.jpg" alt="Global Illumination Screenshot"/></p>
<p><strong>Ray Traced Global Illumination</strong> renders in a single pass at half the native resolution (so <code>960x560</code>), and is then denoised. Control uses precomputed voxel based global illumination performed with an offline path tracer based on static objects, which helps with resolving global illumination with objects far away. Near field indirect global illumination is used based on an object&#x27;s BRDF for surfaces relatively close by.</p>
<p>This pass functions as indirect diffuse lighting as well as a kind of ambient occlusion, since the precomputed GI values are used when rays miss.</p>
<p><img src="https://alain.xyz/blog/frame-analysis-control/assets/shadows-1spp.jpg" alt="Shadow Resolve Screenshot"/></p>
<p>Finally, <strong>Contact Shadows</strong> are rendered with the red channel representing spotlight index, and the green channel representing the point lights index of the current shadow sample.</p>
<p>All the denoising for these passes use data reprojected from previous frames and blurred with a <em>bilateral filter</em>, though each denoising pass is tuned slightly differently. Control uses a variant of the <strong>Spatio-temporal Variance Guided Filter</strong> (SVGF) <a href="#ref_schied2017">[Schied 2017]</a>, with a <em>firefly filter</em> and <em>spatial filter</em> that is based on 4 pixels around a given sample. There&#x27;s some issues with reprojecting at times, and you can occasionally catch the difference between regions with high accumulation and regions with low. There&#x27;s also some noise dancing in the end result but it appears really charming and adds to the game&#x27;s ascetic.</p>
<p>Lighting is then composited to a single render target on a per object basis to overlay material data like albedo, and even volumetric lighting contributions. The image is finally tone mapped.</p>
<h3 id="technical-art-passes">Technical Art Passes</h3>
<p>Control is full of technical art render passes that help it achieve it&#x27;s overall look.</p>
<p>There&#x27;s an <strong>enemy pass</strong>, where enemies are overlaid with various effects such as shields, which is also blurred to then give it the appearance of glowing.</p>
<table><thead><tr><th style="text-align:center">Velocity</th><th style="text-align:center">Pressure</th><th style="text-align:center">Divergence</th></tr></thead><tbody><tr><td style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-control/assets/fluid-velocity.jpg" alt="Fluid Velocity Screenshot"/></td><td style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-control/assets/fluid-pressure.jpg" alt="Fluid Pressure Screenshot"/></td><td style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-control/assets/fluid-divergence.jpg" alt="Fluid Divergence Screenshot"/></td></tr></tbody></table>
<p>This is followed by a <strong>fluid smoke pass</strong>, probably the highlight of the renderer&#x27;s technical artistry. There&#x27;s a feedback loop that uses <em>view space depth</em>, a <em>fluid buffer</em> that consists of the current frame, a <em>fluid velocity</em> buffer that changes over a few <code>DrawInstanced</code> calls, <em>geometric velocity</em>, <em>fluid divergence</em>, and <em>fluid pressure</em> buffers to warp previous and current samples in real time, resulting in a screen space feedback effect that looks like rainbow gas. It&#x27;s reminiscent of <a href="https://www.shadertoy.com/view/MdSczK">ShaderToy</a>s that emulate gaseous flow. <a href="#ref_mcaloon2019">[McAloon 2019]</a></p>
<p>A separate pass uses this fluid warped render pass with a hiss mask to result in a final output image.</p>
<h3 id="deep-learning-super-sampling">Deep Learning Super Sampling</h3>
<p><img src="https://alain.xyz/blog/frame-analysis-control/assets/dlss.png" alt="LDSS Screenshot"/></p>
<p>Deep learning Super Sampling (DLSS) takes in a smaller input image, and upscales it to a target resolution. In this case, it took a <code>1080p</code> input image and upscaled it to <code>4K</code>. This is how the game is able to average above 30 fps at <code>4K</code> on the 2070 Super.</p>
<h3 id="ui-generation">UI Generation</h3>
<p>While this screenshot doesn&#x27;t include it, first a Sobol outline pass is overlaid on top of the final composite for objects that you can control or interact with.</p>
<p><img src="https://alain.xyz/blog/frame-analysis-control/assets/ui.jpg" alt="UI Screenshot"/></p>
<p>UI particles such as health chips are rendered piecemeal by drawing a billboard, and UI elements such as your health are rendered piecemeal as well in screen space. During my testing I found some of the UIs would draw at the same time, like text elements for your objectives. Given that Control is using <a href="https://coherent-labs.com/products/coherent-gameface/">Coherent Labs</a>&#x27;s HTML5 based <strong>Gameface</strong> Renderer for its user interface this makes sense, as the browser renderer could execute all of those draw calls with the CPU.</p>
<p>Finally the frame is finished and presented to the player.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Control was a really fun game, the <em>metroidvania style level traversal</em>, <em>a variety of unlockables</em>, and the mysterious, <em>government controlled setting</em> made for fun gameplay and a fascinating story. If you have any suggestions for a game to analyze, please let me know in the comments below or via Twitter.</p>
<h3 id="more-resources">More Resources</h3>
<ul>
<li>
<p>Daniele Giannetti released a <a href="https://www.gdcvault.com/play/1027657/One-Frame-in-Halo-Infinite">GDC Talk that covers in great detail a single frame of Halo Infinite</a>/</p>
</li>
<li>
<p>Angelo Pesce (<a href="https://twitter.com/kenpex">@kenpex</a>) wrote a blog post on <a href="http://c0de517e.blogspot.com/2020/12/hallucinations-re-rendering-of.html">Cyberpunk 2077&#x27;s Renderer</a>.</p>
</li>
<li>
<p>Emilio López (<a href="https://twitter.com/redorav">@redorav</a>) wrote about the new Unreal Engine 5 renderer in his post: <a href="http://www.elopezr.com/a-macro-view-of-nanite/">A Macro View of Nanite</a>.</p>
</li>
<li>
<p><a href="https://developer.nvidia.com/ray-tracing-gems-ii">Chapter 46 of Ray Tracing Gems 2</a> discusses the ray tracer in Control in far more detail, the denoising section is particularly interesting.</p>
</li>
<li>
<p>NVIDIA has released an SDK to integrate Deep Learning Super Sampling <a href="https://developer.nvidia.com/dlss-getting-started">here</a>. AMD&#x27;s <a href="https://github.com/GPUOpen-Effects/FidelityFX-FSR">Fidelity FX Super resolution</a> is also available for developers to integrate into their applications.</p>
</li>
</ul>
<p>I&#x27;ve also released other articles describing the renderers of different titles:</p>
<ul>
<li>
<p><a href="https://alain.xyz/blog/frame-analysis-minecraftrtx">Minecraft RTX</a></p>
</li>
<li>
<p><a href="https://alain.xyz/blog/frame-analysis-mk11">Mortal Kombat 11</a></p>
</li>
<li>
<p><a href="https://alain.xyz/blog/frame-analysis-overwatch">Overwatch 1</a></p>
</li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_mars2021"><span class="markademic-citename">[Sjöholm et al. 2021]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Ray Tracing in Control</span></strong></em><br/><span class="markademic-authors"><span><span>Juha Sjöholm</span></span><span>, <span>Paula Jukarainen</span></span><span> and <span>Tatu Aalto</span></span></span><br/><span><span class="markademic-publisher">Apress 2021</span><br/></span><span><a href="https://developer.nvidia.com/ray-tracing-gems-ii">developer.nvidia.com</a></span></div></td></tr><tr><td id="ref_schied2017"><span class="markademic-citename">[Schied 2017]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Spatiotemporal Variance-Guided Filtering</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/cschied.png" alt="Christoph Schied&#x27;s picture"/><a href="https://github.com/cschied">Christoph Schied</a> (<a href="https://twitter.com/c_schied">@c_schied</a>)</span></span><br/><span><span class="markademic-publisher">ACM 2017</span><br/></span><span><a href="http://research.nvidia.com/publication/2017-07_Spatiotemporal-Variance-Guided-Filtering%3A">research.nvidia.com</a></span></div></td></tr><tr><td id="ref_mcaloon2019"><span class="markademic-citename">[McAloon 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">A closer look at Control&#x27;s reality-bending VFX</span></strong></em><br/><span class="markademic-authors"><span><span>Alissa McAloon</span></span></span><br/><span><span class="markademic-publisher">Gamasutra 2019</span><br/></span><span><a href="https://www.gamasutra.com/view/news/351829/A_closer_look_at_Controls_realitybending_VFX.php">gamasutra.com</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Intro to Toolbag Custom Shaders</title>
<link>https://alain.xyz/blog/intro-toolbag-custom-shaders</link>
<description>An introduction to writing your own custom shaders using Marmoset Toolbag. Create custom shaders for material behavior or screen space effects.</description>
<guid isPermaLink="true">https://alain.xyz/blog/intro-toolbag-custom-shaders</guid>
<pubDate>Thu, 03 Jun 2021 00:30:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>marmoset</category><category>toolbag</category><category>shaders</category><category>custom</category>
<enclosure url="https://alain.xyz/blog/intro-toolbag-custom-shaders/assets/cover.jpg" length="165686" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/intro-toolbag-custom-shaders/assets/custom-shader.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/intro-toolbag-custom-shaders/assets/material-settings.jpg"/><p>Some artistic visions are hard to achieve without some level of engineering. Creating detailed characters and environments can benefit from using procedural materials to avoid visual repetition and speed up their design. Simulating natural phenomena like broken glass, water caustics <a href="#ref_brinck2021">[Brinck et al. 2021]</a>, newton&#x27;s rings, or stylistic effects like volumetric hue shifting from Pixar&#x27;s Soul requires some understanding of the fundamental design of a rendering system.</p>
<p>Let&#x27;s discuss writing custom shaders with <strong>Marmoset Toolbag 4</strong>. We&#x27;ll approach this from the perspective of rendering a raymarched scene, similar to what you might expect from a <a href="https://www.shadertoy.com/">ShaderToy</a>.</p>
<p>The goal here will be to use this raymarched scene as a <em>generated background</em> for our scene, but it&#x27;s possible to take this a step further, such as <a href="https://alain.xyz/research/realtime-celestial-rendering">using a raymarched scene as an environment map</a>.</p>
<h2 id="general-design">General Design</h2>
<p>So the example we&#x27;ll be using is an effect I made that was heavily inspired by the YouTube channel <a href="https://www.youtube.com/user/marquesbrownlee">MKBHD</a>.</p>
<iframe height="320" style="width:100%" scrolling="no" title="Raymarching Bouncing Spheres" src="https://codepen.io/alaingalvan/embed/VweVdmV?height=320&amp;theme-id=default&amp;default-tab=result" frameBorder="no" loading="lazy" allowtransparency="true" allowFullScreen=""></iframe>
<p>The design here isn&#x27;t too complicated, we use our own camera to ray march a scene of repeating spheres <a href="#ref_inigo2013">[Quílez 2013]</a> that shift and move according to sinusoidal functions.</p>
<h2 id="marmoset-toolbag">Marmoset Toolbag</h2>
<p><img src="https://alain.xyz/blog/intro-toolbag-custom-shaders/assets/custom-shader.jpg" alt="Custom Subroutine Screenshot"/></p>
<p>Every material in Toolbag can have a <em>Custom</em> subroutine attached to it, which can then use a shader file to execute your shader. Custom shaders are executed after all the other logic for rendering has occurred, and can be used to overload that logic with your own behavior.</p>
<h3 id="creating-ui-parameters">Creating UI Parameters</h3>
<p>If you want uniforms to be controllable within the material settings UI, expose a uniform with a comment describing that uniform with the following format:</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🎨 Color</span>
<span class="hljs-keyword">uniform</span> <span class="hljs-type">vec3</span> uColor; <span class="hljs-comment">//color  name &quot;My Color&quot;  default 0.5,0.5,0.5</span>

<span class="hljs-comment">// 🗃️ Dropdown</span>
<span class="hljs-keyword">uniform</span> <span class="hljs-type">int</span> uDropdown; <span class="hljs-comment">//name &quot;My Dropdown&quot;  default 0  labels &quot;Option 1&quot; &quot;Option 2&quot; &quot;Option 3&quot;</span>

<span class="hljs-comment">// 🎚️ Slider</span>
<span class="hljs-keyword">uniform</span> <span class="hljs-type">float</span>   uSlider; <span class="hljs-comment">//name &quot;My Slider&quot;  default 1.0  min 0  max 10</span></code></pre></div>
<h3 id="overloading-a-render-pass">Overloading a Render Pass</h3>
<p>Toolbag has a series of categorized executable steps called <em>Subroutines</em>. Each render pass of Toolbag can use a different set of subroutines, allowing toolbag to easily <em>permute the material shader</em> to match the needs of that render pass. <a href="#ref_russell2014">[Russell 2014]</a></p>
<p><img src="https://alain.xyz/blog/intro-toolbag-custom-shaders/assets/material-settings.jpg" alt="Material Settings Drawers"/></p>
<p>You can actually see all subroutines you can overload with custom shaders, they&#x27;re the drawers that make up the material settings, with the name of the subroutine on the right, and the currently active one on the left. You can also spot all the different subroutines over in the <code>runSubroutines(...)</code> function in <code>&lt;Your Toolbag Installation&gt;/data/shader/mat/mat.frag</code> and <code>&lt;Your Toolbag Installation&gt;/data/shader/mat/mat.comp</code> for ray tracing specific subroutines. <a href="https://marmoset.co/posts/toolbag_custom_shaders/">Marmoset has a blog post that details each subroutine here</a>.</p>
<p>Nearly all Toolbag subroutines follow a simple interface where they accept one argument, the <em>shader state</em> <code>FragmentState</code>, which stores relative information like eye direction, normals, etc. to be used across each subroutine in order.</p>
<h3 id="full-shader">Full Shader</h3>
<p>So here&#x27;s the full custom shader, basically, I&#x27;m feeding the output of the shader above to the albedo output, with a material&#x27;s <em>Diffusion</em> subroutine set to unlit. The material is placed in an object covering the camera, a sphere with all normals pointing inward, and the signed distance field camera is set to match the camera in Toolbag.</p>
<div><pre><code class="language-glsl"><span class="hljs-meta">#define DISTMARCH_STEPS 128</span>
<span class="hljs-meta">#define DISTMARCH_MAXDIST 256.</span>

<span class="hljs-keyword">uniform</span> <span class="hljs-type">vec3</span> uColor1; <span class="hljs-comment">//color  name &quot;Color 1&quot;  default (14.0 / 255.0), (135.0 / 255.0), (163.0 / 255.0)</span>
<span class="hljs-keyword">uniform</span> <span class="hljs-type">vec3</span> uColor2; <span class="hljs-comment">//color  name &quot;Color 2&quot;  default (11.0 / 255.0), (96.0 / 255.0), (178.0 / 255.0)</span>

<span class="hljs-comment">// 🌊 Distance Fields</span>

<span class="hljs-type">float</span> spheredf(<span class="hljs-type">vec3</span> pos, <span class="hljs-type">float</span> r) { <span class="hljs-keyword">return</span> <span class="hljs-built_in">length</span>(pos) - r; }

<span class="hljs-type">float</span> opRep(<span class="hljs-type">vec3</span> p, <span class="hljs-type">vec3</span> c)
{
    <span class="hljs-comment">// Modulus Repeat on XZ</span>
    <span class="hljs-type">vec3</span> spread = <span class="hljs-built_in">max</span>(<span class="hljs-type">vec3</span>(<span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>), <span class="hljs-type">vec3</span>(uCustomAnimationTime - <span class="hljs-number">1.0</span>,
                                                uCustomAnimationTime - <span class="hljs-number">1.0</span>,
                                                uCustomAnimationTime - <span class="hljs-number">1.0</span>));
    <span class="hljs-type">vec3</span> q = <span class="hljs-built_in">mod</span>(<span class="hljs-built_in">abs</span>(p), c) - <span class="hljs-number">0.5</span> * c;
    q.y = p.y;
    <span class="hljs-comment">// Wave effect</span>
    <span class="hljs-type">float</span> yy = <span class="hljs-built_in">sin</span>(uCustomAnimationTime + <span class="hljs-number">.3</span> * p.x) +
               <span class="hljs-built_in">cos</span>(uCustomAnimationTime * <span class="hljs-number">.9</span> + <span class="hljs-number">.2</span> * p.z);
    q += <span class="hljs-number">.5</span> * <span class="hljs-type">vec3</span>(<span class="hljs-number">0.0</span>, yy, <span class="hljs-number">0.0</span>);
    <span class="hljs-keyword">return</span> spheredf(q, <span class="hljs-number">.1</span>);
}

<span class="hljs-type">vec2</span> scenedf(<span class="hljs-type">vec3</span> p)
{
    p.y += <span class="hljs-built_in">max</span>(<span class="hljs-number">2.0</span> / (uCustomAnimationTime + <span class="hljs-number">.001</span>), <span class="hljs-number">0.001</span>);
    <span class="hljs-type">vec2</span> obj = <span class="hljs-type">vec2</span>(opRep(p, <span class="hljs-type">vec3</span>(<span class="hljs-number">2.5</span>, <span class="hljs-number">2.5</span>, <span class="hljs-number">2.5</span>)), p.y);
    <span class="hljs-keyword">return</span> obj;
}

<span class="hljs-type">vec3</span> distmarch(<span class="hljs-type">vec3</span> ro, <span class="hljs-type">vec3</span> rd, <span class="hljs-type">float</span> maxd)
{
    <span class="hljs-type">float</span> epsilon = <span class="hljs-number">0.001</span>;
    <span class="hljs-type">float</span> dist = <span class="hljs-number">10.</span> * epsilon;
    <span class="hljs-type">float</span> t = <span class="hljs-number">0.</span>;
    <span class="hljs-type">float</span> material = <span class="hljs-number">-1.0</span>;
    <span class="hljs-type">float</span> iterations = <span class="hljs-number">0.</span>;

    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; DISTMARCH_STEPS; i++)
    {
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">abs</span>(dist) &lt; epsilon || t &gt; maxd) <span class="hljs-keyword">break</span>;
        <span class="hljs-comment">// advance the distance of the last lookup</span>
        t += dist;
        <span class="hljs-type">vec2</span> dfresult = scenedf(ro + t * rd);
        dist = dfresult.x;
        material = dfresult.y;
        iterations++;
    }

    <span class="hljs-keyword">if</span> (t &gt; maxd)
    {
        iterations = <span class="hljs-number">-1.0</span>;
    }
    <span class="hljs-keyword">return</span> <span class="hljs-type">vec3</span>(t, iterations, material);
}

<span class="hljs-type">void</span> CustomAlbedo(<span class="hljs-keyword">inout</span> FragmentState s)
{
    <span class="hljs-type">vec2</span> uv = s.screenTexCoord;
    uv.y = <span class="hljs-number">1.0</span> - uv.y;
    <span class="hljs-type">vec4</span> outColor = <span class="hljs-type">vec4</span>(<span class="hljs-number">0.</span>, <span class="hljs-number">0.</span>, <span class="hljs-number">0.</span>, <span class="hljs-number">0.</span>);

    <span class="hljs-comment">// 🎥 Setup Camera</span>
    <span class="hljs-type">vec3</span> camOrigin = <span class="hljs-type">vec3</span>(<span class="hljs-number">45.0</span>, <span class="hljs-number">-32.0</span>, <span class="hljs-number">45.0</span>);
    <span class="hljs-type">vec3</span> camDir = <span class="hljs-built_in">normalize</span>(-vertexEye);
    <span class="hljs-comment">// aspect ratio</span>
    <span class="hljs-type">vec2</span> st = uv.xy - <span class="hljs-number">.5</span>;
    st *= (<span class="hljs-type">float</span>(screenCoord.y) / <span class="hljs-type">float</span>(screenCoord.x));

    <span class="hljs-comment">// Perspective Transform</span>
    <span class="hljs-type">float</span> fov = <span class="hljs-number">3.142</span> * <span class="hljs-number">.625</span>;
    <span class="hljs-type">vec3</span> iu = <span class="hljs-type">vec3</span>(<span class="hljs-number">0.</span>, <span class="hljs-number">1.</span>, <span class="hljs-number">0.</span>);
    <span class="hljs-type">vec3</span> iz = camDir;
    <span class="hljs-type">vec3</span> ix = <span class="hljs-built_in">normalize</span>(<span class="hljs-built_in">cross</span>(iz, iu));
    <span class="hljs-type">vec3</span> iy = <span class="hljs-built_in">cross</span>(ix, iz);
    <span class="hljs-type">vec3</span> dir = <span class="hljs-built_in">normalize</span>(st.x * ix + st.y * iy + fov * iz);

    <span class="hljs-comment">// 🥁 Scene Marching</span>
    <span class="hljs-type">vec3</span> scenemarch = distmarch(camOrigin, dir, DISTMARCH_MAXDIST);
    <span class="hljs-type">float</span> sceneCol = <span class="hljs-number">0.0</span>;
    sceneCol = <span class="hljs-built_in">clamp</span>(sceneCol, <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>);

    <span class="hljs-comment">// 🌫️ Fog</span>
    <span class="hljs-keyword">if</span> (scenemarch.x &gt; <span class="hljs-number">0.0001</span>)
    {
        sceneCol = <span class="hljs-built_in">clamp</span>(scenemarch.y, <span class="hljs-number">0.</span>,
                         <span class="hljs-built_in">mix</span>(<span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-built_in">smoothstep</span>(<span class="hljs-number">10.</span>, <span class="hljs-number">180.</span>, scenemarch.x)));
    }

    <span class="hljs-comment">// 🖼️ Output</span>
    outColor.xyz = <span class="hljs-built_in">mix</span>(uColor1, <span class="hljs-number">.95</span> * uColor2,
                       <span class="hljs-built_in">smoothstep</span>(<span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>, scenemarch.z)) *
                   sceneCol;
    outColor.a = sceneCol;

    <span class="hljs-comment">// Write to albedo/baseColor</span>
    s.albedo = outColor;
    s.baseColor = outColor.xyz;
}

<span class="hljs-meta">#ifdef Albedo</span>
<span class="hljs-meta">#undef Albedo</span>
<span class="hljs-meta">#endif</span>
<span class="hljs-meta">#define Albedo CustomAlbedo</span></code></pre></div>
<h2 id="more-resources">More Resources</h2>
<ul>
<li>
<p>Every installation of Marmoset Toolbag has examples of custom shaders that can be found in <code>&lt;Your Toolbag Installation&gt;/data/shader/mat/custom/</code>.</p>
</li>
<li>
<p><a href="https://www.marmoset.co/posts/toolbag_custom_shaders/">Marmoset released a blog post on custom shaders here</a>.</p>
</li>
<li>
<p>Marmoset maintains a <a href="https://marmoset.co/toolbag/add-ons/">library of user submitted custom shaders here</a>.</p>
</li>
<li>
<p>For more information on writing Python add-ons in Toolbag, I wrote <a href="https://alain.xyz/blog/marmoset-toolbag-python-intro">a blog post here</a>.</p>
</li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_brinck2021"><span class="markademic-citename">[Brinck et al. 2021]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">The Technical Art of The Last Of Us Part II</span></strong></em><br/><span class="markademic-authors"><span><span>Waylon Brinck</span></span><span> and <span>Qingzhou Tang</span></span></span><br/><span><span class="markademic-publisher">Siggraph 2021</span><br/></span><span><a href="https://www.naughtydog.com/blog/naughty_dog_at_siggraph_2020">naughtydog.com</a></span></div></td></tr><tr><td id="ref_inigo2013"><span class="markademic-citename">[Quílez 2013]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">3D SDF functions</span></strong></em><br/><span class="markademic-authors"><span><a href="https://www.iquilezles.org">Íñigo Quílez</a> (<a href="https://twitter.com/iquilezles">@iquilezles</a>)</span></span><br/><span><span class="markademic-publisher">ShaderToy 2013</span><br/></span><span><a href="https://iquilezles.untergrund.net/www/articles/distfunctions/distfunctions.htm">iquilezles.untergrund.net</a></span></div></td></tr><tr><td id="ref_russell2014"><span class="markademic-citename">[Russell 2014]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Rendering in Marmoset Toolbag</span></strong></em><br/><span class="markademic-authors"><span><a href="https://marmoset.co/">Jeff Russell</a> (<a href="https://twitter.com/j3ffdr">@j3ffdr</a>)</span></span><br/><span><span class="markademic-publisher">University of Pennsylvania 2014</span><br/></span><span><a href="http://cis700-003-spring-2014.github.io/lectures/mset_rendering_april29_2014.pdf">cis700-003-spring-2014.github.io</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Intro to Writing Toolbag Plugins</title>
<link>https://alain.xyz/blog/marmoset-toolbag-python-intro</link>
<description>A introduction to writing plugins with Marmoset Toolbag. Learn tips and tricks to configure your development setup as we go over examples of different use cases for writing a Toolbag Plugin.</description>
<guid isPermaLink="true">https://alain.xyz/blog/marmoset-toolbag-python-intro</guid>
<pubDate>Fri, 26 Mar 2021 00:01:34 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>marmoset</category><category>toolbag</category><category>glsl</category><category>python</category><category>cpp</category>
<enclosure url="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/cover.jpg" length="367236" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/autocomplete-test.gif"/><link rel="preload" as="image" href="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/integrated-terminal.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/pylance.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/uis.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/importing-models.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/material.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/camera.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/renders.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/pypi.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/burgers.jpg"/><p>Developing Marmoset Toolbag plugins with Python <em>can be challenging</em>. There&#x27;s important questions that you may need to solve like:</p>
<ul>
<li>
<p>How do you design UIs?</p>
</li>
<li>
<p>How do you import models and textures?</p>
</li>
<li>
<p>How do you export data like renders, materials, etc.</p>
</li>
<li>
<p>How do you configure a material, texture project, or your render settings?</p>
</li>
<li>
<p>How do you use external packages from PyPi?</p>
</li>
</ul>
<p><img src="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/autocomplete-test.gif" alt="AutoComplete in Action"/></p>
<p>With the advent of <em>text editors</em> such as <a href="https://code.visualstudio.com/">Microsoft Visual Studio Code</a>, <em>AI assisted auto complete</em> with <a href="https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.vscodeintellicode">Visual Studio IntelliCode</a>, and <em>language servers</em> such as <a href="https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance">PyLance</a>, iterating your plugin&#x27;s design can be very intuitive, and the questions you might have about a given piece of software&#x27;s API can be more easily answered by the text editor&#x27;s auto-complete in addition to our <a href="https://marmoset.co/python/reference.html">documentation</a> and examples.</p>
<h2 id="editor-setup">Editor Setup</h2>
<p>First, download <strong>Visual Studio Code</strong> with the link below:</p>
<ul>
<li><a href="https://code.visualstudio.com/">Microsoft Visual Studio Code</a></li>
</ul>
<p>Then, install the latest version of <strong>Python</strong> to your computer. Marmoset Toolbag is designed to run on Python 3.6 and above. Make sure to add Python to your environment variables in the advance options.</p>
<ul>
<li><a href="https://www.python.org/downloads/">Python Download</a></li>
</ul>
<p>From there you&#x27;ll need to install python packages from the <a href="https://pypi.org/">Python Package Index</a> via the <code>pip</code> command. You will need the Marmoset Toolbag package <a href="https://pypi.org/project/mset/"><code>mset</code></a> for auto-complete support as well as remote execution.</p>
<p><img src="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/integrated-terminal.png" alt="VS Code&#x27;s Integrated Terminal"/></p>
<p>Open up a terminal such as <a href="https://code.visualstudio.com/docs/editor/integrated-terminal">VS Code&#x27;s Integrated Terminal</a> with the shortcut <code>Ctrl + Shift + ~</code> and run:</p>
<div><pre><code class="language-bash">pip install mset</code></pre></div>
<p><strong>Linting</strong> is checking your code early for mistakes and let you know early where those mistakes are and how to fix them. There&#x27;s a number of different packages available for linting. The Visual Studio Code team discusses the differences between different linters <a href="https://code.visualstudio.com/docs/python/linting">here</a>.</p>
<blockquote>
<p>Visual Studio Code will present you with an option for them to download a linter for you, but if that option doesn&#x27;t work you can always open up the terminal with <code>CTRL + Shift + ~</code> and type the following:</p>
<div><pre><code class="language-bash">pip install pylint</code></pre></div>
</blockquote>
<p><img src="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/pylance.png" alt="PyLance Screenshot"/></p>
<p>Finally, install the <strong>PyLance</strong> Visual Studio Code extension as well as <strong>Intellicode</strong>:</p>
<ul>
<li>
<p><a href="https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance">Pylance on the VS Code Marketplace</a></p>
</li>
<li>
<p><a href="https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.vscodeintellicode">Intellicode on the VS Code Marketplace</a></p>
</li>
</ul>
<p>Now you should have everything you need for full autocomplete support!</p>
<h2 id="designing-uis">Designing UIs</h2>
<p><img src="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/uis.jpg" alt="UIs Example Screenshot"/></p>
<p>Marmoset Toolbag uses an <a href="https://alain.xyz/blog/gui-architecture-with-graphics-apis#retained-mode">imperative retained mode API</a> for designing UIs, so you must create UI elements, then add them as children of other elements:</p>
<div><pre><code class="language-py"><span class="hljs-keyword">import</span> mset
<span class="hljs-keyword">import</span> os

<span class="hljs-comment"># Initialize UI Objects</span>

window = mset.UIWindow(<span class="hljs-string">&quot;UI Test&quot;</span>)

<span class="hljs-comment"># 🗄️ Setup Drawer</span>

<span class="hljs-comment"># 🔘 Button</span>
button = mset.UIButton(<span class="hljs-string">&quot;Close Button&quot;</span>)
button.onClick = <span class="hljs-keyword">lambda</span>: mset.shutdownPlugin()
<span class="hljs-comment"># ⏯ Icon Button</span>
icon_button = mset.UIButton()
icon_button.setIcon(os.path.abspath(os.path.join(
    os.curdir, <span class="hljs-string">&quot;data/gui/control/animationplay.tga&quot;</span>)))

drawer = mset.UIDrawer(name=<span class="hljs-string">&quot;Settings&quot;</span>)
drawer_window = mset.UIWindow(name=<span class="hljs-string">&quot;Drawer Window&quot;</span>)
drawer.containedControl = drawer_window

drawer_window.addElement(button)
drawer_window.addReturn()
drawer_window.addElement(icon_button)

<span class="hljs-comment"># 📜 Setup scroll box</span>

scrollbox = mset.UIScrollBox()
scrollbox_window = mset.UIWindow(name=<span class="hljs-string">&quot;Scrollbox Window&quot;</span>)
scrollbox.containedControl = scrollbox_window

<span class="hljs-comment"># 📃 Lists</span>
my_list = mset.UIListBox(<span class="hljs-string">&quot;List 1&quot;</span>)
my_list.addItem(<span class="hljs-string">&quot;1&quot;</span>)
my_list.addItem(<span class="hljs-string">&quot;2&quot;</span>)
my_list.addItem(<span class="hljs-string">&quot;3&quot;</span>)

scrollbox_window.addElement(drawer)
scrollbox_window.addReturn()
scrollbox_window.addElement(my_list)

<span class="hljs-comment"># 🌟 Setup Main Window</span>

<span class="hljs-comment"># 🎚️ Sliders</span>
slider = mset.UISliderInt(<span class="hljs-built_in">min</span>=<span class="hljs-number">5</span>, <span class="hljs-built_in">max</span>=<span class="hljs-number">128</span>, name=<span class="hljs-string">&quot;Slider&quot;</span>)
<span class="hljs-comment"># 🎨 Color Picker</span>
picker = mset.UIColorPicker(<span class="hljs-string">&quot;Color&quot;</span>)
<span class="hljs-comment"># ✏️ Inputs</span>
text = mset.UITextField()
text_float = mset.UITextFieldFloat()
<span class="hljs-comment"># ✅ Checkbox</span>
checkbox = mset.UICheckBox()

<span class="hljs-comment"># 🍱 Add elements to Window</span>
window.addElement(slider)
window.addReturn()
window.addElement(scrollbox)
window.addReturn()
window.addElement(picker)
window.addReturn()
window.addElement(text)
window.addReturn()
window.addElement(text_float)
window.addReturn()
window.addElement(checkbox)

<span class="hljs-comment"># 2️⃣ Create Secondary Window</span>

popup_window = mset.UIWindow(<span class="hljs-string">&quot;UI Popup Test&quot;</span>)</code></pre></div>
<h2 id="importing-models">Importing Models</h2>
<p><img src="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/importing-models.jpg" alt="Importing Models Screenshot"/></p>
<p>Perhaps you&#x27;re automating rendering a number of different models, or have a lot of models that need to be batch imported from different places. The <code>mset.importModel(path: str)</code> function is all you need to do to import a model to your scene. From there you can move it around by changing its position, or make it a child of something like say a Baker&#x27;s Source children.</p>
<div><pre><code class="language-py"><span class="hljs-keyword">import</span> mset

<span class="hljs-comment"># 🏆 Model</span>
model = mset.importModel(<span class="hljs-string">&quot;C:/mymodel.obj&quot;</span>)
model.name = <span class="hljs-string">&quot;My Imported Model&quot;</span>
model.position = [<span class="hljs-number">0.0</span>, <span class="hljs-number">2.0</span>, <span class="hljs-number">0.0</span>]</code></pre></div>
<h2 id="configuring-objects">Configuring Objects</h2>
<p>There&#x27;s a variety of different Objects you may want to configure in Marmoset Toolbag, let&#x27;s go over a couple:</p>
<h3 id="materials">Materials</h3>
<p><img src="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/material.jpg" alt="Configuring Materials Screenshot"/></p>
<p>Materials can be configured to use a specific subroutine, and every parameter that exists can be modified to your liking.</p>
<div><pre><code class="language-py"><span class="hljs-keyword">import</span> mset

<span class="hljs-comment"># Create a Material</span>
mat = mset.Material()

<span class="hljs-comment"># 🔖 Set a subroutine</span>
mat.setSubroutine(<span class="hljs-string">&quot;reflectivity&quot;</span>, <span class="hljs-string">&quot;Metalness&quot;</span>)

<span class="hljs-comment"># 🎛️ Edit a subroutine parameter</span>
surface = mat.getSubroutine(<span class="hljs-string">&quot;surface&quot;</span>)
tex = mset.Texture(<span class="hljs-string">&quot;C:/my_normal.png&quot;</span>)
surface.setField(<span class="hljs-string">&quot;Normal Map&quot;</span>, tex)</code></pre></div>
<h3 id="cameras">Cameras</h3>
<p><img src="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/camera.jpg" alt="Camera Screenshot"/></p>
<p>All cameras in Toolbag can be controlled with Python, and you can adjust their transform parameters like their rotation, as well as lens settings like the camera limits (near, far), depth of field, post-processing.</p>
<div><pre><code class="language-py"><span class="hljs-keyword">import</span> mset

cam = mset.CameraObject()
cam.name = <span class="hljs-string">&quot;Python Camera&quot;</span>

<span class="hljs-comment"># 🔬 Adjust camera limits</span>
cam.limits.farLimit = <span class="hljs-number">10</span>

<span class="hljs-comment"># 🔎 Adjust lens settings</span>
cam.lens.dofEnabled = <span class="hljs-literal">True</span>
cam.lens.dofMaxBokehSize = <span class="hljs-number">64.0</span>

<span class="hljs-comment"># ✨ Adjust post-processing</span>
cam.postEffect.sharpen = <span class="hljs-number">0.3</span>
cam.postEffect.vignetteStrength = <span class="hljs-number">0.4</span>
cam.postEffect.bloomBrightness = <span class="hljs-number">0.1</span>
cam.postEffect.bloomSize = <span class="hljs-number">4.0</span></code></pre></div>
<h2 id="exporting-renders">Exporting Renders</h2>
<p><img src="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/renders.jpg" alt="Renders Screenshot"/></p>
<p>Exporting renders is incredibly easy, there&#x27;s 3 functions you can use to either render a single camera, or the images/video configuration set up in your Render Object.</p>
<div><pre><code class="language-py"><span class="hljs-keyword">import</span> mset

<span class="hljs-comment"># 📷 Render Main Camera</span>
img = mset.renderCamera(path=<span class="hljs-string">&#x27;C:/my_render.png&#x27;</span>)

<span class="hljs-comment"># 📸 Render all images configured in the Render Object</span>
mset.renderImages()

<span class="hljs-comment"># 🎥 Render all videos configured in the Render Object</span>
mset.renderVideos()</code></pre></div>
<h2 id="using-external-packages">Using External Packages</h2>
<p><img src="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/pypi.svg" alt="Python Package Index"/></p>
<p>Marmoset Toolbag PluginS can take advantage of the entire Python Package Index and use that ecosystem to help solve problems like hooking Toolbag up to an HTTP server such as <a href="https://flask.palletsprojects.com/en/1.1.x/">Flask</a> or to a machine learning framework such as <a href="https://pytorch.org/">PyTorch</a> or <a href="https://www.tensorflow.org/">TensorFlow</a>.</p>
<p>A Toolbag Plugin that uses PyPi packages needs to have its files laid out as follows:</p>
<div><pre><code class="language-bash">├─ YourPlugin/
│   ├─ __main__.py
│   ├─ requirements.txt
│   └─ ...
└─ ...</code></pre></div>
<p>Your <code>requirements.txt</code> would be a text file like the following:</p>
<div><pre><code class="language-txt">LinearAlgebra==1.4.1</code></pre></div>
<p>The <code>__main__.py</code> file would serve as the entry point to the plugin, and similar to other Python modules, you can have any file/folder structure you want.</p>
<h2 id="conclusion">Conclusion</h2>
<p><img src="https://alain.xyz/blog/marmoset-toolbag-python-intro/assets/burgers.jpg" alt="Burgers Procedural Geometry"/></p>
<p>There&#x27;s a lot more you can do with Marmoset Toolbag plugins than what we&#x27;ve discussed, from <em>procedural generation of geometry</em> to automating your <em>baking</em>, <em>rendering</em>, or <em>technical art</em> workflows.</p>
<p>For more examples of just what you can do, visit the <code>Examples</code> folder in your Toolbag installation:</p>
<ul>
<li>Windows: <code>&lt;Your Toolbag Installation&gt;/data/plugin/Examples</code></li>
<li>MacOS: <code>&lt;Your Toolbag Application.app&gt;/data/plugin/Examples</code></li>
</ul>
<p>As well as refer to our <a href="https://marmoset.co/python/reference.html">reference documentation here</a>.</p>
<h3 id="more-resources">More Resources</h3>
<ul>
<li>
<p>Marmoset maintains a <a href="https://marmoset.co/toolbag/add-ons/">library of user submitted plugins here</a>.</p>
</li>
<li>
<p>The procedural meshes were done using a library I wrote called <a href="https://github.com/alaingalvan/strange-attractors">Strange Attractors</a>.</p>
</li>
</ul>
<hr/>
<p>More general topics:</p>
<ul>
<li>
<p>There&#x27;s Visual Studio plugins for relevant tasks you may want to do in Toolbag such as custom shaders. We would recommend the <a href="https://marketplace.visualstudio.com/items?itemName=CADENAS.vscode-glsllint">GLSL Lint</a> plugin for linting GLSL shader code.</p>
</li>
<li>
<p>The Theme used for Visual Studio Code in this blog post is called <a href="https://github.com/primer/github-vscode-theme-dark-classic">Github Dark Classic</a>.</p>
</li>
<li>
<p>The font used in this example was <a href="https://www.monolisa.dev/">MonoLisa</a> with ligatures enabled. For a free option that&#x27;s similar try <a href="https://github.com/tonsky/FiraCode/wiki/VS-Code-Instructions">Fira Code</a>.</p>
</li>
<li>
<p>The Windows Powershell terminal was styled using <a href="https://ohmyposh.dev/">Oh My Posh</a>, which works on all platforms.</p>
</li>
</ul>]]></content:encoded>
</item>
<item>
<title>A Comparison of Modern Graphics APIs</title>
<link>https://alain.xyz/blog/comparison-of-modern-graphics-apis</link>
<description>Low level Graphics APIs such as DirectX 12, Vulkan, Metal, and WebGPU are converging to a model similar to the way GPUs are currently built. In this article we&apos;ll review modern graphics APIs and how they compare with older graphics APIs such as OpenGL in their design and data structures.</description>
<guid isPermaLink="true">https://alain.xyz/blog/comparison-of-modern-graphics-apis</guid>
<pubDate>Sun, 31 Jan 2021 01:54:43 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>shader</category><category>vulkan</category><category>directx</category><category>directx12</category><category>ultimate</category><category>opengl</category><category>webgpu</category><category>metal</category><category>graphics</category><category>api</category>
<enclosure url="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/cover.jpg" length="287796" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/dependencies.gif"/><link rel="preload" as="image" href="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/entry.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/physicaldevice.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/logicaldevice.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/queue.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/commandpool.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/surface.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/swapchain.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/framebuffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/texture.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/buffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/shader.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/pipeline-layout.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/pipeline.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/commandbuffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/commandlist.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/fence.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/semaphore.svg"/><link rel="preload" as="image" href="https://github.com/kayvonf.png"/><link rel="preload" as="image" href="https://github.com/tangent-vector.png"/><link rel="preload" as="image" href="https://github.com/msatranjr.png"/><link rel="preload" as="image" href="https://github.com/stevewhims.png"/><p>Low level Graphics APIs such as <strong>Vulkan</strong>, <strong>DirectX</strong>, <strong>Metal</strong>, and <strong>WebGPU</strong>, are converging to a model similar to the way GPUs are currently built. Graphics Processing Units (GPUs) are asynchronous compute units that can handle large quantities of data, such as complex mesh geometry, image textures, output frame buffers, transformation matrices, or anything you want computed.</p>
<p>GPUs weren&#x27;t always like this, originally they were a set of fixed hardware based functions with very little programmability. This changed as applications pushed the limits of what these non-programmable systems could do, which warranted a race between GPU manufacturers and application developers constantly pushing the limits of their design <a href="#ref_peddie2023">[Peddie 2023]</a>. Frame buffers and rasterizers <a href="#ref_fatahalian2018">[Fatahalian 2018]</a> lead to programable shaders, General Purpose GPU (GPGPU) computing, and recently the addition of hardware for ray traversal acceleration and tensor processing for AI. Graphics APIs have evolved right alongside these changes with additions to the fixed graphics pipeline, compute shaders, and more recently ray traversal functionality (DirectX 12 and Vulkan Ray Tracing).</p>
<p>Let&#x27;s look at some of the similarities and differences between graphics APIs. We&#x27;ll be covering the C++ APIs for:</p>
<ul>
<li>
<p>🌋 Vulkan</p>
</li>
<li>
<p>❎ DirectX 12.x</p>
</li>
<li>
<p>✖️ DirectX 11.x</p>
</li>
<li>
<p>🤖 Metal</p>
</li>
<li>
<p>🕸️ WebGPU</p>
</li>
<li>
<p>⚪ OpenGL</p>
</li>
</ul>
<p><strong>OpenGL</strong>&#x27;s design originated during the early days of computer graphics and was designed as a state machine, so its interface differs greatly from modern graphics APIs. <strong>DirectX 11</strong>, while closer to modern GPU architectures than OpenGL, tries to be easy for developers by delegating tasks that Vulkan, DirectX 12, and Metal currently leave the developer responsible for to the driver. <a href="#ref_russell2014">[Russell 2014]</a></p>
<p>It is useful to be aware of the legacy of modern graphics APIs so they are mentioned where relevant.</p>
<h2 id="execution-order">Execution Order</h2>
<p>A graphics application normally follows the following order of execution no matter the API:</p>
<ol>
<li>
<p><strong>Initializing the API</strong> - Creating the core data structures needed to access the inner workings of the API.</p>
</li>
<li>
<p><strong>Loading Assets</strong> - Creating the data structures needed to load things like shaders, describe the graphics pipeline, create and populate commands buffers for the GPU to execute, and send resources to GPU exclusive memory.</p>
</li>
<li>
<p><strong>Updating Assets</strong> - Update any uniforms to your shader and perform application level logic here.</p>
</li>
<li>
<p><strong>Presentation</strong> - Send your list of command buffers to your command queue and present your swapchain.</p>
</li>
<li>
<p><strong>Repeat 2, 3, and 4</strong> until a signal from the application to close.</p>
</li>
<li>
<p><strong>Destroy</strong> - Wait for the GPU to finish any remaining work, and destroy all data structures and handles.</p>
</li>
</ol>
<p>Thus we&#x27;ll be following the creation and use of Graphics API data structures in this order.</p>
<h2 id="importing-dependencies">Importing Dependencies</h2>
<p><img src="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/dependencies.gif" alt="Dependencies Example"/></p>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>#include &lt;vulkan/vulkan.hpp&gt;</code></td></tr><tr><td>DirectX 12</td><td><code>#include &lt;d3d12.h&gt;</code></td></tr><tr><td>DirectX 11</td><td><code>#include &lt;d3d11.h&gt;</code></td></tr><tr><td>Metal</td><td><code>#import &lt;Metal/Metal.h&gt;</code></td></tr><tr><td>WebGPU</td><td><em>Requires <a href="https://caniuse.com/webgpu">Canary Browser</a> with Flags</em></td></tr><tr><td>OpenGL</td><td><em>Varies by OS</em></td></tr></tbody></table>
<p>When starting a new application you need to include any dependencies you have to external APIs, and graphics APIs are no different. Depending on the API, you may also need other libraries in your project such as a shader compiler.</p>
<p><strong>OpenGL</strong> stands out as the exception to every other graphics API, in that depending on the operating system and your personal setup, there can be a <a href="https://github.com/alaingalvan/CrossWindow-Graphics/blob/master/src/CrossWindow/Graphics/OpenGL.h#L24">variety of imports from different locations</a>.</p>
<h3 id="shader-compiler">Shader Compiler</h3>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>#include &quot;glslang/Include/revision.h&quot;</code></td></tr><tr><td>DirectX 12</td><td><code>#include &lt;D3Dcompiler.h&gt;</code></td></tr><tr><td>DirectX 11</td><td><code>#include &lt;D3Dcompiler.h&gt;</code></td></tr><tr><td>Metal</td><td><code>#import &lt;Metal/Metal.h&gt;</code></td></tr><tr><td>WebGPU</td><td><em>N/A</em></td></tr><tr><td>OpenGL</td><td><code>void glShaderSource(...)</code></td></tr></tbody></table>
<p><strong>Vulkan</strong> requires that you use an external shader compiler that generates SPIR-V such as <a href="https://github.com/KhronosGroup/glslang">glslang</a> or the <a href="https://github.com/microsoft/DirectXShaderCompiler/wiki/SPIR%E2%80%90V-CodeGen">DirectX Shader Compiler</a>.</p>
<p>For <strong>DirectX</strong>, It&#x27;s recommended that you use the <a href="https://github.com/microsoft/DirectXShaderCompiler">DirectX Shader Compiler</a> rather than the included compiler since it supports newer shader model versions and more optimizations and speed.</p>
<p><strong>Metal</strong> shaders can be compiled at runtime or compiled at build time with the <code>metallib</code> command line tool included in your MacOS path.</p>
<p><strong>OpenGL</strong> doesn&#x27;t require an external library to compile shaders since it&#x27;s included with the library, though it also supports SPIR-V as an optional alternative to GLSL with OpenGL 4.6.</p>
<p><strong>WebGPU</strong> shaders are plaintext strings, so there&#x27;s no need to compile them, though it is a good idea to strip whitespace and minify/mangle symbols for production.</p>
<blockquote>
<p>For a comparison of shader languages, <a href="https://alain.xyz/blog/a-review-of-shader-languages">visit my blog post reviewing shader languages.</a></p>
</blockquote>
<h2 id="initialize-api">Initialize API</h2>
<h3 id="entry-point">Entry Point</h3>
<p><img src="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/entry.svg" alt="Entry Point Diagram"/></p>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>vk::Instance</code></td></tr><tr><td>DirectX 12</td><td><code>IDXGIFactory4</code></td></tr><tr><td>DirectX 11</td><td><code>IDXGIFactory</code></td></tr><tr><td>Metal</td><td><code>CAMetalLayer</code></td></tr><tr><td>WebGPU</td><td><code>GPU</code></td></tr><tr><td>OpenGL</td><td><em>Varies by OS</em></td></tr></tbody></table>
<p>The entry point to a graphics API generally allows you to access the API&#x27;s inner classes.</p>
<p><strong>Vulkan</strong>&#x27;s entry point involves choosing what version of the API you intend to use as well as any extensions or layers you want, such as error checking, window surfaces, etc.</p>
<p><strong>DirectX 11</strong> and <strong>12</strong> requires that you create a <strong>Factory</strong>, and optionally a <strong>Debug</strong> data structure.</p>
<p>On <strong>Metal</strong>, an <code>NSWindow</code> is required to have an <code>NSView</code> with a <code>CAMetalLayer</code> in it (which is a part of QuartzCore). Once a layer exists and is attached to a window, that window can use the rest of the Metal API.</p>
<p>For <strong>OpenGL</strong>, the closest thing to an entry point is an operating system specific <em>Context</em> that you can request for after creating an OS Window.</p>
<h3 id="physical-device">Physical Device</h3>
<p><img src="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/physicaldevice.svg" alt="Physical Device Diagram"/></p>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>vk::PhysicalDevice</code></td></tr><tr><td>DirectX 12</td><td><code>IDXGIAdapter1</code></td></tr><tr><td>DirectX 11</td><td><code>IDXGIAdapter</code></td></tr><tr><td>Metal</td><td><code>MTLDevice</code></td></tr><tr><td>WebGPU</td><td><code>GPUAdapter</code></td></tr><tr><td>OpenGL</td><td><code>glGetString(GL_VENDOR)</code></td></tr></tbody></table>
<p><strong>Physical Devices</strong> allow you to query for important device specific details such as memory size and feature support.</p>
<p><strong>Metal</strong> is the only outlier here since both the physical and logical device are shared by the same data structure.</p>
<p><strong>OpenGL</strong> cannot query for any device details unless you use a manufacturer exclusive extension. You can get some miscellaneous data though like the driver vendor name, renderer, and OpenGL version.</p>
<h3 id="logical-device">Logical Device</h3>
<p><img src="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/logicaldevice.svg" alt="Logical Device Diagram"/></p>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>vk::Device</code></td></tr><tr><td>DirectX 12</td><td><code>ID3D12Device</code></td></tr><tr><td>DirectX 11</td><td><code>ID3D11Device</code></td></tr><tr><td>Metal</td><td><code>MTLDevice</code></td></tr><tr><td>WebGPU</td><td><code>GPUDevice</code></td></tr><tr><td>OpenGL</td><td><em>N/A</em></td></tr></tbody></table>
<p>A <strong>Device</strong> gives you access to the core inner functions of the API, such as creating graphics data structures like textures, buffers, queues, pipelines, etc. This type of data structure is the same for the most part across all modern graphics APIs with very few changes between them.</p>
<p><strong>Vulkan</strong> and <strong>DirectX 12</strong> offer control over memory via creating memory data structures via a device.</p>
<h3 id="queue">Queue</h3>
<p><img src="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/queue.svg" alt="Queue Diagram"/></p>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>vk::Queue</code></td></tr><tr><td>DirectX 12</td><td><code>ID3D12CommandQueue</code></td></tr><tr><td>DirectX 11</td><td><code>ID3D11DeviceContext</code></td></tr><tr><td>Metal</td><td><code>MTLCommandQueue</code></td></tr><tr><td>WebGPU</td><td><code>GPUQueue</code></td></tr><tr><td>OpenGL</td><td><em>N/A</em></td></tr></tbody></table>
<p>A <strong>Queue</strong> allows you to enqueue tasks for the GPU to execute. A GPU is an asynchronous compute device, so the idea here is to always keep it busy while having control over when items are added to the queue.</p>
<p><strong>Vulkan</strong> queues require you to specify what queues the device will use before even creating it.</p>
<h3 id="command-pool">Command Pool</h3>
<p><img src="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/commandpool.svg" alt="Command Pool"/></p>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>vk::CommandPool</code></td></tr><tr><td>DirectX 12</td><td><code>ID3D12CommandAllocator</code></td></tr><tr><td>DirectX 11</td><td><code>ID3D11DeviceContext</code></td></tr><tr><td>Metal</td><td><code>MTLCommandQueue</code></td></tr><tr><td>WebGPU</td><td><code>GPUDevice</code></td></tr><tr><td>OpenGL</td><td><em>N/A</em></td></tr></tbody></table>
<p>A <strong>Command Pool</strong> is a data structure that allows you to create command buffers.</p>
<p><strong>Metal</strong> stands out by having the queue also be the data structure from which to allocate command buffers.</p>
<h2 id="frame-backings">Frame Backings</h2>
<h3 id="window-surface">Window Surface</h3>
<p><img src="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/surface.svg" alt="Window Surface Diagram"/></p>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>vk::Surface</code></td></tr><tr><td>DirectX 12</td><td><code>ID3D12Resource</code></td></tr><tr><td>DirectX 11</td><td><code>ID3D11Texture2D</code></td></tr><tr><td>Metal</td><td><code>CAMetalLayer</code></td></tr><tr><td>WebGPU</td><td><code>GPUCanvasContext</code></td></tr><tr><td>OpenGL</td><td><em>Varies by OS</em></td></tr></tbody></table>
<p>A window <strong>Surface</strong> allows you to bind all draw calls to an OS specific window.</p>
<p>On <strong>DirectX</strong>, since there&#x27;s only Windows / Xbox as targets for the API, the closest thing to a surface is the texture back buffer you receive from a swapchain. A swapchain receives your window handle and from there it&#x27;ll create a surface internal to the DirectX driver.</p>
<p>Since MacOS and iOS windows feature a hierarchical structure where an Application contains a View, which can contain a layer, the closest thing to a surface in <strong>Metal</strong> is either a metal layer or view that wraps it.</p>
<h3 id="swapchain">Swapchain</h3>
<p><img src="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/swapchain.svg" alt="Swapchain Diagram"/></p>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>vk::Swapchain</code></td></tr><tr><td>DirectX 12</td><td><code>IDXGISwapChain3</code></td></tr><tr><td>DirectX 11</td><td><code>IDXGISwapChain</code></td></tr><tr><td>Metal</td><td><code>CAMetalDrawable</code></td></tr><tr><td>WebGPU</td><td><code>GPUCanvasContext</code></td></tr><tr><td>OpenGL</td><td><em>Varies by OS</em></td></tr></tbody></table>
<p>A <strong>Swapchain</strong> flips between different back buffers for a given window, and controls aspects of rendering such as refresh rate and back buffer swapping behavior.</p>
<p><strong>Metal</strong> and <strong>OpenGL</strong> stands out here in that the API lacks the idea of a swapchain, leaving that to the OS windowing API instead.</p>
<h3 id="frame-buffers">Frame Buffers</h3>
<p><img src="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/framebuffer.svg" alt="Frame Buffer Diagram"/></p>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>vk::Framebuffer</code></td></tr><tr><td>DirectX 12</td><td><code>ID3D12Resource</code></td></tr><tr><td>DirectX 11</td><td><code>ID3D11RenderTargetView</code></td></tr><tr><td>Metal</td><td><code>MTLRenderPassDescriptor</code></td></tr><tr><td>WebGPU</td><td><code>GPURenderPassDescriptor</code></td></tr><tr><td>OpenGL</td><td><code>GLuint</code></td></tr></tbody></table>
<p><strong>Frame Buffers</strong> Are groups of output textures used during a raster based graphics pipeline execution as outputs.</p>
<p><strong>DirectX 12 and 11</strong> don&#x27;t feature an explicit data structure for this per say, rather you can pass a set of Views.</p>
<h2 id="initialize-resources">Initialize Resources</h2>
<h3 id="texture">Texture</h3>
<p><img src="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/texture.svg" alt="Texture Diagram"/></p>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>vk::Image &amp; vk::ImageView</code></td></tr><tr><td>DirectX 12</td><td><code>ID3D12Resource</code></td></tr><tr><td>DirectX 11</td><td><code>ID3D11Texture2D</code></td></tr><tr><td>Metal</td><td><code>MTLTexture</code></td></tr><tr><td>WebGPU</td><td><code>GPUTexture &amp; GPUTextureView</code></td></tr><tr><td>OpenGL</td><td><code>GLuint</code></td></tr></tbody></table>
<p><strong>Textures</strong> are arrays of data that store color information, and serve as inputs/outputs for rendering. Vulkan, DirectX 12, and WebGPU introduce the idea of having multiple views of a given texture that can view that texture in different encoded formats or color spaces. Vulkan introduces the idea of managed memory for Images and buffers, thus a texture is a triplet of an <em>Image</em>, <em>Image View</em> when used (there can be multiple of these), and <em>Memory</em> in either device only or in CPU-GPU accessible space.</p>
<p>For a more traditional way of managing memory in Vulkan, I would highly recommend the <a href="https://gpuopen.com/vulkan-memory-allocator/">AMD Vulkan Memory Allocator</a>. For DirectX 12 the same authors released the <a href="https://gpuopen.com/d3d12-memory-allocator/">AMD D3D12 Memory Allocator</a>.</p>
<h3 id="buffer">Buffer</h3>
<p><img src="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/buffer.svg" alt="Buffer Diagram"/></p>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>vk::Buffer &amp; vk::BufferView</code></td></tr><tr><td>DirectX 12</td><td><code>ID3D12Resource</code></td></tr><tr><td>DirectX 11</td><td><code>ID3D11Buffer</code></td></tr><tr><td>Metal</td><td><code>MTLBuffer</code></td></tr><tr><td>WebGPU</td><td><code>GPUBuffer &amp; GPUBufferView</code></td></tr><tr><td>OpenGL</td><td><code>GLuint</code></td></tr></tbody></table>
<p>A <strong>Buffer</strong> is an array of data, such as a mesh&#x27;s positional data, color data, index data, etc. Similar rules for images apply to buffers in Vulkan and WebGPU.</p>
<h3 id="shader">Shader</h3>
<p><img src="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/shader.svg" alt="Shader Diagram"/></p>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>vk::ShaderModule</code></td></tr><tr><td>DirectX 12</td><td><code>ID3DBlob</code></td></tr><tr><td>DirectX 11</td><td><code>ID3D11VertexShader or ID3D11PixelShader</code></td></tr><tr><td>Metal</td><td><code>MTLLibrary</code></td></tr><tr><td>WebGPU</td><td><code>GPUShaderModule</code></td></tr><tr><td>OpenGL</td><td><code>GLuint</code></td></tr></tbody></table>
<p>A <strong>Shader</strong> tends to be a handle to a compiled blob of shader (HLSL, GLSL, MSL, etc.) code to be fed to a given Pipeline.</p>
<h3 id="shader-bindings">Shader Bindings</h3>
<p><img src="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/pipeline-layout.svg" alt="Command Buffer"/></p>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>vk::PipelineLayout &amp; vk::DescriptorSet</code></td></tr><tr><td>DirectX 12</td><td><code>ID3D12RootSignature</code></td></tr><tr><td>DirectX 11</td><td><code>ID3D11DeviceContext::VSSetConstantBuffers(...)</code></td></tr><tr><td>Metal</td><td><code>[MTLRenderCommandEncoder setVertexBuffer: uniformBuffer]</code></td></tr><tr><td>WebGPU</td><td><code>GPUPipelineLayout</code></td></tr><tr><td>OpenGL</td><td><code>GLint</code></td></tr></tbody></table>
<p>Most modern graphics APIs feature a binding data structure to help connect uniform buffers and textures to graphics pipelines that need that data. Metal is unique in that you can bind uniforms with <code>setVertexBuffer</code> in a command encoder, making it much easier to architect compared to Vulkan, DirectX 12, and WebGPU.</p>
<h3 id="pipeline">Pipeline</h3>
<p><img src="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/pipeline.svg" alt="Command Buffer"/></p>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>vk::Pipeline</code></td></tr><tr><td>DirectX 12</td><td><code>ID3D12PipelineState</code></td></tr><tr><td>DirectX 11</td><td><em>Various State Calls</em></td></tr><tr><td>Metal</td><td><code>MTLRenderPipelineState</code></td></tr><tr><td>WebGPU</td><td><code>GPURenderPipeline</code></td></tr><tr><td>OpenGL</td><td><em>Various State Calls</em></td></tr></tbody></table>
<p><strong>Pipelines</strong> are an overarching description of what will be executed when performing a raster draw call, compute dispatch, or ray tracing dispatch.</p>
<p><a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-rssetstate">DirectX 11</a> and OpenGL are unique here where they don&#x27;t have a dedicated object for the graphics pipeline, but instead use calls to set the pipeline state in between executing draw calls.</p>
<h3 id="command-buffer">Command Buffer</h3>
<p><img src="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/commandbuffer.svg" alt="Command Buffer"/></p>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>vk::CommandBuffer</code></td></tr><tr><td>DirectX 12</td><td><code>ID3D12GraphicsCommandList</code></td></tr><tr><td>DirectX 11</td><td><code>ID3D11DeviceContext</code></td></tr><tr><td>Metal</td><td><code>MTLRenderCommandEncoder</code></td></tr><tr><td>WebGPU</td><td><code>GPUCommandEncoder</code></td></tr><tr><td>OpenGL</td><td><em>Intenal to Driver or with <code>GL_NV_command_list</code></em></td></tr></tbody></table>
<p>A <strong>Command Buffer</strong> is an asynchronous computing unit, where you describe procedures for the GPU to execute, such as draw calls, copying data from CPU-GPU accessible memory to GPU exclusive memory, and set various aspects of the graphics pipeline dynamically such as the current scissor.</p>
<p>Previously you would declare what you wanted the GPU to execute procedurally and it would do those tasks, but GPUs are inherently asynchronous, so the driver would have been responsible for figuring out when to schedule tasks to the GPU.</p>
<h3 id="command-list">Command List</h3>
<p><img src="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/commandlist.svg" alt="Command List Diagram"/></p>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>vk::SubmitInfo</code></td></tr><tr><td>DirectX 12</td><td><code>ID3D12CommandList[]</code></td></tr><tr><td>DirectX 11</td><td><code>ID3D11CommandList</code></td></tr><tr><td>Metal</td><td><code>MTLCommandBuffer</code></td></tr><tr><td>WebGPU</td><td><code>GPUCommandEncoder[]</code></td></tr><tr><td>OpenGL</td><td><em>Intenal to Driver or with <code>GL_NV_command_list</code></em></td></tr></tbody></table>
<p><strong>Command Lists</strong> are groups of command buffers pushed in batches to the GPU. The reason for doing this is to keep the GPU constantly busy, leading to less de-synchronization between the CPU and GPU <a href="#ref_foley2015">[Foley 2015]</a>.</p>
<h3 id="fence">Fence</h3>
<p><img src="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/fence.svg" alt="Fence Diagram"/></p>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>vk::Fence</code></td></tr><tr><td>DirectX 12</td><td><code>ID3D12Fence</code></td></tr><tr><td>DirectX 11</td><td><code>ID3D11Fence</code></td></tr><tr><td>Metal</td><td><code>MTLFence</code></td></tr><tr><td>WebGPU</td><td><em>N/A</em></td></tr><tr><td>OpenGL</td><td><code>glFenceSync</code></td></tr></tbody></table>
<p><strong>Fences</strong> are objects used to synchronize the CPU and GPU. Both the CPU and GPU can be instructed to wait at a fence so that the other can catch up. This can be used to manage resource allocation and deallocation, making it easier to manage overall graphics memory usage. <a href="#ref_satran2018">[Satran et al. 2018]</a></p>
<h3 id="barriers">Barriers</h3>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>vkCmdPipelineBarrier</code></td></tr><tr><td>DirectX 12</td><td><code>D3D12_RESOURCE_BARRIER</code></td></tr><tr><td>DirectX 11</td><td><em>N/A</em></td></tr><tr><td>Metal</td><td><code>MTLFence</code></td></tr><tr><td>WebGPU</td><td><em>N/A</em></td></tr><tr><td>OpenGL</td><td><code>glMemoryBarrier</code></td></tr></tbody></table>
<p>A more granular form of synchronization, inside command buffers. Hans-Kristian Arntzen wrote an <a href="https://themaister.net/blog/2019/08/14/yet-another-blog-explaining-vulkan-synchronization/">article on synchronization in Vulkan that&#x27;s worth a look</a>.</p>
<h3 id="semaphore">Semaphore</h3>
<p><img src="https://alain.xyz/blog/comparison-of-modern-graphics-apis/assets/semaphore.svg" alt="Semaphore Diagram"/></p>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>vk::Semaphore</code></td></tr><tr><td>DirectX 12</td><td><code>HANDLE</code></td></tr><tr><td>DirectX 11</td><td><code>HANDLE</code></td></tr><tr><td>Metal</td><td><code>dispatch_semaphore_t</code></td></tr><tr><td>WebGPU</td><td><em>N/A</em></td></tr><tr><td>OpenGL</td><td><em>Varies by OS</em></td></tr></tbody></table>
<p><strong>Semaphores</strong> are objects used introduce dependencies between operations, such as waiting before acquiring the next image in the swapchain before submitting command buffers to your device queue.</p>
<p>Vulkan is unique in that semaphores are a part of the API, with <a href="https://docs.microsoft.com/en-us/windows/win32/direct3d12/user-mode-heap-synchronization">DirectX</a> and <a href="https://developer.apple.com/documentation/metal/synchronization/synchronizing_cpu_and_gpu_work">Metal</a> delegating that to OS calls.</p>
<h2 id="spaces-alignments">Spaces, Alignments</h2>
<p>Each graphics API can have different defaults for axis direction, NDC coordinate direction, matrix alignment, texture alignment, and more, for the most part this isn&#x27;t much of an issue, and you&#x27;ll just have to flip a <code>y</code> value in your UVs in your fragment shader.</p>
<h3 id="texture-alignment">Texture Alignment</h3>
<table><thead><tr><th>API</th><th>Structure</th></tr></thead><tbody><tr><td>Vulkan</td><td><code>Bottom Left</code></td></tr><tr><td>DirectX 12</td><td><code>Top Left</code></td></tr><tr><td>DirectX 11</td><td><code>Top Left</code></td></tr><tr><td>Metal</td><td><code>Top Left</code></td></tr><tr><td>WebGPU</td><td><code>Bottom Left</code></td></tr><tr><td>OpenGL</td><td><code>Bottom Left</code></td></tr></tbody></table>
<p>DirectX uses the upper <a href="https://docs.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-coordinates">left corner for pixel space coordinates</a>, as does most close source APIs, with open source opting to use the bottom left.</p>
<h2 id="conclusion">Conclusion</h2>
<p>While each of these APIs have their subtle differences, they are extremely close to one another in design. It&#x27;s up to library architects to decide on where the limits to their desired API surface area are, be it concise and simple like Metal/WebGPU, complex like Vulkan.</p>
<h3 id="additional-resources">Additional Resources</h3>
<p>Here&#x27;s a few frameworks, libraries, and blog posts focused on graphics api abstractions in no particular order:</p>
<h4 id="blog-posts">Blog Posts</h4>
<ul>
<li>
<p><a href="https://twitter.com/longbool">Alex Tardif</a>&#x27;s <a href="http://alextardif.com/RenderingAbstractionLayers.html">Opinionated Post on Modern Rendering Abstraction Layers</a></p>
</li>
<li>
<p>Gijs Richard Kaerts (<a href="https://twitter.com/BelgianRenderer">@BelgianRenderer</a>) released his <a href="http://www.gijskaerts.com/wordpress/?p=98">musings on cross-platform graphics engine architectures here</a>.</p>
</li>
</ul>
<h4 id="libraries">Libraries</h4>
<ul>
<li>
<p>Andre Weissflog (<a href="https://twitter.com/flohofwoe">@FlohOfWoe</a>) wrote <a href="https://github.com/floooh/sokol/blob/master/sokol_gfx.h">Sokol</a>, an <a href="https://github.com/nothings/stb/blob/master/docs/stb_howto.txt">STB (eg. <code>stb_image.h</code>)</a> style cross platform rendering library, with his <a href="https://floooh.github.io/archive/">blog</a> archiving his thoughts during development.</p>
</li>
<li>
<p><a href="https://twitter.com/bkaradzic">Branimir Karadzic</a>&#x27;s <a href="https://github.com/bkaradzic/bgfx">bgfx</a> library.</p>
</li>
<li>
<p><a href="https://dawn.googlesource.com/dawn">Dawn</a> is a WebGPU implementation that uses DirectX 12, Metal, Vulkan, or OpenGL as a possible backend.</p>
</li>
<li>
<p><a href="https://github.com/kvark">Dzmitry Malyshau</a>&#x27;s <a href="https://github.com/gfx-rs/gfx">gfx</a>, a rust abstraction library.</p>
</li>
<li>
<p>NVIDIA&#x27;s GameWorks division released <a href="https://github.com/NVIDIAGameWorks/Falcor">Falcor</a>, a research framework with a thin abstraction over Vulkan and DirectX 12.</p>
</li>
<li>
<p>NVIDIA&#x27;s <a href="https://twitter.com/more_fps">Alexey Panteleev (@more_fps)</a>, <a href="https://neilbickford.com/">Neil Bickford</a> (<a href="https://twitter.com/neilbickford">@neilbickford</a>) et al. also released the <a href="https://github.com/NVIDIAGameWorks/nvrhi">NVIDIA Render Hardware Interface (NVRHI)</a>, an abstraction layer on top of Vulkan/DirectX 12/11.</p>
</li>
<li>
<p>Wolfgang Engel (<a href="https://twitter.com/wolfgangengel">@wolfgangengel</a>) and his team released <a href="https://github.com/ConfettiFX/The-Forge">The Forge</a>, a cross platform rendering framework that supports DirectX 12, 11, Metal, or Vulkan.</p>
</li>
</ul>
<p>For a look at the driver level, some vendors release aspects of what the driver does under the hood with these calls:</p>
<ul>
<li><a href="https://github.com/GPUOpen-Drivers/pal">AMD&#x27;s Platform Abstraction Library (PAL) implementation is extremely similar to Vulkan and is Open Source</a>.</li>
</ul>
<hr/>
<p>For more details on each of the APIs discussed, here&#x27;s their specification pages:</p>
<ul>
<li>
<p><a href="https://www.khronos.org/registry/vulkan/specs/1.2/html/vkspec.html#preamble">The Vulkan 1.2 Specification</a></p>
</li>
<li>
<p><a href="https://docs.microsoft.com/en-us/windows/win32/direct3d12/direct3d-12-graphics">The DirectX 12 Programming Guide</a></p>
</li>
<li>
<p><a href="https://docs.microsoft.com/en-us/windows/win32/direct3d11/atoc-dx-graphics-direct3d-11">The DirectX 11 Programming Guide</a></p>
</li>
<li>
<p><a href="https://developer.apple.com/documentation/metal">Apple Metal Documentation</a></p>
</li>
<li>
<p><a href="https://gpuweb.github.io/gpuweb/">WebGPU Specification</a></p>
</li>
<li>
<p><a href="https://www.khronos.org/registry/OpenGL/specs/gl/glspec46.core.pdf">OpenGL 4.6 Core Specification</a></p>
</li>
<li>
<p><a href="https://www.khronos.org/registry/webgl/specs/latest/1.0/">WebGL 1.0 Specification</a></p>
</li>
</ul>
<hr/>
<p>In addition, you can find specific posts on every graphics API here:</p>
<ul>
<li>
<p><a href="https://alain.xyz/blog/raw-vulkan">Raw Vulkan</a></p>
</li>
<li>
<p><a href="https://alain.xyz/blog/raw-metal">Raw Metal</a></p>
</li>
<li>
<p><a href="https://alain.xyz/blog/raw-webgpu">Raw WebGPU</a></p>
</li>
<li>
<p><a href="https://alain.xyz/blog/raw-directx12">Raw DirectX 12</a></p>
</li>
<li>
<p><a href="https://alain.xyz/blog/raw-directx11">Raw DirectX 11</a></p>
</li>
<li>
<p><a href="https://alain.xyz/blog/raw-opengl">Raw OpenGL</a></p>
</li>
<li>
<p><a href="https://alain.xyz/blog/raw-webgl">Raw WebGL</a></p>
</li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_peddie2023"><span class="markademic-citename">[Peddie 2023]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">The History of the GPU - Steps to Invention</span></strong></em><br/><span class="markademic-authors"><span><span>Jon Peddie</span></span></span><br/><span><span class="markademic-publisher">Springer 2023</span><br/></span><span><a href="https://link.springer.com/book/10.1007/978-3-031-10968-3">link.springer.com</a></span></div></td></tr><tr><td id="ref_fatahalian2018"><span class="markademic-citename">[Fatahalian 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Visual Computing Systems</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/kayvonf.png" alt="Kayvon Fatahalian&#x27;s picture"/><a href="http://graphics.stanford.edu/~kayvonf/">Kayvon Fatahalian</a> (<a href="https://twitter.com/kayvonf">@kayvonf</a>)</span></span><br/><span><span class="markademic-publisher">Stanford 2018</span><br/></span><span><a href="http://graphics.stanford.edu/courses/cs348v-18-winter/">graphics.stanford.edu</a></span></div></td></tr><tr><td id="ref_russell2014"><span class="markademic-citename">[Russell 2014]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Rendering in Marmoset Toolbag</span></strong></em><br/><span class="markademic-authors"><span><a href="https://marmoset.co/">Jeff Russell</a> (<a href="https://twitter.com/j3ffdr">@j3ffdr</a>)</span></span><br/><span><span class="markademic-publisher">University of Pennsylvania 2014</span><br/></span><span><a href="http://cis700-003-spring-2014.github.io/lectures/mset_rendering_april29_2014.pdf">cis700-003-spring-2014.github.io</a></span></div></td></tr><tr><td id="ref_foley2015"><span class="markademic-citename">[Foley 2015]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Next-Generation Graphics APIs:  Similarities and Differences</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/tangent-vector.png" alt="Theresa Foley&#x27;s picture"/><a href="https://tangentvector.wordpress.com/">Theresa Foley</a> (<a href="https://twitter.com/tangentvector">@tangentvector</a>)</span></span><br/><span><span class="markademic-publisher">Siggraph 2015</span><br/></span><span><a href="http://nextgenapis.realtimerendering.com/">nextgenapis.realtimerendering.com</a></span></div></td></tr><tr><td id="ref_satran2018"><span class="markademic-citename">[Satran et al. 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Fence-Based Resource Management</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/msatranjr.png" alt="Michael Satran&#x27;s picture"/><a href="https://github.com/msatranjr">Michael Satran</a></span><span> and <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/stevewhims.png" alt="Steven White&#x27;s picture"/><a href="https://github.com/stevewhims">Steven White</a></span></span><br/><span><span class="markademic-publisher">Microsoft 2018</span><br/></span><span><a href="https://docs.microsoft.com/en-us/windows/win32/direct3d12/fence-based-resource-management">docs.microsoft.com</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Ray Tracing Denoising</title>
<link>https://alain.xyz/blog/ray-tracing-denoising</link>
<description>An overview of ray tracing denoising, reviewing filtering, spatiotemporal, sampling and AI techniques to improve convergence and temporal coherency in real time ray traced applications.</description>
<guid isPermaLink="true">https://alain.xyz/blog/ray-tracing-denoising</guid>
<pubDate>Tue, 06 Oct 2020 22:00:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>denoise</category><category>denosing</category><category>ray tracing</category><category>restir</category><category>filter</category><category>svgf</category><category>asvgf</category><category>a-trous</category><category>atrous</category><category>blue</category><category>noise</category>
<enclosure url="https://alain.xyz/blog/ray-tracing-denoising/assets/cover.jpg" length="343028" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-denoising/assets/4096spp.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-denoising/assets/bilateral.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-denoising/assets/1spp-oidn.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-denoising/assets/4spp.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-denoising/assets/lightprobes.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/machine-learning-denoising/assets/coronal-testscene/odin-denoise.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/machine-learning-denoising/assets/coronal-testscene/optix-denoise.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-denoising/assets/ideal-denoiser.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-denoising/assets/normal.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-denoising/assets/albedo.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-denoising/assets/depth.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-denoising/assets/position.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-denoising/assets/objid.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-denoising/assets/roughness.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-filtering/assets/motion-buffer.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-filtering/assets/history-length.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/machine-learning-denoising/assets/cornell-2048spp-nrm-direct.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/machine-learning-denoising/assets/cornell-2048spp-nrm-indirect.jpg"/><link rel="preload" as="image" href="https://github.com/ingowald.png"/><link rel="preload" as="image" href="https://github.com/xmeng525.png"/><link rel="preload" as="image" href="https://github.com/erich666.png"/><link rel="preload" as="image" href="https://github.com/NeoSpark314.png"/><link rel="preload" as="image" href="https://github.com/morgan3d.png"/><link rel="preload" as="image" href="https://github.com/tunabrain.png"/><link rel="preload" as="image" href="https://github.com/cschied.png"/><link rel="preload" as="image" href="https://github.com/yueruchen.png"/><link rel="preload" as="image" href="https://github.com/bmild.png"/><link rel="preload" as="image" href="https://github.com/jonbarron.png"/><link rel="preload" as="image" href="https://github.com/pratulsrinivasan.png"/><link rel="preload" as="image" href="https://github.com/rmbrualla/.png"/><link rel="preload" as="image" href="https://github.com/toomuchvoltage.png"/><link rel="preload" as="image" href="https://github.com/mmp.png"/><link rel="preload" as="image" href="https://github.com/gboisse.png"/><link rel="preload" as="image" href="https://github.com/Anteru.png"/><link rel="preload" as="image" href="https://github.com/yusuketokuyoshi.png"/><link rel="preload" as="image" href="https://github.com/jtalbot.png"/><link rel="preload" as="image" href="https://github.com/behindthepixels.png"/><p>Monte-carlo ray tracing is a technique that relies on accumulating random samples to reach a satisfying approximation of a given scene. This process was traditionally a <em>slow one</em> with plenty of variance, but with the advent of real time ray tracing solutions with recent graphics cards, there&#x27;s been a resurgence of research in denoising techniques.</p>
<p>These denoising techniques include <strong>filtering</strong> using guided blurring kernels, <strong>machine learning</strong> to drive filters or importance sampling, improving <strong>sampling</strong> schemes through better quasi-random sequences such as blue noise and spatio-temporal reuse of either rays or the final luminance, and <strong>approximation techniques</strong> that attempt to quantize and cache information with some sort of spatial structure such as probes, irradiance caches, neural radiance fields (NeRFs), etc.</p>
<p><img src="https://alain.xyz/blog/ray-tracing-denoising/assets/4096spp.png" alt="4096 sample per pixel example"/></p>
<p>A robust denoiser should consider using <em>all</em> of these techniques depending on the trade-offs and needs of your application.</p>
<p>Recent research has focused on moving denoising to earlier in rendering by improving sampling schemes and re-sampling pixels with cached information, with prior research focused on filtering, autoencoders in machine learning, importance sampling, and real time methods that are currently in production in commercial games and renderers. We&#x27;ll be discussing key papers on denoising and their implementations, <strong>focusing on how to build your own robust real time ray tracing denoiser</strong>.</p>
<blockquote>
<p>Denoising has seen a significant number of new publications since the release of this article, with techniques such as ReSTIR, NVIDIA&#x27;s denoising suite, and machine learning techniques improving significantly. This article could use some updates for these recent papers, but the ideas here are still applicable today.</p>
</blockquote>
<h2 id="prior-art">Prior Art</h2>
<p><img src="https://alain.xyz/blog/ray-tracing-denoising/assets/bilateral.png" alt="Bilateral Filter"/></p>
<p><strong>Filtering techniques</strong> such as <em>Gaussian</em>, <em>Bilateral</em>, <em>À-Trous</em> <a href="#ref_dammertz2010">[Dammertz et al. 2010]</a>, <em>Guided</em> <a href="#ref_he2012">[He et al. 2012]</a>, and <em>Median</em> <a href="#ref_mara2017">[Mara et al. 2017]</a> filters have been used to blur monte-carlo ray traced images. In particular Guided filters driven by <em>feature buffers</em> such as common G-Buffer attachments in Deferred Rendering like <em>normals</em>, <em>albedo</em>, <em>depth/position</em>, as well as specialized buffers such as <em>first-bounce data</em>, <em>reprojected path length</em>, or <em>view position</em> have been used in recent denoising papers and commercial implementations.</p>
<p>While these filtering techniques are effective and cheap to compute, they come at the cost of a lossy representation of the scene and the reduction of high frequency information like sharp edges. This loss of information can be so drastic that it can even lead to differences in the level of brightness in scenes with salt &amp; peppering occurring in either highlights or shadows.</p>
<p><img src="https://alain.xyz/blog/ray-tracing-denoising/assets/1spp-oidn.png" alt="1 Sample Per Pixel OIDN"/></p>
<p><strong>Machine Learning</strong> has been used in a wide range of problem domains related to denoising, from general image reconstruction to real time ray tracing denoising through the use of <em>denoising autoencoders</em> <a href="#ref_kalantari2013">[Khademi Kalantari et al. 2013]</a> <a href="#ref_kalantari2015">[Khademi Kalantari et al. 2015]</a> <a href="#ref_chaitanya2017">[R. Alla Chaitanya et al. 2017]</a> <a href="#ref_vogels2018">[Vogels et al. 2018]</a>, driving filtering techniques <a href="#ref_wang2018">[Wang et al. 2018]</a> <a href="#ref_xu2019">[Xu et al. 2019]</a> <a href="#ref_meng2020">[Meng et al. 2020]</a> <a href="#ref_isik2021">[Işık et al. 2021]</a>, varying convolution sizes to reduce computational costs <a href="#ref_jiang2022">[Jiang et al. 2022]</a>, spatio-temporal techniques <a href="#ref_hasselgren2020">[Hasselgren et al. 2020]</a>, NeRFs to approximate view dependent effects like reflections <a href="#ref_verbin2021">[Verbin et al. 2021]</a> or to denoise a scene <a href="#ref_mildenhall2022">[Mildenhall et al. 2022]</a>, and upscaling images while maintaining detail through super-resolution/super-sampling <a href="#ref_dong2015">[Dong et al. 2015]</a> <a href="#ref_ledig2016">[Ledig et al. 2016]</a> <a href="#ref_xiao2020">[Xiao et al. 2020]</a>. Denoising and supersampling can even be done together using a neural network <a href="#ref_thomas2022">[Mathew Thomas et al. 2022]</a>, with NVIDIA integrating both in <a href="https://www.nvidia.com/en-us/geforce/news/nvidia-dlss-3-5-ray-reconstruction/">DLSS 3.5</a>.</p>
<p>Industry leaders such as Intel and NVIDIA have sponsored research in machine learning based denoisers, <a href="https://openimagedenoise.github.io/">Intel Open Image Denoise</a> and the <a href="https://developer.nvidia.com/optix">NVIDIA Optix Autoencoder</a> both use a denoising autoencoder to denoise images to great success. <a href="https://www.nvidia.com/en-us/geforce/news/nvidia-dlss-2-0-a-big-leap-in-ai-rendering/">NVIDIA&#x27;s Deep Learning Super Sampling (DLSS 2.0)</a> has also been used to upscale ray traced applications such as Minecraft RTX, Remedy Entertainment’s <a href="https://controlgame.com/">Control</a>, and more, with the goal of reducing computational costs by upscaling to native resolutions a <em>fraction</em> of the original image.</p>
<p><img src="https://alain.xyz/blog/ray-tracing-denoising/assets/4spp.png" alt="1 Sample Per Pixel"/></p>
<p><strong>Sampling techniques</strong> have seen a resurgence in new literature. While a naive monte carlo ray tracer would simply accumulate samples on an unchanging scene, it&#x27;s possible to reuse samples in a moving scene. Examples include Temporal Anti-Aliasing <a href="#ref_korein1983">[Korein et al. 1983]</a> <a href="#ref_lei2020">[Yang et al. 2020]</a>, the Spatio-Temporal Filter <a href="#ref_mara2017">[Mara et al. 2017]</a>, the Spatio-Temporal Variance Guided Filter (SVGF) <a href="#ref_schied2017">[Schied 2017]</a>, Spatial Denoising employed by <a href="#ref_baktash2018">[Abdollah-shamshir-saz 2018]</a>, Adaptive SVGF (A-SVGF) <a href="#ref_schied2018">[Schied et al. 2018]</a>, Blockwise Multi-Order Feature Regression (BMFR) <a href="#ref_koskela2019">[Koskela et al. 2019]</a>).</p>
<p>These techniques would rely on using high frequency quasi-random sequences such as blue noise (which would be used in combination with filtering) <a href="#ref_benyoub2019">[Benyoub 2019]</a> , as well as common tools such as firefly rejection <a href="#ref_liu2019">[Liu et al. 2019]</a>, next event estimation (NEE), and importance sampling <a href="#ref_veach1998">[Veach 1998]</a>.</p>
<p>There have also been attempts to move denoising to earlier in rendering by making sampling less biased through ray hashing <a href="#ref_pantaleoni2020">[Pantaleoni 2020]</a> or reusing statistics from neighboring sampling probabilities <a href="#ref_bitterli2020">[Bitterli et al. 2020]</a>. Variations of these techniques have emerged that target a smaller subset of ray tracing such as Global Illumination <a href="#ref_boisse2021">[Boisse 2021]</a> and motion blur <a href="#ref_oberberger2022">[Oberberger et al. 2022]</a>, or using tiles for point lights <a href="#ref_tokuyoshi2022">[Tokuyoshi 2022]</a>.</p>
<p><img src="https://alain.xyz/blog/ray-tracing-denoising/assets/lightprobes.png" alt="Light Probes"/></p>
<p>In addition, there&#x27;s <strong>approximation techniques</strong> that attempt to fine tune behavior for different aspects of a path tracer. The <em>RTX Global Illumination</em> paper  approximated ray traced global illumination (GI) with light probes which used ray tracing to better determine the proper radiance of each probe and to position probes in the scene to avoid bleeding or inaccurate interiors. Techniques that can avoid screen space monte carlo integration entirely such as RTXGI also avoid the need for real time denoising routines <em>but can be used together with screen space techniques</em>. RTXGI have been recently integrated to commercial game engines such as Unreal Engine 4 and Unity <a href="#ref_majercik2020">[Majercik et al. 2020]</a>.</p>
<h2 id="sampling">Sampling</h2>
<h3 id="svgf">SVGF</h3>
<p>The <strong>Spatio-Temporal Variance Guided Filter (SVGF)</strong> <a href="#ref_schied2017">[Schied 2017]</a> is a denoiser that uses spatio-temporal reprojection along with feature buffers like normals, depth, and variance calculations to drive a bilateral filter to blur regions of high variance.</p>
<p><a href="https://alain.xyz/blog/frame-analysis-minecraftrtx">Minecraft RTX</a> uses a form of SVGF, with the addition of irradiance caching, the use of ray length for better driving reflections, and splitting rendering for transmissive surfaces such as water. SVGF, while very effective, does introduce temporal lag that is <a href="https://twitter.com/NateMorrical/status/1262187056911970306">noticable in game</a>.</p>
<h3 id="a-svgf">A-SVGF</h3>
<p><strong>Adaptive Spatio-Temporal Variance Guided Filtering (A-SVGF)</strong> <a href="#ref_schied2018">[Schied et al. 2018]</a> improves on SVGF by adaptively reusing previous samples that have been spatially reprojected according to temporal hueristics such as the change in variance, viewing angle, etc. encoded in a <em>Moment Buffer</em>, and filtering that with a fast bilateral filter. So rather than accumulating samples based on history length, the moment buffer acts as an alternative hueristic that uses the change in variance to drive the proportion of old samples and newer samples, resulting in less ghosting. While SVGF only used the moment buffer to drive blurring, A-SVGF uses it for both the filtering and accumulation steps.</p>
<p>Though the introduction of a moment buffer helps with temporal lag it doesn&#x27;t completely get rid of it. There can be differences in brightness between regions with a high number of accumulated samples and new regions. This is especially evident in darker regions of a raytraced scene such as interiors. To mitigate this, rather than using 1 sample per pixel (1 spp), it&#x27;s best to use 2 spp in dark areas of a scene.</p>
<p><a href="https://store.steampowered.com/app/1089130/Quake_II_RTX/">Quake 2 RTX</a> uses A-SVGF as its denoising solution.</p>
<h3 id="restir">ReSTIR</h3>
<p><strong>Spatiotemporal Importance Resampling for Many-Light Ray Tracing (ReSTIR)</strong> <a href="#ref_bitterli2020">[Bitterli et al. 2020]</a> attempts to move the spatio-temporal reprojection step of real time denoisers to earlier in rendering, reusing statistics from neighboring sampling probabilities. This is essentially a combination of an earlier paper by <a href="#ref_talbot2005">[Talbot et al. 2005]</a> discussing Resampled Importance Sampling, and adding ideas introduced by spatio-temporal denoisers.</p>
<p>ReSTIR will be available for use in <a href="https://news.developer.nvidia.com/render-millions-of-direct-lights-in-real-time-with-rtx-direct-illumination-rtxdi/">NVIDIA&#x27;s RTXDI SDK</a>.</p>
<h2 id="machine-learning">Machine Learning</h2>
<blockquote>
<p>These remarkable results show yet another example of how rapidly DNNs are finding their place in and evolving modern renders. ~ Marco Salvi (<a href="https://twitter.com/marcosalvi/status/1304106953283530752">@marcosalvi</a>)</p>
</blockquote>
<p>Machine learning techniques such as denoising autoencoders, sample map estimators driving sample counts or importance sampling, neural bilateral grids driving filtering, and super-sampling result in the most drastic improvements in image quality, though these techniques are slower than other algorithms such as A-SVGF.</p>
<h3 id="intel-oidn">Intel OIDN</h3>
<p><img src="https://alain.xyz/blog/machine-learning-denoising/assets/coronal-testscene/odin-denoise.jpg" alt="OIDN Denoise"/></p>
<p>Intel Open Image Denoise (OIDN) is a machine learning autoencoder that takes in a albedo, first bounce normals, and your input noisy image and output a filtered image.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare Handles</span>

<span class="hljs-comment">// Images loaded from stb as 3 components</span>
<span class="hljs-type">float</span>* color;
<span class="hljs-type">float</span>* normal;
<span class="hljs-type">float</span>&amp; output;
<span class="hljs-type">unsigned</span> width;
<span class="hljs-type">unsigned</span> height;

oidn::DeviceRef device = oidn::<span class="hljs-built_in">newDevice</span>();
device.<span class="hljs-built_in">commit</span>();
oidn::FilterRef filter = device.<span class="hljs-built_in">newFilter</span>(<span class="hljs-string">&quot;RT&quot;</span>);
filter.<span class="hljs-built_in">setImage</span>(<span class="hljs-string">&quot;color&quot;</span>, color, oidn::Format::Float3, width, height);
filter.<span class="hljs-built_in">setImage</span>(<span class="hljs-string">&quot;normal&quot;</span>, normal, oidn::Format::Float3, width, height);
filter.<span class="hljs-built_in">setImage</span>(<span class="hljs-string">&quot;output&quot;</span>, output, oidn::Format::Float3, width, height);
filter.<span class="hljs-built_in">set</span>(<span class="hljs-string">&quot;hdr&quot;</span>, <span class="hljs-literal">true</span>);
filter.<span class="hljs-built_in">commit</span>();
filter.<span class="hljs-built_in">execute</span>();</code></pre></div>
<h3 id="nvidia-optix">NVIDIA Optix</h3>
<p><img src="https://alain.xyz/blog/machine-learning-denoising/assets/coronal-testscene/optix-denoise.jpg" alt="Optix Denoise"/></p>
<p>NVIDIA&#x27;s <strong>Optix 7 Denoising Autoencoder</strong> <a href="#ref_chaitanya2017">[R. Alla Chaitanya et al. 2017]</a> takes in the same inputs as OIDN, an optional albedo, normal, and input noisy image, and outputs a filtered image much faster than Intel&#x27;s solution at the cost of quality.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare Handles</span>
OptixContext ctx;
OptixDevice device;
<span class="hljs-type">unsigned</span> width;
<span class="hljs-type">unsigned</span> height;
CUDABuffer denoiserState;
CUDABuffer denoiserScratch;

<span class="hljs-comment">// https://github.com/ingowald/optix7course/blob/master/example12_denoiseSeparateChannels/SampleRenderer.cpp#L764</span>
OptixDenoiser denoiser;
OptixDenoiserOptions opt;
opt.inputKind = OPTIX_DENOISER_INPUT_RGB_ALBEDO;
opt.pixelFormat = OPTIX_PIXEL_FORMAT_FLOAT4;
<span class="hljs-built_in">optixDenoiserCreate</span>(ctx, &amp;opt, &amp;denoiser);
<span class="hljs-built_in">optixDenoiserSetModel</span>(denoiser, OPTIX_DENOISER_MODEL_KIND_HDR, <span class="hljs-literal">nullptr</span>, <span class="hljs-number">0</span>);
OptixDenoiserSizes denoiserReturnSizes;
<span class="hljs-built_in">optixDenoiserComputeMemoryResources</span>(denoiser, width, height, &amp;denoiserReturnSizes);
denoiserState.<span class="hljs-built_in">resize</span>(denoiserReturnSizes.stateSizeInBytes);
<span class="hljs-built_in">optixDenoiserSetup</span>(denoiser,<span class="hljs-number">0</span>,
                width, height,
                denoiserState.<span class="hljs-built_in">d_pointer</span>(),
                denoiserState.<span class="hljs-built_in">size</span>(),
                denoiserScratch.<span class="hljs-built_in">d_pointer</span>(),
                denoiserScratch.<span class="hljs-built_in">size</span>());</code></pre></div>
<h3 id="dlss">DLSS</h3>
<p>Deep Learning Super Sampling (DLSS) is an upscaling technique that that uses a small color buffer and a direction map to multiply the resolution of the output 2-4 times. This is exclusive to developers pre-approved by NVIDIA, so there&#x27;s currently no way to use this publicly, that being said there&#x27;s alternatives such as <a href="https://github.com/microsoft/DirectML/tree/master/Samples/DirectMLSuperResolution/Samples/ML/DirectMLSuperResolution">DirectML&#x27;s SuperResolution Sample</a>.</p>
<h2 id="denoiser-design">Denoiser Design</h2>
<p><img src="https://alain.xyz/blog/ray-tracing-denoising/assets/ideal-denoiser.svg" alt="SVGF Execution Diagram"/></p>
<p>An ideal denoiser that combines ideas from the latest state of the art papers could look like the following:</p>
<ol>
<li>
<p><strong>Prepass</strong> - Calculate the NDC space velocity of the scene, write common G-Buffer attachments such as albedo, normals etc. You may also want first bounce versions of those buffers, which would require a ray-trace based prepass rather than a raster based one.</p>
</li>
<li>
<p><strong>Ray Trace</strong> - Use AI Adaptive sampling from <a href="#ref_kuznetsov2018">[Kuznetsov et al. 2018]</a> <a href="#ref_hasselgren2020">[Hasselgren et al. 2020]</a> with a sample map to better determine which regions should recieve more samples, generally highlights/shadows to help avoid salt/peppering and maintain luminance over time. A split denoiser with Specular Reflections and Global Illumination written to separate attachments would be ideal as <em>Reflection</em> denoising would do better 1st bounce data, <em>Global Illumination/Ambient Occlusion/Shadows</em> can afford to use simpler spatio-temporal accumulation based off less data.</p>
</li>
<li>
<p><strong>Accumulation</strong> - Use Spatio-Temporal Reprojection as often as possible, this is easier to do with lambertian data such as Global Illumination/Ambient Occlusion, and harder with specular data like reflections. For better results, use heuristic data like normals/albedo/object IDs to translate previous samples to the current position, as well as 1st bounce data such as view direction, 1st bounce normals/albedo, etc. Any successful reprojections can then be used to either importance sample <a href="#ref_bitterli2020">[Bitterli et al. 2020]</a> or their radiance encoded to a radiance history buffer <a href="#ref_schied2018">[Schied et al. 2018]</a>.</p>
</li>
<li>
<p><strong>Statistical Analysis</strong> - estimate the variance of the current ray traced image, calculate the change in variance in luminance/velocity and use that to drive spatio-temporal reprojection and filtering. Attempt to reject fireflies with that variance information.</p>
</li>
<li>
<p><strong>Filtering</strong> - This can be done quickly with an À-Trous bilateral filter, repeat this step 3-5 times depending on how strong of a blur you want, and decrease the <code>stepWidth</code> by <a href="https://twitter.com/NateMorrical/status/1180302300549500928">a power of 2 each time</a> (so a sequence of <code>4</code>, <code>2</code>, <code>1</code> in the case of 3 iterations). Alternatively, you could use a denoising autoencoder which will be slower, but can produce better filtering results. That result could then be fed into a super sampling autoencoder that could upscale your results, similar to NVIDIA&#x27;s DLSS 2.0.</p>
</li>
<li>
<p><strong>History Blit</strong> - Write the current prepass data such as <em>Albedos</em>, <em>Depth</em>, etc. for reprojection next frame.</p>
</li>
</ol>
<p>NVIDIA released an example implementation of a denoiser similar to this that uses ReSTIR <a href="#ref_wyman2021">[Wyman et al. 2021]</a>.</p>
<h3 id="prepass">Prepass</h3>
<table><thead><tr><th style="text-align:center"></th><th style="text-align:center"></th></tr></thead><tbody><tr><td style="text-align:center"><img src="https://alain.xyz/blog/ray-tracing-denoising/assets/normal.png" alt="Normal"/></td><td style="text-align:center"><img src="https://alain.xyz/blog/ray-tracing-denoising/assets/albedo.png" alt="Albedo"/></td></tr><tr><td style="text-align:center"><img src="https://alain.xyz/blog/ray-tracing-denoising/assets/depth.png" alt="Depth"/></td><td style="text-align:center"><img src="https://alain.xyz/blog/ray-tracing-denoising/assets/position.png" alt="Position"/></td></tr><tr><td style="text-align:center"><img src="https://alain.xyz/blog/ray-tracing-denoising/assets/objid.png" alt="Object ID"/></td><td style="text-align:center"><img src="https://alain.xyz/blog/ray-tracing-denoising/assets/roughness.png" alt="Roughness"/></td></tr></tbody></table>
<p>Prior to denoising, it&#x27;s important to encode material information such as Normals, Albedo, Depth/Position, Object ID, Roughness/Metalness, etc. with some sort of General Pass (G-Pass). In addition, having access to velocity makes it possible to translate previous samples to the current position.</p>
<p>A <strong>Velocity Buffer</strong> can be calculated by determining the previous and current NDC space coordinate positions of each vertex being rendered, and taking the difference of the two.</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>V</mi><mo>⃗</mo></mover><mo>=</mo><msub><mover accent="true"><mrow><mi>N</mi><mi>D</mi><mi>C</mi></mrow><mo>⃗</mo></mover><mrow><mi>c</mi><mi>u</mi><mi>r</mi></mrow></msub><mo>−</mo><msub><mover accent="true"><mrow><mi>N</mi><mi>D</mi><mi>C</mi></mrow><mo>⃗</mo></mover><mrow><mi>p</mi><mi>r</mi><mi>e</mi><mi>v</mi></mrow></msub></mrow><annotation encoding="application/x-tex">\vec{V} = \vec{NDC}_{cur} - \vec{NDC}_{prev}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9663em"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9663em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.2222em">V</span></span><span style="top:-3.2523em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.2355em"><span class="overlay" style="height:0.714em;width:0.471em"><svg xmlns="http://www.w3.org/2000/svg" width="0.471em" height="0.714em" style="width:0.471em" viewBox="0 0 471 714" preserveAspectRatio="xMinYMin"><path d="M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z"></path></svg></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.1163em;vertical-align:-0.15em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9663em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.109em">N</span><span class="mord mathnormal" style="margin-right:0.0278em">D</span><span class="mord mathnormal" style="margin-right:0.0715em">C</span></span></span><span style="top:-3.2523em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.2355em"><span class="overlay" style="height:0.714em;width:0.471em"><svg xmlns="http://www.w3.org/2000/svg" width="0.471em" height="0.714em" style="width:0.471em" viewBox="0 0 471 714" preserveAspectRatio="xMinYMin"><path d="M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z"></path></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">c</span><span class="mord mathnormal mtight">u</span><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.2524em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9663em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.109em">N</span><span class="mord mathnormal" style="margin-right:0.0278em">D</span><span class="mord mathnormal" style="margin-right:0.0715em">C</span></span></span><span style="top:-3.2523em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.2355em"><span class="overlay" style="height:0.714em;width:0.471em"><svg xmlns="http://www.w3.org/2000/svg" width="0.471em" height="0.714em" style="width:0.471em" viewBox="0 0 471 714" preserveAspectRatio="xMinYMin"><path d="M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z"></path></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">p</span><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span><span class="mord mathnormal mtight">e</span><span class="mord mathnormal mtight" style="margin-right:0.0359em">v</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span></span>
<p>Therefore, one would need the previous <code>modelViewProjection</code> matrix of an object, as well as that object&#x27;s <em>animation vertex velocity</em>, the difference between in position between the current and previous animation sample.</p>
<p><img src="https://alain.xyz/blog/ray-tracing-filtering/assets/motion-buffer.jpg" alt="Motion Buffer Example"/></p>
<div><pre><code class="language-hlsl"><span class="hljs-comment">// 🏃 NDC space velocity</span>
<span class="hljs-type">float3</span> ndc = inPosition.xyz / inPosition.w;
<span class="hljs-type">float3</span> ndcPrev = inPositionPrev.xyz / inPositionPrev.w;
outVelocity = ndc.xy - ndcPrev.xy;</code></pre></div>
<p>It&#x27;s possible to take this concept further, such as using a motion vector for first bounce glossy reflections, a shadow motion vector for better shadow reprojection while objects are moving, and even dual motion vectors for occlusions. <a href="#ref_yan2021">[Zeng et al. 2021]</a></p>
<h3 id="accumulation">Accumulation</h3>
<p><strong>Spatiotemporal reprojection</strong> is reusing the data from previous frames, <em>spatially</em> reprojecting them to the current frame. Translating previous samples to the current frame requires that you first find the coordinates in view space for previous frame data, which can be done by just adding the velocity buffer. By comparing difference between the current position/normal/object ID/etc of this screen space coordinate with that of it&#x27;s previous, you can tell if an object has been previously occluded and is now in view, or reuse previous samples.</p>
<p><img src="https://alain.xyz/blog/ray-tracing-filtering/assets/history-length.jpg" alt="Normalized history buffer"/></p>
<p>When performing spatio-temporal reprojection, having a buffer describing the time for which a given sample had to accumulate is very valuable, a <strong>History Buffer</strong>. It can be used to drive a filter to blur stronger in regions with few accumulated samples or be used to estimate the variance of the current image (higher history would mean less variance).</p>
<div><pre><code class="language-hlsl">outHistoryLength = successfulReprojection ? prevHistoryLength + <span class="hljs-number">1.0</span> : <span class="hljs-number">0.0</span>;</code></pre></div>
<p>Your history length can then be used as an <em>accumulation factor</em> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi></mrow><annotation encoding="application/x-tex">\alpha</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0037em">α</span></span></span></span>, the contribution factor that current samples should have over the final radiance.</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>C</mi><mo>∗</mo><mrow><mi>f</mi><mi>i</mi><mi>n</mi><mi>a</mi><mi>l</mi></mrow><mo>=</mo><mo stretchy="false">(</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>α</mi><mo stretchy="false">)</mo><mo>∗</mo><mi>C</mi><mo>∗</mo><mrow><mi>p</mi><mi>r</mi><mi>e</mi><mi>v</mi><mi>i</mi><mi>o</mi><mi>u</mi><mi>s</mi></mrow><mo stretchy="false">)</mo><mo>+</mo><mo stretchy="false">(</mo><mi>α</mi><mo>∗</mo><mi>C</mi><mi mathvariant="normal">_</mi><mrow><mi>c</mi><mi>u</mi><mi>r</mi><mi>r</mi><mi>e</mi><mi>n</mi><mi>t</mi></mrow><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">C*{final} = ( (1 - \alpha ) \ast C*{previous} ) + ( \alpha \ast C\_{current} )</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0715em">C</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1076em">f</span><span class="mord mathnormal">ina</span><span class="mord mathnormal" style="margin-right:0.0197em">l</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">((</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0037em">α</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0715em">C</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal">p</span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.0359em">v</span><span class="mord mathnormal">i</span><span class="mord mathnormal">o</span><span class="mord mathnormal">u</span><span class="mord mathnormal">s</span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0037em">α</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.06em;vertical-align:-0.31em"></span><span class="mord mathnormal" style="margin-right:0.0715em">C</span><span class="mord" style="margin-right:0.0278em">_</span><span class="mord"><span class="mord mathnormal">c</span><span class="mord mathnormal">u</span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mord mathnormal">e</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span></span><span class="mclose">)</span></span></span></span></span>
<div><pre><code class="language-hlsl">outColor = <span class="hljs-built_in">lerp</span>(colorPrevious, colorCurrent, accumulationFactor);</code></pre></div>
<p>While a history buffer is a useful thing to have, there&#x27;s better ways of determining an accumulation factor than the ratio of successful reprojections such as the use of the change in luminance, we can use statistical analysis to prevent temporal lag instead.</p>
<h3 id="statistical-analysis">Statistical Analysis</h3>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>s</mi><mi>i</mi><mi>g</mi><mi>m</mi><msup><mi>a</mi><mn>2</mn></msup><mo>=</mo><mfrac><mrow><mi mathvariant="normal">Σ</mi><mo stretchy="false">(</mo><mi>x</mi><mo>−</mo><mover accent="true"><mi>x</mi><mo>^</mo></mover><msup><mo stretchy="false">)</mo><mn>2</mn></msup></mrow><mi>n</mi></mfrac></mrow><annotation encoding="application/x-tex">sigma^2 = \frac{\Sigma(x - \hat{x})^2}{n}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0585em;vertical-align:-0.1944em"></span><span class="mord mathnormal">s</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.0359em">g</span><span class="mord mathnormal">m</span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.1771em;vertical-align:-0.686em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.4911em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal">n</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">Σ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">x</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.2222em"><span class="mord">^</span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span>
<p><strong>Variance</strong> is the squared difference of a signal&#x27;s average (mean). One can take the average of the current signal and that signal squared with a 3x3 gaussian kernel (what is essentially a tensor, <a href="https://t.co/SviUAUe897?amp=1">Bart Wronski</a> (<a href="https://twitter.com/BartWronsk">@BartWronsk</a>) <a href="https://bartwronski.com/2021/02/28/computing-gradients-on-grids-forward-central-and-diagonal-differences/">goes into more detail as to different ways of writing tensor operations here</a>), then taking the difference of the two.</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi>σ</mi><mn>2</mn></msup><mo>=</mo><mfrac><mrow><mi mathvariant="normal">Σ</mi><msup><mi>x</mi><mn>2</mn></msup></mrow><mi>n</mi></mfrac><mo>−</mo><msup><mover accent="true"><mi>x</mi><mo>^</mo></mover><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">\sigma^2 = \frac{\Sigma x^2}{n} - \hat{x}^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8641em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">σ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.1771em;vertical-align:-0.686em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.4911em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal">n</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">Σ</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.8641em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">x</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.2222em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span></span>
<div><pre><code class="language-hlsl"><span class="hljs-keyword">const</span> <span class="hljs-type">float</span> radius = <span class="hljs-number">2</span>; <span class="hljs-comment">//5x5 kernel</span>
<span class="hljs-type">float2</span> sigmaVariancePair = <span class="hljs-type">float2</span>(<span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>);
<span class="hljs-type">float</span> sampCount = <span class="hljs-number">0.0</span>;
<span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> y = -radius; y &lt;= radius; ++y)
{
    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x = -radius; x &lt;= radius; ++x)
    {
        <span class="hljs-comment">// ⬇️ Sample current point data with current uv</span>
        <span class="hljs-type">int2</span> p = ipos + <span class="hljs-type">int2</span>(xx, yy);
        <span class="hljs-type">float4</span> curColor = tColor.Load(p);

        <span class="hljs-comment">// 💡 Determine the average brightness of this sample</span>
        <span class="hljs-comment">// 🌎 Using International Telecommunications Union&#x27;s ITU BT.601 encoding params</span>
        <span class="hljs-type">float</span> samp = luminance(curColor);
        <span class="hljs-type">float</span> sampSquared = samp * samp;
        sigmaVariancePair += <span class="hljs-type">float2</span>(samp, sampSquared);

        sampCount += <span class="hljs-number">1.0</span>;
    }
}
sigmaVariancePair /= sampCount;
<span class="hljs-type">float</span> variance = <span class="hljs-built_in">max</span>(<span class="hljs-number">0.0</span>, sigmaVariancePair.y - sigmaVariancePair.x * sigmaVariancePair.x);</code></pre></div>
<p>Christoph Schied (<a href="https://twitter.com/c_schied">@c_schied</a>) does this in A-SVGF estimating the spatial variance as a combination of an edge avoiding guassian filter (similar to the a-trous guided filter) and using this in a feedback loop to drive the <code>accumulationFactor</code> during spatio-temporal reprojection. In addition to managing accumulation, estimating variance can also allow for you to tone down the weight of your filter temporally, <a href="#ref_olejnik2020">[Olejnik et al. 2020]</a> uses a poisson disk filter similar to A-SVGF to better render contact shadows.</p>
<div><pre><code class="language-hlsl"><span class="hljs-comment">/**
 * Variance Estimation
 * Copyright (c) 2018, Christoph Schied
 * All rights reserved.
 * 🎓 Slightly simplified for this example:
 */</span>

<span class="hljs-comment">// ⛏️ Setup</span>
<span class="hljs-type">float</span> weightSum = <span class="hljs-number">1.0</span>;
<span class="hljs-type">int</span> radius = <span class="hljs-number">3</span>; <span class="hljs-comment">// ⚪ 7x7 Gaussian Kernel</span>
<span class="hljs-type">float2</span> moment = tMomentPrev.Load(ipos).rg;
<span class="hljs-type">float4</span> c = tColor.Load(ipos);
<span class="hljs-type">float</span> histlen = tHistoryLength, ipos, <span class="hljs-number">0</span>).r;

<span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> yy = -radius; yy &lt;= radius; ++yy)
{
    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> xx = -radius; xx &lt;= radius; ++xx)
    {
        <span class="hljs-comment">// ☝️ We already have the center data</span>
        <span class="hljs-keyword">if</span> (xx != <span class="hljs-number">0</span> &amp;&amp; yy != <span class="hljs-number">0</span>) { <span class="hljs-keyword">continue</span>; }

        <span class="hljs-comment">// ⬇️ Sample current point data with current uv</span>
        <span class="hljs-type">int2</span> p = ipos + <span class="hljs-type">int2</span>(xx, yy);
        <span class="hljs-type">float4</span> curColor = tColor.Load(p);
        <span class="hljs-type">float</span> curDepth = tDepth.Load(p).x;
        <span class="hljs-type">float3</span> curNormal = tNormal.Load(p).xyz;

        <span class="hljs-comment">// 💡 Determine the average brightness of this sample</span>
        <span class="hljs-comment">// 🌎 Using International Telecommunications Union&#x27;s ITU BT.601 encoding params</span>
        <span class="hljs-type">float</span> l = luminance(curColor.rgb);

        <span class="hljs-type">float</span> weightDepth = <span class="hljs-built_in">abs</span>(curDepth - depth.x) / (depth.y * <span class="hljs-built_in">length</span>(<span class="hljs-type">float2</span>(xx, yy)) + <span class="hljs-number">1.0e-2</span>);
        <span class="hljs-type">float</span> weightNormal = <span class="hljs-built_in">pow</span>(<span class="hljs-built_in">max</span>(<span class="hljs-number">0</span>, <span class="hljs-built_in">dot</span>(curNormal, normal)), <span class="hljs-number">128.0</span>);

        <span class="hljs-type">uint</span> curMeshID =  floatBitsToUint(tMeshID, p, <span class="hljs-number">0</span>).r);

        <span class="hljs-type">float</span> w = <span class="hljs-built_in">exp</span>(-weightDepth) * weightNormal * (meshID == curMeshID ? <span class="hljs-number">1.0</span> : <span class="hljs-number">0.0</span>);

        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">isnan</span>(w))
            w = <span class="hljs-number">0.0</span>;

        weightSum += w;

        moment += <span class="hljs-type">float2</span>(l, l * l) * w;
        c.rgb += curColor.rgb * w;
    }
}

moment /= weightSum;
c.rgb /= weightSum;

varianceSpatial = (<span class="hljs-number">1.0</span> + <span class="hljs-number">2.0</span> * (<span class="hljs-number">1.0</span> - histlen)) * <span class="hljs-built_in">max</span>(<span class="hljs-number">0.0</span>, moment.y - moment.x * moment.x);
outFragColor = <span class="hljs-type">float4</span>(c.rgb, (<span class="hljs-number">1.0</span> + <span class="hljs-number">3.0</span> * (<span class="hljs-number">1.0</span> - histlen)) * <span class="hljs-built_in">max</span>(<span class="hljs-number">0.0</span>, moment.y - moment.x * moment.x));</code></pre></div>
<h4 id="firefly-rejection">Firefly Rejection</h4>
<p>Firefly rejection can be done in a variety of ways, from adjusting how you&#x27;re sampling during raytracing, to using filtering techniques or huristics about your output radiance.</p>
<h5 id="increase-roughness-per-bounce">Increase Roughness Per Bounce</h5>
<div><pre><code class="language-hlsl"><span class="hljs-comment">//https://twitter.com/YuriyODonnell/status/1199253959086612480</span>
<span class="hljs-comment">//http://cg.ivd.kit.edu/publications/p2013/PSR_Kaplanyan_2013/PSR_Kaplanyan_2013.pdf</span>
<span class="hljs-comment">//http://jcgt.org/published/0007/04/01/paper.pdf</span>
<span class="hljs-type">float</span> oldRoughness = payload.roughness;
payload.roughness = <span class="hljs-built_in">min</span>(<span class="hljs-number">1.0</span>, payload.roughness + roughnessBias);
roughnessBias += oldRoughness * <span class="hljs-number">0.75f</span>;</code></pre></div>
<h5 id="clamp-rejection">Clamp Rejection</h5>
<div><pre><code class="language-hlsl"><span class="hljs-comment">// 🗜️ Ray Tracing Gems Chapter 17</span>
<span class="hljs-type">float3</span> fireflyRejectionClamp(<span class="hljs-type">float3</span> radiance, <span class="hljs-type">float3</span> maxRadiance)
{
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">min</span>(radiance, maxRadiance);
}</code></pre></div>
<h5 id="variance-rejection">Variance Rejection</h5>
<div><pre><code class="language-hlsl"><span class="hljs-comment">// 🧯 Ray Tracing Gems Chapter 25</span>
<span class="hljs-type">float3</span> fireflyRejectionVariance(<span class="hljs-type">float3</span> radiance, <span class="hljs-type">float3</span> variance, <span class="hljs-type">float3</span> shortMean, <span class="hljs-type">float3</span> dev)
{
    <span class="hljs-type">float3</span> dev = <span class="hljs-built_in">sqrt</span>(<span class="hljs-built_in">max</span>(<span class="hljs-number">1.0e-5</span>, variance));
    <span class="hljs-type">float3</span> highThreshold = <span class="hljs-number">0.1</span> + shortMean + dev * <span class="hljs-number">8.0</span>;
    <span class="hljs-type">float3</span> overflow = <span class="hljs-built_in">max</span>(<span class="hljs-number">0.0</span>, radiance - highThreshold);
    <span class="hljs-keyword">return</span> radiance - overflow;
}</code></pre></div>
<h3 id="filtering">Filtering</h3>
<p>A-Trous avoids sampling in a slightly dithered pattern to cover a wider radius than would normally be possible in a 3x3 or 5x5 guassian kernel while at the same time having the ability to be repeated multiple times, and avoid bluring across edges thanks number of different inputs.</p>
<p>This can be done in combination with:</p>
<ul>
<li>
<p>Subsampling according to a dithering pattern, thus reducing the number of samples in your bluring kernel even more.</p>
</li>
<li>
<p>Drive your blur with more information such as surface roughness <a href="#ref_baktash2018">[Abdollah-shamshir-saz 2018]</a>, the aproximate Specular BRDF lobe <a href="#ref_tokuyoshi2015">[Tokuyoshi 2015]</a>, shadow penumbras <a href="#ref_liu2019">[Liu et al. 2019]</a>, etc.</p>
</li>
</ul>
<h3 id="limitations">Limitations</h3>
<p>All of these classes of algorithms rely on reusing data, thus when that isn&#x27;t possible, such as in the case of fast moving objects, highly complex geometry, or areas with little history information, the quality of each method decreases. There are ways to make use of some cached data to help avoid this, such as using irradiance caching to have a better default color as in the case of Minecraft RTX.</p>
<table><thead><tr><th style="text-align:center">Direct Normals</th><th style="text-align:center">First Bounce Normals</th></tr></thead><tbody><tr><td style="text-align:center"><img src="https://alain.xyz/blog/machine-learning-denoising/assets/cornell-2048spp-nrm-direct.jpg" alt="Direct Normals"/></td><td style="text-align:center"><img src="https://alain.xyz/blog/machine-learning-denoising/assets/cornell-2048spp-nrm-indirect.jpg" alt="Indirect Normals"/></td></tr></tbody></table>
<p>Spatio-temporal reprojection is also significantly more difficult with reflections, so often times denoisers will rely on <em>first-bounce</em> data, where the normals of a reflective surface, positional data, etc. are based on the first reflection rather than the original surface.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Denoising can help bridge the gap between a low sample per pixel image and ground truth by <em>reusing previous samples</em> through spatio-temporal reprojection - adaptively resampling radiance or statistical information for importance sampling, and using <em>filters</em> such as fast gaussian/bilateral filters or <em>AI techniques</em> like denoising autoencoders and upscaling though super sampling.</p>
<p>While denoising isn&#x27;t perfect as temporal techniques can introduce a lag in radiance and any filter will introduce some loss of sharpness due to it attempting to blur the original image, guided filters can help maintain sharpness, and adaptively sampling or increasing samples per pixels for each frame can make the difference between denoised and ground truth images negligable. Still, there&#x27;s no substitute for higher samples per pixel, so experiment with these techniques with different sample per pixel (spp) counts.</p>
<h3 id="samples">Samples</h3>
<ul>
<li>
<p>Dihara Wijetunga (<a href="https://twitter.com/diharaw94">@diharaw94</a>) <a href="https://diharaw.github.io/post/adventures_in_hybrid_rendering/">released a blog post that discusses denoising different aspects of a hybrid renderer</a>.</p>
</li>
<li>
<p>NVIDIA released their Real-Time Denoiser for limited review. You can sign up to view it <a href="https://developer.nvidia.com/nvidia-rt-denoiser">here</a>.</p>
</li>
<li>
<p><a href="https://research.nvidia.com/publication/2021-03_Interactive-Path-Tracing">Interactive Path Tracing and Reconstruction of Sparse Volumes</a> uses a machine learning denoiser to denoise volumes with adaptive sampling.</p>
</li>
<li>
<p>Peter Kristof of Microsoft made a really robust DirectX Ray Tracing Ambient Occlusion example with SVGF <a href="https://github.com/microsoft/DirectX-Graphics-Samples/tree/master/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingRealTimeDenoisedAmbientOcclusion">here</a>.</p>
</li>
<li>
<p>The Apple Metal Team released an example of using <a href="https://developer.apple.com/documentation/metalperformanceshaders/mpssvgfdenoiser">Metal Performance Shaders, Temporal Anti-Aliasing (MPSTAA), and SVGF (MPSSVGF)</a> to <a href="https://developer.apple.com/documentation/metalperformanceshaders/animating_and_denoising_a_raytraced_scene">denoise a ray traced scene</a>.</p>
</li>
<li>
<p>AMD&#x27;s FidelityFX SSSR features spatio-temporal reprojection to denoise screen space reflections. Here&#x27;s their <a href="https://github.com/GPUOpen-Effects/FidelityFX-SSSR/tree/cc1bb86af57f385c6532e41020b5e3a11200d654">Github</a>. AMD also released several denoisers for reflections and shadows on this<a href="https://github.com/GPUOpen-Effects/FidelityFX-Denoiser">Github Repo</a>.</p>
</li>
<li>
<p>NVIDIA has released an SDK to itegrate Deep Learning Super Sampling <a href="https://developer.nvidia.com/dlss-getting-started">here</a>. AMD&#x27;s <a href="https://github.com/GPUOpen-Effects/FidelityFX-FSR">Fidelity FX Super resolution</a> is also available for developers to integrate into their applications.</p>
</li>
<li>
<img class="profile-picture" src="https://github.com/ingowald.png" alt="Ingo Wald&#x27;s picture"/>
<p>Ingo Wald (<a href="https://twitter.com/IngoWald">@IngoWald</a>)) released a Optix course
which showcases how to use the Optix denoiser
<a href="https://github.com/ingowald/optix7course/blob/master/example12_denoiseSeparateChannels/SampleRenderer.cpp#L794">here</a>.</p>
</li>
<li>
<p>Christof Shied (<a href="https://twitter.com/c_schied">@c_schied</a>) and Alexey Panteleev (<a href="https://twitter.com/more_fps">@more_fps</a>) of NVIDIA wrote the denoiser for Quake 2 RTX which is on Github <a href="https://github.com/NVIDIA/Q2RTX/blob/master/src/refresh/vkpt/shader/">here</a>.</p>
</li>
<li>
<p><a href="https://github.com/microsoft/DirectML-Samples/tree/master/DirectMLSuperResolution">Microsoft&#x27;s DirectML Super Resolution Example</a>, while not NVIDIA Deep Learning Super Sampling 2.0 (DLSS 2.0), is similar in that both perform upscaling.</p>
</li>
<li>
<img class="profile-picture" src="https://github.com/xmeng525.png" alt="Xiaoxu Meng&#x27;s picture"/>
<p><a href="https://xiaoxumeng1993.wixsite.com/xiaoxumeng">Xiaoxu Meng</a> released the
source of their Neural Bilateral Grid paper
<a href="https://github.com/xmeng525/RealTimeDenoisingNeuralBilateralGrid">here</a>.</p>
</li>
<li>
<p>In the <a href="https://cis565-fall-2020.github.io/">University of Pennsylvania&#x27;s CIS565 course</a>, a number of students, teaching assistants, and former students made awesome projects that implement the latest in denoising research:</p>
<ul>
<li>
<p><a href="https://github.com/ZheyuanXie/CUDA-Path-Tracer-Denoising">CUDA SVGF</a> by Zheyuan Xie, Yan Dong, and Weiqi Chen</p>
</li>
<li>
<p><a href="https://github.com/gztong/BMFR-DXR-Denoiser">Blockwise Multi-Order Feature Regression for Real-Time Path Tracing Reconstruction (BMFR)</a> by Jiangping Xu, Gangzheng Tong, and Tianming Xu</p>
</li>
<li>
<p><a href="https://github.com/Black-Phoenix/Ai-Path-Tracer-Denoiser">Interactive Reconstruction of Monte Carlo Image Sequences using a Recurrent Denoising Autoencoder</a> by Dewang Sultania &amp; Vaibhav Arcot implemented with Pytorch.</p>
</li>
<li>
<p><a href="https://github.com/tatran5/Reservoir-Spatio-Temporal-Importance-Resampling-ReSTIR">ReSTIR in DirectX 12</a> by <a href="https://youtu.be/8jFfHmBhf7Y">Sydney Miller</a>, <a href="https://sites.google.com/view/sireeshaputcha/home">Sireesha Putcha</a>, <a href="https://tatran5.github.io/demo-reel.html">Thy Tran</a>.</p>
</li>
<li>
<p><a href="https://github.com/lindayukeyi/ReSTIR_DX12">ReSTIR in DirectX 12</a> by Jilin Liu (<a href="https://twitter.com/Jilin18043110">@Jilin18043110</a>), Keyi Yu, and Li Zheng.</p>
</li>
<li>
<p><a href="https://github.com/lukedan/ReSTIR-Vulkan">ReSTIR in Vulkan</a> by <a href="https://github.com/lukedan">Xuanyi Zhou</a>, <a href="https://github.com/hehehaha12139">Xuecheng Sun</a>, <a href="https://github.com/WaikeiChan">Jiarui Yan</a>.</p>
</li>
</ul>
</li>
</ul>
<h3 id="talks">Talks</h3>
<ul>
<li>
<p>Chris Wyman&#x27;s High Performance Graphics 2020 <a href="https://youtu.be/hhUUb5Op1-4?t=1590">talk on ReSTIR is available here</a>.</p>
</li>
<li>
<img class="profile-picture" src="https://github.com/erich666.png" alt="Eric Haines&#x27;s picture"/>
<p>Eric Haines (<a href="https://twitter.com/pointinpolygon">@pointinpolygon</a>) released
<a href="https://www.youtube.com/watch?v=6O2B9BZiZjQ">Ray Tracing Essentials Part 7: Denoising for Ray
Tracing</a>, a great introduction to
denoising.</p>
</li>
<li>
<p>Tomasz Stachowiak (<a href="https://twitter.com/h3r2tic">@h3r2tic</a>) et al. designed the Pica Pica renderer, an amazing reference hybrid renderer featured in Ray Tracing gems. Their <a href="https://www.ea.com/seed/news/seed-dd18-presentation-slides-raytracing">talk is available here</a>.</p>
</li>
</ul>
<h3 id="articles">Articles</h3>
<ul>
<li>
<p>Kostas Anagnostou (<a href="https://twitter.com/KostasAAA">@KostasAAA</a>) wrote <a href="https://interplayoflight.wordpress.com/2022/03/26/raytraced-global-illumination-denoising/">an article describing a method of handling global illumination denoising inspired in part by Metro Exodus</a>.</p>
</li>
<li>
<p>Chris Wyman and a number of co-authors for denoising papers at NVIDIA released <a href="https://intro-to-restir.cwyman.org/">A Gentle Introduction to ReSTIR: Path Reuse in Real-time</a>.</p>
</li>
</ul>
<hr/>
<p>I&#x27;ve also written blog posts on <a href="https://alain.xyz/blog/ray-tracing-filtering">image based denoising with adaptive spatiotemporal filtering, bilateral filtering</a>, and using <a href="https://alain.xyz/blog/machine-learning-denoising">machine learning autoencoders</a>, and denoisers of commercial games like <a href="https://alain.xyz/blog/frame-analysis-minecraftrtx">Minecraft RTX</a>:</p>
<table class="markademic-citations"><tbody><tr><td id="ref_dammertz2010"><span class="markademic-citename">[Dammertz et al. 2010]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Edge-Avoiding À-Trous Wavelet Transform for fast Global Illumination Filtering</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/NeoSpark314.png" alt="Holger Dammertz&#x27;s picture"/><a href="http://holger.dammertz.org/">Holger Dammertz</a> (<a href="https://twitter.com/NeoSpark314">@NeoSpark314</a>)</span><span>, <span>Daniel Sewtz</span></span><span>, <span>Johannes Hanika</span></span><span> and <span>Hendrik P.A. Lensch</span></span></span><br/><span><span class="markademic-publisher">High Performance Graphics 2010</span><br/></span><span><a href="https://jo.dreggn.org/home/2010_atrous.pdf">jo.dreggn.org</a></span></div></td></tr><tr><td id="ref_he2012"><span class="markademic-citename">[He et al. 2012]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Guided Image Filtering</span></strong></em><br/><span class="markademic-authors"><span><span>Kaiming He</span></span><span>, <span>Jian Sun</span></span><span> and <span>Xiaoou Tang</span></span></span><br/><span><span class="markademic-publisher">2012</span><br/></span><span><a href="http://kaiminghe.com/publications/pami12guidedfilter.pdf">kaiminghe.com</a></span></div></td></tr><tr><td id="ref_mara2017"><span class="markademic-citename">[Mara et al. 2017]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">An Efficient Denoising Algorithm for Global Illumination</span></strong></em><br/><span class="markademic-authors"><span><span>Michael Mara</span></span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/morgan3d.png" alt="Morgan McGuire&#x27;s picture"/><a href="https://casual-effects.com/">Morgan McGuire</a> (<a href="https://twitter.com/CasualEffects">@CasualEffects</a>)</span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/tunabrain.png" alt="Benedikt Bitterli&#x27;s picture"/><a href="https://benedikt-bitterli.me">Benedikt Bitterli</a> (<a href="https://twitter.com/tunabrain">@tunabrain</a>)</span><span> and <a href="https://cs.dartmouth.edu/~wjarosz/">Wojciech Jarosz</a> (<a href="https://twitter.com/wkjarosz">@wkjarosz</a>)</span></span><br/><span><span class="markademic-publisher">ACM, High Performance Graphics 2017</span><br/></span><span><a href="https://cs.dartmouth.edu/~wjarosz/publications/mara17towards.html">cs.dartmouth.edu</a></span></div></td></tr><tr><td id="ref_kalantari2013"><span class="markademic-citename">[Khademi Kalantari et al. 2013]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Removing the Noise in Monte Carlo Rendering with General Image Denoising Algorithms</span></strong></em><br/><span class="markademic-authors"><span><span>Nima Khademi Kalantari</span></span><span> and <span>Pradeep Sen</span></span></span><br/><span><span class="markademic-publisher">Eurographics 2013</span><br/></span><span><a href="https://research.nvidia.com/publication/2020-05_Neural-Temporal-Adaptive">research.nvidia.com</a></span></div></td></tr><tr><td id="ref_kalantari2015"><span class="markademic-citename">[Khademi Kalantari et al. 2015]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">A Machine Learning Approach for Filtering Monte Carlo Noise</span></strong></em><br/><span class="markademic-authors"><span><span>Nima Khademi Kalantari</span></span><span>, <span>Pradeep Sen</span></span><span> and <span>Steve Bako</span></span></span><br/><span><span class="markademic-publisher">ACM Transactions on Graphics (TOG) 2015</span><br/></span><span><a href="http://cvc.ucsb.edu/graphics/Papers/SIGGRAPH2015_LBF/">cvc.ucsb.edu</a></span></div></td></tr><tr><td id="ref_chaitanya2017"><span class="markademic-citename">[R. Alla Chaitanya et al. 2017]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Interactive Reconstruction of Monte Carlo Image Sequences using a Recurrent Denoising Autoencoder</span></strong></em><br/><span class="markademic-authors"><span><span>Chakravarty R. Alla Chaitanya</span></span><span>, <span>Anton S. Kaplanyan</span></span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/cschied.png" alt="Christoph Schied&#x27;s picture"/><a href="https://github.com/cschied">Christoph Schied</a> (<a href="https://twitter.com/c_schied">@c_schied</a>)</span><span>, <span>Marco Salvi</span> (<a href="https://twitter.com/marcosalvi">@marcosalvi</a>)</span><span>, <span>Aaron Lefohn</span></span><span>, <span>Derek Nowrouzezahrai</span></span><span> and <span>Timo Aila</span></span></span><br/><span><span class="markademic-publisher">ACM 2017</span><br/></span><span><a href="https://research.nvidia.com/sites/default/files/publications/dnn_denoise_author.pdf">research.nvidia.com</a></span></div></td></tr><tr><td id="ref_vogels2018"><span class="markademic-citename">[Vogels et al. 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Denoising with Kernel Prediction and Asymmetric Loss Functions</span></strong></em><br/><span class="markademic-authors"><span><span>Thijs Vogels</span></span><span>, <span>Fabrice Rousselle</span></span><span>, <span>Brian Mcwilliams</span></span><span>, <span>Gerhard Rothlin</span></span><span>, <span>Alex Harvill</span></span><span>, <span>David Adler</span></span><span>, <span>Mark Meyer</span></span><span> and <span>Jan Novak</span></span></span><br/><span><span class="markademic-publisher">ACM, ACM Transactions on Graphics 2018</span><br/></span><span><a href="http://doi.acm.org/10.1145/3197517.3201388">doi.acm.org</a></span></div></td></tr><tr><td id="ref_wang2018"><span class="markademic-citename">[Wang et al. 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">A Fully Progressive Approach to Single-Image Super-Resolution</span></strong></em><br/><span class="markademic-authors"><span><span>Yifan Wang</span></span><span>, <span>Federico Perazzi</span></span><span>, <span>Brian McWilliams</span></span><span>, <span>Alexander Sorkine-Hornung</span></span><span>, <span>Olga Sorkine-Hornung</span></span><span> and <span>Christopher Schroers</span></span></span><br/><span><span class="markademic-publisher">CoRR 2018</span><br/></span><span><a href="http://arxiv.org/abs/1804.02900">arxiv.org</a></span></div></td></tr><tr><td id="ref_xu2019"><span class="markademic-citename">[Xu et al. 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Adversarial Monte Carlo Denoising with Conditioned Auxiliary Feature Modulation</span></strong></em><br/><span class="markademic-authors"><span><span>Bing Xu</span></span><span>, <span>Junfei Zhang</span></span><span>, <span>Rui Wang</span></span><span>, <span>Kun Xu</span></span><span>, <span>Yong-Liang Yang</span></span><span>, <span>Chuan Li</span></span><span> and <span>Rui Tang</span></span></span><br/><span><span class="markademic-publisher">ACM, ACM Transactions on Graphics 2019</span><br/></span><span><a href="http://adversarial.mcdenoising.org/">adversarial.mcdenoising.org</a></span></div></td></tr><tr><td id="ref_meng2020"><span class="markademic-citename">[Meng et al. 2020]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Real-time Monte Carlo Denoising with the Neural Bilateral Grid</span></strong></em><br/><span class="markademic-authors"><span><span>Xiaoxu Meng</span></span><span>, <span>Quan Zheng</span></span><span>, <span>Amitabh Varshney</span></span><span>, <span>Gurprit Singh</span></span><span> and <span>Matthias Zwicker</span></span></span><br/><span><span class="markademic-publisher">Eurographics 2020</span><br/></span><span><a href="https://diglib.eg.org/handle/10.2312/sr20201133">diglib.eg.org</a></span></div></td></tr><tr><td id="ref_isik2021"><span class="markademic-citename">[Işık et al. 2021]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Interactive Monte Carlo Denoising using Affinity of Neural Features</span></strong></em><br/><span class="markademic-authors"><span><span>Mustafa Işık</span></span><span>, <span>Krishna Mullia</span></span><span>, <span>‪Matthew Fisher</span></span><span>, <span>‪Jonathan Eisenmann</span></span><span> and <span>Michaël Gharbi</span></span></span><br/><span><span class="markademic-publisher">Siggraph 2021</span><br/></span><span><a href="https://www.mustafaisik.net/anf/">mustafaisik.net</a></span></div></td></tr><tr><td id="ref_jiang2022"><span class="markademic-citename">[Jiang et al. 2022]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Fast and High-Quality Image Denoising via Malleable Convolutions</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/yueruchen.png" alt="Yifan Jiang&#x27;s picture"/><a href="https://yifanjiang.net">Yifan Jiang</a></span><span>, <span>Bartlomiej Wronski</span></span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/bmild.png" alt="Ben Mildenhall&#x27;s picture"/><a href="https://bmild.github.io/">Ben Mildenhall</a> (<a href="https://twitter.com/BenMildenhall">@BenMildenhall</a>)</span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/jonbarron.png" alt="Jonathan T. Barron&#x27;s picture"/><a href="https://jonbarron.info/">Jonathan T. Barron</a> (<a href="https://twitter.com/jon_barron">@jon_barron</a>)</span><span>, <span>Zhangyang Wang</span></span><span> and <span>Tianfan Xue</span></span></span><br/><span><span class="markademic-publisher">arXiv 2022</span><br/></span><span><a href="https://yifanjiang.net/MalleConv.html">yifanjiang.net</a></span></div></td></tr><tr><td id="ref_hasselgren2020"><span class="markademic-citename">[Hasselgren et al. 2020]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Neural Temporal Adaptive Sampling and Denoising</span></strong></em><br/><span class="markademic-authors"><span><span>Jon Hasselgren</span></span><span>, <span>Jacob Munkberg</span></span><span>, <span>Marco Salvi</span> (<a href="https://twitter.com/marcosalvi">@marcosalvi</a>)</span><span>, <span>Anjul Patney</span></span><span> and <span>Aaron Lefohn</span></span></span><br/><span><span class="markademic-publisher">Eurographics 2020</span><br/></span><span><a href="https://research.nvidia.com/publication/2020-05_Neural-Temporal-Adaptive">research.nvidia.com</a></span></div></td></tr><tr><td id="ref_verbin2021"><span class="markademic-citename">[Verbin et al. 2021]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Ref-NeRF: Structured View-Dependent Appearance for Neural Radiance Fields</span></strong></em><br/><span class="markademic-authors"><span><span>Dor Verbin</span></span><span>, <a href="https://www.phogzone.com/">Peter Hedman</a> (<a href="https://twitter.com/peterhedman3">@peterhedman3</a>)</span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/bmild.png" alt="Ben Mildenhall&#x27;s picture"/><a href="https://bmild.github.io/">Ben Mildenhall</a> (<a href="https://twitter.com/BenMildenhall">@BenMildenhall</a>)</span><span>, <span>Todd Zickler</span></span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/jonbarron.png" alt="Jonathan T. Barron&#x27;s picture"/><a href="https://jonbarron.info/">Jonathan T. Barron</a> (<a href="https://twitter.com/jon_barron">@jon_barron</a>)</span><span> and <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/pratulsrinivasan.png" alt="Pratul Srinivasan&#x27;s picture"/><a href="https://pratulsrinivasan.github.io/">Pratul Srinivasan</a> (<a href="https://twitter.com/_pratul_">@_pratul_</a>)</span></span><br/><span><span class="markademic-publisher">CVPR 2021</span><br/></span><span><a href="https://dorverbin.github.io/refnerf/">dorverbin.github.io</a></span></div></td></tr><tr><td id="ref_mildenhall2022"><span class="markademic-citename">[Mildenhall et al. 2022]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">NeRF in the Dark: High Dynamic Range View Synthesis from Noisy Raw Images</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/bmild.png" alt="Ben Mildenhall&#x27;s picture"/><a href="https://bmild.github.io/">Ben Mildenhall</a> (<a href="https://twitter.com/BenMildenhall">@BenMildenhall</a>)</span><span>, <a href="https://www.phogzone.com/">Peter Hedman</a> (<a href="https://twitter.com/peterhedman3">@peterhedman3</a>)</span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/rmbrualla/.png" alt="Ricardo Martin-Brualla&#x27;s picture"/><a href="http://ricardomartinbrualla.com/">Ricardo Martin-Brualla</a> (<a href="https://twitter.com/rmbrualla">@rmbrualla</a>)</span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/pratulsrinivasan.png" alt="Pratul Srinivasan&#x27;s picture"/><a href="https://pratulsrinivasan.github.io/">Pratul Srinivasan</a> (<a href="https://twitter.com/_pratul_">@_pratul_</a>)</span><span> and <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/jonbarron.png" alt="Jonathan T. Barron&#x27;s picture"/><a href="https://jonbarron.info/">Jonathan T. Barron</a> (<a href="https://twitter.com/jon_barron">@jon_barron</a>)</span></span><br/><span><span class="markademic-publisher">CVPR 2022</span><br/></span><span><a href="https://github.com/google-research/multinerf">github.com</a></span></div></td></tr><tr><td id="ref_dong2015"><span class="markademic-citename">[Dong et al. 2015]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Image Super-Resolution Using Deep Convolutional Networks</span></strong></em><br/><span class="markademic-authors"><span><span>Chao Dong</span></span><span>, <span>Chen Change Loy</span></span><span>, <span>Kaiming He</span></span><span> and <span>Xiaoou Tang</span></span></span><br/><span><span class="markademic-publisher">CoRR 2015</span><br/></span><span><a href="http://arxiv.org/abs/1501.00092">arxiv.org</a></span></div></td></tr><tr><td id="ref_ledig2016"><span class="markademic-citename">[Ledig et al. 2016]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Photo-Realistic Single Image Super-Resolution Using a Generative Adversarial Network</span></strong></em><br/><span class="markademic-authors"><span><span>Christian Ledig</span></span><span>, <span>Lucas Theis</span></span><span>, <span>Ferenc Huszar</span></span><span>, <span>Jose Caballero</span></span><span>, <span>Andrew P. Aitken</span></span><span>, <span>Alykhan Tejani</span></span><span>, <span>Johannes Totz</span></span><span>, <span>Zehan Wang</span></span><span> and <span>Wenzhe Shi</span></span></span><br/><span><span class="markademic-publisher">CoRR 2016</span><br/></span><span><a href="http://arxiv.org/abs/1609.04802">arxiv.org</a></span></div></td></tr><tr><td id="ref_xiao2020"><span class="markademic-citename">[Xiao et al. 2020]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Neural Supersampling for Real-time Rendering</span></strong></em><br/><span class="markademic-authors"><span><span>Lei Xiao</span></span><span>, <span>Salah Nouri</span></span><span>, <span>Matt Chapman</span></span><span>, <span>Alexander Fix</span></span><span>, <span>Douglas Lanman</span></span><span> and <span>Anton Kaplanyan</span></span></span><br/><span><span class="markademic-publisher">Facebook Research 2020</span><br/></span><span><a href="https://research.fb.com/publications/neural-supersampling-for-real-time-rendering/">research.fb.com</a></span></div></td></tr><tr><td id="ref_thomas2022"><span class="markademic-citename">[Mathew Thomas et al. 2022]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Temporally Stable Real-Time Joint Neural Denoising and Supersampling</span></strong></em><br/><span class="markademic-authors"><span><span>Manu Mathew Thomas</span></span><span>, <span>Gabor Liktor</span></span><span>, <a href="http://momentsingraphics.de/">Christoph Peters</a> (<a href="https://twitter.com/MomentsInCG">@MomentsInCG</a>)</span><span>, <span>Sungye Kim</span></span><span>, <span>Karthik Vaidyanathan</span></span><span> and <span>Angus G. Forbes</span></span></span><br/><span><span class="markademic-publisher">High Performance Graphics 2022</span><br/></span><span><a href="https://momentsingraphics.de/Media/HPG2022/thomas2022-temporally_stable_real_time_joint_neural_denoising_and_supersampling.pdf">momentsingraphics.de</a></span></div></td></tr><tr><td id="ref_korein1983"><span class="markademic-citename">[Korein et al. 1983]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Temporal Anti-Aliasing in Computer Generated Animation</span></strong></em><br/><span class="markademic-authors"><span><span>Jonathan Korein</span></span><span> and <span>Norman Badler</span></span></span><br/><span><span class="markademic-publisher">ACM 1983</span><br/></span><span><a href="https://dl.acm.org/doi/10.1145/800059.801168">dl.acm.org</a></span></div></td></tr><tr><td id="ref_lei2020"><span class="markademic-citename">[Yang et al. 2020]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">A Survey of Temporal Antialiasing Techniques</span></strong></em><br/><span class="markademic-authors"><span><span>Lei Yang</span></span><span>, <span>Shiqiu Liu</span></span><span> and <span>Marco Salvi</span> (<a href="https://twitter.com/marcosalvi">@marcosalvi</a>)</span></span><br/><span><span class="markademic-publisher">Computer Graphics Forum 2020</span><br/></span><span><a href="https://onlinelibrary.wiley.com/doi/abs/10.1111/cgf.14018">onlinelibrary.wiley.com</a></span></div></td></tr><tr><td id="ref_schied2017"><span class="markademic-citename">[Schied 2017]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Spatiotemporal Variance-Guided Filtering</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/cschied.png" alt="Christoph Schied&#x27;s picture"/><a href="https://github.com/cschied">Christoph Schied</a> (<a href="https://twitter.com/c_schied">@c_schied</a>)</span></span><br/><span><span class="markademic-publisher">ACM 2017</span><br/></span><span><a href="http://research.nvidia.com/publication/2017-07_Spatiotemporal-Variance-Guided-Filtering%3A">research.nvidia.com</a></span></div></td></tr><tr><td id="ref_baktash2018"><span class="markademic-citename">[Abdollah-shamshir-saz 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Voxel Based Hybrid Path Tracing with Spatial Denoising</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/toomuchvoltage.png" alt="Baktash Abdollah-shamshir-saz&#x27;s picture"/><a href="http://toomuchvoltage.com">Baktash Abdollah-shamshir-saz</a> (<a href="https://twitter.com/TooMuchVoltage">@TooMuchVoltage</a>)</span></span><br/><span><span class="markademic-publisher">i3D 2018</span><br/></span><span><a href="http://toomuchvoltage.com/pub/vbhptwstd/abstract.pdf">toomuchvoltage.com</a></span></div></td></tr><tr><td id="ref_schied2018"><span class="markademic-citename">[Schied et al. 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Gradient Estimation for Real-Time Adaptive Temporal Filtering</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/cschied.png" alt="Christoph Schied&#x27;s picture"/><a href="https://github.com/cschied">Christoph Schied</a> (<a href="https://twitter.com/c_schied">@c_schied</a>)</span><span>, <span>Peters, Christoph</span></span><span> and <span>Carsten Dachsbacher</span></span></span><br/><span><span class="markademic-publisher">ACM 2018</span><br/></span><span><a href="https://cg.ivd.kit.edu/atf.php">cg.ivd.kit.edu</a></span></div></td></tr><tr><td id="ref_koskela2019"><span class="markademic-citename">[Koskela et al. 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Blockwise Multi-Order Feature Regression for Real-Time Path Tracing Reconstruction</span></strong></em><br/><span class="markademic-authors"><span><span>Matias Koskela</span></span><span> and <span>Kalle Immonen</span></span></span><br/><span><span class="markademic-publisher">ACM Transactions on Graphics (TOG) 2019</span><br/></span><span><a href="http://www.tut.fi/vga/publications/Blockwise_Multi-Order_Feature_Regression_for_Real-Time_Path_Tracing_Reconstruction.html">tut.fi</a></span></div></td></tr><tr><td id="ref_benyoub2019"><span class="markademic-citename">[Benyoub 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Leveraging Ray Tracing Hardware Acceleration In Unity</span></strong></em><br/><span class="markademic-authors"><span><a href="https://auzaiffe.wordpress.com/blog/">Anis Benyoub</a> (<a href="https://twitter.com/Auzaiffe">@Auzaiffe</a>)</span></span><br/><span><span class="markademic-publisher">Digital Dragons 2019</span><br/></span><span><a href="https://auzaiffe.files.wordpress.com/2019/05/digital-dragons-leveraging-ray-tracing-hardware-acceleration-in-unity.pdf">auzaiffe.files.wordpress.com</a></span></div></td></tr><tr><td id="ref_liu2019"><span class="markademic-citename">[Liu et al. 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Cinematic Rendering in UE4 with Real-Time Ray Tracing and Denoising</span></strong></em><br/><span class="markademic-authors"><span><span>Edward Liu</span></span><span>, <span>Ignacio Llamas</span></span><span>, <span>Juan Cañada</span></span><span> and <span>Patrick Kelly</span></span></span><br/><span><span class="markademic-publisher">Ray Tracing Gems 2019</span><br/></span><span><a href="http://www.realtimerendering.com/raytracinggems/">realtimerendering.com</a></span></div></td></tr><tr><td id="ref_veach1998"><span class="markademic-citename">[Veach 1998]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Robust Monte Carlo Methods for Light Transport Simulation</span></strong></em><br/><span class="markademic-authors"><span><span>Eric Veach</span></span></span><br/><span><span class="markademic-publisher">Stanford University 1998</span><br/></span><span><a href="http://graphics.stanford.edu/papers/veach_thesis/">graphics.stanford.edu</a></span></div></td></tr><tr><td id="ref_pantaleoni2020"><span class="markademic-citename">[Pantaleoni 2020]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Online Path Sampling Control with Progressive Spatio-Temporal Filtering</span></strong></em><br/><span class="markademic-authors"><span><span>Jacopo Pantaleoni</span></span></span><br/><span><span class="markademic-publisher">NVIDIA 2020</span><br/></span><span><a href="https://arxiv.org/pdf/2005.07547.pdf">arxiv.org</a></span></div></td></tr><tr><td id="ref_bitterli2020"><span class="markademic-citename">[Bitterli et al. 2020]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Spatiotemporal Reservoir Resampling for Real-Time Ray Tracing With Dynamic Direct Lighting</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/tunabrain.png" alt="Benedikt Bitterli&#x27;s picture"/><a href="https://benedikt-bitterli.me">Benedikt Bitterli</a> (<a href="https://twitter.com/tunabrain">@tunabrain</a>)</span><span>, <a href="http://cwyman.org/">Chris Wyman</a> (<a href="https://twitter.com/_cwyman_">@_cwyman_</a>)</span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/mmp.png" alt="Matt Pharr&#x27;s picture"/><a href="https://pharr.org/matt/">Matt Pharr</a></span><span>, <a href="https://www.petershirley.com/">Peter Shirley</a> (<a href="https://twitter.com/Peter_shirley">@Peter_shirley</a>)</span><span>, <span>Aaron Lefohn</span></span><span> and <a href="https://cs.dartmouth.edu/~wjarosz/">Wojciech Jarosz</a> (<a href="https://twitter.com/wkjarosz">@wkjarosz</a>)</span></span><br/><span><span class="markademic-publisher">ACM Transaction on Graphics 2020</span><br/></span><span><a href="https://research.nvidia.com/publication/2020-07_Spatiotemporal-reservoir-resampling">research.nvidia.com</a></span></div></td></tr><tr><td id="ref_boisse2021"><span class="markademic-citename">[Boisse 2021]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">World-Space Spatiotemporal Reservoir Reuse for Ray-Traced Global Illumination</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/gboisse.png" alt="Guillaume Boisse&#x27;s picture"/><a href="https://github.com/gboisse">Guillaume Boisse</a> (<a href="https://twitter.com/guitio2002">@guitio2002</a>)</span></span><br/><span><span class="markademic-publisher">Siggraph Asia 2021</span><br/></span><span><a href="https://gpuopen.com/download/publications/SA2021_WorldSpace_ReSTIR.pdf">gpuopen.com</a></span></div></td></tr><tr><td id="ref_oberberger2022"><span class="markademic-citename">[Oberberger et al. 2022]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Spatiotemporal Variance-Guided Filtering for Motion Blur</span></strong></em><br/><span class="markademic-authors"><span><span>Max Oberberger</span></span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/Anteru.png" alt="Matthäus G. Chajdas&#x27;s picture"/><a href="https://anteru.net/">Matthäus G. Chajdas</a> (<a href="https://twitter.com/NIV_Anteru">@NIV_Anteru</a>)</span><span>, <a href="http://momentsingraphics.de/">Rüdiger Westermann</a> (<a href="https://twitter.com/MomentsInCG">@MomentsInCG</a>)</span><span>, <span>Sungye Kim</span></span><span>, <span>Karthik Vaidyanathan</span></span><span> and <span>Angus G. Forbes</span></span></span><br/><span><span class="markademic-publisher">High Performance Graphics 2022</span><br/></span><span><a href="https://gpuopen.com/download/publications/Spatiotemporal_Variance-Guided_Filtering_for_Motion_Blur.pdf">gpuopen.com</a></span></div></td></tr><tr><td id="ref_tokuyoshi2022"><span class="markademic-citename">[Tokuyoshi 2022]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Tiled Reservoir Sampling for Many-Light Rendering</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/yusuketokuyoshi.png" alt="Yusuke Tokuyoshi&#x27;s picture"/><a href="https://yusuketokuyoshi.com/">Yusuke Tokuyoshi</a> (<a href="https://twitter.com/yusuketokuyoshi">@yusuketokuyoshi</a>)</span></span><br/><span><span class="markademic-publisher">2022</span><br/></span><span><a href="https://gpuopen.com/download/publications/TiledReservoirSampling21-11-ecdc.pdf">gpuopen.com</a></span></div></td></tr><tr><td id="ref_majercik2020"><span class="markademic-citename">[Majercik et al. 2020]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Scaling Probe-Based Real-Time Dynamic Global Illumination for Production</span></strong></em><br/><span class="markademic-authors"><span><span>Zander Majercik</span></span><span>, <span>Adam Marrs</span></span><span>, <span>Josef Spjut</span></span><span> and <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/morgan3d.png" alt="Morgan McGuire&#x27;s picture"/><a href="https://casual-effects.com/">Morgan McGuire</a> (<a href="https://twitter.com/CasualEffects">@CasualEffects</a>)</span></span><br/><span><span class="markademic-publisher">arXiv 2020</span><br/></span><span><a href="https://arxiv.org/abs/2009.10796">arxiv.org</a></span></div></td></tr><tr><td id="ref_talbot2005"><span class="markademic-citename">[Talbot et al. 2005]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Importance Resampling for Global Illumination</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/jtalbot.png" alt="Justin Talbot&#x27;s picture"/><a href="http://justintalbot.com/">Justin Talbot</a></span><span>, <span>David Cline</span></span><span> and <span>Parris Egbert</span></span></span><br/><span><span class="markademic-publisher">Eurographics 2005</span><br/></span></div></td></tr><tr><td id="ref_kuznetsov2018"><span class="markademic-citename">[Kuznetsov et al. 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Deep Adaptive Sampling for Low Sample Count Rendering</span></strong></em><br/><span class="markademic-authors"><span><a href="https://www.alexku.me/">Alexandr Kuznetsov</a> (<a href="https://twitter.com/straintensor">@straintensor</a>)</span><span>, <a href="https://people.engr.tamu.edu/nimak/index.html">Nima Khademi Kalantari</a></span><span> and <a href="https://cseweb.ucsd.edu/~ravir/">Ravi Ramamoorthi</a></span></span><br/><span><span class="markademic-publisher">Eurographics 2018</span><br/></span><span><a href="https://cseweb.ucsd.edu//~ravir/adaptive.pdf">cseweb.ucsd.edu</a></span></div></td></tr><tr><td id="ref_wyman2021"><span class="markademic-citename">[Wyman et al. 2021]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Rearchitecting Spatiotemporal Resampling for Production</span></strong></em><br/><span class="markademic-authors"><span><span>Chris Wyman</span></span><span> and <span>Alexey Panteleev</span></span></span><br/><span><span class="markademic-publisher">Eurographics 2021</span><br/></span><span><a href="https://research.nvidia.com/publication/2021-07_rearchitecting-spatiotemporal-resampling-production">research.nvidia.com</a></span></div></td></tr><tr><td id="ref_yan2021"><span class="markademic-citename">[Zeng et al. 2021]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Temporally Reliable Motion Vectors for Real-time Ray Tracing</span></strong></em><br/><span class="markademic-authors"><span><a href="https://zheng95z.github.io/">Zheng Zeng</a></span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/behindthepixels.png" alt="Edward Liu&#x27;s picture"/><a href="http://behindthepixels.io/">Edward Liu</a> (<a href="https://twitter.com/edliu1105">@edliu1105</a>)</span><span>, <span>Jinglei Yang</span></span><span>, <span>Lu Wang</span></span><span> and <span>Ling-Qi Yan</span></span></span><br/><span><span class="markademic-publisher">Eurographics 2021</span><br/></span><span><a href="https://sites.cs.ucsb.edu/~lingqi/publications/paper_trmv.pdf">sites.cs.ucsb.edu</a></span></div></td></tr><tr><td id="ref_olejnik2020"><span class="markademic-citename">[Olejnik et al. 2020]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Raytraced Shadows in Call of Duty: Modern Warfare</span></strong></em><br/><span class="markademic-authors"><span><span>Michal Olejnik</span> (<a href="https://twitter.com/olej3d">@olej3d</a>)</span><span> and <span>Pawel Koztowski</span> (<a href="https://twitter.com/PawPawDH">@PawPawDH</a>)</span></span><br/><span><span class="markademic-publisher">Digital Dragons 2020</span><br/></span></div></td></tr><tr><td id="ref_tokuyoshi2015"><span class="markademic-citename">[Tokuyoshi 2015]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Specular Lobe-Aware Filtering and Upsampling for Interactive Indirect Illumination</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/yusuketokuyoshi.png" alt="Yusuke Tokuyoshi&#x27;s picture"/><a href="https://yusuketokuyoshi.com/">Yusuke Tokuyoshi</a> (<a href="https://twitter.com/yusuketokuyoshi">@yusuketokuyoshi</a>)</span></span><br/><span><span class="markademic-publisher">Eurographics 2015</span><br/></span><span><a href="http://www.jp.square-enix.com/tech/library/pdf/Specular%20Lobe-Aware%20Filtering%20and%20Upsampling%20for%20Interactive%20Indirect%20Illumination.pdf">jp.square-enix.com</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Portfolio Design and Engineering</title>
<link>https://alain.xyz/blog/portfolio-design-engineering</link>
<description>Let&apos;s discuss what it means to design and engineer a portfolio, looking at case studies, engineering, and user experience design.</description>
<guid isPermaLink="true">https://alain.xyz/blog/portfolio-design-engineering</guid>
<pubDate>Sun, 12 Jul 2020 16:00:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>portfolio</category><category>web</category><category>design</category><category>ux</category><category>diagrams</category><category>theory</category><category>vlog</category>
<enclosure url="https://alain.xyz/blog/portfolio-design-engineering/assets/cover.jpg" length="772772" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/portfolio-design-engineering/assets/split-portfolios.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/portfolio-design-engineering/assets/job-icons.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/portfolio-design-engineering/assets/arda-portfolio.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/portfolio-design-engineering/assets/javier-portfolio.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/portfolio-design-engineering/assets/kun-portfolio.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/portfolio-design-engineering/assets/alyssa-portfolio.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/portfolio-design-engineering/assets/alan-portfolio.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/portfolio-design-engineering/assets/yining-portfolio.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/portfolio-design-engineering/assets/ciechanowski-portfolio.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/portfolio-design-engineering/assets/jason-portfolio.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/portfolio-design-engineering/assets/benedikt-portfolio.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/portfolio-design-engineering/assets/niladrish-portfolio.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/portfolio-design-engineering/assets/host-logos.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/portfolio-design-engineering/assets/motion-blur-logos.png"/><link rel="preload" as="image" href="https://alain.xyz/assets/gatsby-logo.svg"/><link rel="preload" as="image" href="https://alain.xyz/assets/wordpress.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/portfolio-design-engineering/assets/fast-sitehosts.svg"/><link rel="preload" as="image" href="https://thumbs.gfycat.com/MealyAchingDalmatian.webp"/><p>An independent and personal website is a showcase of who you are, both as a person and professionally. Whether you&#x27;re an <strong>engineer</strong>, <strong>artist</strong>, <strong>manager</strong>, or your role blends depending on what&#x27;s necessary for your company, your personal website, your <em>portfolio</em>, serves as a way of making clear your 🏆 <em>accomplishments</em>, 😊 <em>personality</em>, and 🎸 <em>professional &amp; personal interests</em> in a way that is <em>much</em> more interactive and clear than a resume.</p>
<p><img src="https://alain.xyz/blog/portfolio-design-engineering/assets/split-portfolios.jpg" alt="Split Portfolios"/></p>
<p>What is the purpose of your portfolio? Is it to acquire a position at a large tech company as a <strong>👩‍💻 Software Engineer</strong>, <strong>🏫 Research Scientist</strong>, <strong>🔺 Technical Artist</strong>, <strong>🎨 Artist</strong>, <strong>💼 Project Manager</strong>, or whatever role you desire? Either way, its purpose should be clear and specific, like your choice of major in higher education or research focus when working towards a Ph.D.</p>
<p><img src="https://alain.xyz/blog/portfolio-design-engineering/assets/job-icons.svg" alt="Job Roles"/></p>
<p>Do you want to be an <strong>independent contractor</strong>, or to work as a part of a research team, team at a tech company large or small, or perhaps lead your own team? Whatever your choice there&#x27;s benefits and costs, as an independent contractor you&#x27;re free to set your own hours, but are limited to the terms of your contract. As a salaried employee you may receive benefits your employer agrees to provide you, such as stock options, medical, etc, however you&#x27;re expected to follow a stricter time schedule.</p>
<p>It&#x27;s important to note the distinction between <em>contractor</em> and <em>employee</em> can blur, especially with the rise of a <em>💻 Work from Home</em> culture in the world due in part to the 🦠 COVID-19 pandemic and rise of 🌎 globalization (which also means you&#x27;re competing with the <strong>global</strong> <em>state of the art</em>, no pressure 😅). Plenty of employers are accommodating to your needs, if for instance if you need to pick up your children from school.</p>
<p>Do you wish to enter into <strong>research</strong>, either in higher education or in industry? If you choose to stay at a university you would be expected to teach courses that would interrupt your research. Industry researchers have the benefit of completely dedicating their time and efforts on a subject, and wouldn&#x27;t be expected to occasionally pause their work to teach a course (though industry researchers <a href="https://www.youtube.com/watch?v=gfY2LfRfE1E&amp;list=PLUPhVMQuDB_b2kcOooEduedthcBH53mvC">also teach courses in symposiums such as Disney&#x27;s Rajesh Sharma in Siggraph</a>).</p>
<p>It&#x27;s also possible to join an <strong>open source</strong> project and contribute to it, though this comes at the cost of working for free, though there are exceptions to this such as projects supported by <a href="https://www.patreon.com/">Patreon</a>, <a href="https://opencollective.com/">Open Collective</a>, <a href="https://github.com/sponsors">GitHub Sponsors</a>, or the project belonging to a company such as <a href="https://www.unrealengine.com/en-US/">Epic&#x27;s Unreal Engine</a> or <a href="https://www.qt.io/">Qt</a>.</p>
<p>Then there&#x27;s <strong>leadership</strong>, from managing a company, either directly or indirectly depending on your interests, leading in the maintenance and creation of features for a product, or following in the footsteps of startups and independent developers that turn their hobbies and passion into a profitable business.</p>
<hr/>
<p>Each one of these roles has a portfolio where they showcase their work, from volunteer talks to research published to distinguished symposiums, impressive open source contributions, art featured in a project or publication, attributions to large projects, and more, let&#x27;s analyse a few case studies and discuss how to engineer your own portfolio depending on your abilities/time interest.</p>
<h2 id="case-studies">Case Studies</h2>
<h3 id="artists">Artists</h3>
<h4 id="arda-koyuncu">Arda Koyuncu</h4>
<p><a href="https://www.ardakoyuncu.com/"><img src="https://alain.xyz/blog/portfolio-design-engineering/assets/arda-portfolio.jpg" alt="Arda&#x27;s Portfolio"/></a></p>
<p><a href="https://www.ardakoyuncu.com/">Arda Koyuncu</a> is a <strong>Principal Character Artist</strong> who has participated in a variety of different projects, from Bioshock Infinite to God of War, Dishonored, Epic&#x27;s Troll RTX film, and much more. His portfolio is reminiscent to the design of Artstation, and his featured works tell a story of impressive technical prowess and attention to detail.</p>
<p>Arda shows how important it is to make your portfolio 🖼️ visually impressive and dense in quality.</p>
<h4 id="javier-perez">Javier Perez</h4>
<p><a href="https://www.meshmodeler.com/"><img src="https://alain.xyz/blog/portfolio-design-engineering/assets/javier-portfolio.jpg" alt="Javier&#x27;s Portfolio"/></a></p>
<p><a href="https://www.meshmodeler.com/">Javier Perez</a> (<a href="https://twitter.com/MeshModeler">@MeshModeler</a>) is a <strong>Senior Material Artist at Playstation Visual Arts Service Group</strong>, and works on impressive material designs with Substance Designer.</p>
<p>He shows us that we should <em>have as many 👀 eyes on our portfolio as possible</em>, seek out constructive criticism as if you were still on an Internet art forum in this day of unified social media.</p>
<h4 id="wanchana-intrasombat">Wanchana Intrasombat</h4>
<p><a href="https://www.kun-victorior.com/"><img src="https://alain.xyz/blog/portfolio-design-engineering/assets/kun-portfolio.jpg" alt="Wanchana&#x27;s Portfolio"/></a></p>
<p><a href="https://www.kun-victorior.com/">Wanchana Intrasombat</a> (<a href="https://twitter.com/VictoriorCG">@VictoriorCG</a>)&#x27;s a <strong>Visual Development Artist</strong> who contributed to the PlayStation 5&#x27;s <a href="https://www.youtube.com/watch?v=OxzWlIbnp3U">Kena: Bridge of Spirits</a> game as a concept artist. His portfolio is specific and succinct, full of professional examples of his work.</p>
<h4 id="alyssa-x">Alyssa X</h4>
<p><a href="https://alyssax.com/"><img src="https://alain.xyz/blog/portfolio-design-engineering/assets/alyssa-portfolio.jpg" alt="Alyssa&#x27;s Portfolio"/></a></p>
<p><a href="https://alyssax.com/">Alyssa</a> (<a href="https://twitter.com/alyssaxuu">@alyssaxuu</a>) is a <strong>🦄 UX Designer</strong> who offers interesting insights in product design.</p>
<p>She teaches us to <em>consider the design of your portfolio carefully</em>, go so far as to include video, draw inspiration from other UX designers on communities like <a href="https://dribbble.com/">Dribbble</a> and <a href="https://www.figma.com/">Figma</a>, and even <a href="https://twitter.com/alyssaxuu/status/1148253489144979456">treat Twitter as a portfolio</a>.</p>
<h3 id="engineers">Engineers</h3>
<h4 id="alan-wolfe">Alan Wolfe</h4>
<p><a href="https://blog.demofox.org/"><img src="https://alain.xyz/blog/portfolio-design-engineering/assets/alan-portfolio.jpg" alt="Alan&#x27;s Portfolio"/></a></p>
<p><a href="https://blog.demofox.org/">Alan Wolfe</a> (<a href="https://twitter.com/Atrix256">@Atrix256</a>) is a <strong>Dev Tech Graphics Engineer at NVIDIA</strong>, who write a <em>lot</em>! His blog is full of engineering insights for computer graphics, game development, audio programming, and much more. The perpetual student, he&#x27;s constantly looking for new and interesting ways to improve his craft.</p>
<p>Alan shows us that we should <em>never stop being a 👨‍🎓 student</em>, and study your interests to the best of your ability and associate with other people that do the same.</p>
<h4 id="yining-karl-li">Yining Karl Li</h4>
<p><a href="https://www.yiningkarlli.com/"><img src="https://alain.xyz/blog/portfolio-design-engineering/assets/yining-portfolio.jpg" alt="Yining&#x27;s Portfolio"/></a></p>
<p><a href="https://www.yiningkarlli.com/">Yining Karl Li</a> (<a href="https://twitter.com/yiningkarlli">@yiningkarlli</a>) is a <strong>Senior Software Engineer at Disney&#x27;s Hyperion Renderer</strong>, his portfolio features recent events as well as research publications and experience that he has.</p>
<p>Karl shows us that we should <em>keep your portfolio up to 📅 date</em>, even with granular events, either with Twitter, RSS, or manually. Consider also updating it with a variety of contributions, podcasts, talks, a demo reel, and much more.</p>
<h4 id="bartosz-ciechanowski">Bartosz Ciechanowski</h4>
<p><a href="https://ciechanow.ski/"><img src="https://alain.xyz/blog/portfolio-design-engineering/assets/ciechanowski-portfolio.jpg" alt="Ciechanowski&#x27;s Portfolio"/></a></p>
<p><a href="https://ciechanow.ski/">Bartosz Ciechanowski</a> (<a href="https://twitter.com/bciechanowski">@BCiechanowski</a>)&#x27;s portfolio is full of highly detailed notes that are reminiscent of the notes of <a href="https://mathigon.org/">Dr. Wolfram&#x27;s Mathigon E-Book</a> or <a href="https://www.awwwards.com/sites/macbook-pro-16-inch">Apple product showcases</a>, with a focus on concise information and interactivity.</p>
<p>Bartosz shows us just how important it is to make your portfolio 👓 <em>readable</em> and <em>interactive</em>.</p>
<h4 id="jason-lengstorf">Jason Lengstorf</h4>
<p><a href="https://www.jason.af/"><img src="https://alain.xyz/blog/portfolio-design-engineering/assets/jason-portfolio.jpg" alt="Jason&#x27;s Portfolio"/></a></p>
<p><a href="https://www.jason.af/">Jason Lengstorf</a> (<a href="https://twitter.com/jlengstorf">@jlengstorf</a>) is a <strong>Principal Developer Experience Engineer</strong> at <a href="https://www.netlify.com">Netlify</a>.</p>
<p>Jason shows us just how important it is to consider the experience of visiting a portfolio, make it like a game, interactive with ⭕ buttons and 🎶 sounds, and consider all the possibilities of what someone visiting it might want to know and see.</p>
<h3 id="academics">Academics</h3>
<h4 id="benedikt-bitterli">Benedikt Bitterli</h4>
<p><a href="https://benedikt-bitterli.me/index.html"><img src="https://alain.xyz/blog/portfolio-design-engineering/assets/benedikt-portfolio.jpg" alt="Benedikt&#x27;s Portfolio"/></a></p>
<p><a href="https://benedikt-bitterli.me/index.html">Benedikt Bitterli</a> (<a href="https://twitter.com/tunabrain">@tunabrain</a>) is a PhD student at Dartmouth College who contributed to the Disney Hyperion Renderer. His portfolio is full of research papers and interactive projects he worked on independently. His <a href="https://news.developer.nvidia.com/rendering-millions-of-dynamics-lights-in-realtime/">Siggraph 2020 paper on Spatiotemporal Importance Sampling</a> is particularly impressive.</p>
<p>Benedikt shows us how important it is to choose your 🔍 research focus carefully, from early in his educational career he was researching computer graphics and stayed consistent, specializing further and further along the way.</p>
<h4 id="niladrish-chatterjee">Niladrish Chatterjee</h4>
<p><a href="https://niladrish.org/"><img src="https://alain.xyz/blog/portfolio-design-engineering/assets/niladrish-portfolio.jpg" alt="Niladrish&#x27;s Portfolio"/></a></p>
<p><a href="https://niladrish.org/">Niladrish Chatterjee</a> is a computer architect at NVIDIA Research.</p>
<p>Like many academics he participates as much as possible in his field, with as many 📖 <em>research papers</em> and <em>patents</em> as possible in addition to participating heavily in conference committees/review boards to offer your constructive criticism. He also has an <a href="https://scholar.google.ca/citations?hl=en&amp;user=NuCyyPgAAAAJ"><em>h-index</em> of 17</a>.</p>
<h2 id="technologies">Technologies</h2>
<p>Depending on your engineering interests you may want something more or less programable, but the end goal should be to have a portfolio that&#x27;s 💃 <em>beautiful</em>, 🦄 <em>unique</em>, 🤫 <em>succinct</em>, and 😁 <em>easy to update</em>.</p>
<p>Building a custom portfolio entails software engineering questions such as, what do you intend to host in your website, and how would you want to update it? Do you want a web interface? What about a command line interface? Will you be writing blog posts? How interactive will your portfolio be?</p>
<p><img src="https://alain.xyz/blog/portfolio-design-engineering/assets/host-logos.svg" alt="DigitalOcean and Vercel"/></p>
<p>Depending on your interests and willingness to pay a premium, you can either use a ☁ cloud hosting provider such as <a href="https://www.digitalocean.com/">DigitalOcean</a>, <a href="https://aws.amazon.com/">Amazon Web Services (AWS)</a>, or <a href="https://vercel.com/">Vercel</a>, or opt for other services to host your site for free.</p>
<p><img src="https://alain.xyz/blog/portfolio-design-engineering/assets/motion-blur-logos.png" alt="Motion Blur Logos"/></p>
<p>Now it is possible to create a custom portfolio, there&#x27;s no shortage of libraries and tools to do so, I personally use <a href="https://reactjs.org/">React</a>, <a href="https://webpack.js.org/">WebPack</a>, and treat <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import">every page as a JavaScript module</a> on my portfolio, <a href="https://alain.xyz/blog/the-making-of-alain-xyz">here&#x27;s some more details on my site&#x27;s engineering if you&#x27;re interested</a>.</p>
<img src="https://alain.xyz/assets/gatsby-logo.svg" alt="Gatsby Logo" style="width:50%"/>
<p>If you want a programmable portfolio but don&#x27;t want to make a custom solution, <strong>my personal recommendation</strong> is to try using <a href="https://www.gatsbyjs.org/">Gatsby</a>. There is a learning curve to get started though, since even Gatsby&#x27;s cloud solution requires understanding at least <code>git</code> and a high level, and digging deeper requires understanding React and GraphQL. At the very least using some parts of Gatsby&#x27;s ecosystem such as <a href="https://github.com/mdx-js/mdx">mdx</a> would be beneficial to even a custom solution.</p>
<p>Here&#x27;s a starter Github repo by <a href="https://www.christopherbiscardi.com/">Chris Biscardi</a> (<a href="https://twitter.com/chrisbiscardi">@chrisbiscardi</a>) showcasing <a href="https://github.com/ChristopherBiscardi/gatsby-starter-mdx-basic">Gatsby and MDX</a>.</p>
<p><em>Alain.xyz site may not use it</em>, but was engineered with <a href="https://github.com/alaingalvan/foil">similar ideas and depends on the mostly the same technologies</a>.</p>
<img src="https://alain.xyz/assets/wordpress.svg" alt="Wordpress Logo" style="width:25%"/>
<p>The obvious choice for making an easy to maintain portfolio though is <a href="https://wordpress.com/">Wordpress</a>, with this web engine being so popular it makes up a huge chunk of portfolios and professional business websites.</p>
<p><img src="https://alain.xyz/blog/portfolio-design-engineering/assets/fast-sitehosts.svg" alt="Github, Glitch, Artstation"/></p>
<p>Then there&#x27;s tools that are even simpler such as using <a href="https://pages.github.com/">GitHub Pages</a>, <a href="https://glitch.com/">Glitch</a>, <a href="https://www.artstation.com/about/artists/website-builder">Artstation Sites</a>, <a href="https://portfolio.adobe.com/">Adobe Portfolio</a>, or even commercial website hosting tools like <a href="https://www.squarespace.com/">SquareSpace</a> or <a href="https://www.wix.com/">Wix</a>.</p>
<p>It can be fun to 🚲 bike-shed around a design and technologies, but bear in mind that in the end it&#x27;s the value of what&#x27;s in your portfolio that matters, though presentation does help.</p>
<h2 id="conclusion">Conclusion</h2>
<p>To be clear here, if I were to cover every impressive portfolio in the world this post would never end (After all, I follow more than <a href="https://twitter.com/Alainxyz/following">4000 impressive individuals on Twitter alone</a>) let alone other social media like <a href="https://www.artstation.com/alaingalvan/following">Artstation</a>, <a href="https://github.com/alaingalvan?tab=following">GitHub</a>, and if you&#x27;re determined to be as impressive or even more so, you too would also be counted among the individuals listed here, so get out there and make whatever you can!</p>
<p><img src="https://thumbs.gfycat.com/MealyAchingDalmatian.webp" alt="Shia&#x27;s Encouragement Gif"/></p>
<p>What does future of portfolio design look like? It may be one where in addition to templates, drag and drop designers like <a href="https://webflow.com/">Webflow</a>, and hand crafted engineering (like a web version of the <a href="https://www.demoscene.info/">demoscene</a>), there&#x27;s no-code solutions driven by <a href="https://twitter.com/sharifshameem/status/1284807152603820032">AI writing code for you</a>.</p>
<blockquote>
<p>Don&#x27;t go into a lifetime of debt to get an art degree. I don&#x27;t have one and many in my field don&#x27;t either. This industry is 100% portfolio based. ~ Simon Fuchs (<a href="https://twitter.com/SF_simonfox">@SF_simonfox</a>)</p>
</blockquote>
<p>It certainly seems as though the future of industry is to value individual work, and portfolios are a great way to measure that (as difficult as it is to measure that due to differences in presentation, incomes, <a href="https://twitter.com/AnaCarolina_Art/status/1286357324702023681">location</a>, etc).</p>
<p>Do you know someone really impressive? Feel free to share and offer your constructive criticism in the comments.</p>
<h3 id="more-resources">More Resources</h3>
<ul>
<li>
<p><a href="https://twitter.com/hashtag/portfolioday">Share your portfolio with <code>#PortfolioDay</code></a>.</p>
</li>
<li>
<p>Raf Grassetti (<a href="https://twitter.com/rafagrassetti">@rafagrassetti</a>)&#x27;s <a href="https://www.youtube.com/channel/UCwq9zfcXi6uMW30ZQoL7GqA">YouTube</a> features a number of different interviews of professional artists.</p>
</li>
<li>
<p><a href="https://www.wappalyzer.com/">Wappalyzer&#x27;s a browser extension that lets you see the tech stack of any website you visit</a>.</p>
</li>
<li>
<p><a href="https://www.awwwards.com">Awwwards</a> is a web awards platform that features a ton of impressive websites vying for an award. Visit sites like these for inspiration:</p>
<ul>
<li><a href="https://openai.com/">OpenAI.com</a></li>
<li><a href="https://www.apple.com/macbook-pro-16/">Apple.com</a>&#x27;s product pages</li>
</ul>
</li>
<li>
<p>For perspectives on compensation and investments, these individual engineers, artists, etc. share their salaries, budgets, housing, and/or stocks:</p>
<ul>
<li><a href="https://twitter.com/AliciaAdamczyk/status/1238136389415456772">Bukola Ayodele</a></li>
<li><a href="https://twitter.com/CHERdotdev/status/1260917391191674882">Cher Scarlett (@CHERdotdev)</a></li>
<li><a href="https://twitter.com/dan_abramov/status/1228454264915271683">Dan Abramov (@dan_abramov)</a></li>
<li><a href="https://twitter.com/jaredpalmer/status/1229035731408113669">Jared Palmer (@jaredpalmer)</a></li>
<li><a href="https://twitter.com/kortizart/status/1282208113567784960">Karla Ortiz (@kortizart)</a></li>
<li><a href="https://twitter.com/kchironis/status/1269634929609945088">Katie Chironis (@kchironis)</a></li>
</ul>
</li>
<li>
<p>Portfolios and resumes go hand and hand, Dale Kim (<a href="https://twitter.com/unpacklo">@unpacklo</a>) describes a few tips on <a href="https://github.com/unpacklo/game-programmer-resume-tips">resume design here</a>.</p>
</li>
<li>
<p>Kristen Kwong (<a href="https://twitter.com/kristenkwng">@kristenkwng</a>) wrote a blog post <a href="https://kristen.dev/blog/2020-07-09-recruiting-process/">discussing her educational career leading up to joining Apple</a>.</p>
</li>
<li>
<p>Life can be a bit more complicated than metrics and the <em>quantified self</em>, pomodoro timers and the like, (<a href="https://twitter.com/krazamtv">@krazamtv</a>) gives an bit of a <a href="https://www.youtube.com/watch?v=_o7qjN3KF8U">satirical scenario on the ideal day of a software engineer</a>.</p>
</li>
<li>
<p>Paolo shows us a <a href="https://www.youtube.com/watch?v=e_TxH59MclA">A day in the life of a software engineer at Bandai Namco in Japan</a>.</p>
</li>
<li>
<p>Georgian Avasilcutei (<a href="https://twitter.com/nimlot26">@nimlot26</a>) uses <a href="https://www.twitch.tv/nimlot26">Twitch as a educational platform where students can simply subscribe</a> and learn from him, will this be the future of mentorship?</p>
</li>
<li>
<p>Join communities where you can associate with others that want to tackle the state of the art in your domain of choice on Discord, Facebook Groups, Twitter, Websites, and wherever communities will form next (Virtual Reality? Zoom?).</p>
<ul>
<li><a href="https://twitter.com">🐦 Twitter</a></li>
<li><a href="https://alain.xyz/artstation.com">Artstation</a></li>
<li><a href="https://shadertoy.com">ShaderToy</a>
<ul>
<li><a href="https://discord.gg/XtmMN6E">Discord</a></li>
</ul>
</li>
<li><a href="https://polycount.com/">PolyCount</a>
<ul>
<li><a href="https://discord.gg/0Vu6wFfSmdogS2Lm">Discord</a></li>
<li><a href="https://polycount.com/discussion/170535/polycount-discord-slack-skype-hangout-etc">Forum</a></li>
</ul>
</li>
<li><a href="https://cgsociety.org/">CGSociety</a></li>
<li><a href="https://leetcode.com/">Leetcode</a></li>
<li><a href="https://github.com/">GitHub</a></li>
<li><a href="https://linkedin.com">LinkedIn</a></li>
<li><a href="https://twitch.tv">Twitch</a></li>
</ul>
</li>
</ul>]]></content:encoded>
</item>
<item>
<title>Frame Analysis - Minecraft RTX Beta</title>
<link>https://alain.xyz/blog/frame-analysis-minecraftrtx</link>
<description>A review of the rendering architecture of the new Minecraft RTX renderer, reviewing their implementation of SVGF, their path tracer, and their DLSS implementation.</description>
<guid isPermaLink="true">https://alain.xyz/blog/frame-analysis-minecraftrtx</guid>
<pubDate>Mon, 18 May 2020 19:00:34 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>game engine</category><category>frame</category><category>analysis</category><category>render</category><category>pbr</category><category>prepass</category><category>minecraft</category>
<enclosure url="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/cover.jpg" length="483316" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/pbr-material-lut.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/horse-bvh.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/scene-bvh.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/primary-normal.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/primary-position.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/primary-light-shadow.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/primary-albedo.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/primary-roughness.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/diffuse-1spp.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/specular-1spp.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/volumetric-light.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/specular-bilateral2.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/interleave-current.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/interleave-final.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/ui.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/final.jpg"/><link rel="preload" as="image" href="https://github.com/cschied.png"/><iframe width="100%" height="512" src="https://www.youtube.com/embed/YacXMb7dBoc" frameBorder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowFullScreen=""></iframe>
<p>Minecraft is a creative survival game where you must mine for resources, build tools, farms, explore the world in search for valuable treasures, and build portals to neighboring worlds - the Nether and End.</p>
<p>It&#x27;s a social game as well, where friends from different platforms can join in to work together to build interesting structures, and an intuitive interface to program your own servers and mods.</p>
<p>Recently, Minecraft for Windows 10 had an update to its renderer that introduced dramatic improvements to its design, adding real time ray tracing, ray tracing denoising, and deep learning super sampling.</p>
<p>So let&#x27;s review how Minecraft RTX renders a single frame, referencing research papers where appropriate.</p>
<blockquote>
<p>⚠️ <strong>Note</strong>: This is not an official analysis of the Minecraft RTX renderer, this is a beta version of the renderer so the design presented here may change in the final release. Please support Minecraft RTX by grabbing a copy of it on the Windows Store, thanks!</p>
</blockquote>
<h2 id="ray-tracer">Ray Tracer</h2>
<p><img src="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/pbr-material-lut.png" alt="PBR Material Look Up Texture"/></p>
<p>Prior to rendering some preprocessing is done to cache Physically Based Rendering (PBR) material textures to a <code>2048x2048</code> lookup texture (LUT).</p>
<p><img src="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/horse-bvh.png" alt="Horse BVH"/></p>
<p>Each frame of Minecraft RTX begins by building bottom level acceleration data structures <code>BuildRayTracingAccelerationStructure(...)</code> for any animated objects in the scene such as animals or players.</p>
<p><img src="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/scene-bvh.png" alt="Scene BVH"/></p>
<p>Our top level acceleration structure is built, and we proceed to <code>dispatchRays</code> for our primary rays and light shadows, writing to a variety of different outputs such as:</p>
<table><thead><tr><th style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/primary-normal.jpg" alt="Normals"/></th><th style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/primary-position.jpg" alt="Position"/></th><th style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/primary-light-shadow.jpg" alt="Light Shadows"/></th></tr></thead><tbody><tr><td style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/primary-albedo.jpg" alt="Albedo"/></td><td style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/primary-roughness.jpg" alt="Roughness"/></td><td style="text-align:center"></td></tr></tbody></table>
<ul>
<li>Normals</li>
<li>Albedo/Metalness</li>
<li>Emissive/Roughness</li>
<li>Opacity/Material ID</li>
<li>Velocity</li>
<li>Cached Irradiance (per triangle)</li>
<li>Reprojected Primary Path Length</li>
<li>Low Precision Position</li>
<li>Geometric Normals</li>
<li>Volumetric light Ray Shadows</li>
<li>View Position</li>
<li>View Direction</li>
<li>Throughput</li>
</ul>
<p>All passes are rendered twice next to one another on the x axis at <code>742x835</code> for a total of <code>1484x835</code> instead of <code>2560x1440</code>. This odd number appears to just be a quarter on X and half on Y of the output resolution plus some padding. On the left is primary ray hit data, on the right is primary ray hit data through transmissive surfaces such as water.</p>
<p>For sampling, Minecraft uses a blue noise array of 128 <code>256x256</code> <code>RGBA8</code> images.</p>
<p>More Rays are cast for non-primary rays, writing to:</p>
<p><img src="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/diffuse-1spp.jpg" alt="Diffuse 1spp"/></p>
<ul>
<li>Indirect Diffuse</li>
<li>indirect Diffuse Chroma</li>
</ul>
<p>Then another ray dispatch is done, writing to:</p>
<p><img src="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/specular-1spp.jpg" alt="Specular 1spp"/></p>
<ul>
<li>Indirect Specular</li>
<li>Reflection Distance</li>
</ul>
<p><img src="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/volumetric-light.jpg" alt="Volumetric Light 1spp"/></p>
<p>Volumetric fog is computed last.</p>
<h2 id="svgf-denoising">SVGF Denoising</h2>
<p>Minecraft uses <strong>Spatio-Temporal Variance Guided Filtering</strong> <a href="#ref_schied2017">[Schied 2017]</a>, which consists of a <em>variance driven spatio-temporal reprojection step</em> which takes our moment buffers, history length, and previous frames and tries putting that data where it would be in the current frame.</p>
<blockquote>
<p>For more information on real time tracing denoising with A-SVGF, check out <a href="https://alain.xyz/blog/ray-tracing-denoising">my blog post on the subject</a>.</p>
</blockquote>
<p>This is followed by a <em>bilateral filtering step</em>, where over the course of serveral passes, they adaptively blur these frames. They also use a form of irradiance caching to help with resolving reprojected regions with little history information quicker.</p>
<p><img src="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/specular-bilateral2.png" alt="Bilateral Specular 3rd"/></p>
<ul>
<li>Diffuse Global Illumination w/ a 7x bilateral filter</li>
<li>Reflections w/ a 3x bilateral filter</li>
<li>Crepuscular Volumetric Rays with a 5x Guided bilateral filter</li>
</ul>
<table><thead><tr><th style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/interleave-current.jpg" alt="Interleave Current"/></th><th style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/interleave-final.jpg" alt="UI Render Target"/></th></tr></thead><tbody><tr><td style="text-align:center"></td><td style="text-align:center"></td></tr></tbody></table>
<p>Finally, interleaved buffers are combined to produce the final output that will be fed to the DLSS 2.0 kernel.</p>
<h2 id="deep-learning-super-sampling">Deep Learning Super Sampling</h2>
<p>Deep Learning Super Sampling 2.0 (DLSS 2.0) uses an autoencoder that takes as input a <em>jittered render target</em> at a fraction of our output resolution, a <em>jittered velocity buffer</em> similar to Temporal Anti-Aliasing, and outputs a <em>upscaled version of the final output</em>.</p>
<h2 id="ui-pass">UI Pass</h2>
<p><img src="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/ui.png" alt="UI Render Target"/></p>
<p>UIs are rendered out at full resolution to a separate render target and composited at the end of the frame.</p>
<h2 id="postprocessing">Postprocessing</h2>
<p>Minecraft finishes the frame with tone mapping and any enabled postprocessing effects such as vignette.</p>
<h2 id="conclusion">Conclusion</h2>
<p><img src="https://alain.xyz/blog/frame-analysis-minecraftrtx/assets/final.jpg" alt="Final Output"/></p>
<p>Minecraft is a game that really benefits from a ray tracing system, with its dynamic environments and extreme variance in shadows and lighting, traversing cave systems and exploring structures feels much more immersive and interesting. If this was insightful or if you have any suggestions for another game to analyse let me know in the comments, and I&#x27;ll see you next time.</p>
<h3 id="more-resources">More Resources</h3>
<ul>
<li>
<p>NVIDIA and Microsoft released the video <a href="https://www.youtube.com/watch?v=PyIgZTE66eM">Introduction to Real Time Ray Tracing with Minecraft</a>.</p>
</li>
<li>
<p><a href="https://www.youtube.com/watch?v=TVtSsJf86_Y">Digital Foundry&#x27;s Minecraft RTX developer interview</a> provides a high level overview of the RenderDragon DX12 RTX renderer as well as the challenges the developer team had to face.</p>
</li>
<li>
<p><a href="https://www.linkedin.com/in/peterkristof/">Peter Kristof</a> of Microsoft made a really robust RTX Ambient Occlusion example an implementation of SVGF <a href="https://github.com/microsoft/DirectX-Graphics-Samples/tree/master/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingRealTimeDenoisedAmbientOcclusion">here</a>.</p>
</li>
<li>
<p><a href="https://github.com/microsoft/DirectML-Samples/tree/master/DirectMLSuperResolution">Microsoft&#x27;s DirectML Super Resolution Example</a>, while not NVIDIA Deep Learning Super Sampling 2.0 (DLSS 2.0), is similar in that both perform upscaling.</p>
</li>
<li>
<p>NVIDIA has released an SDK to itegrate Deep Learning Super Sampling <a href="https://developer.nvidia.com/dlss-getting-started">here</a>. AMD&#x27;s <a href="https://github.com/GPUOpen-Effects/FidelityFX-FSR">Fidelity FX Super resolution</a> is also available for developers to integrate into their applications.</p>
</li>
<li>
<p><a href="https://developer.nvidia.com/gtc/2020/video/s22677">GTC 2020 - Creating Physically Based Materials for Minecraft with RTX</a> reviews how Minecraft RTX designed their Physically Based Materials using Adobe Substance Designer.</p>
</li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_schied2017"><span class="markademic-citename">[Schied 2017]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Spatiotemporal Variance-Guided Filtering</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/cschied.png" alt="Christoph Schied&#x27;s picture"/><a href="https://github.com/cschied">Christoph Schied</a> (<a href="https://twitter.com/c_schied">@c_schied</a>)</span></span><br/><span><span class="markademic-publisher">ACM 2017</span><br/></span><span><a href="http://research.nvidia.com/publication/2017-07_Spatiotemporal-Variance-Guided-Filtering%3A">research.nvidia.com</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Frame Analysis - Mortal Kombat 11</title>
<link>https://alain.xyz/blog/frame-analysis-mk11</link>
<description>Because of Mortal Kombat 11&apos;s impressive presentation, I just had to take a look at their renderer to see what&apos;s powering those awesome graphics, and I&apos;m eager to share what&apos;s in it.</description>
<guid isPermaLink="true">https://alain.xyz/blog/frame-analysis-mk11</guid>
<pubDate>Mon, 27 Apr 2020 16:00:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>graphics</category><category>api</category><category>debugging</category>
<enclosure url="https://alain.xyz/blog/frame-analysis-mk11/assets/cover.jpg" length="543051" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-mk11/assets/ss-shadow-depth.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-mk11/assets/msm-4comp.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-mk11/assets/shadow-resolve.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-mk11/assets/baked-ao.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-mk11/assets/linear-depth.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-mk11/assets/gpass-lum.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-mk11/assets/gpass-normx.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-mk11/assets/hzb.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-mk11/assets/reconstructed-view-normals.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-mk11/assets/composited-ao.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-mk11/assets/specular-lighting.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-mk11/assets/diffuse-lighting.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-mk11/assets/fog.gif"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-mk11/assets/velocity.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-mk11/assets/ui.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-mk11/assets/final.jpg"/><iframe width="100%" height="512" src="https://www.youtube.com/embed/6CPFQjDNcMU" frameBorder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowFullScreen=""></iframe>
<p>Mortal Kombat 11 is one of the most impressive looking games to come out in 2019, with amazing facial animations, shadows, lighting, and postprocessing effects. That&#x27;s besides the fact that the game is really fun, with complex fight sequences and a thrilling storyline.</p>
<p>Because of Mortal Kombat 11&#x27;s impressive presentation, I just had to take a look at their renderer to see what&#x27;s powering those awesome graphics, and I&#x27;m eager to share what&#x27;s in it.</p>
<blockquote>
<p>Note: This is not an official analysis of their renderer. Please support Mortal Kombat 11 by purchasing a copy of it to see this tech in action.</p>
</blockquote>
<h2 id="shadow-pass">Shadow Pass</h2>
<p>Each frame starts with the <strong>Shadow Pass</strong>.</p>
<table><thead><tr><th style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-mk11/assets/ss-shadow-depth.jpg" alt="Shadow Depth 4x MSAA"/></th><th style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-mk11/assets/msm-4comp.jpg" alt="4 Channel Comparison"/></th></tr></thead><tbody><tr><td style="text-align:center"></td><td style="text-align:center"></td></tr></tbody></table>
<p>Each shadow map is composed of a 4x super-sampled (SSAA) shadow depth stencil map, which is then converted to a 4 channel <strong>Moment Shadow Map (MSMs)</strong> <a href="#ref_peters2016">[Peters et al. 2016]</a>, with each channel storing ( z ), ( z^2 ), ( z^3 ) and ( z^4 ).</p>
<p><img src="https://alain.xyz/blog/frame-analysis-mk11/assets/shadow-resolve.jpg" alt="Shadow Resolve"/></p>
<p>This map is later used in a shadow resolve pass prior to lighting. The result is some of the best shadow quality raster based shadow methods can offer.</p>
<p>This scene had 3 of these shadow casting lights encoded in a texture array.</p>
<h2 id="g-pass">G-Pass</h2>
<table><thead><tr><th style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-mk11/assets/baked-ao.jpg" alt="Baked Ambient Occlusion"/></th><th style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-mk11/assets/linear-depth.jpg" alt="Linear Depth"/></th></tr></thead><tbody><tr><td style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-mk11/assets/gpass-lum.jpg" alt="G-Pass Luminosity"/></td><td style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-mk11/assets/gpass-normx.jpg" alt="G-Pass Normals X"/></td></tr></tbody></table>
<p>The <strong>General Pass (G-Pass)</strong> stood out for looking pretty strange in Mortal Kombat 11. There are 4 feature buffers storing compressed data such as luminosity, view space normals on the X axis, linear depth (which implies that Mortal Kombat uses meters (m) units), and odd data such as dithered ambient occlusion and material IDs.</p>
<p>Nearly every material input in the G-Pass is in a <a href="https://docs.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression">Block Compression format</a>.</p>
<h2 id="ambient-occlusion">Ambient Occlusion</h2>
<table><thead><tr><th style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-mk11/assets/hzb.png" alt="Hierarchical Z Buffer"/></th><th style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-mk11/assets/reconstructed-view-normals.jpg" alt="Reconstructed View Normal"/></th><th style="text-align:center"><img src="https://alain.xyz/blog/frame-analysis-mk11/assets/composited-ao.jpg" alt="Final AO"/></th></tr></thead><tbody><tr><td style="text-align:center"></td><td style="text-align:center"></td><td style="text-align:center"></td></tr></tbody></table>
<p>Prior to the ambient occlusion pass, view space normals and depth are reconstructed from their compressed G-Buffers, and a hierarchical z buffer is computed <a href="#ref_greene93">[Greene et al. 1993]</a>. Then ambient occlusion was computed with those buffers in combination with a <code>32x32</code> blue noise texture array of size <code>8</code>.</p>
<h2 id="specular-lighting">Specular Lighting</h2>
<p><img src="https://alain.xyz/blog/frame-analysis-mk11/assets/specular-lighting.jpg" alt="Specular Lighting Pass"/></p>
<p>Specular lighting takes advantage of IBL lighting from a <code>512x512</code> cubemap as well as lights in the scene, and is computed in patches with a compute shader.</p>
<h2 id="diffuse-lighting">Diffuse Lighting</h2>
<p><img src="https://alain.xyz/blog/frame-analysis-mk11/assets/diffuse-lighting.jpg" alt="Diffuse Lighting Pass"/></p>
<p>The diffuse lighting pass also uses a compute shader and is done in patches.</p>
<h2 id="volumetric-lighting">Volumetric Lighting</h2>
<p><img src="https://alain.xyz/blog/frame-analysis-mk11/assets/fog.gif" alt="UI"/></p>
<p>Volumetric lighting is done by using linear depth and a scattering and transmission texture.</p>
<h2 id="temporal-anti-aliasing">Temporal Anti-Aliasing</h2>
<p><img src="https://alain.xyz/blog/frame-analysis-mk11/assets/velocity.jpg" alt="Velocity Buffer"/></p>
<p>As standard in temporal anti-aliasing, a velocity buffer is used to reproject previous samples over to get the final anti-aliased result.</p>
<h2 id="ui-pass">UI Pass</h2>
<p><img src="https://alain.xyz/blog/frame-analysis-mk11/assets/ui.png" alt="UI"/></p>
<p>All UIs in Mortal Kombat are rendered to a separate render target, then layered on top of the final tone-mapped result.</p>
<h2 id="conclusion">Conclusion</h2>
<p><img src="https://alain.xyz/blog/frame-analysis-mk11/assets/final.jpg" alt="Final Output"/></p>
<p>Mortal Kombat is arguably one of the best looking games of the raster based rendering generation. If you have suggestions for a game to analyse, feel free to let me know in the comments below.</p>
<h2 id="more-resources">More Resources</h2>
<ul>
<li>
<p><a href="https://www.artstation.com/artwork/PooyG8">Passion Republic, a studio headquartered in Kuala Lumpur, Malaysia, released a number of art assets they were responsible for making for Mortal Kombat 11.</a></p>
</li>
<li>
<p>Stephen McAuley gave a talk on the <a href="https://ubm-twvideo01.s3.amazonaws.com/o1/vault/gdc2015/presentations/McAuley_Stephen_Rendering_the_World.pdf">Far Cry 4</a> renderer which discusses a similar compute based lighting approach.</p>
</li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_peters2016"><span class="markademic-citename">[Peters et al. 2016]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Moment Shadow Mapping</span></strong></em><br/><span class="markademic-authors"><span><span>Christoph Peters</span></span><span> and <span>Reinhard Klein</span></span></span><br/><span><span class="markademic-publisher">i3D 2016</span><br/></span><span><a href="http://cg.cs.uni-bonn.de/en/publications/paper-details/peters-2015-msm/">cg.cs.uni-bonn.de</a></span></div></td></tr><tr><td id="ref_greene93"><span class="markademic-citename">[Greene et al. 1993]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Hierarchical Z-Buffer Visibility</span></strong></em><br/><span class="markademic-authors"><span><span>Ned Greene</span></span><span>, <span>Michael Kass</span></span><span> and <span>Gavin Miller</span></span></span><br/><span><span class="markademic-publisher">Association for Computing Machinery 1993</span><br/></span><span><a href="https://doi.org/10.1145/166117.166147">doi.org</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Frame Analysis - Overwatch 1</title>
<link>https://alain.xyz/blog/frame-analysis-overwatch</link>
<description>A review of the rendering architecture of the game Overwatch 1, from its depth shadow pass all the way to its UI pass and presenting to the screen.</description>
<guid isPermaLink="true">https://alain.xyz/blog/frame-analysis-overwatch</guid>
<pubDate>Mon, 13 Apr 2020 00:36:34 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>game engine</category><category>frame</category><category>analysis</category><category>render</category><category>pbr</category><category>prepass</category><category>overwatch</category>
<enclosure url="https://alain.xyz/blog/frame-analysis-overwatch/assets/cover.jpg" length="752748" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-overwatch/assets/shadow-depth.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-overwatch/assets/view-normals.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-overwatch/assets/view-depth.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-overwatch/assets/emissive.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-overwatch/assets/albedo.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-overwatch/assets/metalness.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-overwatch/assets/roughness.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-overwatch/assets/near-to-bilinear.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-overwatch/assets/baked-ambient-occlusion.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-overwatch/assets/pre-ss-reflection.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-overwatch/assets/ss-reflection.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-overwatch/assets/diffuse-lighting.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-overwatch/assets/specular-lighting.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-overwatch/assets/pre-ui.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/frame-analysis-overwatch/assets/final.jpg"/><iframe width="100%" height="512" src="https://www.youtube.com/embed/wvILHa_o4u8" frameBorder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowFullScreen=""></iframe>
<p>Overwatch is a game that focuses on teamwork.</p>
<p>As a team you need to work together to wrestle control of a portion of the map from the enemy team by directing aggression and protect teammates as in the Tank role, hitting weak points in the enemy&#x27;s defenses in the Damage Role, and improving your team&#x27;s performance by healing your team as well as increasing their damage, speed, and keeping them alive when they&#x27;re in danger with the support role.</p>
<p>Now I&#x27;m not the best player of the game, I certainly don&#x27;t put in the hours upon hours a professional streamer would constantly playing the game. Therefore take what I say with a grain of salt. That being said I do enjoy the game and find it pretty engaging, challenging, and fun.</p>
<h2 id="whats-fun">What&#x27;s Fun</h2>
<p>Overwatch is a shooter, so those familiar with this genre will find themselves right at home here. That being said, this game is much more creative than other shooters with unique weapons and abilities on top of the usual shotgun, sniper, or rifle.</p>
<p>These unique abilities form individual roles that a player could have:</p>
<ul>
<li>
<p><strong>Tanks</strong> - which could be subdivided into individuals that specialize in <strong>Shields</strong> (Reinhard, Sigma, Orisa), <strong>Protection</strong> (Zarya, Dva, Winston), <strong>Distraction</strong> (Winston, Hammond, Roadhog).</p>
</li>
<li>
<p><strong>Damage</strong> - Which can also be subdivided into specialists in <strong>Sniping</strong> (Widow, Ashe, Hanzo, McCree, Soldier), <strong>Demolition</strong> (Junkrat, Pharah, Reaper), <strong>Flanking</strong> (Genji, Tracer, Doomfist, Sombra), or <strong>Utility</strong> (Torbjorn, Symetra, Mei).</p>
</li>
<li>
<p><strong>Healer</strong> - Which again can be subdivided into <strong>Ult Protectors</strong> (Lucio, Zenyatta) that protect their team in the event of a powerful attack from the enemy, <strong>Damage Boosters</strong> (Ana, Baptiste) that can help with damage dealing, and <strong>Midfight Boosters</strong> (Moira, Mercy) where their contribution is enough to tip the scale of a midfight in favor of their team.</p>
</li>
</ul>
<p>There&#x27;s more to these individual roles, but the combination of these roles in a team can lead to interesting macroscopic patterns:</p>
<ul>
<li>
<p><strong>Death Ball</strong> - Stay entirely together and fight as a single unit. This can be slow or extremely mobile, with the more mobile variant referred to as <strong>Dive</strong> and the slower one referred to as sometimes as <strong>Goats</strong> (but that term tends to be specific to 3 tanks, 3 supports).</p>
</li>
<li>
<p><strong>Long Range</strong> - Kill off far away targets before their team has a chance to protect them. These can come in 2 variants, <strong>Poke</strong>, where the constant damage pressure overwhelms supports and tanks, and <strong>Snipers</strong> where a peaking player would instantly be picked off from long range.</p>
</li>
<li>
<p><strong>Bunker</strong> - Hold a position outright, and try to prevent the enemy team from taking it. This can also come in two variants, <strong>Firepower</strong> where there&#x27;s extreme damage pressure from the bunker, and <strong>Cow</strong> where the bunker is isolated in a contested position such as a choke or area with clear line of sight on the objective.</p>
</li>
</ul>
<p>Each of these play styles <em>counter</em> the other, but despite the &quot;rock paper scissors&quot; appearance, should your team play well enough it&#x27;s possible to beat a team that&#x27;s countering yours.</p>
<h2 id="whats-not-fun">What&#x27;s not Fun</h2>
<p>Overwatch is a very social game, and is also a game that offers internet anonymity, as a result there can be some aspects of the community that aren&#x27;t sportsperson like.</p>
<ul>
<li>
<p><strong>Upset Teammates</strong> - It can be easy for someone to let their emotions get the better of them and start complaining, but it&#x27;s possible to block players, mute players and hide chat should that be a problem.</p>
</li>
<li>
<p><strong>Hackers</strong> - They&#x27;re extremely rare, especially with a game that has so much <em>social credit</em>, where players with a lot of experience tend to play together, reducing the likelihood of running into one of these people considerably.</p>
</li>
<li>
<p><strong>Experienced Players targeting Newcomers</strong> - A copy of the game isn&#x27;t too much money for someone determined to do this, so it&#x27;s become somewhat common for people to have multiple accounts, and the matchmaker sometimes has trouble detecting these individuals and matching them with their skill tier (known colloquially as <em>smurfs</em>). Odds are most folks that are presumed as smurfs <em>just happen to play well at that moment</em>.</p>
</li>
<li>
<p><strong>Losing</strong> - This game involves a lot of team work, and should your team lack that, or make decisions on their own that lead to their isolated death, it will lead to your loss.</p>
</li>
</ul>
<h2 id="rendering-architecture">Rendering Architecture</h2>
<p>So let&#x27;s review how Overwatch renders a single frame at medium settings, with 150% resolution scaling.</p>
<blockquote>
<p>⚠️ <strong>Note</strong>: This is not an official analysis of the Overwatch renderer, we won&#x27;t be discussing everything that their renderer does, just key aspects of it and referencing techniques where appropriate.</p>
</blockquote>
<h3 id="shadow-depth-passes">Shadow Depth Passes</h3>
<p><img src="https://alain.xyz/blog/frame-analysis-overwatch/assets/shadow-depth.jpg" alt="Shadow Orto Depth" title="Shadow Depth"/></p>
<p>Overwatch begins a frame by generating orthographic depth projections for each of their shadow casting lights, rendered at 2048x2048 in medium settings. In this scene there is 1 directional light and 1 omnidirectional light rendered as a cubemap that&#x27;s 512x512 per face.</p>
<p>Following the depth prepass there&#x27;s a pass where shadows are resolved in a supertexture with different noise samples that are composited together later during lighting.</p>
<h3 id="prepass">Prepass</h3>
<table><thead><tr><th style="text-align:center"><a href="https://alain.xyz/blog/frame-analysis-overwatch/assets/view-normals.jpg" title="View Space Normals"><img src="https://alain.xyz/blog/frame-analysis-overwatch/assets/view-normals.jpg" alt="View Normals"/></a></th><th style="text-align:center"><a href="https://alain.xyz/blog/frame-analysis-overwatch/assets/view-depth.jpg" title="View Space Depth"><img src="https://alain.xyz/blog/frame-analysis-overwatch/assets/view-depth.jpg" alt="Depth"/></a></th><th style="text-align:center"><a href="https://alain.xyz/blog/frame-analysis-overwatch/assets/emissive.jpg" title="Emissive"><img src="https://alain.xyz/blog/frame-analysis-overwatch/assets/emissive.jpg" alt="Emissive"/></a></th></tr></thead><tbody><tr><td style="text-align:center"><a href="https://alain.xyz/blog/frame-analysis-overwatch/assets/albedo.jpg" title="Albedo"><img src="https://alain.xyz/blog/frame-analysis-overwatch/assets/albedo.jpg" alt="Albedo"/></a></td><td style="text-align:center"><a href="https://alain.xyz/blog/frame-analysis-overwatch/assets/metalness.jpg" title="Metalness"><img src="https://alain.xyz/blog/frame-analysis-overwatch/assets/metalness.jpg" alt="Metalness"/></a></td><td style="text-align:center"><a href="https://alain.xyz/blog/frame-analysis-overwatch/assets/roughness.jpg" title="Roughness"><img src="https://alain.xyz/blog/frame-analysis-overwatch/assets/roughness.jpg" alt="Roughness"/></a></td></tr></tbody></table>
<p>Overwatch has a standard prepass, with <strong>View Space Normals</strong>, <strong>Albedo</strong>, <strong>Metalness</strong>, <strong>Roughness</strong>, <strong>Emissive</strong>, and <strong>View Space Depth</strong>, encoded for use in later passes. The distances in view space depth imply that they use centimeters (cm) units.</p>
<p><img src="https://alain.xyz/blog/frame-analysis-overwatch/assets/near-to-bilinear.jpg" alt="Nearest Neighbor vs. Bilinear"/></p>
<p>These and subsequent passes are rendered with <code>50%</code> more pixels on the Y axis for fast anti-aliasing.</p>
<h3 id="ambient-occlusion">Ambient Occlusion</h3>
<p><img src="https://alain.xyz/blog/frame-analysis-overwatch/assets/baked-ambient-occlusion.jpg" alt="AO"/></p>
<p><strong>Ambient Occlusion</strong> is rendered first with view space AO, then composited with baked ambient occlusion.</p>
<h3 id="reflection">Reflection</h3>
<p><img src="https://alain.xyz/blog/frame-analysis-overwatch/assets/pre-ss-reflection.jpg" alt="Reflection Prepass"/></p>
<p>The reflection pass begins with a prepass that&#x27;s used to determine target surfaces to use for reflections, which is then composited with the metalness and roughness of that surface to determine the final mask in green.</p>
<p><img src="https://alain.xyz/blog/frame-analysis-overwatch/assets/ss-reflection.jpg" alt="Reflection"/></p>
<p>Then there&#x27;s a raycast from those appropriately reflective surfaces, a horizontal blur to help with azimuth reflection scaling, something physically based reflections approximations have trouble doing, a grow pass, then a mip level pass for metalness and roughness pre-integration.</p>
<h3 id="diffuse-lighting">Diffuse Lighting</h3>
<p><img src="https://alain.xyz/blog/frame-analysis-overwatch/assets/diffuse-lighting.jpg" alt="Diffuse Lighting"/></p>
<p>The diffuse lighting pass renders lighting for each individual light object visible in the scene, this scene had more than 20.</p>
<h3 id="specular-lighting">Specular Lighting</h3>
<p><img src="https://alain.xyz/blog/frame-analysis-overwatch/assets/specular-lighting.jpg" alt="Specular Lighting"/></p>
<p>If the prepass wasn&#x27;t already indicative of the presence of a PBR renderer, this confirms it, here we&#x27;re rendering our specular lighting by taking advantage of our prepass and image based lighting from the skybox.</p>
<h3 id="bloom">Bloom</h3>
<p>The bloom pass here is pretty standard across the gaming industry, the same scaling-bluring-compositing effect that can be found everywhere from Unreal to Unity to everything in-between.</p>
<h3 id="masks">Masks</h3>
<p>In the mask pass, Overwatch renders out allies, then enemies. First they render the mask, then grow the mask, then apply a sobol operator to the mask to get the final outline.</p>
<h3 id="ui-pass">UI Pass</h3>
<p><img src="https://alain.xyz/blog/frame-analysis-overwatch/assets/pre-ui.png" alt="Pre UI Pass"/></p>
<p>Prior to UI pass, there&#x27;s a prepass that renders out aspects of the UI that are encoded as scalable vector art to a render target. It also renders these at different blur levels for shadows or bloom effects.</p>
<p>Finally, the UI pass, where all the UIs are drawn, completing the frame.</p>
<h2 id="conclusion">Conclusion</h2>
<p><img src="https://alain.xyz/blog/frame-analysis-overwatch/assets/final.jpg" alt="Final Pass"/></p>
<p>All in all, it&#x27;s clear that the rendering engineers, technical artists, and individual artists over at Blizzard are very attentive to their rendering budget, so while not extremely flashy, this design is clearly meant to run absolutely anywhere and everywhere at a smooth 60 frames a second and it shows.</p>
<p>If this was insightful or if you have any suggestions for a game to analyse, let me know in the comments below, and I&#x27;ll see you all next time.</p>
<h3 id="more-resources">More Resources</h3>
<ul>
<li><a href="https://playoverwatch.com/">Blizzard Entertainment&#x27;s Overwatch</a> game released an article discussing their editor <a href="https://playoverwatch.com/en-us/news/23453344/what-it-s-like-to-work-on-overwatch-s-tools-and-engine/">TED</a>, along with a number of other articles related to their process from concept to in game character model.</li>
</ul>]]></content:encoded>
</item>
<item>
<title>GUI Architecture with Graphics APIs</title>
<link>https://alain.xyz/blog/gui-architecture-with-graphics-apis</link>
<description>An overview of the state of the art in user interfaces and the underlying architecture that drives graphical user interfaces built on top of Graphics APIs.</description>
<guid isPermaLink="true">https://alain.xyz/blog/gui-architecture-with-graphics-apis</guid>
<pubDate>Mon, 02 Mar 2020 19:00:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>shader</category><category>glsl</category><category>hlsl</category><category>vulkan</category><category>directx</category><category>opengl</category><category>spir-v</category><category>metal</category><category>gl</category><category>gui</category>
<enclosure url="https://alain.xyz/blog/gui-architecture-with-graphics-apis/assets/cover.jpg" length="119256" type="image/jpeg"/>
<content:encoded><![CDATA[<blockquote>
<p>Is performance still an issue in this era of cheap 486 computers and super-fast Pentium computers? You bet. How many programs that you use really run so fast that you wouldn&#x27;t be happier if they ran faster? We&#x27;re so used to slow software that when a compile-and-link sequence that took 2 minutes on PC takes just 10 seconds on a 486 computer, we&#x27;re ecstatic - when in truth we should be settling for nothing less than instantaneous response. ~ <a href="https://www.linkedin.com/in/michael-abrash-25a01933/">Michael Abrash&#x27;s</a> <a href="https://www.drdobbs.com/parallel/graphics-programming-black-book/184404919">Graphics Programming Black Book</a>, featured in <a href="https://twitter.com/bryphe">Bryan Phelps&#x27;s</a> <a href="https://www.youtube.com/watch?v=UDz-IqwAzIs">Talk at ReasonConf</a></p>
</blockquote>
<h2 id="overview">Overview</h2>
<p>The topic of <em>&quot;designing&quot;</em> user interfaces can be a bit subjective, we won&#x27;t be going into design trends like say, how you can achieve <a href="https://uxdesign.cc/neumorphism-in-user-interfaces-b47cef3bf3a6">Neumorphic Design</a> or <a href="https://material.io/design/">Material Design</a> in your applications, but the underlying graphics API architecture that drives user interfaces, the sequence of events that takes a <a href="https://github.com/servo/webrender/blob/f110751b93ebab1e5c9621597bcc17cee50f8f53/webrender/src/scene_building.rs#L1650">Web Application from DOM to graphics API calls</a>, or how <a href="https://reactnative.dev/docs/view">React Native goes to OS User interfaces</a> which then executes raster calls to the GPU, or game engine user interfaces that are custom built and immersive <a href="#ref_victor2012">[Victor 2012]</a> <a href="#ref_sakamoto2015">[Sakamoto 2015]</a>. We&#x27;ll be discussing:</p>
<ol>
<li>
<p><strong>Current Solutions</strong> - A very brief review of currently available solutions to solving the creation of user interfaces for native applications, web applications, and even projects that attempt to bridge the gap between the two paradigms.</p>
<ul>
<li>Qt</li>
<li>React Native</li>
<li>ImGUI</li>
<li>Revery</li>
</ul>
</li>
<li>
<p><strong>UI Abstractions</strong> - What a declarative API is, what an immediate mode API is, and how these could be written out in your own applications. We&#x27;ll be using C++ as our primary programming language here.</p>
</li>
<li>
<p><strong>Implementation Details</strong> - All UI frameworks build on top of the same fundamental draw calls and architecture, so we&#x27;ll be discussing the implementation details of these abstractions using graphics programming terminology. This somewhat touches on game engine architecture.</p>
</li>
<li>
<p><strong>Case Studies</strong> - We&#x27;ll be reviewing the underlying graphics calls that drive user interfaces in commercial applications such as:</p>
<ul>
<li>Unreal Engine 4</li>
<li>Unity 3D</li>
</ul>
</li>
</ol>
<h2 id="current-solutions">Current Solutions</h2>
<h3 id="qt">Qt</h3>
<p>Qt is the most popular cross platform UI framework in the world, used by developers to design user interfaces with native abstractions or to even interface with graphics APIs to design custom interfaces.</p>
<p>Users include us at Marmoset with <a href="https://www.marmoset.co/hexels/">Marmoset Hexels</a>, BMW with certain iterations of their vehicles, medical user interfaces. It is licensed <a href="https://www1.qt.io/qt-licensing-terms/">LGPL-3</a> which can be a bit overbearing of a license since it stipulates that any changes to Qt that you do must be provided to them, so bear that in mind if you decide to start a project with Qt. that being said, Qt can even be used along side some of these other UI solutions.</p>
<h3 id="react-native">React Native</h3>
<p>React Native is an open source graphical user interface library designed to make it easy to convert web UIs written in the DOM abstraction library React into native applications and UIs. (so essentially reusing the same code across codebases).</p>
<p>Users include facebook apps like Facebook, Messenger, Instagram, and apps like Uber, Tesla, and this can even be found in automotive user interfaces as well. User interfaces built using web abstractions can be easy to update and maintain, so it&#x27;s perfectly understandable to use this project in greenfield applications.</p>
<h3 id="revery">Revery</h3>
<p>Revery is a cross platform GUI abstraction written in a Reason, a dialect of Ocaml that looks very similar to TypeScript/JavaScript. I put this project on this list because of just how similar its goals are to the goals of game engine UI renderers.</p>
<p>It&#x27;s currently a research project by Facebook and uses GPU draw calls to render UIs on both native platforms and the Web.</p>
<h3 id="imgui">ImGui</h3>
<p>ImGui is an open source immediate mode graphical user interface API written in C++. It&#x27;s currently the most common graphical user interface library for greenfield projects due to its extreme ease of use and performance. For instance, EA&#x27;s research uses ImGui for their real time ray tracing seed demo, NVIDIA uses ImGui for their Falcor research renderer library, it&#x27;s used by countless indie projects such as <a href="https://lighttracer.org/app.html">LightTracer</a> and it&#x27;s even found in commercial applications such as Fallout 76, Tom Clancy&#x27;s Ghost Recon, and much more.</p>
<hr/>
<p>And there&#x27;s commercial game engines such as:</p>
<ul>
<li>
<p><a href="https://www.unrealengine.com/en-US/">Unreal Engine</a> - Unreal Engine&#x27;s open source architecture makes it easily extendable, and its active community can help solve any problems you might run into.</p>
</li>
<li>
<p><a href="https://unity.com/">Unity</a> - Unity&#x27;s critical mass adoption makes it easy to solve similar problems, though it is not open source.</p>
</li>
</ul>
<p>There&#x27;s no right answer here, but there&#x27;s things you as a developer should be concerned about. We&#x27;re building software user interfaces <em>for people</em>, and so using custom interfaces introduces the challenge of accessability support (which native user interfaces and web DOM elements can handle quite well).</p>
<p>That being said, custom user interfaces can enable more ease of use and programmatic control by means of real time rendering techniques such as shader execution to generate custom effects. (The grid for instance is a custom shader).</p>
<h2 id="ui-abstractions">UI Abstractions</h2>
<p>Solving the problem of rendering user interfaces for interactive applications can involve a variety of different domains:</p>
<ul>
<li>
<p><strong>Graphics API</strong> - DirectX 12, Vulkan, Metal, WebGPU, WebGL, OpenGL</p>
</li>
<li>
<p><strong>System UIs</strong> - Apple SwiftUI, NSObjects, Android UIs, Qt XML, HTML5, etc.</p>
</li>
</ul>
<p>But no matter the underlying method for drawing your UIs, there&#x27;s design patterns that persist everywhere:</p>
<h3 id="declarative-apis">Declarative APIs</h3>
<p>With declarative APIs, you write out the <strong>data</strong> that describes what you want to have executed. This is in contrast to imperative programming, where you ask the application what to execute:</p>
<div><pre><code class="language-html"><span class="hljs-comment">&lt;!--💬 Declarative--&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">&quot;M938.5,432c-5.5,9.6-15.9,16-27.7,16c-17.7,0-32-14.3-32-32&quot;</span> /&gt;</span></code></pre></div>
<div><pre><code class="language-ts"><span class="hljs-comment">// 💪 Imperative</span>
<span class="hljs-keyword">var</span> canvas = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">&#x27;mycanvas&#x27;</span>);
<span class="hljs-keyword">var</span> ctx = canvas.<span class="hljs-title function_">getContext</span>(<span class="hljs-string">&#x27;2d&#x27;</span>);
ctx.<span class="hljs-title function_">moveTo</span>(<span class="hljs-number">938.5</span>, <span class="hljs-number">432</span>);
ctx.<span class="hljs-title function_">bezierCurveTo</span>(-<span class="hljs-number">5.5</span>, <span class="hljs-number">9.6</span>, -<span class="hljs-number">15.9</span>, <span class="hljs-number">16</span>, -<span class="hljs-number">27.7</span>, <span class="hljs-number">16</span>);
ctx.<span class="hljs-title function_">bezierCurveTo</span>(-<span class="hljs-number">17.7</span>, <span class="hljs-number">0</span>, -<span class="hljs-number">32</span>, -<span class="hljs-number">14.3</span>, -<span class="hljs-number">32</span>, -<span class="hljs-number">32</span>);</code></pre></div>
<h3 id="immediate-mode">Immediate Mode</h3>
<p><strong>Immediate Mode</strong> programming is a polling based paradigm where UIs are designed by means of stack based calls every frame.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 🏃‍♀️ Immediate Mode (Polling Based)</span>
<span class="hljs-built_in">Begin</span>();
<span class="hljs-built_in">Text</span>(<span class="hljs-string">&quot;Hello World %d&quot;</span>, <span class="hljs-number">123</span>);
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">Button</span>(<span class="hljs-string">&quot;Save&quot;</span>))
{
    <span class="hljs-comment">// Do Something...</span>
}
<span class="hljs-built_in">End</span>();</code></pre></div>
<h3 id="retained-mode">Retained Mode</h3>
<p>Compare this with <strong>Retained Mode</strong> APIs such as those found in common user interface application and you&#x27;ll find that immediate mode programming allows for more dynamically changing UIs at the cost of performance due to constantly recreating the UI each frame. <a href="#ref_guillemot2016">[Guillemot 2016]</a></p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👨‍👩‍👧‍👦  Retained Mode (Interrupt Based)</span>
Window* window = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Window</span>();
window-&gt;<span class="hljs-built_in">add</span>(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Text</span>(<span class="hljs-string">&quot;Hello, world %d&quot;</span>, <span class="hljs-number">123</span>));
Button* button = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Button</span>(<span class="hljs-string">&quot;Save&quot;</span>);
button-&gt;onClick = []()
{
    <span class="hljs-comment">// Do something lambda...</span>
};
window-&gt;<span class="hljs-built_in">add</span>(button);</code></pre></div>
<p>Either way your API calls would eventually become <strong>draw lists</strong> that your application requests to be executed by your target graphics API (be it OpenGL, WebGL, DirectX 11, 12, Vulkan, WebGPU, Metal, etc.)</p>
<h2 id="conclusion">Conclusion</h2>
<p>GUI architecture tends to follow fairly clear design patterns, at a high level it&#x27;s possible to architect your GUI either with a Retained Mode architecture, an Immediate Mode architecture, or naively with imperative programming. Either way, you would build a description of your GUI, which would then be converted into a list of draw commands, which is then converted to the draw calls of your graphics API of choice (OpenGL, Vulkan, WebGL, DirectX, etc.)</p>
<h3 id="resources">Resources</h3>
<ul>
<li>
<p>Falcor - <a href="https://github.com/NVIDIAGameWorks/Falcor">GitHub.com/NVIDIAGameWorks/Falcor</a></p>
</li>
<li>
<p>ImGui - <a href="https://github.com/ocornut/imgui">Github.com/ocornut/imgui</a></p>
</li>
<li>
<p>Qt - <a href="https://www.qt.io/">Qt.io</a></p>
</li>
<li>
<p>React Native - <a href="https://reactnative.dev/">ReactNative.dev/</a></p>
</li>
<li>
<p>Revery - <a href="https://github.com/revery-ui/revery">Github.com/revery-ui/revery</a></p>
</li>
<li>
<p>Skia - <a href="https://skia.org/">Skia.org</a></p>
</li>
<li>
<p>wxWidgets - <a href="https://www.wxwidgets.org/">wxWidgets.org</a></p>
</li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_victor2012"><span class="markademic-citename">[Victor 2012]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Inventing on Principle</span></strong></em><br/><span class="markademic-authors"><span><span>Bret Victor</span></span></span><br/><span><span class="markademic-publisher">CUSEC 2012</span><br/></span></div></td></tr><tr><td id="ref_sakamoto2015"><span class="markademic-citename">[Sakamoto 2015]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Hearthstone: How to Create an Immersive User Interface</span></strong></em><br/><span class="markademic-authors"><span><span>Derek Sakamoto</span></span></span><br/><span><span class="markademic-publisher">GDC 2015</span><br/></span></div></td></tr><tr><td id="ref_guillemot2016"><span class="markademic-citename">[Guillemot 2016]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Dear ImGui</span></strong></em><br/><span class="markademic-authors"><span><span>Nicolas Guillemot</span></span></span><br/><span><span class="markademic-publisher">GDC 2016</span><br/></span><span><a href="https://www.youtube.com/watch?v=LSRJ1jZq90k">youtube.com</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Raw WebGPU</title>
<link>https://alain.xyz/blog/raw-webgpu</link>
<description>An overview on how to write a WebGPU application. Learn what key data structures and types are needed to draw in WebGPU.</description>
<guid isPermaLink="true">https://alain.xyz/blog/raw-webgpu</guid>
<pubDate>Fri, 27 Dec 2019 00:30:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>webgpu</category><category>tutorial</category><category>github</category><category>ts</category><category>js</category>
<enclosure url="https://alain.xyz/blog/raw-webgpu/assets/cover.jpg" length="62790" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgpu/assets/webgpu-logo.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgpu/assets/os-window.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgpu/assets/adapter.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgpu/assets/device.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgpu/assets/queue.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgpu/assets/canvas.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgpu/assets/attachments.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgpu/assets/buffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgpu/assets/shaders.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgpu/assets/shadermodules.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgpu/assets/uniform-buffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgpu/assets/pipeline-layout.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgpu/assets/raster-pipeline.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgpu/assets/command-encoder.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgpu/assets/triangle-raster.gif"/><link rel="preload" as="image" href="https://github.com/Twinklebear.png"/><link rel="preload" as="image" href="https://github.com/austinEng.png"/><link rel="preload" as="image" href="https://github.com/tsherif.png"/><iframe height="480" style="width:100%" scrolling="no" title="Raw WebGPU" src="https://codepen.io/alaingalvan/embed/preview/GRgvLGw?height=480&amp;theme-id=default&amp;default-tab=result" frameBorder="0" allowtransparency="true" allowFullScreen=""><p>See the Pen &lt;a href=&#x27;<a href="https://codepen.io/alaingalvan/pen/GRgvLGw&#x27;&amp;gt;Raw">https://codepen.io/alaingalvan/pen/GRgvLGw&#x27;&amp;gt;Raw</a>
WebGPU&lt;/a&gt; by Alain Galvan (&lt;a
href=&#x27;<a href="https://codepen.io/alaingalvan&#x27;&amp;gt;@alaingalvan&amp;lt;/a">https://codepen.io/alaingalvan&#x27;&amp;gt;@alaingalvan&amp;lt;/a</a>&gt;) on &lt;a
href=&#x27;<a href="https://codepen.io&#x27;&amp;gt;CodePen&amp;lt;/a">https://codepen.io&#x27;&amp;gt;CodePen&amp;lt;/a</a>&gt;.</p></iframe>
<p><img src="https://alain.xyz/blog/raw-webgpu/assets/webgpu-logo.svg" alt="WebGPU Logo by WebGPU Team"/></p>
<p>WebGPU is a new graphics API for the web that follows the architecture of <a href="https://alain.xyz/blog/comparison-of-modern-graphics-apis">modern computer graphics APIs</a> such as Microsoft <a href="https://alain.xyz/blog/raw-directx12">DirectX 12</a>, <a href="https://alain.xyz/blog/raw-vulkan">Vulkan</a>, and Apple <a href="https://alain.xyz/blog/raw-metal">Metal</a>. This shift in paradigm for web graphics APIs allows users to take advantage of the same benefits native graphics APIs bring: faster applications thanks to the ability to keep the GPU busy with work, less graphics driver specific bugs, and the potential for new features should they be implemented in the future.</p>
<p>WebGPU is arguably the most complex out of all rendering APIs on the web, though this cost is offset by both the increase in performance and guarantee of future support that the API provides. This post aims to demystify the API, making it easier to piece together how to write web apps that use it.</p>
<hr/>
<p>I&#x27;ve prepared a <a href="https://github.com/alaingalvan/webgpu-seed">Github repo</a> with everything you need to get started. We&#x27;ll walk through writing a WebGPU <em>Hello Triangle</em> application in <a href="https://www.typescriptlang.org/">TypeScript</a>.</p>
<blockquote>
<p>Check out my other post on <a href="https://alain.xyz/blog/raw-webgl">WebGL</a> for writing graphics applications with an older but widely supported web graphics API.</p>
</blockquote>
<h2 id="setup">Setup</h2>
<p>WebGPU is supported on a <a href="https://caniuse.com/?search=webgpu">wide range of platforms</a>:</p>
<ul>
<li>
<p><a href="https://www.google.com/chrome/">Google Chrome</a>: WebGPU is available on the latest release of Chrome.</p>
</li>
<li>
<p><a href="https://www.mozilla.org/en-US/firefox/channel/desktop/">Mozilla Firefox</a>: WebGPU is supported but you may need to visit <code>about:config</code> and enable <code>dom.webgpu.enabled</code>.</p>
</li>
<li>
<p><a href="https://www.microsoftedgeinsider.com/en-us/download">Microsoft Edge</a>: WebGPU is functionally the same as Chrome.</p>
</li>
<li>
<p>Apple Safari: Can be enabled with the <code>WebGPU</code> feature flag.</p>
</li>
</ul>
<p>Once you have a browser that features WebGPU, install the following:</p>
<ul>
<li>
<p><a href="https://git-scm.com/">Git</a></p>
</li>
<li>
<p><a href="https://nodejs.org/en/">Node.js</a></p>
</li>
<li>
<p>A Text Editor such as <a href="https://code.visualstudio.com/">Visual Studio Code</a>.</p>
</li>
</ul>
<p>Then type the following in any terminal such as <a href="https://code.visualstudio.com/docs/editor/integrated-terminal">VS Code&#x27;s Integrated Terminal</a>.</p>
<div><pre><code class="language-bash"><span class="hljs-comment"># 🐑 Clone the repo</span>
git <span class="hljs-built_in">clone</span> https://github.com/alaingalvan/webgpu-seed

<span class="hljs-comment"># 💿 go inside the folder</span>
<span class="hljs-built_in">cd</span> webgpu-seed

<span class="hljs-comment"># 🔨 Start building the project</span>
npm start</code></pre></div>
<blockquote>
<p>Refer to <a href="https://alain.xyz/blog/designing-a-web-app">this blog post on designing web libraries and apps</a> for more details on Node.js, packages, etc.</p>
</blockquote>
<h2 id="project-layout">Project Layout</h2>
<p>As your project becomes more complex, you&#x27;ll want to separate files and organize your application to something more akin to a game or renderer, check out this post on <a href="https://alain.xyz/blog/game-engine-architecture">game engine architecture</a> and this one on <a href="https://alain.xyz/blog/realtime-renderer-architectures">real time renderer architecture</a> for more details.</p>
<div><pre><code class="language-bash">├─ 📂 node_modules/   <span class="hljs-comment"># 👶 Dependencies</span>
│  ├─ 📁 gl-matrix      <span class="hljs-comment"># ➕ Linear Algebra</span>
│  └─ 📁 ...            <span class="hljs-comment"># 🕚 Other Dependencies (TypeScript, Webpack, etc.)</span>
├─ 📂 src/            <span class="hljs-comment"># 🌟 Source Files</span>
│  ├─ 📄 renderer.ts    <span class="hljs-comment"># 🔺 Triangle Renderer</span>
│  └─ 📄 main.ts        <span class="hljs-comment"># 🏁 Application Main</span>
├─ 📄 .gitignore      <span class="hljs-comment"># 👁️ Ignore Certain Files in Git Repo</span>
├─ 📄 package.json    <span class="hljs-comment"># 📦 Node Package File</span>
├─ 📄 license.md      <span class="hljs-comment"># ⚖️ Your License (Unlicense)</span>
└─ 📃readme.md        <span class="hljs-comment"># 📖 Read Me!</span></code></pre></div>
<h3 id="dependencies">Dependencies</h3>
<ul>
<li>
<p><a href="https://github.com/toji/gl-matrix">gl-matrix</a> - A JavaScript library that allows users to write <code>glsl</code> like JavaScript code, with types for vectors, matrices, etc. While not in use in this sample, it&#x27;s incredibly useful for programming more advanced topics such as camera matrices.</p>
</li>
<li>
<p><a href="https://github.com/microsoft/typescript">TypeScript</a> - JavaScript with types, makes it significantly easier to program web apps with instant autocomplete and type checking.</p>
</li>
<li>
<p><a href="https://github.com/webpack/webpack">Webpack</a> - A JavaScript compilation tool to build minified outputs and test our apps faster.</p>
</li>
</ul>
<h2 id="overview">Overview</h2>
<p>In this application we will need to do the following:</p>
<ol>
<li>
<p><strong>Initialize the API</strong> - Check if <code>navigator.gpu</code> exists, and if it does, request a <code>GPUAdapter</code>, then request a <code>GPUDevice</code>, and get that device&#x27;s default <code>GPUQueue</code>.</p>
</li>
<li>
<p><strong>Setup Frame Backings</strong> - create a <code>GPUCanvasContext</code> and configure it to receive a <code>GPUTexture</code> for the current frame, as well as any other attachments you might need (such as a depth-stencil texture, etc.). Create <code>GPUTextureView</code>s for those textures.</p>
</li>
<li>
<p><strong>Initialize Resources</strong> - Create your Vertex and Index <code>GPUBuffer</code>s, load your WebGPU Shading Language (WGSL) shaders as <code>GPUShaderModule</code>s, create your <code>GPURenderPipeline</code> by describing every stage of the graphics pipeline. Finally, build your <code>GPUCommandEncoder</code> with what what render passes you intend to run, then a <code>GPURenderPassEncoder</code> with all the draw calls you intend to execute for that render pass.</p>
</li>
<li>
<p><strong>Render</strong> - Submit your <code>GPUCommandEncoder</code> by calling <code>.finish()</code>, and submitting that to your <code>GPUQueue</code>. Refresh the canvas context by calling <code>requestAnimationFrame</code>.</p>
</li>
<li>
<p><strong>Destroy</strong> - Destroy any data structures after you&#x27;re done using the API.</p>
</li>
</ol>
<p>The following will explain snippets from that can be found in the <a href="https://github.com/alaingalvan/webgpu-seed">Github repo</a>, with certain parts omitted, and <em>member variables (<code>this.memberVariable</code>) declared inline without the <code>this.</code> prefix so their type is easier to see and the examples here can work on their own</em>.</p>
<h2 id="initialize-api">Initialize API</h2>
<h3 id="entry-point">Entry Point</h3>
<p><img src="https://alain.xyz/blog/raw-webgpu/assets/os-window.svg" alt="Entry Point Diagram"/></p>
<p>To access the WebGPU API, you need to see if there exists a <code>gpu</code> object in the global <code>navigator</code>.</p>
<div><pre title="Checking if WebGPU is Supported"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// 🏭 Entry to WebGPU</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">const</span> <span class="hljs-attr">entry</span>: <span class="hljs-variable">GPU</span> = navigator.<span class="hljs-property">gpu</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"><span class="hljs-keyword">if</span> (!entry) {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4">  <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title">Error</span>(<span class="hljs-string">&#x27;WebGPU is not supported on this browser.&#x27;</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5">}</td></tr></tbody></table></code></pre></div>
<h3 id="adapter">Adapter</h3>
<p><img src="https://alain.xyz/blog/raw-webgpu/assets/adapter.svg" alt="Adapter Diagram"/></p>
<p>An <strong>Adapter</strong> describes the physical properties of a given GPU, such as its name, extensions, and device limits.</p>
<div><pre title="Finding a GPUAdapter"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// ✋ Declare adapter handle</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">let</span> <span class="hljs-attr">adapter</span>: <span class="hljs-title">GPUAdapter</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-comment">// 🙏 Inside an async function...</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"><span class="hljs-comment">// 🔌 Physical Device Adapter</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">adapter = <span class="hljs-keyword">await</span> entry.<span class="hljs-title">requestAdapter</span>();</td></tr></tbody></table></code></pre></div>
<h3 id="device">Device</h3>
<p><img src="https://alain.xyz/blog/raw-webgpu/assets/device.svg" alt="Device Diagram"/></p>
<p>A <strong>Device</strong> is how you access the <em>core</em> of the WebGPU API, and will allow you to create the data structures you&#x27;ll need.</p>
<div><pre title="Finding a GPUDevice"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// ✋ Declare device handle</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">let</span> <span class="hljs-attr">device</span>: <span class="hljs-title">GPUDevice</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-comment">// 🙏 Inside an async function...</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"><span class="hljs-comment">// 💻 Logical Device</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">device = <span class="hljs-keyword">await</span> adapter.<span class="hljs-title">requestDevice</span>();</td></tr></tbody></table></code></pre></div>
<h3 id="queue">Queue</h3>
<p><img src="https://alain.xyz/blog/raw-webgpu/assets/queue.svg" alt="Queue Diagram"/></p>
<p>A <strong>Queue</strong> allows you to send work asynchronously to the GPU. As of the writing of this post, you can only access a single default queue from a given <code>GPUDevice</code>.</p>
<div><pre title="Finding a GPUQueue"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// ✋ Declare queue handle</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">let</span> <span class="hljs-attr">queue</span>: <span class="hljs-title">GPUQueue</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-comment">// 📦 Queue</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5">queue = device.<span class="hljs-property">queue</span>;</td></tr></tbody></table></code></pre></div>
<h2 id="frame-backings">Frame Backings</h2>
<h3 id="canvas-context">Canvas Context</h3>
<p><img src="https://alain.xyz/blog/raw-webgpu/assets/canvas.svg" alt="Canvas Element Diagram"/></p>
<p>In order to see what you&#x27;re drawing, you&#x27;ll need an <code>HTMLCanvasElement</code> and to setup a <strong>Canvas Context</strong> from that canvas. A Canvas Context manages a series of textures you&#x27;ll use to present your final render output to your <code>&lt;canvas&gt;</code> element.</p>
<div><pre title="Creating a WebGPU Context"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// ✋ Declare context handle</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">const</span> <span class="hljs-attr">context</span>: <span class="hljs-title">GPUCanvasContext</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-comment">// ⚪ Create Context</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5">context = canvas.<span class="hljs-title">getContext</span>(<span class="hljs-string">&#x27;webgpu&#x27;</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7"><span class="hljs-comment">// ⛓️ Configure Context</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8"><span class="hljs-keyword">const</span> <span class="hljs-attr">canvasConfig</span>: <span class="hljs-title">GPUCanvasConfiguration</span> = {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">  <span class="hljs-attr">device</span>: <span class="hljs-variable">this</span>.<span class="hljs-property">device</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">  <span class="hljs-attr">format</span>: <span class="hljs-string">&#x27;bgra8unorm&#x27;</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">  <span class="hljs-attr">usage</span>: <span class="hljs-title">GPUTextureUsage</span>.<span class="hljs-property">RENDER_ATTACHMENT</span> | <span class="hljs-title">GPUTextureUsage</span>.<span class="hljs-property">COPY_SRC</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12">  <span class="hljs-attr">alphaMode</span>: <span class="hljs-string">&#x27;opaque&#x27;</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15">context.<span class="hljs-title">configure</span>(canvasConfig);</td></tr></tbody></table></code></pre></div>
<h3 id="frame-buffer-attachments">Frame Buffer Attachments</h3>
<p><img src="https://alain.xyz/blog/raw-webgpu/assets/attachments.svg" alt="Texture Attachments Diagram"/></p>
<p>When executing different passes of your rendering system, you&#x27;ll need output textures to write to, be it depth textures for depth testing or shadows, or attachments for various aspects of a deferred renderer such as view space normals, PBR reflectivity/roughness, etc.</p>
<p>Frame buffers attachments are references to texture views, which you&#x27;ll see later when we write our rendering logic.</p>
<div><pre title="Creating Frame Buffer Attachments"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// ✋ Declare attachment handles</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">let</span> <span class="hljs-attr">depthTexture</span>: <span class="hljs-title">GPUTexture</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"><span class="hljs-keyword">let</span> <span class="hljs-attr">depthTextureView</span>: <span class="hljs-title">GPUTextureView</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"><span class="hljs-comment">// 🤔 Create Depth Backing</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"><span class="hljs-keyword">const</span> <span class="hljs-attr">depthTextureDesc</span>: <span class="hljs-title">GPUTextureDescriptor</span> = {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">  <span class="hljs-attr">size</span>: [canvas.<span class="hljs-property">width</span>, canvas.<span class="hljs-property">height</span>, <span class="hljs-number">1</span>],</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">  <span class="hljs-attr">dimension</span>: <span class="hljs-string">&#x27;2d&#x27;</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">  <span class="hljs-attr">format</span>: <span class="hljs-string">&#x27;depth24plus-stencil8&#x27;</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">  <span class="hljs-attr">usage</span>: <span class="hljs-title">GPUTextureUsage</span>.<span class="hljs-property">RENDER_ATTACHMENT</span> | <span class="hljs-title">GPUTextureUsage</span>.<span class="hljs-property">COPY_SRC</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">depthTexture = device.<span class="hljs-title">createTexture</span>(depthTextureDesc);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14">depthTextureView = depthTexture.<span class="hljs-title">createView</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16"><span class="hljs-comment">// ✋ Declare canvas context image handles</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17"><span class="hljs-keyword">let</span> <span class="hljs-attr">colorTexture</span>: <span class="hljs-title">GPUTexture</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18"><span class="hljs-keyword">let</span> <span class="hljs-attr">colorTextureView</span>: <span class="hljs-title">GPUTextureView</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="20"><div class="hljs-ln-n" data-line-number="20"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="20">colorTexture = context.<span class="hljs-title">getCurrentTexture</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="21"><div class="hljs-ln-n" data-line-number="21"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="21">colorTextureView = colorTexture.<span class="hljs-title">createView</span>();</td></tr></tbody></table></code></pre></div>
<h2 id="initialize-resources">Initialize Resources</h2>
<h3 id="vertex--index-buffers">Vertex &amp; Index Buffers</h3>
<p><img src="https://alain.xyz/blog/raw-webgpu/assets/buffer.svg" alt="Buffers Diagram"/></p>
<p>A <strong>Buffer</strong> is an array of data, such as a mesh&#x27;s positional data, color data, index data, etc. When rendering triangles with a raster based <em>graphics pipeline</em>, you&#x27;ll need 1 or more buffers of vertex data (commonly referred to as <em>Vertex Buffer Objects</em> or <em>VBO</em>s), and 1 buffer of the indices that correspond with each triangle vertex that you intend to draw (otherwise known as an <em>Index Buffer Object</em> or <em>IBO</em>).</p>
<div><pre title="Creating GPUBuffers"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// 📈 Position Vertex Buffer Data</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">const</span> positions = <span class="hljs-keyword">new</span> <span class="hljs-title">Float32Array</span>([</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3">  <span class="hljs-number">1.0</span>, -<span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>, -<span class="hljs-number">1.0</span>, -<span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4">]);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"><span class="hljs-comment">// 🎨 Color Vertex Buffer Data</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7"><span class="hljs-keyword">const</span> colors = <span class="hljs-keyword">new</span> <span class="hljs-title">Float32Array</span>([</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">  <span class="hljs-number">1.0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">  <span class="hljs-number">0.0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">  <span class="hljs-number">0.0</span>, <span class="hljs-comment">// 🔴</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">  <span class="hljs-number">0.0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12">  <span class="hljs-number">1.0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">  <span class="hljs-number">0.0</span>, <span class="hljs-comment">// 🟢</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14">  <span class="hljs-number">0.0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15">  <span class="hljs-number">0.0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16">  <span class="hljs-number">1.0</span> <span class="hljs-comment">// 🔵</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17">]);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19"><span class="hljs-comment">// 📇 Index Buffer Data</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="20"><div class="hljs-ln-n" data-line-number="20"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="20"><span class="hljs-keyword">const</span> indices = <span class="hljs-keyword">new</span> <span class="hljs-title">Uint16Array</span>([<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>]);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="21"><div class="hljs-ln-n" data-line-number="21"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="21"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="22"><div class="hljs-ln-n" data-line-number="22"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="22"><span class="hljs-comment">// ✋ Declare buffer handles</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="23"><div class="hljs-ln-n" data-line-number="23"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="23"><span class="hljs-keyword">let</span> <span class="hljs-attr">positionBuffer</span>: <span class="hljs-title">GPUBuffer</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="24"><div class="hljs-ln-n" data-line-number="24"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="24"><span class="hljs-keyword">let</span> <span class="hljs-attr">colorBuffer</span>: <span class="hljs-title">GPUBuffer</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="25"><div class="hljs-ln-n" data-line-number="25"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="25"><span class="hljs-keyword">let</span> <span class="hljs-attr">indexBuffer</span>: <span class="hljs-title">GPUBuffer</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="26"><div class="hljs-ln-n" data-line-number="26"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="26"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="27"><div class="hljs-ln-n" data-line-number="27"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="27"><span class="hljs-comment">// 👋 Helper function for creating GPUBuffer(s) out of Typed Arrays</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="28"><div class="hljs-ln-n" data-line-number="28"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="28"><span class="hljs-keyword">const</span> <span class="hljs-title">createBuffer</span> = (<span class="hljs-params"><span class="hljs-attr">arr</span>: <span class="hljs-title">Float32Array</span> | <span class="hljs-title">Uint16Array</span>, <span class="hljs-attr">usage</span>: <span class="hljs-built_in">number</span></span>) =&gt; {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="29"><div class="hljs-ln-n" data-line-number="29"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="29">  <span class="hljs-comment">// 📏 Align to 4 bytes (thanks @chrimsonite)</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="30"><div class="hljs-ln-n" data-line-number="30"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="30">  <span class="hljs-keyword">let</span> desc = {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="31"><div class="hljs-ln-n" data-line-number="31"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="31">    <span class="hljs-attr">size</span>: (arr.<span class="hljs-property">byteLength</span> + <span class="hljs-number">3</span>) &amp; ~<span class="hljs-number">3</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="32"><div class="hljs-ln-n" data-line-number="32"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="32">    usage,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="33"><div class="hljs-ln-n" data-line-number="33"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="33">    <span class="hljs-attr">mappedAtCreation</span>: <span class="hljs-literal">true</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="34"><div class="hljs-ln-n" data-line-number="34"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="34">  };</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="35"><div class="hljs-ln-n" data-line-number="35"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="35">  <span class="hljs-keyword">let</span> buffer = device.<span class="hljs-title">createBuffer</span>(desc);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="36"><div class="hljs-ln-n" data-line-number="36"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="36"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="37"><div class="hljs-ln-n" data-line-number="37"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="37">  <span class="hljs-keyword">const</span> writeArray =</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="38"><div class="hljs-ln-n" data-line-number="38"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="38">    arr <span class="hljs-keyword">instanceof</span> <span class="hljs-title">Uint16Array</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="39"><div class="hljs-ln-n" data-line-number="39"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="39">      ? <span class="hljs-keyword">new</span> <span class="hljs-title">Uint16Array</span>(buffer.<span class="hljs-title">getMappedRange</span>())</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="40"><div class="hljs-ln-n" data-line-number="40"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="40">      : <span class="hljs-keyword">new</span> <span class="hljs-title">Float32Array</span>(buffer.<span class="hljs-title">getMappedRange</span>());</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="41"><div class="hljs-ln-n" data-line-number="41"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="41">  writeArray.<span class="hljs-title">set</span>(arr);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="42"><div class="hljs-ln-n" data-line-number="42"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="42">  buffer.<span class="hljs-title">unmap</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="43"><div class="hljs-ln-n" data-line-number="43"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="43">  <span class="hljs-keyword">return</span> buffer;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="44"><div class="hljs-ln-n" data-line-number="44"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="44">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="45"><div class="hljs-ln-n" data-line-number="45"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="45"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="46"><div class="hljs-ln-n" data-line-number="46"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="46">positionBuffer = <span class="hljs-title">createBuffer</span>(positions, <span class="hljs-title">GPUBufferUsage</span>.<span class="hljs-property">VERTEX</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="47"><div class="hljs-ln-n" data-line-number="47"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="47">colorBuffer = <span class="hljs-title">createBuffer</span>(colors, <span class="hljs-title">GPUBufferUsage</span>.<span class="hljs-property">VERTEX</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="48"><div class="hljs-ln-n" data-line-number="48"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="48">indexBuffer = <span class="hljs-title">createBuffer</span>(indices, <span class="hljs-title">GPUBufferUsage</span>.<span class="hljs-property">INDEX</span>);</td></tr></tbody></table></code></pre></div>
<h3 id="shaders">Shaders</h3>
<p><img src="https://alain.xyz/blog/raw-webgpu/assets/shaders.svg" alt="WGSL Shaders"/></p>
<p>With WebGPU comes a new shader language: <strong>WebGPU Shading Language (WGSL)</strong>:</p>
<p>WebGPU Shading Language is similar to other languages like Rust, Metal Shading Language (MSL), and DirectX High Level Shading Language, with JavaScript style decorators such as <code>@location(0)</code>, Rust style code formatting with <code>snake_case</code> functions/members, <code>CamelCase</code> structs, and functions following <code>fn my_func() -&gt; i32</code>:</p>
<p>Here&#x27;s the vertex shader source:</p>
<div><pre title="Basic Vertex Shader"><code class="language-wgsl"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-keyword">struct</span> <span class="hljs-title">VSOut</span> {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2">    <span class="hljs-meta-keyword">@builtin</span>(position) nds_position: <span class="hljs-type">vec4</span>&lt;<span class="hljs-type">f32</span>&gt;,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3">    <span class="hljs-meta-keyword">@location</span>(<span class="hljs-number">0</span>) color: <span class="hljs-type">vec3</span>&lt;<span class="hljs-type">f32</span>&gt;,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"><span class="hljs-meta-keyword">@vertex</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span>(<span class="hljs-meta-keyword">@location</span>(<span class="hljs-number">0</span>) in_pos: <span class="hljs-type">vec3</span>&lt;<span class="hljs-type">f32</span>&gt;,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">        <span class="hljs-meta-keyword">@location</span>(<span class="hljs-number">1</span>) in_color: <span class="hljs-type">vec3</span>&lt;<span class="hljs-type">f32</span>&gt;) -&gt; <span class="hljs-title">VSOut</span> {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">    <span class="hljs-type">var</span> vs_out: VSOut;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">    vs_out.nds_position = <span class="hljs-type">vec4</span>&lt;<span class="hljs-type">f32</span>&gt;(in_pos, <span class="hljs-number">1.0</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">    vs_out.color = inColor;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12">    <span class="hljs-keyword">return</span> vsOut;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">}</td></tr></tbody></table></code></pre></div>
<p>Here&#x27;s the fragment shader source:</p>
<div><pre title="Basic Fragment Shader"><code class="language-wgsl"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-meta-keyword">@fragment</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span>(<span class="hljs-meta-keyword">@location</span>(<span class="hljs-number">0</span>) in_color: <span class="hljs-type">vec3</span>&lt;<span class="hljs-type">f32</span>&gt;) -&gt; <span class="hljs-meta-keyword">@location</span>(<span class="hljs-number">0</span>) <span class="hljs-type">vec4</span>&lt;<span class="hljs-type">f32</span>&gt; {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3">    <span class="hljs-keyword">return</span> <span class="hljs-type">vec4</span>&lt;<span class="hljs-type">f32</span>&gt;(in_color, <span class="hljs-number">1.0</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4">}</td></tr></tbody></table></code></pre></div>
<blockquote>
<p>It is possible to transpile other shading languages like HLSL or GLSL to WGSL using a transpiler like <a href="https://github.com/gfx-rs/naga">Mozilla Naga</a> or <a href="https://dawn.googlesource.com/tint/">Google Tint</a>, but the process is a bit involved.</p>
</blockquote>
<h3 id="shader-modules">Shader Modules</h3>
<p><img src="https://alain.xyz/blog/raw-webgpu/assets/shadermodules.svg" alt="Shader Modules Diagram"/></p>
<p><strong>Shader Module</strong>s are <em>plain text WGSL files</em> that execute on the GPU when executing a given pipeline.</p>
<div><pre title="Creating Shader Modules"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// 📄 dDeclare in line your WGSL code or import if using Webpack asset/source:</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">import</span> vertShaderCode <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./shaders/triangle.vert.wgsl&#x27;</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"><span class="hljs-keyword">import</span> fragShaderCode <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./shaders/triangle.frag.wgsl&#x27;</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"><span class="hljs-comment">// ✋ Declare shader module handles</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"><span class="hljs-keyword">let</span> <span class="hljs-attr">vertModule</span>: <span class="hljs-title">GPUShaderModule</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7"><span class="hljs-keyword">let</span> <span class="hljs-attr">fragModule</span>: <span class="hljs-title">GPUShaderModule</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9"><span class="hljs-keyword">const</span> vsmDesc = { <span class="hljs-attr">code</span>: vertShaderCode };</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">vertModule = device.<span class="hljs-title">createShaderModule</span>(vsmDesc);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12"><span class="hljs-keyword">const</span> fsmDesc = { <span class="hljs-attr">code</span>: fragShaderCode };</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">fragModule = device.<span class="hljs-title">createShaderModule</span>(fsmDesc);</td></tr></tbody></table></code></pre></div>
<blockquote>
<p>In Webpack you can import WebGPU shaders as strings by adding this object to your list of rules:</p>
<div><pre title="Webpack WGSL Loader"><code class="language-js"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1">{</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2">  <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.(wgsl|glsl)$/</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3">  <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;asset/source&quot;</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4">}</td></tr></tbody></table></code></pre></div>
</blockquote>
<h3 id="uniform-buffer">Uniform Buffer</h3>
<p><img src="https://alain.xyz/blog/raw-webgpu/assets/uniform-buffer.svg" alt="Uniform Buffer Diagram"/></p>
<p>You&#x27;ll often times need to feed data directly to your shader modules, and to do this you&#x27;ll need to specify a uniform. In order to create a <strong>Uniform Buffer</strong> in your shader, declare the following prior to your main function:</p>
<div><pre title="WGSL Uniforms"><code class="language-wgsl"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-keyword">struct</span> <span class="hljs-title">UBO</span> {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2">  modelViewProj: <span class="hljs-type">mat4x4</span>&lt;<span class="hljs-type">f32</span>&gt;,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3">  primaryColor: <span class="hljs-type">vec4</span>&lt;<span class="hljs-type">f32</span>&gt;,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4">  accentColor: <span class="hljs-type">vec4</span>&lt;<span class="hljs-type">f32</span>&gt;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7"><span class="hljs-meta-keyword">@group</span>(<span class="hljs-number">0</span>) <span class="hljs-meta-keyword">@binding</span>(<span class="hljs-number">0</span>)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8"><span class="hljs-type">var</span>&lt;<span class="hljs-keyword">uniform</span>&gt; uniforms: UBO;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10"><span class="hljs-comment">// ❗ Then in your Vertex Shader&#x27;s main file,</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11"><span class="hljs-comment">// replace the 4th to last line with:</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12">vsOut.Position = uniforms.modelViewProj * <span class="hljs-type">vec4</span>&lt;<span class="hljs-type">f32</span>&gt;(inPos, <span class="hljs-number">1.0</span>);</td></tr></tbody></table></code></pre></div>
<p>Then in your JavaScript code, create a uniform buffer as you would with an index/vertex buffer.</p>
<blockquote>
<p>You&#x27;ll want to use a library like <a href="https://github.com/toji/gl-matrix">gl-matrix</a> in order to better manage linear algebra calculations such as matrix multiplication.</p>
</blockquote>
<div><pre title="Creating uniform buffers"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// 👔 Uniform Data</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">const</span> uniformData = <span class="hljs-keyword">new</span> <span class="hljs-title">Float32Array</span>([</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4">    <span class="hljs-comment">// ♟️ ModelViewProjection Matrix (Identity)</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5">    <span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6">    <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">    <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">    <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">    <span class="hljs-comment">// 🔴 Primary Color</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">    <span class="hljs-number">0.9</span>, <span class="hljs-number">0.1</span>, <span class="hljs-number">0.3</span>, <span class="hljs-number">1.0</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">    <span class="hljs-comment">// 🟣 Accent Color</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14">    <span class="hljs-number">0.8</span>, <span class="hljs-number">0.2</span>, <span class="hljs-number">0.8</span>, <span class="hljs-number">1.0</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15">]);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17"><span class="hljs-comment">// ✋ Declare buffer handles</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18"><span class="hljs-keyword">let</span> <span class="hljs-attr">uniformBuffer</span>: <span class="hljs-title">GPUBuffer</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="20"><div class="hljs-ln-n" data-line-number="20"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="20">uniformBuffer = <span class="hljs-title">createBuffer</span>(uniformData, <span class="hljs-title">GPUBufferUsage</span>.<span class="hljs-property">UNIFORM</span> | <span class="hljs-title">GPUBufferUsage</span>.<span class="hljs-property">COPY_DST</span>);</td></tr></tbody></table></code></pre></div>
<h3 id="pipeline-layout">Pipeline Layout</h3>
<p><img src="https://alain.xyz/blog/raw-webgpu/assets/pipeline-layout.svg" alt="Pipeline Layout Diagram"/></p>
<p>Once you have a uniform, you can create a <strong>Pipeline Layout</strong> to describe where that uniform will be when executing a <em>Graphics Pipeline</em>.</p>
<p>You have 2 options here, you can either let WebGPU create your pipeline layout for you, or get it from a pipeline that&#x27;s already been created:</p>
<div><pre title="Creating Pipeline Layouts with Implicit Layouts"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-keyword">let</span> <span class="hljs-attr">bindGroupLayout</span>: <span class="hljs-title">GPUBindGroupLayout</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">let</span> <span class="hljs-attr">uniformBindGroup</span>: <span class="hljs-title">GPUBindGroup</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-comment">// 👨‍🔧 Create your graphics pipeline...</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"><span class="hljs-comment">// 🧙‍♂️ Then get your implicit pipeline layout:</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">bindGroupLayout = pipeline.<span class="hljs-title">getBindGroupLayout</span>(<span class="hljs-number">0</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9"><span class="hljs-comment">// 🗄️ Bind Group</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10"><span class="hljs-comment">// ✍ This would be used when *encoding commands*</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">uniformBindGroup = device.<span class="hljs-title">createBindGroup</span>({</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12">  <span class="hljs-attr">layout</span>: bindGroupLayout,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">  <span class="hljs-attr">entries</span>: [</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14">    {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15">      <span class="hljs-attr">binding</span>: <span class="hljs-number">0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16">      <span class="hljs-attr">resource</span>: {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17">        <span class="hljs-attr">buffer</span>: uniformBuffer</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18">      }</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19">    }</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="20"><div class="hljs-ln-n" data-line-number="20"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="20">  ]</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="21"><div class="hljs-ln-n" data-line-number="21"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="21">});</td></tr></tbody></table></code></pre></div>
<p>Or if you know the layout in advance, you can describe it yourself and use it during pipeline creation:</p>
<div><pre title="Creating Pipeline Layouts Manually"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// ✋ Declare handles</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">let</span> <span class="hljs-attr">uniformBindGroupLayout</span>: <span class="hljs-title">GPUBindGroupLayout</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"><span class="hljs-keyword">let</span> <span class="hljs-attr">uniformBindGroup</span>: <span class="hljs-title">GPUBindGroup</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-keyword">let</span> <span class="hljs-attr">layout</span>: <span class="hljs-title">GPUPipelineLayout</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"><span class="hljs-comment">// 📁 Bind Group Layout</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">uniformBindGroupLayout = device.<span class="hljs-title">createBindGroupLayout</span>({</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">  <span class="hljs-attr">entries</span>: [</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">    {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">      <span class="hljs-attr">binding</span>: <span class="hljs-number">0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">      <span class="hljs-attr">visibility</span>: <span class="hljs-title">GPUShaderStage</span>.<span class="hljs-property">VERTEX</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12">      <span class="hljs-attr">buffer</span>: {}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">    }</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14">  ]</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15">});</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17"><span class="hljs-comment">// 🗄️ Bind Group</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18"><span class="hljs-comment">// ✍ This would be used when *encoding commands*</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19">uniformBindGroup = device.<span class="hljs-title">createBindGroup</span>({</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="20"><div class="hljs-ln-n" data-line-number="20"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="20">  <span class="hljs-attr">layout</span>: uniformBindGroupLayout,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="21"><div class="hljs-ln-n" data-line-number="21"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="21">  <span class="hljs-attr">entries</span>: [</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="22"><div class="hljs-ln-n" data-line-number="22"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="22">    {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="23"><div class="hljs-ln-n" data-line-number="23"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="23">      <span class="hljs-attr">binding</span>: <span class="hljs-number">0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="24"><div class="hljs-ln-n" data-line-number="24"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="24">      <span class="hljs-attr">resource</span>: {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="25"><div class="hljs-ln-n" data-line-number="25"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="25">        <span class="hljs-attr">buffer</span>: uniformBuffer</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="26"><div class="hljs-ln-n" data-line-number="26"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="26">      }</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="27"><div class="hljs-ln-n" data-line-number="27"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="27">    }</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="28"><div class="hljs-ln-n" data-line-number="28"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="28">  ]</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="29"><div class="hljs-ln-n" data-line-number="29"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="29">});</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="30"><div class="hljs-ln-n" data-line-number="30"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="30"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="31"><div class="hljs-ln-n" data-line-number="31"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="31"><span class="hljs-comment">// 🗂️ Pipeline Layout</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="32"><div class="hljs-ln-n" data-line-number="32"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="32"><span class="hljs-comment">// 👩‍🔧 This would be used as a member of a GPUPipelineDescriptor when *creating a pipeline*</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="33"><div class="hljs-ln-n" data-line-number="33"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="33"><span class="hljs-keyword">const</span> pipelineLayoutDesc = { <span class="hljs-attr">bindGroupLayouts</span>: [uniformBindGroupLayout] };</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="34"><div class="hljs-ln-n" data-line-number="34"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="34">layout = device.<span class="hljs-title">createPipelineLayout</span>(pipelineLayoutDesc);</td></tr></tbody></table></code></pre></div>
<p>When encoding commands, you can use this uniform with <code>setBindGroup</code>:</p>
<div><pre><code class="language-ts"><span class="hljs-comment">// ✍ Later when you&#x27;re encoding commands:</span>
passEncoder.<span class="hljs-title function_">setBindGroup</span>(<span class="hljs-number">0</span>, uniformBindGroup);</code></pre></div>
<h3 id="graphics-pipeline">Graphics Pipeline</h3>
<p><img src="https://alain.xyz/blog/raw-webgpu/assets/raster-pipeline.svg" alt="Pipeline Diagram"/></p>
<p>A <strong>Graphics Pipeline</strong> describes all the data that&#x27;s to be fed into the execution of a raster based graphics pipeline. This includes:</p>
<ul>
<li>
<p>🔣 <strong>Input Assembly</strong> - What does each vertex look like? Which attributes are where, and how do they align in memory?</p>
</li>
<li>
<p>🖍️ <strong>Shader Modules</strong> - What shader modules will you be using when executing this graphics pipeline?</p>
</li>
<li>
<p>✏️ <strong>Depth/Stencil State</strong> - Should you perform depth testing? If so, what function should you use to test depth?</p>
</li>
<li>
<p>🍥 <strong>Blend State</strong> - How should colors be blended between the previously written color and current one?</p>
</li>
<li>
<p>🔺 <strong>Rasterization</strong> - How does the rasterizer behave when executing this graphics pipeline? Does it cull faces? Which direction should the face be culled?</p>
</li>
<li>
<p>💾 <strong>Uniform Data</strong> - What kind of uniform data should your shaders expect? In WebGPU this is done by describing a <strong>Pipeline Layout</strong>.</p>
</li>
</ul>
<p>WebGPU features smart defaults for the graphics pipeline state, so most of the time you won&#x27;t need to even set parts of it:</p>
<div><pre title="GPURenderPipeline Creation"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// ✋ Declare pipeline handle</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">let</span> <span class="hljs-attr">pipeline</span>: <span class="hljs-title">GPURenderPipeline</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-comment">// ⚗️ Graphics Pipeline</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"><span class="hljs-comment">// 🔣 Input Assembly</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7"><span class="hljs-keyword">const</span> <span class="hljs-attr">positionAttribDesc</span>: <span class="hljs-title">GPUVertexAttribute</span> = {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">  <span class="hljs-attr">shaderLocation</span>: <span class="hljs-number">0</span>, <span class="hljs-comment">// @location(0)</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">  <span class="hljs-attr">offset</span>: <span class="hljs-number">0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">  <span class="hljs-attr">format</span>: <span class="hljs-string">&#x27;float32x3&#x27;</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12"><span class="hljs-keyword">const</span> <span class="hljs-attr">colorAttribDesc</span>: <span class="hljs-title">GPUVertexAttribute</span> = {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">  <span class="hljs-attr">shaderLocation</span>: <span class="hljs-number">1</span>, <span class="hljs-comment">// @location(1)</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14">  <span class="hljs-attr">offset</span>: <span class="hljs-number">0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15">  <span class="hljs-attr">format</span>: <span class="hljs-string">&#x27;float32x3&#x27;</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17"><span class="hljs-keyword">const</span> <span class="hljs-attr">positionBufferDesc</span>: <span class="hljs-title">GPUVertexBufferLayout</span> = {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18">  <span class="hljs-attr">attributes</span>: [positionAttribDesc],</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19">  <span class="hljs-attr">arrayStride</span>: <span class="hljs-number">4</span> * <span class="hljs-number">3</span>, <span class="hljs-comment">// sizeof(float) * 3</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="20"><div class="hljs-ln-n" data-line-number="20"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="20">  <span class="hljs-attr">stepMode</span>: <span class="hljs-string">&#x27;vertex&#x27;</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="21"><div class="hljs-ln-n" data-line-number="21"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="21">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="22"><div class="hljs-ln-n" data-line-number="22"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="22"><span class="hljs-keyword">const</span> <span class="hljs-attr">colorBufferDesc</span>: <span class="hljs-title">GPUVertexBufferLayout</span> = {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="23"><div class="hljs-ln-n" data-line-number="23"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="23">  <span class="hljs-attr">attributes</span>: [colorAttribDesc],</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="24"><div class="hljs-ln-n" data-line-number="24"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="24">  <span class="hljs-attr">arrayStride</span>: <span class="hljs-number">4</span> * <span class="hljs-number">3</span>, <span class="hljs-comment">// sizeof(float) * 3</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="25"><div class="hljs-ln-n" data-line-number="25"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="25">  <span class="hljs-attr">stepMode</span>: <span class="hljs-string">&#x27;vertex&#x27;</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="26"><div class="hljs-ln-n" data-line-number="26"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="26">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="27"><div class="hljs-ln-n" data-line-number="27"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="27"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="28"><div class="hljs-ln-n" data-line-number="28"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="28"><span class="hljs-comment">// 🌑 Depth</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="29"><div class="hljs-ln-n" data-line-number="29"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="29"><span class="hljs-keyword">const</span> <span class="hljs-attr">depthStencil</span>: <span class="hljs-title">GPUDepthStencilState</span> = {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="30"><div class="hljs-ln-n" data-line-number="30"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="30">  <span class="hljs-attr">depthWriteEnabled</span>: <span class="hljs-literal">true</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="31"><div class="hljs-ln-n" data-line-number="31"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="31">  <span class="hljs-attr">depthCompare</span>: <span class="hljs-string">&#x27;less&#x27;</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="32"><div class="hljs-ln-n" data-line-number="32"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="32">  <span class="hljs-attr">format</span>: <span class="hljs-string">&#x27;depth24plus-stencil8&#x27;</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="33"><div class="hljs-ln-n" data-line-number="33"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="33">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="34"><div class="hljs-ln-n" data-line-number="34"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="34"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="35"><div class="hljs-ln-n" data-line-number="35"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="35"><span class="hljs-comment">// 🦄 Uniform Data</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="36"><div class="hljs-ln-n" data-line-number="36"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="36"><span class="hljs-keyword">const</span> pipelineLayoutDesc = { <span class="hljs-attr">bindGroupLayouts</span>: [] };</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="37"><div class="hljs-ln-n" data-line-number="37"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="37"><span class="hljs-keyword">const</span> layout = device.<span class="hljs-title">createPipelineLayout</span>(pipelineLayoutDesc);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="38"><div class="hljs-ln-n" data-line-number="38"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="38"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="39"><div class="hljs-ln-n" data-line-number="39"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="39"><span class="hljs-comment">// 🎭 Shader Stages</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="40"><div class="hljs-ln-n" data-line-number="40"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="40"><span class="hljs-keyword">const</span> <span class="hljs-attr">vertex</span>: <span class="hljs-title">GPUVertexState</span> = {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="41"><div class="hljs-ln-n" data-line-number="41"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="41">  <span class="hljs-attr">module</span>: vertModule,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="42"><div class="hljs-ln-n" data-line-number="42"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="42">  <span class="hljs-attr">entryPoint</span>: <span class="hljs-string">&#x27;main&#x27;</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="43"><div class="hljs-ln-n" data-line-number="43"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="43">  <span class="hljs-attr">buffers</span>: [positionBufferDesc, colorBufferDesc]</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="44"><div class="hljs-ln-n" data-line-number="44"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="44">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="45"><div class="hljs-ln-n" data-line-number="45"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="45"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="46"><div class="hljs-ln-n" data-line-number="46"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="46"><span class="hljs-comment">// 🌀 Color/Blend State</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="47"><div class="hljs-ln-n" data-line-number="47"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="47"><span class="hljs-keyword">const</span> <span class="hljs-attr">colorState</span>: <span class="hljs-title">GPUColorTargetState</span> = {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="48"><div class="hljs-ln-n" data-line-number="48"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="48">  <span class="hljs-attr">format</span>: <span class="hljs-string">&#x27;bgra8unorm&#x27;</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="49"><div class="hljs-ln-n" data-line-number="49"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="49">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="50"><div class="hljs-ln-n" data-line-number="50"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="50"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="51"><div class="hljs-ln-n" data-line-number="51"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="51"><span class="hljs-keyword">const</span> <span class="hljs-attr">fragment</span>: <span class="hljs-title">GPUFragmentState</span> = {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="52"><div class="hljs-ln-n" data-line-number="52"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="52">  <span class="hljs-attr">module</span>: fragModule,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="53"><div class="hljs-ln-n" data-line-number="53"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="53">  <span class="hljs-attr">entryPoint</span>: <span class="hljs-string">&#x27;main&#x27;</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="54"><div class="hljs-ln-n" data-line-number="54"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="54">  <span class="hljs-attr">targets</span>: [colorState]</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="55"><div class="hljs-ln-n" data-line-number="55"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="55">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="56"><div class="hljs-ln-n" data-line-number="56"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="56"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="57"><div class="hljs-ln-n" data-line-number="57"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="57"><span class="hljs-comment">// 🟨 Rasterization</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="58"><div class="hljs-ln-n" data-line-number="58"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="58"><span class="hljs-keyword">const</span> <span class="hljs-attr">primitive</span>: <span class="hljs-title">GPUPrimitiveState</span> = {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="59"><div class="hljs-ln-n" data-line-number="59"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="59">  <span class="hljs-attr">frontFace</span>: <span class="hljs-string">&#x27;cw&#x27;</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="60"><div class="hljs-ln-n" data-line-number="60"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="60">  <span class="hljs-attr">cullMode</span>: <span class="hljs-string">&#x27;none&#x27;</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="61"><div class="hljs-ln-n" data-line-number="61"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="61">  <span class="hljs-attr">topology</span>: <span class="hljs-string">&#x27;triangle-list&#x27;</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="62"><div class="hljs-ln-n" data-line-number="62"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="62">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="63"><div class="hljs-ln-n" data-line-number="63"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="63"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="64"><div class="hljs-ln-n" data-line-number="64"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="64"><span class="hljs-keyword">const</span> <span class="hljs-attr">pipelineDesc</span>: <span class="hljs-title">GPURenderPipelineDescriptor</span> = {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="65"><div class="hljs-ln-n" data-line-number="65"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="65">  layout,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="66"><div class="hljs-ln-n" data-line-number="66"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="66">  vertex,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="67"><div class="hljs-ln-n" data-line-number="67"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="67">  fragment,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="68"><div class="hljs-ln-n" data-line-number="68"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="68">  primitive,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="69"><div class="hljs-ln-n" data-line-number="69"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="69">  depthStencil</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="70"><div class="hljs-ln-n" data-line-number="70"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="70">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="71"><div class="hljs-ln-n" data-line-number="71"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="71"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="72"><div class="hljs-ln-n" data-line-number="72"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="72">pipeline = device.<span class="hljs-title">createRenderPipeline</span>(pipelineDesc);</td></tr></tbody></table></code></pre></div>
<h3 id="command-encoder">Command Encoder</h3>
<p><img src="https://alain.xyz/blog/raw-webgpu/assets/command-encoder.svg" alt="Command Encoder Diagrams"/></p>
<p><strong>Command Encoder</strong>s encode all the draw commands you intend to execute in groups of <strong>Render Pass Encoders</strong>. Once you&#x27;ve finished encoding commands, you&#x27;ll receive a <strong>Command Buffer</strong> that you could submit to your queue.</p>
<p>In that sense a command buffer is analogous to a <em>callback</em> that executes draw functions on the GPU once it&#x27;s submitted to the queue.</p>
<div><pre title="Encoding Commands"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// ✋ Declare command handles</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">let</span> <span class="hljs-attr">commandEncoder</span>: <span class="hljs-title">GPUCommandEncoder</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"><span class="hljs-keyword">let</span> <span class="hljs-attr">passEncoder</span>: <span class="hljs-title">GPURenderPassEncoder</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"><span class="hljs-comment">// ✍️ Write commands to send to the GPU</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"><span class="hljs-keyword">const</span> <span class="hljs-title">encodeCommands</span> = (<span class="hljs-params"></span>) =&gt; {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">  <span class="hljs-keyword">let</span> <span class="hljs-attr">colorAttachment</span>: <span class="hljs-title">GPURenderPassColorAttachment</span> = {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">    <span class="hljs-attr">view</span>: <span class="hljs-variable">this</span>.<span class="hljs-property">colorTextureView</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">    <span class="hljs-attr">clearValue</span>: { <span class="hljs-attr">r</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">g</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">b</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">a</span>: <span class="hljs-number">1</span> },</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">    <span class="hljs-attr">loadOp</span>: <span class="hljs-string">&#x27;clear&#x27;</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">    <span class="hljs-attr">storeOp</span>: <span class="hljs-string">&#x27;store&#x27;</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12">  };</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14">  <span class="hljs-keyword">const</span> <span class="hljs-attr">depthAttachment</span>: <span class="hljs-title">GPURenderPassDepthStencilAttachment</span> = {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15">    <span class="hljs-attr">view</span>: <span class="hljs-variable">this</span>.<span class="hljs-property">depthTextureView</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16">    <span class="hljs-attr">depthClearValue</span>: <span class="hljs-number">1</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17">    <span class="hljs-attr">depthLoadOp</span>: <span class="hljs-string">&#x27;clear&#x27;</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18">    <span class="hljs-attr">depthStoreOp</span>: <span class="hljs-string">&#x27;store&#x27;</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19">    <span class="hljs-attr">stencilClearValue</span>: <span class="hljs-number">0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="20"><div class="hljs-ln-n" data-line-number="20"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="20">    <span class="hljs-attr">stencilLoadOp</span>: <span class="hljs-string">&#x27;clear&#x27;</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="21"><div class="hljs-ln-n" data-line-number="21"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="21">    <span class="hljs-attr">stencilStoreOp</span>: <span class="hljs-string">&#x27;store&#x27;</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="22"><div class="hljs-ln-n" data-line-number="22"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="22">  };</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="23"><div class="hljs-ln-n" data-line-number="23"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="23"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="24"><div class="hljs-ln-n" data-line-number="24"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="24">  <span class="hljs-keyword">const</span> <span class="hljs-attr">renderPassDesc</span>: <span class="hljs-title">GPURenderPassDescriptor</span> = {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="25"><div class="hljs-ln-n" data-line-number="25"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="25">    <span class="hljs-attr">colorAttachments</span>: [colorAttachment],</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="26"><div class="hljs-ln-n" data-line-number="26"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="26">    <span class="hljs-attr">depthStencilAttachment</span>: depthAttachment</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="27"><div class="hljs-ln-n" data-line-number="27"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="27">  };</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="28"><div class="hljs-ln-n" data-line-number="28"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="28"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="29"><div class="hljs-ln-n" data-line-number="29"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="29">  commandEncoder = device.<span class="hljs-title">createCommandEncoder</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="30"><div class="hljs-ln-n" data-line-number="30"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="30"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="31"><div class="hljs-ln-n" data-line-number="31"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="31">  <span class="hljs-comment">// 🖌️ Encode drawing commands</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="32"><div class="hljs-ln-n" data-line-number="32"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="32">  passEncoder = commandEncoder.<span class="hljs-title">beginRenderPass</span>(renderPassDesc);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="33"><div class="hljs-ln-n" data-line-number="33"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="33">  passEncoder.<span class="hljs-title">setPipeline</span>(pipeline);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="34"><div class="hljs-ln-n" data-line-number="34"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="34">  passEncoder.<span class="hljs-title">setViewport</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, canvas.<span class="hljs-property">width</span>, canvas.<span class="hljs-property">height</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="35"><div class="hljs-ln-n" data-line-number="35"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="35">  passEncoder.<span class="hljs-title">setScissorRect</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, canvas.<span class="hljs-property">width</span>, canvas.<span class="hljs-property">height</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="36"><div class="hljs-ln-n" data-line-number="36"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="36">  passEncoder.<span class="hljs-title">setVertexBuffer</span>(<span class="hljs-number">0</span>, positionBuffer);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="37"><div class="hljs-ln-n" data-line-number="37"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="37">  passEncoder.<span class="hljs-title">setVertexBuffer</span>(<span class="hljs-number">1</span>, colorBuffer);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="38"><div class="hljs-ln-n" data-line-number="38"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="38">  passEncoder.<span class="hljs-title">setIndexBuffer</span>(indexBuffer, <span class="hljs-string">&#x27;uint16&#x27;</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="39"><div class="hljs-ln-n" data-line-number="39"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="39">  passEncoder.<span class="hljs-title">drawIndexed</span>(<span class="hljs-number">3</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="40"><div class="hljs-ln-n" data-line-number="40"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="40">  passEncoder.<span class="hljs-title">endPass</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="41"><div class="hljs-ln-n" data-line-number="41"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="41"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="42"><div class="hljs-ln-n" data-line-number="42"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="42">  queue.<span class="hljs-title">submit</span>([commandEncoder.<span class="hljs-title">finish</span>()]);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="43"><div class="hljs-ln-n" data-line-number="43"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="43">};</td></tr></tbody></table></code></pre></div>
<h2 id="render">Render</h2>
<p><img src="https://alain.xyz/blog/raw-webgpu/assets/triangle-raster.gif" alt="Triangle Raster Gif"/></p>
<p><strong>Rendering</strong> in WebGPU is a simple matter of updating any uniforms you intend to update, getting the next attachments from your context, submitting your command encoders to be executed, and using the <code>requestAnimationFrame</code> callback to do all of that again.</p>
<div><pre title="Basic Render Loop"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-keyword">const</span> <span class="hljs-title">render</span> = (<span class="hljs-params"></span>) =&gt; {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2">  <span class="hljs-comment">// ⏭ Acquire next image from context</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3">  colorTexture = context.<span class="hljs-title">getCurrentTexture</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4">  colorTextureView = colorTexture.<span class="hljs-title">createView</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6">  <span class="hljs-comment">// 📦 Write and submit commands to queue</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">  <span class="hljs-title">encodeCommands</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">  <span class="hljs-comment">// ➿ Refresh canvas</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">  <span class="hljs-title">requestAnimationFrame</span>(render);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">};</td></tr></tbody></table></code></pre></div>
<h2 id="conclusion">Conclusion</h2>
<p>WebGPU might be more difficult than other graphics APIs, but it&#x27;s an API that <em>more closely aligns with the design of modern graphics cards</em>, and as a result, should not only result in faster applications, but also applications that should last longer.</p>
<p>There were a few things I didn&#x27;t cover in this post as they would have been a beyond the scope of this post, such as:</p>
<ul>
<li>
<p><strong>Matrices</strong>, be it for cameras or for transforming objects in the scene. <a href="https://github.com/toji/gl-matrix">gl-matrix</a> is an invaluable resource there.</p>
</li>
<li>
<p>A detailed overview of <strong>every possible state of a graphics pipeline</strong>. The <a href="https://github.com/gpuweb/types">WebGPU type definitions</a> are very helpful there.</p>
</li>
<li>
<p><strong>Blend Modes</strong>, it can be helpful to see this visually, Anders Riggelsen wrote a tool to <a href="https://www.andersriggelsen.dk/glblendfunc.php">see blend mode behavior with OpenGL here</a>.</p>
</li>
<li>
<p><strong>Compute pipelines</strong>, review the specification or some of the examples below if you want to try that.</p>
</li>
<li>
<p><strong>Loading textures</strong>, this can be a bit involved, the examples below introduce this very well.</p>
</li>
</ul>
<h3 id="additional-resources">Additional Resources</h3>
<p>Here&#x27;s a few articles/projects for WebGPU in <em>no particular order</em>:</p>
<ul>
<li>
<p><a href="https://webgpufundamentals.org/">WebGPU Fundimentals</a> is an online public book <a href="https://github.com/webgpu/webgpufundamentals">written and edited by various authors on GitHub</a> that tries to be both comprehensive and consise.</p>
</li>
<li>
<img class="profile-picture" src="https://github.com/Twinklebear.png" alt="William Usher&#x27;s picture"/>
<p>William Usher (<a href="https://twitter.com/_wusher">@_wusher</a>)&#x27;s article, updated
for the Chrome 113 release of WebGPU: <a href="https://www.willusher.io/graphics/2023/04/10/0-to-gltf-triangle">From 0 to glTF with
WebGPU</a>.</p>
</li>
<li>
<p><a href="https://github.com/kvark">Dzmitry Malyshau</a> wrote an article similar to this one <a href="https://hacks.mozilla.org/2020/04/experimental-webgpu-in-firefox/">introducing WebGPU in Mozilla FireFox</a>.</p>
</li>
<li>
<p><a href="https://metalbyexample.com/">Warren Moore</a> (<a href="https://twitter.com/warrenm">@warrenm</a>) wrote an article to help folks transition from the Metal API to <a href="https://metalbyexample.com/webgpu-part-one/">WebGPU</a>.</p>
</li>
<li>
<p><a href="https://blog.tojicode.com/">Brandon Jones</a> (<a href="https://twitter.com/Tojiro">@Tojiro</a>) wrote an article describing how to <a href="https://toji.github.io/webgpu-gltf-case-study/">write a GLTF renderer in WebGPU</a>.</p>
</li>
<li>
<p><a href="https://sotrh.github.io/learn-wgpu/#what-is-wgpu">Learn WGPU</a> is an introduction to writing WebGPU applications with Rust.</p>
</li>
<li>
<p><a href="https://eliemichel.github.io/LearnWebGPU/">Learn WebGPU</a> for native graphics in C++.</p>
</li>
</ul>
<p>There&#x27;s also a number of open source projects including:</p>
<ul>
<li>
<img class="profile-picture" src="https://github.com/austinEng.png" alt="Austin Eng picture"/>
<p><a href="http://austineng.github.io/">Austin Eng</a>&#x27;s <a href="http://austin-eng.com/webgpu-samples/samples/texturedCube">WebGPU
Samples</a></p>
</li>
<li>
<img class="profile-picture" src="https://github.com/tsherif.png" alt="Tarek Sherif picture"/>
<p><a href="http://tareksherif.net/">Tarek Sherif</a>
(<a href="https://twitter.com/thsherif">@tsherif</a>)&#x27;s <a href="https://github.com/tsherif/webgpu-examples">WebGPU
Examples</a></p>
</li>
<li>
<p><a href="https://github.com/redcamel/RedGPU">RedGPU</a> by <a href="https://twitter.com/redcamel15">@RedCamel15</a>, a series of examples written for WebGPU.</p>
</li>
<li>
<p><a href="https://github.com/mrdoob/three.js/tree/dev/examples/jsm/renderers/webgpu">Three.js&#x27; WebGPU Source</a></p>
</li>
<li>
<p><a href="https://github.com/BabylonJS/Babylon.js/blob/WebGPU/src/Engines/webgpuEngine.ts">BabylonJS&#x27;s WebGPU Source</a></p>
</li>
<li>
<p><a href="https://github.com/gpuweb/types">WebGPU&#x27;s Type Definitions</a></p>
</li>
<li>
<p><a href="https://github.com/gpuweb/cts">WebGPU&#x27;s Conformance Tests</a></p>
</li>
<li>
<p><a href="https://dawn.googlesource.com/dawn">Dawn - A C++ Implementation of WebGPU</a> used to power Chromium&#x27;s implementation of WebGPU. Carl Woffenden released a <a href="https://github.com/cwoffenden/hello-webgpu">Hello Triangle example with WebGPU and Dawn</a>.</p>
</li>
<li>
<p><a href="https://github.com/gfx-rs/wgpu-native">wgpu-native</a> is a native WebGPU implementation in Rust, which can easily be used in all sorts of native projects.</p>
</li>
</ul>
<p>The specification for WebGPU and WebGPU Shading Language is also worth taking a look:</p>
<ul>
<li>
<p><a href="https://gpuweb.github.io/gpuweb/">WebGPU Specification</a></p>
</li>
<li>
<p><a href="https://gpuweb.github.io/gpuweb/wgsl/">WebGPU Shading Language (WGSL) Specification</a></p>
</li>
</ul>
<hr/>
<p>You can find all the source for this post in the <a href="https://github.com/alaingalvan/webgpu-seed">GitHub Repo</a> here.</p>]]></content:encoded>
</item>
<item>
<title>Machine Learning Denoising</title>
<link>https://alain.xyz/blog/machine-learning-denoising</link>
<description>A review of different techniques for denoising monte-carlo images with machine learning. Learn about autoencoders and currently available implementations such as OIDN and the Optix Autoencoder.</description>
<guid isPermaLink="true">https://alain.xyz/blog/machine-learning-denoising</guid>
<pubDate>Wed, 11 Sep 2019 00:36:34 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>draft</category><category>wip</category><category>machine learning</category><category>neural networks</category><category>autoencoder</category><category>theory</category><category>graphics</category><category>denoising</category><category>optix</category><category>odin</category>
<enclosure url="https://alain.xyz/blog/machine-learning-denoising/assets/cover.jpg" length="622134" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/machine-learning-denoising/assets/coronal-testscene/1spp.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/machine-learning-denoising/assets/coronal-testscene/optix-denoise.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/machine-learning-denoising/assets/coronal-testscene/groundtruth.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/machine-learning-denoising/assets/coronal-testscene/odin-denoise.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/machine-learning-denoising/assets/autoencoder.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/machine-learning-denoising/assets/xu-gan-comparison.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/machine-learning-denoising/assets/tensorflow-logo.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/machine-learning-denoising/assets/convolution.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/machine-learning-denoising/assets/relu.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/machine-learning-denoising/assets/maxpool.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/machine-learning-denoising/assets/cornell-2048spp.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/machine-learning-denoising/assets/cornell-1spp.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/machine-learning-denoising/assets/cornell-2048spp-nrm-direct.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/machine-learning-denoising/assets/cornell-2048spp-nrm-indirect.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/machine-learning-denoising/assets/cornell-2048spp-alb.jpg"/><link rel="preload" as="image" href="https://github.com/RikoOphorst.png"/><link rel="preload" as="image" href="https://github.com/xmeng525.png"/><link rel="preload" as="image" href="https://github.com/ingowald.png"/><link rel="preload" as="image" href="https://github.com/mklefrancois.png"/><link rel="preload" as="image" href="https://github.com/cschied.png"/><p>Machine Learning has had years of literature focused on denoising. While it may have started as a solution to the problem of classifying noisy text <a href="#ref_gallinari1987">[Gallinari et al. 1987]</a> <a href="#ref_mixon2018">[G. Mixon  et al. 2018]</a>, eventually the problem domain was expanded on to include more computer graphics focused problems such as denoising monte-carlo path traced images <a href="#ref_kalantari2013">[Khademi Kalantari et al. 2013]</a> <a href="#ref_bako2017">[Bako et al. 2017]</a> <a href="#ref_vogels2018">[Vogels et al. 2018]</a> <a href="#ref_laine2019">[Laine et al. 2019]</a>, real time ray tracing denoising <a href="#ref_kalantari2015">[Khademi Kalantari et al. 2015]</a> <a href="#ref_chaitanya2017">[R. Alla Chaitanya et al. 2017]</a>, improving ray tracing sampling <a href="#ref_hasselgren2020">[Hasselgren et al. 2020]</a> <a href="#ref_muller2021">[Muller et al. 2021]</a>, general image reconstruction <a href="#ref_mildenhall2017">[Mildenhall  et al. 2017]</a> <a href="#ref_lehtinen2018">[Lehtinen  et al. 2018]</a>, and upscaling images while maintaining detail through super-resolution/super-sampling <a href="#ref_dong2015">[Dong et al. 2015]</a> <a href="#ref_ledig2016">[Ledig et al. 2016]</a> <a href="#ref_wang2018">[Wang et al. 2018]</a> <a href="#ref_xiao2020">[Xiao et al. 2020]</a>.</p>
<p>Industry leaders such as Intel and NVIDIA have sponsored research in machine learning based denoisers, <a href="https://openimagedenoise.github.io/">Intel Open Image Denoise</a> and the <a href="https://developer.nvidia.com/optix">NVIDIA Optix Autoencoder</a> both use a <strong>denoising autoencoder</strong> to denoise images to great success.</p>
<p>The difficulty in using these lies in their computational cost. Without dedicated hardware or trade offs in quality and performance, it&#x27;s is difficult to achieve real time high quality denoising using autoencoders.</p>
<p>Thus there&#x27;s been research in the area of algorithmic techniques to denoise images in real time using spherical harmonics to encode low frequency data, guided filters to blur while avoiding sharp changes in normals or albedo information, and spatio-temporal reprojection to reuse data that isn&#x27;t view dependent such as global illumination or shadows. <a href="#ref_schied2019">[Schied et al. 2019]</a></p>
<blockquote>
<p>You can find more information about these techniques in my post on <a href="https://alain.xyz/blog/ray-tracing-filtering">Realtime Ray Tracing Denoising</a>.</p>
</blockquote>
<table><thead><tr><th style="text-align:center"><img src="https://alain.xyz/blog/machine-learning-denoising/assets/coronal-testscene/1spp.jpg" alt="1spp Image"/> <strong>1spp Input</strong></th><th style="text-align:center"><img src="https://alain.xyz/blog/machine-learning-denoising/assets/coronal-testscene/optix-denoise.jpg" alt="Optix Denoise"/> <strong>Optix</strong></th></tr></thead><tbody><tr><td style="text-align:center"><img src="https://alain.xyz/blog/machine-learning-denoising/assets/coronal-testscene/groundtruth.jpg" alt="Ground Truth"/> <strong>Ground Truth (10K spp)</strong></td><td style="text-align:center"><img src="https://alain.xyz/blog/machine-learning-denoising/assets/coronal-testscene/odin-denoise.jpg" alt="OIDN Denoise"/> <strong>OIDN</strong></td></tr></tbody></table>
<p>There&#x27;s even ways of combining these two methods, with <a href="#ref_hasselgren2020">[Hasselgren et al. 2020]</a> using spatio-temporal methods to feed a sample map estimator network to help dictate how many rays a renderer should dispatch. This helps solve the issue of denoising salt/peppering highlights/shadows in real time. It&#x27;s also possible to use a different denoising scheme depending on the number of samples in your input such as in <a href="#ref_meng2020">[Meng et al. 2020]</a> where <code>64 spp</code> images were used as input for one of their networks.</p>
<p>Nevertheless It&#x27;s clear that with images with little history information or with sparse low frequency information, using machine learning results in significantly better quality outputs than algorithmic alternatives such as <em>A-SVGF</em> or custom implementations such as Quake 2/Minecraft RTX&#x27;s use of split output buffers, upscaling, and spherical harmonics encoding for low frequency data.</p>
<p>It&#x27;s likely that neural network based denoising solutions will either replace algorithmic techniques or the two will be used in tandem depending on user requirements.</p>
<p>We&#x27;ll be reviewing machine learning techniques for denoising monte-carlo images, and looking at the state of the art in this area of research.</p>
<h2 id="autoencoders">Autoencoders</h2>
<p><img src="https://alain.xyz/blog/machine-learning-denoising/assets/autoencoder.svg" alt="Autoencoder Diagram"/></p>
<p>An <strong>Autoencoder</strong> is a neural network that is trained to attempt to imperfectly copy its input to its output. A <strong>Denoising Autoencoder</strong> take one step further from copying, attempting to learn how to undo the corruption of its input. <a href="#ref_goodfellow2016">[Goodfellow et al. 2016]</a></p>
<p><a href="#ref_chaitanya2017">[R. Alla Chaitanya et al. 2017]</a> makes use of a noisy LDR input, 3 component signed
normals, depth, and roughness to train the autoencoder, with its lack of an
albedo channel most likely the cause of its weakness in estimating the final
color of its output. More recent techniques such as Intel Open Image Denoise and
NVIDIA Optix using a noisy HDR input, 3 component signed normals, and albedo.</p>
<table><thead><tr><th style="text-align:center"><img src="https://alain.xyz/blog/machine-learning-denoising/assets/xu-gan-comparison.jpg" alt="Xu GAN vs. No GAN"/></th></tr></thead><tbody><tr><td style="text-align:center">Comparison of an autoencoder trained without and with a GAN by <em>Xu et al</em>. <strong>Note</strong> sharpness on right.</td></tr></tbody></table>
<p>The most recent state of the art <a href="#ref_li2020">[Li 2020]</a> points to a network being passed the diffuse and specular terms separately like <em>Quake 2 RTX&#x27;s A-SVGF</em>, and providing auxiliary buffers (Normals, Albedo, Depth) with element wise biasing and scaling of weights. There&#x27;s a variety of approaches that can be taken to both train and execute the model. Recent literature points to the use of generative adversarial networks (GANs) <a href="#ref_xu2019">[Xu et al. 2019]</a> in combination with autonomous reinforcement learning.</p>
<h2 id="overview">Overview</h2>
<p><img src="https://alain.xyz/blog/machine-learning-denoising/assets/tensorflow-logo.svg" alt="TensorFlow Logo"/></p>
<p><a href="https://www.tensorflow.org/">Google TensorFlow</a>, next to <a href="https://pytorch.org/">Facebook PyTorch</a>, is the most popular neural network framework, so we&#x27;ll be using TensorFlow to quickly design and train our neural network, then converting our model to <a href="https://docs.microsoft.com/en-us/windows/ai/">DirectML</a>&#x27;s hardware accelerated machine learning operations, and loading the same weights we calculate with TensorFlow.</p>
<p>GPU accelerated tensor operations isn&#x27;t necessarily new, with industry leaders like <a href="https://developer.apple.com/videos/play/wwdc2019/614/">Apple exposing a Metal based Machine Learning API in Metal 2</a>, projects like <a href="https://tenso.rs/demos/fast-neural-style/">TensorFire</a> and the paper <a href="https://distill.pub/2020/growing-ca/">Growing Neural Cellular Automata</a> showcasing the <a href="https://github.com/distillpub/post--growing-ca/blob/master/public/ca.js#L40">execution of tensors with WebGL</a>, and <a href="https://www.diva-portal.org/smash/get/diva2:829298/FULLTEXT01.pdf">thesis papers</a> on writing compute shaders to train and execute models on the GPU. With the advent of dedicated tensor cores on modern GPUs, this is now even easier to do in real time.</p>
<h2 id="neural-network-graph">Neural Network Graph</h2>
<p>We&#x27;ll be building an <em>autoencoder</em> that takes in the <em>noisy input texture</em>, <em>albedo</em>, <em>view depth</em>, and <em>view normals (with 1st bounce)</em>, <em>metalness</em> and <em>gloss</em> (for a total of <code>12</code> components), and outputs a single <code>RGBA32</code> output. This will then be trained using supervised learning on a dataset of monte carlo ray traced images to produce a trained output.</p>
<p>These pixels with <code>12</code> components are passed to a series of 2D convolutional neural network layers connected with Leaky Rectifier Linear Activation Units (Leaky RLU) which connect with chunks of the input until it&#x27;s reduced to a small tensor, then the reverse occurs where convolutions are scaled to a single output.</p>
<p>For instance, <a href="https://github.com/OpenImageDenoise/oidn/blob/master/core/autoencoder.cpp#L384">Intel OIDN</a> opts to:</p>
<ol>
<li>Perform a initial convolution that reorders inputs</li>
<li>A series of 5 convolutions + max pool operations</li>
<li>5 convolutions + upsample operations</li>
<li>Reordering outputs</li>
</ol>
<p>This design isn&#x27;t exclusive to OIDN, NVIDIA&#x27;s recurrent autoencoder also has 5 downsample convolutions and 5 upsample convolutions, as does <em>Xu et al</em> and <em>Hasselgren et al</em>.</p>
<p>Let&#x27;s discuss the design of the network, its primitives, and why they&#x27;re used where they are.</p>
<blockquote>
<p>You can find out more about convolutional layers and primitives in Machine Learning in the <a href="https://www.tensorflow.org/guide">TensorFlow Guide</a>.</p>
</blockquote>
<h3 id="conv2d">Conv2D</h3>
<p><img src="https://alain.xyz/blog/machine-learning-denoising/assets/convolution.svg" alt="Convolution"/></p>
<div><pre><code class="language-py"><span class="hljs-comment"># 🍱🚿 TensorFlow</span>
<span class="hljs-keyword">import</span> tensorflow <span class="hljs-keyword">as</span> tf
<span class="hljs-keyword">from</span> tensorflow.python.keras <span class="hljs-keyword">import</span> layers
layers.Conv2D(filters, kernel_size, ...)</code></pre></div>
<div><pre><code class="language-cpp"><span class="hljs-comment">// ❌🧠 DirectML</span>

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">DML_CONVOLUTION_OPERATOR_DESC</span>
{
    <span class="hljs-type">const</span> DML_TENSOR_DESC* InputTensor;
    <span class="hljs-type">const</span> DML_TENSOR_DESC* FilterTensor;
    _In_opt_ <span class="hljs-type">const</span> DML_TENSOR_DESC* BiasTensor;
    <span class="hljs-type">const</span> DML_TENSOR_DESC* OutputTensor;
    DML_CONVOLUTION_MODE Mode;
    DML_CONVOLUTION_DIRECTION Direction;
    UINT DimensionCount;
    _In_reads_(DimensionCount) <span class="hljs-type">const</span> UINT* Strides;
    _In_reads_(DimensionCount) <span class="hljs-type">const</span> UINT* Dilations;
    _In_reads_(DimensionCount) <span class="hljs-type">const</span> UINT* StartPadding;
    _In_reads_(DimensionCount) <span class="hljs-type">const</span> UINT* EndPadding;
    _In_reads_(DimensionCount) <span class="hljs-type">const</span> UINT* OutputPadding;
    UINT GroupCount;
    _In_opt_ <span class="hljs-type">const</span> DML_OPERATOR_DESC* FusedActivation;
};</code></pre></div>
<p>A node that attaches to all subsequent nodes. A <strong>Convolution</strong> is a function that takes in all the inputs of the previous tensor and connects them with all your specified outputs according to each layer&#x27;s shape.</p>
<p>This is analogous to a <strong>guassian filter</strong> in computer graphics. We use this operation to compute weights across a large kernel, similar to how guassian filters compute the average color of a kernel. Operations such as bluring and sharpening an image are <em>convolutions</em>, a matrix operation around a central pixel.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">/**
 * GLSL Guassian Blur Convolution Example
 * By csblo from 🌎 Wikipedia (ShaderToy)
 * https://en.wikipedia.org/wiki/Kernel_(image_processing)#Concrete_implementation
 * 🎓 Slightly modified for this example:
 */</span>

<span class="hljs-meta">#define gaussian_blur mat3(1, 2, 1, 2, 4, 2, 1, 2, 1) * 0.0625</span>

<span class="hljs-comment">// 🟧 Find coordinate of matrix element from index</span>
<span class="hljs-type">vec2</span> kpos(<span class="hljs-type">int</span> <span class="hljs-keyword">index</span>)
{
    <span class="hljs-keyword">return</span> <span class="hljs-type">vec2</span>[<span class="hljs-number">9</span>] (
        <span class="hljs-type">vec2</span>(<span class="hljs-number">-1</span>, <span class="hljs-number">-1</span>), <span class="hljs-type">vec2</span>(<span class="hljs-number">0</span>, <span class="hljs-number">-1</span>), <span class="hljs-type">vec2</span>(<span class="hljs-number">1</span>, <span class="hljs-number">-1</span>),
        <span class="hljs-type">vec2</span>(<span class="hljs-number">-1</span>, <span class="hljs-number">0</span>), <span class="hljs-type">vec2</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>), <span class="hljs-type">vec2</span>(<span class="hljs-number">1</span>, <span class="hljs-number">0</span>),
        <span class="hljs-type">vec2</span>(<span class="hljs-number">-1</span>, <span class="hljs-number">1</span>), <span class="hljs-type">vec2</span>(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>), <span class="hljs-type">vec2</span>(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>)
    )[<span class="hljs-keyword">index</span>] / iResolution.xy;
}


<span class="hljs-comment">// 🟥 Extract region of dimension 3x3 from sampler centered in uv</span>
<span class="hljs-comment">// sampler : texture sampler</span>
<span class="hljs-comment">// uv : current coordinates on sampler</span>
<span class="hljs-comment">// return : an array of mat3, each index corresponding with a color channel</span>
<span class="hljs-type">mat3</span>[<span class="hljs-number">3</span>] region3x3(<span class="hljs-type">sampler2D</span> sampler, <span class="hljs-type">vec2</span> uv)
{
    <span class="hljs-comment">// ⚪ Create each pixels for region</span>
    <span class="hljs-type">vec4</span>[<span class="hljs-number">9</span>] region;

    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">9</span>; i++)
        region[i] = <span class="hljs-built_in">texture</span>(sampler, uv + kpos(i));

    <span class="hljs-comment">// 💠 Create 3x3 region with 3 color channels (red, green, blue)</span>
    <span class="hljs-type">mat3</span>[<span class="hljs-number">3</span>] mRegion;

    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++)
        mRegion[i] = <span class="hljs-type">mat3</span>(
            region[<span class="hljs-number">0</span>][i], region[<span class="hljs-number">1</span>][i], region[<span class="hljs-number">2</span>][i],
            region[<span class="hljs-number">3</span>][i], region[<span class="hljs-number">4</span>][i], region[<span class="hljs-number">5</span>][i],
            region[<span class="hljs-number">6</span>][i], region[<span class="hljs-number">7</span>][i], region[<span class="hljs-number">8</span>][i]
        );

    <span class="hljs-keyword">return</span> mRegion;
}

<span class="hljs-comment">// 🕸️ Convolve a texture with kernel</span>
<span class="hljs-comment">// kernel : kernel used for convolution</span>
<span class="hljs-comment">// sampler : texture sampler</span>
<span class="hljs-comment">// uv : current coordinates on sampler</span>
<span class="hljs-type">vec3</span> convolution(<span class="hljs-type">mat3</span> kernel, <span class="hljs-type">sampler2D</span> sampler, <span class="hljs-type">vec2</span> uv)
{
    <span class="hljs-type">vec3</span> fragment;

    <span class="hljs-comment">// 💉 Extract a 3x3 region centered in uv</span>
    <span class="hljs-type">mat3</span>[<span class="hljs-number">3</span>] region = region3x3(sampler, uv);

    <span class="hljs-comment">// 🔴🟢🔵 for each color channel of region</span>
    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++)
    {
        <span class="hljs-comment">// 🔴 get region channel</span>
        <span class="hljs-type">mat3</span> rc = region[i];

        <span class="hljs-comment">// ∏ component wise multiplication of kernel by region channel</span>
        <span class="hljs-type">mat3</span> c = <span class="hljs-built_in">matrixCompMult</span>(kernel, rc);

        <span class="hljs-comment">// Σ add each component of matrix</span>
        <span class="hljs-type">float</span> r = c[<span class="hljs-number">0</span>][<span class="hljs-number">0</span>] + c[<span class="hljs-number">1</span>][<span class="hljs-number">0</span>] + c[<span class="hljs-number">2</span>][<span class="hljs-number">0</span>]
                + c[<span class="hljs-number">0</span>][<span class="hljs-number">1</span>] + c[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>] + c[<span class="hljs-number">2</span>][<span class="hljs-number">1</span>]
                + c[<span class="hljs-number">0</span>][<span class="hljs-number">2</span>] + c[<span class="hljs-number">1</span>][<span class="hljs-number">2</span>] + c[<span class="hljs-number">2</span>][<span class="hljs-number">2</span>];

        <span class="hljs-comment">// 🔴 for fragment at channel i, set result</span>
        fragment[i] = r;
    }

    <span class="hljs-keyword">return</span> fragment;
}

<span class="hljs-type">void</span> mainImage( <span class="hljs-keyword">out</span> <span class="hljs-type">vec4</span> fragColor, <span class="hljs-keyword">in</span> <span class="hljs-type">vec2</span> fragCoord )
{
    <span class="hljs-comment">// 📈 Normalized pixel coordinates (from 0 to 1)</span>
    <span class="hljs-type">vec2</span> uv = fragCoord/iResolution.xy;

    <span class="hljs-comment">// 🕸️ Convolve kernel with texture</span>
    <span class="hljs-type">vec3</span> col = convolution(gaussian_blur, iChannel0, uv);

    <span class="hljs-comment">// ✍️ Output to screen</span>
    fragColor = <span class="hljs-type">vec4</span>(col, <span class="hljs-number">1.0</span>);
}</code></pre></div>
<h3 id="leaky-relu">Leaky ReLU</h3>
<p><img src="https://alain.xyz/blog/machine-learning-denoising/assets/relu.svg" alt="Convolution"/></p>
<div><pre><code class="language-py"><span class="hljs-comment"># 🍱🚿 TensorFlow</span>
<span class="hljs-keyword">import</span> tensorflow <span class="hljs-keyword">as</span> tf
<span class="hljs-keyword">from</span> tensorflow.python.keras <span class="hljs-keyword">import</span> layers
layers.LeakyReLU(filters, alpha=<span class="hljs-number">.3</span>, ...)</code></pre></div>
<div><pre><code class="language-cpp"><span class="hljs-comment">// ❌🧠 DirectML</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">DML_ACTIVATION_LEAKY_RELU_OPERATOR_DESC</span>
{
    <span class="hljs-type">const</span> DML_TENSOR_DESC* InputTensor;
    <span class="hljs-type">const</span> DML_TENSOR_DESC* OutputTensor;
    FLOAT Alpha;
};</code></pre></div>
<p>A <strong>Leaky Rectifier Linear Unit</strong> is an activation function that reduces the magnitude of its input below a threshold value, but otherwise returns a scaled value if it&#x27;s above the threshold.</p>
<p>This is analogous to:</p>
<div><pre><code class="language-glsl"><span class="hljs-type">float</span> x = x &lt; threshold ? x * alpha : x;</code></pre></div>
<p>We use this to help the neural network find what features it needs to filter, that being areas of high variance in monte carlo images. ReLUs are very good at finding features in stochastic data, but can result in dead neurons in your network, that being, neurons that do not activate. </p>
<h3 id="max-pool">Max Pool</h3>
<p><img src="https://alain.xyz/blog/machine-learning-denoising/assets/maxpool.svg" alt="Max Pool"/></p>
<div><pre><code class="language-py"><span class="hljs-comment"># 🍱🚿 TensorFlow</span>
<span class="hljs-keyword">import</span> tensorflow <span class="hljs-keyword">as</span> tf
<span class="hljs-keyword">from</span> tensorflow.python.keras <span class="hljs-keyword">import</span> layers
layers.MaxPooling2D(pool_size=(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>), ...)</code></pre></div>
<div><pre><code class="language-cpp"><span class="hljs-comment">// ❌🧠 DirectML</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">DML_MAX_POOLING_OPERATOR_DESC</span>
{
    <span class="hljs-type">const</span> DML_TENSOR_DESC* InputTensor;
    <span class="hljs-type">const</span> DML_TENSOR_DESC* OutputTensor;
    UINT DimensionCount;
    _In_reads_(DimensionCount) <span class="hljs-type">const</span> UINT* Strides;
    _In_reads_(DimensionCount) <span class="hljs-type">const</span> UINT* WindowSize;
    _In_reads_(DimensionCount) <span class="hljs-type">const</span> UINT* StartPadding;
    _In_reads_(DimensionCount) <span class="hljs-type">const</span> UINT* EndPadding;
};
</code></pre></div>
<blockquote>
<p>The size of the pooling operation or filter is smaller than the size of the feature map; specifically, it is almost always 2×2 pixels applied with a stride of 2 pixels.</p>
</blockquote>
<p>A <strong>Max Pooling</strong> is a tensor operation that checks neighboring features and outputs the maximum of that area.</p>
<p>This is analogous to <strong>downsampling</strong> in graphics, where neighboring pixels are often averaged then written to an output smaller than the original as in super-sampled anti-aliasing.</p>
<p>The pattern of <em>convolutions</em> followed by a <em>nonlinear function</em>, followed by <em>pooling</em> is quite common to neural networks. <a href="#ref_brownlee2019">[Brownlee 2019]</a></p>
<h2 id="training">Training</h2>
<table><thead><tr><th style="text-align:center"><img src="https://alain.xyz/blog/machine-learning-denoising/assets/cornell-2048spp.jpg" alt="Ground Truth"/> <strong>Ground Truth</strong></th><th style="text-align:center"><img src="https://alain.xyz/blog/machine-learning-denoising/assets/cornell-1spp.jpg" alt="1 Sample"/> <strong>1spp</strong></th></tr></thead><tbody><tr><td style="text-align:center"><img src="https://alain.xyz/blog/machine-learning-denoising/assets/cornell-2048spp-nrm-direct.jpg" alt="Direct Normals"/> <strong>Direct Normal</strong></td><td style="text-align:center"><img src="https://alain.xyz/blog/machine-learning-denoising/assets/cornell-2048spp-nrm-indirect.jpg" alt="Indirect Normals"/> <strong>Indirect Normal</strong></td></tr><tr><td style="text-align:center"></td><td style="text-align:center"><img src="https://alain.xyz/blog/machine-learning-denoising/assets/cornell-2048spp-alb.jpg" alt="Albedo"/> <strong>Indirect Albedo</strong></td></tr></tbody></table>
<p>It&#x27;s worth noting what data the autoencoder should expect and in what format so it&#x27;s better tuned for denoising.</p>
<ul>
<li>
<p>Should the autoencoder be able to <em>denoise direct ray data (so direct normals)</em>, or <em>direct and indirect (such as reflected normals, reflected albedo, etc.)</em>. <strong>Intel Open Image Denoise</strong> opts for training for both cases, but prefers indirect data as it results in more information for the network to make better decisions with. The decision of which to use can also be ambiguous, as the visibility of indirect rays can depend on the incidence of the first bounce along with their BRDF behavior (imagine capturing normals under a 🚗 car windshield for instance.)</p>
</li>
<li>
<p>How should you make sure your denoised output is temporally coherent? It&#x27;s possible to feed motion vectors as an additional feature buffer to help with this.</p>
</li>
<li>
<p>Is Monte-Carlo biased noise (sobol noise, importance sampling) a factor when training? What about renderers? (Blender Cycles vs. Mitsuba vs. PBRT, etc.)</p>
</li>
<li>
<p>Should all inputs be anti-aliased or not? Most commercial autoencoders suggest all inputs be anti-aliased, and it is easy to anti-alias ray traced images anyways though offseting samples so this is understandable.</p>
</li>
<li>
<p>When measuring error, should the <em>variance of the output vs ground truth</em> be used as a metric as error loss, or should the <em>color cosine difference of the output and ground truth</em> be used? It&#x27;s possible to compare both and determine the best choice by visually comparing models. <em>Xu et al</em> recommends using Wasserstein distance, a probability based distance common to recent GANs.</p>
</li>
</ul>
<div><pre><code class="language-py">//https://gist.github.com/mjdietzx/a8121604385ce6da251d20d018f9a6d6
<span class="hljs-keyword">import</span> tensorflow <span class="hljs-keyword">as</span> tf

<span class="hljs-keyword">def</span> <span class="hljs-title function_">em_loss</span>(<span class="hljs-params">y_coefficients, y_pred</span>):
    <span class="hljs-keyword">return</span> tf.reduce_mean(tf.multiply(y_coefficients, y_pred))</code></pre></div>
<h2 id="conclusion">Conclusion</h2>
<p>Autoencoders have had great success in denoising images, filling in missing details, and in general getting an image closer to its ground truth.</p>
<p>Here&#x27;s a few additional resources when developing your own machine learning denoisers:</p>
<ul>
<li>
<p>In the <a href="https://cis565-fall-2019.github.io/">University of Pennsylvania&#x27;s CIS565 course</a>, Dewang Sultania &amp; Vaibhav Arcot implemented a Pytorch version of the paper <a href="https://github.com/Black-Phoenix/Ai-Path-Tracer-Denoiser">Interactive Reconstruction of Monte Carlo Image Sequences using a Recurrent Denoising Autoencoder</a>.</p>
</li>
<li>
<img class="profile-picture" src="https://github.com/RikoOphorst.png" alt="Riko Ophorst&#x27;s picture"/>
<p>Riko Ophorst (<a href="https://twitter.com/sanctumed">@sanctumed</a>) made a <a href="https://github.com/RikoOphorst/dxr-path-tracing">project
that uses DXR and NVIDIA&#x27;s official autoencoder to denoise ray traced images
in real time</a>.</p>
</li>
<li>
<p>Rajesh Sharma (<a href="https://twitter.com/xarmalarma">@xarmalarma</a>) of Disney Research hosted a Siggraph 2020 Course on <a href="https://www.youtube.com/watch?v=gfY2LfRfE1E">Machine Learning for Computer Graphics</a>.</p>
</li>
<li>
<img class="profile-picture" src="https://github.com/xmeng525.png" alt="Xiaoxu Meng&#x27;s picture"/>
<p><a href="https://xiaoxumeng1993.wixsite.com/xiaoxumeng">Xiaoxu Meng</a> released the
source of their Neural Bilateral Grid paper
<a href="https://github.com/xmeng525/RealTimeDenoisingNeuralBilateralGrid">here</a>.</p>
</li>
<li>
<img class="profile-picture" src="https://github.com/ingowald.png" alt="Ingo Wald&#x27;s picture"/>
<p>Ingo Wald (<a href="https://alain.xyz/(https://twitter.com/IngoWald)">@IngoWald</a>) released a Optix
course providing examples for using the Optix denoiser
<a href="https://github.com/ingowald/optix7course/blob/master/example12_denoiseSeparateChannels/SampleRenderer.cpp#L794">here</a>.</p>
</li>
<li>
<img class="profile-picture" src="https://github.com/mklefrancois.png" alt="Martin-Karl Lefrançois&#x27;s picture"/>
<p>Martin-Karl Lefrançois (<a href="https://twitter.com/doragonhanta">@doragonhanta</a>) and
Christoph Kubisch (<a href="https://twitter.com/pixeljetstream">@pixeljetstream</a>) of
NVIDIA released <a href="https://github.com/nvpro-samples/vk_denoise">VKDenoise</a>, an
example showcasing how to denoise a Vulkan scene with the Optix Denoiser.</p>
</li>
<li>
<p>NVIDIA has released an SDK to itegrate Deep Learning Super Sampling <a href="https://developer.nvidia.com/dlss-getting-started">here</a>. AMD&#x27;s <a href="https://github.com/GPUOpen-Effects/FidelityFX-FSR">Fidelity FX Super resolution</a> is also available for developers to integrate into their applications.</p>
</li>
<li>
<p><a href="https://github.com/microsoft/DirectML">DirectML</a> has released a few resources for using it in combination with <a href="https://github.com/microsoft/DirectML">TensorFlow</a> and the <a href="https://github.com/microsoft/onnxruntime">ONNX Runtime</a>.</p>
</li>
<li>
<p><a href="https://mml-book.github.io">Matematics for Machine Learning</a></p>
</li>
<li>
<p><a href="http://neuralnetworksanddeeplearning.com/">Neural Networks and Deep Learning</a></p>
</li>
<li>
<p><a href="https://udlbook.github.io/udlbook/">Understanding Deep Learning</a></p>
</li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_gallinari1987"><span class="markademic-citename">[Gallinari et al. 1987]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Memoires associatives distribuees: Une comparaison</span></strong></em><br/><span class="markademic-authors"><span><span>P. Gallinari</span></span><span>, <span>Yann Lecun</span></span><span>, <span>S. Thiria</span></span><span> and <span>Fogelman F. Soulie</span></span></span><br/><span><span class="markademic-publisher">Cesta-Afcet 1987</span><br/></span><span><a href="https://nyuscholars.nyu.edu/en/publications/memoires-associatives-distribuees-une-comparaison-distributed-ass">nyuscholars.nyu.edu</a></span></div></td></tr><tr><td id="ref_mixon2018"><span class="markademic-citename">[G. Mixon  et al. 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">SUNLayer: Stable denoising with generative networks</span></strong></em><br/><span class="markademic-authors"><span><span>Dustin G. Mixon </span></span><span> and <span>Soledad Villar </span></span></span><br/><span><span class="markademic-publisher">ArXiv 2018</span><br/></span><span><a href="http://www.deeplearningbook.org">deeplearningbook.org</a></span></div></td></tr><tr><td id="ref_kalantari2013"><span class="markademic-citename">[Khademi Kalantari et al. 2013]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Removing the Noise in Monte Carlo Rendering with General Image Denoising Algorithms</span></strong></em><br/><span class="markademic-authors"><span><span>Nima Khademi Kalantari</span></span><span> and <span>Pradeep Sen</span></span></span><br/><span><span class="markademic-publisher">Eurographics 2013</span><br/></span><span><a href="https://research.nvidia.com/publication/2020-05_Neural-Temporal-Adaptive">research.nvidia.com</a></span></div></td></tr><tr><td id="ref_bako2017"><span class="markademic-citename">[Bako et al. 2017]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Kernel-predicting convolutional networks for denoising Monte Carlo renderings</span></strong></em><br/><span class="markademic-authors"><span><span>Steve Bako</span></span><span>, <span>Thijs Vogels</span></span><span>, <span>Brian Mcwilliams</span></span><span>, <span>Mark Meyer</span></span><span>, <span>Jan Novak</span></span><span>, <span>Alex Harvill</span></span><span>, <span>Pradeep Sen</span></span><span>, <span>T. Derose</span></span><span> and <span>Fabrice Rousselle</span></span></span><br/><span><span class="markademic-publisher">ACM Transactions on Graphics 2017</span><br/></span><span><a href="http://cvc.ucsb.edu/graphics/Papers/SIGGRAPH2017_KPCN/">cvc.ucsb.edu</a></span></div></td></tr><tr><td id="ref_vogels2018"><span class="markademic-citename">[Vogels et al. 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Denoising with Kernel Prediction and Asymmetric Loss Functions</span></strong></em><br/><span class="markademic-authors"><span><span>Thijs Vogels</span></span><span>, <span>Fabrice Rousselle</span></span><span>, <span>Brian Mcwilliams</span></span><span>, <span>Gerhard Rothlin</span></span><span>, <span>Alex Harvill</span></span><span>, <span>David Adler</span></span><span>, <span>Mark Meyer</span></span><span> and <span>Jan Novak</span></span></span><br/><span><span class="markademic-publisher">ACM, ACM Transactions on Graphics 2018</span><br/></span><span><a href="http://doi.acm.org/10.1145/3197517.3201388">doi.acm.org</a></span></div></td></tr><tr><td id="ref_laine2019"><span class="markademic-citename">[Laine et al. 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Self-Supervised Deep Image Denoising</span></strong></em><br/><span class="markademic-authors"><span><span>Samuli Laine</span></span><span>, <span>Jaakko Lehtinen</span></span><span> and <span>Timo Aila</span></span></span><br/><span><span class="markademic-publisher">CoRR 2019</span><br/></span><span><a href="http://arxiv.org/abs/1901.10277">arxiv.org</a></span></div></td></tr><tr><td id="ref_kalantari2015"><span class="markademic-citename">[Khademi Kalantari et al. 2015]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">A Machine Learning Approach for Filtering Monte Carlo Noise</span></strong></em><br/><span class="markademic-authors"><span><span>Nima Khademi Kalantari</span></span><span>, <span>Steve Bako</span></span><span> and <span>Pradeep Sen</span></span></span><br/><span><span class="markademic-publisher">ACM Transactions on Graphics (TOG) 2015</span><br/></span><span><a href="http://cvc.ucsb.edu/graphics/Papers/SIGGRAPH2015_LBF/">cvc.ucsb.edu</a></span></div></td></tr><tr><td id="ref_chaitanya2017"><span class="markademic-citename">[R. Alla Chaitanya et al. 2017]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Interactive Reconstruction of Monte Carlo Image Sequences Using a Recurrent Denoising Autoencoder</span></strong></em><br/><span class="markademic-authors"><span><span>Chakravarty R. Alla Chaitanya</span></span><span>, <span>Anton S. Kaplanyan</span></span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/cschied.png" alt="Christoph Schied&#x27;s picture"/><a href="https://github.com/cschied">Christoph Schied</a> (<a href="https://twitter.com/c_schied">@c_schied</a>)</span><span>, <span>Marco Salvi</span> (<a href="https://twitter.com/marcosalvi">@marcosalvi</a>)</span><span>, <span>Aaron Lefohn</span></span><span>, <span>Derek Nowrouzezahrai</span></span><span> and <span>Timo Aila</span></span></span><br/><span><span class="markademic-publisher">ACM, ACM Transactions on Graphics 2017</span><br/></span><span><a href="http://doi.acm.org/10.1145/3072959.3073601">doi.acm.org</a></span></div></td></tr><tr><td id="ref_hasselgren2020"><span class="markademic-citename">[Hasselgren et al. 2020]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Neural Temporal Adaptive Sampling and Denoising</span></strong></em><br/><span class="markademic-authors"><span><span>Jon Hasselgren</span></span><span>, <span>Jacob Munkberg</span></span><span>, <span>Marco Salvi</span> (<a href="https://twitter.com/marcosalvi">@marcosalvi</a>)</span><span>, <span>Anjul Patney</span></span><span> and <span>Aaron Lefohn</span></span></span><br/><span><span class="markademic-publisher">Eurographics 2020</span><br/></span><span><a href="https://research.nvidia.com/publication/2020-05_Neural-Temporal-Adaptive">research.nvidia.com</a></span></div></td></tr><tr><td id="ref_muller2021"><span class="markademic-citename">[Muller et al. 2021]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Real-time neural radiance caching for path tracing</span></strong></em><br/><span class="markademic-authors"><span><span>Thomas Muller</span></span><span>, <span>Fabrice Rousselle</span></span><span>, <span>Jan Novak</span></span><span> and <span>Alexander Keller</span></span></span><br/><span><span class="markademic-publisher">Association for Computing Machinery (ACM), ACM Transactions on Graphics 2021</span><br/></span><span><a href="http://dx.doi.org/10.1145/3450626.3459812">dx.doi.org</a></span></div></td></tr><tr><td id="ref_mildenhall2017"><span class="markademic-citename">[Mildenhall  et al. 2017]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Burst Denoising with Kernel Prediction Networks</span></strong></em><br/><span class="markademic-authors"><span><span>Ben Mildenhall </span></span><span>, <span>Jonathan T. Barron</span></span><span>, <span>Jiawen Chen</span></span><span>, <span>Dillon Sharlet</span></span><span>, <span>Ren Ng </span></span><span> and <span>Robert Carroll</span></span></span><br/><span><span class="markademic-publisher">CoRR 2017</span><br/></span><span><a href="http://arxiv.org/abs/1712.02327">arxiv.org</a></span></div></td></tr><tr><td id="ref_lehtinen2018"><span class="markademic-citename">[Lehtinen  et al. 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Noise2Noise: Learning Image Restoration without Clean Data</span></strong></em><br/><span class="markademic-authors"><span><span>Jaakko Lehtinen </span></span><span>, <span>Jacob Munkberg </span></span><span>, <span>Jon Hasselgren </span></span><span>, <span>Samuli Laine </span></span><span>, <span>Tero Karras </span></span><span>, <span>Miika Aittala </span></span><span> and <span>Timo Aila </span></span></span><br/><span><span class="markademic-publisher">CoRR 2018</span><br/></span><span><a href="http://arxiv.org/abs/1803.04189">arxiv.org</a></span></div></td></tr><tr><td id="ref_dong2015"><span class="markademic-citename">[Dong et al. 2015]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Image Super-Resolution Using Deep Convolutional Networks</span></strong></em><br/><span class="markademic-authors"><span><span>Chao Dong</span></span><span>, <span>Chen Change Loy</span></span><span>, <span>Kaiming He</span></span><span> and <span>Xiaoou Tang</span></span></span><br/><span><span class="markademic-publisher">CoRR 2015</span><br/></span><span><a href="http://arxiv.org/abs/1501.00092">arxiv.org</a></span></div></td></tr><tr><td id="ref_ledig2016"><span class="markademic-citename">[Ledig et al. 2016]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Photo-Realistic Single Image Super-Resolution Using a Generative Adversarial Network</span></strong></em><br/><span class="markademic-authors"><span><span>Christian Ledig</span></span><span>, <span>Lucas Theis</span></span><span>, <span>Ferenc Huszar</span></span><span>, <span>Jose Caballero</span></span><span>, <span>Andrew P. Aitken</span></span><span>, <span>Alykhan Tejani</span></span><span>, <span>Johannes Totz</span></span><span>, <span>Zehan Wang</span></span><span> and <span>Wenzhe Shi</span></span></span><br/><span><span class="markademic-publisher">CoRR 2016</span><br/></span><span><a href="http://arxiv.org/abs/1609.04802">arxiv.org</a></span></div></td></tr><tr><td id="ref_wang2018"><span class="markademic-citename">[Wang et al. 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">A Fully Progressive Approach to Single-Image Super-Resolution</span></strong></em><br/><span class="markademic-authors"><span><span>Yifan Wang</span></span><span>, <span>Federico Perazzi</span></span><span>, <span>Brian McWilliams</span></span><span>, <span>Alexander Sorkine-Hornung</span></span><span>, <span>Olga Sorkine-Hornung</span></span><span> and <span>Christopher Schroers</span></span></span><br/><span><span class="markademic-publisher">CoRR 2018</span><br/></span><span><a href="http://arxiv.org/abs/1804.02900">arxiv.org</a></span></div></td></tr><tr><td id="ref_xiao2020"><span class="markademic-citename">[Xiao et al. 2020]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Neural Supersampling for Real-time Rendering</span></strong></em><br/><span class="markademic-authors"><span><span>Lei Xiao</span></span><span>, <span>Salah Nouri</span></span><span>, <span>Matt Chapman</span></span><span>, <span>Alexander Fix</span></span><span>, <span>Douglas Lanman</span></span><span> and <span>Anton Kaplanyan</span></span></span><br/><span><span class="markademic-publisher">Facebook Research 2020</span><br/></span><span><a href="https://research.fb.com/publications/neural-supersampling-for-real-time-rendering/">research.fb.com</a></span></div></td></tr><tr><td id="ref_schied2019"><span class="markademic-citename">[Schied et al. 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Real-Time Path Tracing and Denoising in &#x27;Quake 2&#x27;</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/cschied.png" alt="Christoph Schied&#x27;s picture"/><a href="https://github.com/cschied">Christoph Schied</a> (<a href="https://twitter.com/c_schied">@c_schied</a>)</span><span> and <span>Alexey Panteleev</span></span></span><br/><span><span class="markademic-publisher">GDC 2019</span><br/></span><span><a href="https://www.gdcvault.com/play/1026185/">gdcvault.com</a></span><span> <a href="https://youtu.be/FewqoJjHR0A">youtu.be</a></span></div></td></tr><tr><td id="ref_meng2020"><span class="markademic-citename">[Meng et al. 2020]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Real-time Monte Carlo Denoising with the Neural Bilateral Grid</span></strong></em><br/><span class="markademic-authors"><span><span>Xiaoxu Meng</span></span><span>, <span>Quan Zheng</span></span><span>, <span>Amitabh Varshney</span></span><span>, <span>Gurprit Singh</span></span><span> and <span>Matthias Zwicker</span></span></span><br/><span><span class="markademic-publisher">Eurographics 2020</span><br/></span><span><a href="https://diglib.eg.org/handle/10.2312/sr20201133">diglib.eg.org</a></span></div></td></tr><tr><td id="ref_goodfellow2016"><span class="markademic-citename">[Goodfellow et al. 2016]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Deep Learning</span></strong></em><br/><span class="markademic-authors"><span><span>Ian Goodfellow</span></span><span>, <span>Yoshua Bengio</span></span><span> and <span>Aaron Courville</span></span></span><br/><span><span class="markademic-publisher">MIT Press 2016</span><br/></span><span><a href="http://www.deeplearningbook.org">deeplearningbook.org</a></span></div></td></tr><tr><td id="ref_li2020"><span class="markademic-citename">[Li 2020]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Neural Rendering</span></strong></em><br/><span class="markademic-authors"><span><span>Chuan Li</span></span></span><br/><span><span class="markademic-publisher">MIT 2020</span><br/></span><span><a href="https://www.youtube.com/watch?v=BCZ56MU-KhQ">youtube.com</a></span></div></td></tr><tr><td id="ref_xu2019"><span class="markademic-citename">[Xu et al. 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Adversarial Monte Carlo Denoising with Conditioned Auxiliary Feature Modulation</span></strong></em><br/><span class="markademic-authors"><span><span>Bing Xu</span></span><span>, <span>Junfei Zhang</span></span><span>, <span>Rui Wang</span></span><span>, <span>Kun Xu</span></span><span>, <span>Yong-Liang Yang</span></span><span>, <span>Chuan Li</span></span><span> and <span>Rui Tang</span></span></span><br/><span><span class="markademic-publisher">ACM, ACM Transactions on Graphics 2019</span><br/></span><span><a href="http://adversarial.mcdenoising.org/">adversarial.mcdenoising.org</a></span></div></td></tr><tr><td id="ref_brownlee2019"><span class="markademic-citename">[Brownlee 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">A Gentle Introduction to Pooling Layers for Convolutional Neural Networks</span></strong></em><br/><span class="markademic-authors"><span><span>Jason Brownlee</span></span></span><br/><span><span class="markademic-publisher">2019</span><br/></span><span><a href="https://machinelearningmastery.com/pooling-layers-for-convolutional-neural-networks/">machinelearningmastery.com</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Raw Metal</title>
<link>https://alain.xyz/blog/raw-metal</link>
<description>An overview on how to write modern Apple Metal applications. Learn what key data structures are needed for it and how to draw objects.</description>
<guid isPermaLink="true">https://alain.xyz/blog/raw-metal</guid>
<pubDate>Mon, 09 Sep 2019 00:36:34 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>opengl</category><category>triangle</category><category>hello</category><category>theory</category><category>diagrams</category>
<enclosure url="https://alain.xyz/blog/raw-metal/assets/cover.jpg" length="230558" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/raw-metal/assets/metal-logo.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-metal/assets/apple-window.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-metal/assets/apple-layers.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-metal/assets/device.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-metal/assets/queue.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-metal/assets/vertex-buffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-metal/assets/index-buffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-metal/assets/uniform-buffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-metal/assets/vertex-shader.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-metal/assets/fragment-shader.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-metal/assets/shader-libraries.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-metal/assets/raster-pipeline.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-metal/assets/render-pass.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-metal/assets/command-buffer.svg"/><p><img src="https://alain.xyz/blog/raw-metal/assets/metal-logo.svg" alt="Metal 2 Logo"/></p>
<p>Apple Metal is Apple&#x27;s primary computer graphics API, and after depreciating OpenGL, the only graphics API supported and maintained by Apple.</p>
<p><img src="https://alain.xyz/blog/raw-metal/assets/apple-window.svg" alt="Metal Hello Triangle on Apple Platforms"/></p>
<p>Metal is limited to only Apple operating systems:</p>
<ul>
<li>
<p>🍎 <strong>Mac OS</strong></p>
</li>
<li>
<p>📱 <strong>iOS</strong> / <strong>iPad OS / tvOS / watchOS</strong></p>
</li>
</ul>
<p>Metal is also limited to only a few languages, with the rest having to opt for external calls to Objective C/C++ or Swift.</p>
<ul>
<li>
<p><strong>Objective C</strong></p>
</li>
<li>
<p><strong>Objective C++</strong></p>
</li>
<li>
<p><strong>Swift</strong></p>
</li>
</ul>
<p>Though despite these limitations, Apple Metal is an extremely elegant, concise, and robust API with decent support on all Apple supported platforms and driver engineers happy to help with any issues that may arise during development. You&#x27;ll also find that writing the same code in Apple Metal tends to take significantly fewer lines of code than other graphics APIs like Vulkan or DirectX 12.</p>
<hr/>
<p>I&#x27;ve prepared a <a href="http://github.com/alaingalvan/metal-seed">Github Repo</a> with everything we need to get started. We&#x27;re going to walk through a <em>Hello Triangle</em> app in modern C++, a program that creates a triangle and renders it onto the screen.</p>
<h2 id="setup">Setup</h2>
<p>First install:</p>
<ul>
<li>
<p><a href="https://git-scm.com/">Git</a></p>
</li>
<li>
<p><a href="https://cmake.org">CMake</a></p>
</li>
<li>
<p>An IDE such as <a href="https://visualstudio.microsoft.com/downloads/">Visual Studio</a>, <a href="https://developer.apple.com/xcode/">XCode</a>, or a compiler such as <a href="https://gcc.gnu.org/">GCC</a>.</p>
</li>
</ul>
<p>Then type the following in your <a href="https://hyper.is/">terminal</a>.</p>
<div><pre><code class="language-bash"><span class="hljs-comment"># 🐑 Clone the repo</span>
git <span class="hljs-built_in">clone</span> https://github.com/alaingalvan/metal-seed --recurse-submodules

<span class="hljs-comment"># 💿 go inside the folder</span>
<span class="hljs-built_in">cd</span> metal-seed

<span class="hljs-comment"># 👯 If you forget to `recurse-submodules` you can always run:</span>
git submodule update --init

<span class="hljs-comment"># 🍎 To build your XCode project on Mac OS</span>
cmake -B build -G Xcode

<span class="hljs-comment"># 🍎📱 To build your XCode project targeting iOS / iPad OS / Apple TV OS / WatchOS</span>
cmake -B build -G Xcode -DCMAKE_SYSTEM_NAME=iOS

<span class="hljs-comment"># 🔨 Build on any platform:</span>
cmake --build .</code></pre></div>
<h2 id="overview">Overview</h2>
<p>The following will explain snippets that can be found in the <a href="https://github.com/alaingalvan/metal-seed">Github repo</a>, with certain parts omitted, and <em>member variables (<code>mMemberVariable</code>) declared inline without the <code>m</code> prefix so their type is easier to see and the examples here can work on their own</em>.</p>
<h2 id="window-creation">Window Creation</h2>
<p>We&#x27;re using CrossWindow to handle cross platform window creation, so creating a window and updating it is very easy:</p>
<div><pre><code class="language-mm"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&quot;CrossWindow/CrossWindow.h&quot;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&quot;Renderer.h&quot;</span></span>

<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;iostream&gt;</span></span>

<span class="hljs-type">void</span> xmain(<span class="hljs-type">int</span> argc, <span class="hljs-keyword">const</span> <span class="hljs-type">char</span>** argv)
{
  <span class="hljs-comment">// 🖼 Create Window</span>
  xwin::WindowDesc wdesc;
  wdesc.title = <span class="hljs-string">&quot;Metal Seed&quot;</span>;
  wdesc.name = <span class="hljs-string">&quot;MainWindow&quot;</span>;
  wdesc.visible = <span class="hljs-literal">true</span>;
  wdesc.width = <span class="hljs-number">640</span>;
  wdesc.height = <span class="hljs-number">640</span>;
  wdesc.fullscreen = <span class="hljs-literal">false</span>;

  xwin::Window window;
  xwin::EventQueue eventQueue;

  <span class="hljs-keyword">if</span> (!window.create(wdesc, eventQueue))
  { <span class="hljs-keyword">return</span>; };

  <span class="hljs-comment">// 🌋 Create a renderer</span>
  Renderer renderer(window);

  <span class="hljs-comment">// 🏁 Engine loop</span>
  <span class="hljs-type">bool</span> isRunning = <span class="hljs-literal">true</span>;
  <span class="hljs-keyword">while</span> (isRunning)
  {
    <span class="hljs-type">bool</span> shouldRender = <span class="hljs-literal">true</span>;

    <span class="hljs-comment">// ♻️ Update the event queue</span>
    eventQueue.update();

    <span class="hljs-comment">// 🎈 Iterate through that queue:</span>
    <span class="hljs-keyword">while</span> (!eventQueue.empty())
    {
      <span class="hljs-comment">//Update Events</span>
      <span class="hljs-keyword">const</span> xwin::Event&amp; event = eventQueue.front();

      <span class="hljs-comment">// 💗 On Resize:</span>
      <span class="hljs-keyword">if</span> (event.type == xwin::EventType::Resize)
      {
        <span class="hljs-keyword">const</span> xwin::ResizeData data = event.data.resize;
        renderer.resize(data.width, data.height);
        shouldRender = <span class="hljs-literal">false</span>;
      }

      <span class="hljs-comment">// ❌ On Close:</span>
      <span class="hljs-keyword">if</span> (event.type == xwin::EventType::Close)
      {
        window.close();
        shouldRender = <span class="hljs-literal">false</span>;
        isRunning = <span class="hljs-literal">false</span>;
      }

      eventQueue.pop();
    }

    <span class="hljs-comment">// ✨ Update Visuals</span>
    <span class="hljs-keyword">if</span> (shouldRender)
    {
      renderer.render();
    }
  }
}</code></pre></div>
<p>Just note that on iOS, iPad OS, tvOS, and watchOS, the backend API will be <strong>UIKit</strong>, with MacOS using the <strong>Cocoa</strong> windowing API.</p>
<p>As an alternative to CrossWindow, you could use another library like , SFML, SDL, QT, or just interface directly with your OS windowing API.</p>
<h2 id="initialize-api">Initialize API</h2>
<h3 id="metal-layer">Metal Layer</h3>
<p><img src="https://alain.xyz/blog/raw-metal/assets/apple-layers.svg" alt="MacOS Layers Diagram"/></p>
<p>Every Apple Window can have layers attached to it that handle things like OpenGL or Metal.</p>
<blockquote>
<p>For more information on <a href="https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/TheViewControllerHierarchy.html">the application hierarchy of Cocoa and UIKit applications visit their documentation here</a>.</p>
</blockquote>
<div><pre><code class="language-mm"><span class="hljs-comment">// ☕ Use CrossWindow-Graphics to create Metal Layer</span>
xgfx::createMetalLayer(&amp;window);
xwin::WindowDelegate&amp; del = window.getDelegate();
<span class="hljs-built_in">CAMetalLayer</span>* layer = (<span class="hljs-built_in">CAMetalLayer</span>*)del.layer;</code></pre></div>
<h3 id="device">Device</h3>
<p><img src="https://alain.xyz/blog/raw-metal/assets/device.svg" alt="Device Diagram"/></p>
<p>A <strong>Device</strong> is the entry point to the Metal API.</p>
<div><pre><code class="language-mm"><span class="hljs-comment">// 👋 Declare handles</span>
<span class="hljs-built_in">MTLCommandBuffer</span>* device;

<span class="hljs-comment">// 🎮 Create device</span>
layer.device = <span class="hljs-built_in">MTLCreateSystemDefaultDevice</span>();
device = layer.device;</code></pre></div>
<h3 id="command-queue">Command Queue</h3>
<p><img src="https://alain.xyz/blog/raw-metal/assets/queue.svg" alt="Queue"/></p>
<p>A <strong>Command Queue</strong> functions similarly to other modern graphics APIs, a queue from which you can send graphics function calls to the GPU.</p>
<div><pre><code class="language-mm"><span class="hljs-comment">// 👋 Declare handles</span>
<span class="hljs-built_in">MTLCommandQueue</span>* commandQueue;

<span class="hljs-comment">// 📦 Create the command queue</span>
commandQueue = [device newCommandQueue];</code></pre></div>
<h2 id="initialize-resources">Initialize Resources</h2>
<h3 id="vertex-buffer">Vertex Buffer</h3>
<p><img src="https://alain.xyz/blog/raw-metal/assets/vertex-buffer.svg" alt="Vertex Buffer Diagram"/></p>
<p><strong>Vertex Buffers</strong> are blocks of data stored in the GPU that are used to create triangles.</p>
<p>You could describe this data with <em>one big buffer</em> containing everything or with <em>independent arrays</em> for each element in your vertex layout, whichever best fits your use case and performance requirements.</p>
<p>Having them split can be easier to update if you&#x27;re changing your vertex buffer data often, which may be useful for <strong>CPU animations</strong> or <strong>procedurally generated geometry</strong>.</p>
<div><pre><code class="language-mm"><span class="hljs-comment">// 📈 Describe Position Vertex Buffer Data</span>
<span class="hljs-type">float</span> positions[<span class="hljs-number">3</span>*<span class="hljs-number">3</span>] = { <span class="hljs-number">1.0</span>f,  <span class="hljs-number">1.0</span>f,  <span class="hljs-number">0.0</span>f,
                        <span class="hljs-number">-1.0</span>f,  <span class="hljs-number">1.0</span>f,  <span class="hljs-number">0.0</span>f,
                         <span class="hljs-number">0.0</span>f, <span class="hljs-number">-1.0</span>f,  <span class="hljs-number">0.0</span>f };

<span class="hljs-comment">// 🎨 Describe Color Vertex Buffer Data</span>
<span class="hljs-type">float</span> colors[<span class="hljs-number">3</span>*<span class="hljs-number">3</span>] = { <span class="hljs-number">1.0</span>f,  <span class="hljs-number">0.0</span>f,  <span class="hljs-number">0.0</span>f,
                      <span class="hljs-number">0.0</span>f,  <span class="hljs-number">1.0</span>f,  <span class="hljs-number">0.0</span>f,
                      <span class="hljs-number">0.0</span>f,  <span class="hljs-number">0.0</span>f,  <span class="hljs-number">1.0</span>f };

<span class="hljs-comment">// 👋 Declare handles</span>
<span class="hljs-built_in">MTLBuffer</span>* positionBuffer;
<span class="hljs-built_in">MTLBuffer</span>* colorBuffer;

<span class="hljs-comment">// ⚪ Create VBO</span>
positionBuffer = [device newBufferWithLength:<span class="hljs-keyword">sizeof</span>(Vertex) * <span class="hljs-number">3</span>
                                    options:<span class="hljs-built_in">MTLResourceOptionCPUCacheModeDefault</span>];
<span class="hljs-comment">// 💬 Label VBO</span>
[positionBuffer setLabel:<span class="hljs-string">@&quot;PositionBuffer&quot;</span>];
<span class="hljs-comment">// 💾 Push data to VBO</span>
memcpy(positionBuffer.contents, positions, <span class="hljs-keyword">sizeof</span>(<span class="hljs-type">float</span>) * <span class="hljs-number">3</span> * <span class="hljs-number">3</span>);

<span class="hljs-comment">// ⚪ Create VBO</span>
colorBuffer = [device newBufferWithLength:<span class="hljs-keyword">sizeof</span>(Vertex) * <span class="hljs-number">3</span>
                                 options:<span class="hljs-built_in">MTLResourceOptionCPUCacheModeDefault</span>];
<span class="hljs-comment">// 💬 Label VBO</span>
[colorBuffer setLabel:<span class="hljs-string">@&quot;ColorBuffer&quot;</span>];
<span class="hljs-comment">// 💾 Push data to VBO</span>
memcpy(colorBuffer.contents, colors, <span class="hljs-keyword">sizeof</span>(<span class="hljs-type">float</span>) * <span class="hljs-number">3</span> * <span class="hljs-number">3</span>);</code></pre></div>
<h3 id="index-buffer">Index Buffer</h3>
<p><img src="https://alain.xyz/blog/raw-metal/assets/index-buffer.svg" alt="Index Buffer Diagram"/></p>
<p><strong>Index Buffers</strong> are blocks of data stored in the GPU used to describe <em>triangles</em>, each 3 numbers corresponds ot the <em>index</em> of that triangle.</p>
<p>If you&#x27;re making triangles, there should be <em>3 points per triangle</em> in the index buffer, for <em>lines there should be 2</em>, and <em>points only need 1</em>.</p>
<div><pre><code class="language-mm"><span class="hljs-comment">// 🗄️ Describe Index Buffer Data</span>
<span class="hljs-type">unsigned</span> indexBufferData[<span class="hljs-number">3</span>] = { <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span> };

<span class="hljs-comment">// ✋ Declare Index Buffer Handle</span>
<span class="hljs-built_in">MTLBuffer</span>* indexBuffer;

<span class="hljs-comment">// 🃏 Index Data</span>
indexBuffer = [device newBufferWithLength:<span class="hljs-keyword">sizeof</span>(<span class="hljs-type">unsigned</span>) * <span class="hljs-number">3</span>
                                    options:<span class="hljs-built_in">MTLResourceOptionCPUCacheModeDefault</span>];
[indexBuffer setLabel:<span class="hljs-string">@&quot;IBO&quot;</span>];
memcpy(indexBuffer.contents, indexBufferData, <span class="hljs-keyword">sizeof</span>(<span class="hljs-type">unsigned</span>) * <span class="hljs-number">3</span>);
</code></pre></div>
<h3 id="uniform-buffer">Uniform Buffer</h3>
<p><img src="https://alain.xyz/blog/raw-metal/assets/uniform-buffer.svg" alt="Uniform Buffer Diagram"/></p>
<p><strong>Uniform Buffers</strong> are blocks of memory describing data that&#x27;s meant to be sent to your shader during rendering, such values to control effects, positional matrices, etc.</p>
<div><pre><code class="language-mm"><span class="hljs-comment">// 👋 Declare handles</span>
<span class="hljs-built_in">MTLBuffer</span>* uniformBuffer;

<span class="hljs-comment">// 🗄️ Describe Uniform Data</span>
<span class="hljs-keyword">struct</span> UniformData
{
    mat4 projectionMatrix;
    mat4 modelMatrix;
    mat4 viewMatrix;
} uboVS;

<span class="hljs-comment">// 🎛️ Create Uniform Buffer</span>
uniformBuffer = [device newBufferWithLength:(<span class="hljs-keyword">sizeof</span>(UniformData) + <span class="hljs-number">255</span>) &amp; ~<span class="hljs-number">255</span>
                                      options:<span class="hljs-built_in">MTLResourceOptionCPUCacheModeDefault</span>];
[uniformBuffer setLabel:<span class="hljs-string">@&quot;UBO&quot;</span>];

<span class="hljs-comment">// Update Uniforms...</span></code></pre></div>
<h3 id="vertex-shader">Vertex Shader</h3>
<p><img src="https://alain.xyz/blog/raw-metal/assets/vertex-shader.svg" alt="Vertex Shader Diagram"/></p>
<p>A <strong>Vertex Shader</strong> is a GPU program that executes on every vertex of what you&#x27;re currently drawing. Often times developers will place code that handles positioning geometry here.</p>
<div><pre><code class="language-msl"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;metal_stdlib&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;simd/simd.h&gt;</span></span>

<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> metal;

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">UBO</span>
{
    <span class="hljs-type">float4x4</span> projectionMatrix;
    <span class="hljs-type">float4x4</span> modelMatrix;
    <span class="hljs-type">float4x4</span> viewMatrix;
};

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">VertexIn</span>
{
    <span class="hljs-type">float3</span> inColor [[<span class="hljs-built_in">attribute</span>(<span class="hljs-number">1</span>)]];
    <span class="hljs-type">float3</span> inPos [[<span class="hljs-built_in">attribute</span>(<span class="hljs-number">0</span>)]];
};

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">VertexOut</span>
{
    <span class="hljs-type">float3</span> outColor [[<span class="hljs-built_in">user</span>(locn0)]];
    <span class="hljs-type">float4</span> position [[position]];
};

<span class="hljs-function"><span class="hljs-keyword">vertex</span> VertexOut <span class="hljs-title">main</span><span class="hljs-params">(VertexIn in [[<span class="hljs-keyword">stage_in</span>]], <span class="hljs-type">constant</span> UBO&amp; ubo [[<span class="hljs-keyword">buffer</span>(<span class="hljs-number">1</span>)]])</span>
</span>{
    VertexOut out = {};
    out.outColor = in.inColor;
    out.position = ((ubo.projectionMatrix * ubo.viewMatrix) * ubo.modelMatrix) * <span class="hljs-built_in">float4</span>(in.inPos, <span class="hljs-number">1.0</span>);
    <span class="hljs-keyword">return</span> out;
}

</code></pre></div>
<h3 id="fragment-shader">Fragment Shader</h3>
<p><img src="https://alain.xyz/blog/raw-metal/assets/fragment-shader.svg" alt="Fragment Shader Diagram"/></p>
<p>A <strong>Fragment Shader</strong> executes on every fragment. A fragment is like a pixel, but not limited to just 8 bit RGB, there could be multiple attachments that you&#x27;re writing to, in multiple encoded formats.</p>
<div><pre><code class="language-msl"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;metal_stdlib&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;simd/simd.h&gt;</span></span>

<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> metal;

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">FragmentIn</span>
{
    <span class="hljs-type">float3</span> inColor [[<span class="hljs-built_in">user</span>(locn0)]];
};

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">FragmentOut</span>
{
    <span class="hljs-type">float4</span> outFragColor [[<span class="hljs-built_in">color</span>(<span class="hljs-number">0</span>)]];
};

<span class="hljs-function"><span class="hljs-keyword">fragment</span> FragmentOut <span class="hljs-title">main</span><span class="hljs-params">(FragmentIn in [[<span class="hljs-keyword">stage_in</span>]])</span>
</span>{
    FragmentOut out = {};
    out.outFragColor = <span class="hljs-built_in">float4</span>(in.inColor, <span class="hljs-number">1.0</span>);
    <span class="hljs-keyword">return</span> out;
}</code></pre></div>
<h3 id="shader-libraries">Shader Libraries</h3>
<p><img src="https://alain.xyz/blog/raw-metal/assets/shader-libraries.svg" alt="Shader Libraries Diagram"/></p>
<p><strong>Shader Libraries</strong> are unique to Metal, they function as intermediary objects that can perform reflection on those shaders. As the name suggests, you can create <em>uber-shaders</em>, large shaders with different main functions.</p>
<p><strong>Shader Functions</strong> are handles to a particular shader function in a shader library.</p>
<div><pre><code class="language-mm"><span class="hljs-comment">// Load all the shader files with a .msl file extension in the project</span>
<span class="hljs-built_in">NSError</span>* err = <span class="hljs-literal">nil</span>;

<span class="hljs-comment">// 📂 Load shader files, add null terminator to the end.</span>
std::vector&lt;<span class="hljs-type">char</span>&gt; vertSource = readFile(<span class="hljs-string">&quot;triangle.vert.msl&quot;</span>);
vertSource.emplace_back(<span class="hljs-number">0</span>);
std::vector&lt;<span class="hljs-type">char</span>&gt; fragSource = readFile(<span class="hljs-string">&quot;triangle.frag.msl&quot;</span>);
fragSource.emplace_back(<span class="hljs-number">0</span>);

<span class="hljs-built_in">NSString</span>* vertPath = [<span class="hljs-built_in">NSString</span> stringWithCString:vertSource.data() encoding:[<span class="hljs-built_in">NSString</span> defaultCStringEncoding]];
<span class="hljs-built_in">MTLLibrary</span>* vertLibrary = [device newLibraryWithSource:vertPath options:<span class="hljs-literal">nil</span> error:&amp;err];
[vertPath dealloc];

<span class="hljs-built_in">NSString</span>* fragPath = [<span class="hljs-built_in">NSString</span> stringWithCString:fragSource.data() encoding:[<span class="hljs-built_in">NSString</span> defaultCStringEncoding]];
<span class="hljs-built_in">MTLLibrary</span>* fragLibrary = [device newLibraryWithSource:fragPath options:<span class="hljs-literal">nil</span> error:&amp;err];
[fragPath dealloc];

<span class="hljs-comment">// Load the vertex function from the library</span>
<span class="hljs-built_in">MTLFunction</span>* vertexFunction = [vertLibrary newFunctionWithName:<span class="hljs-string">@&quot;main0&quot;</span>];

<span class="hljs-comment">// Load the fragment function from the library</span>
<span class="hljs-built_in">MTLFunction</span>* fragmentFunction = [fragLibrary newFunctionWithName:<span class="hljs-string">@&quot;main0&quot;</span>];</code></pre></div>
<h3 id="pipeline-state">Pipeline State</h3>
<p><img src="https://alain.xyz/blog/raw-metal/assets/raster-pipeline.svg" alt="Pipeline State Diagram"/></p>
<p><strong>Pipeline State</strong> describes all the data that&#x27;s to be fed into the execution of a raster based graphics pipeline.</p>
<div><pre><code class="language-mm"><span class="hljs-comment">// 👋 Declare handles</span>
<span class="hljs-built_in">MTLRenderPipelineState</span>* pipelineState;

<span class="hljs-comment">// ⚗️ Graphics Pipeline</span>
<span class="hljs-built_in">MTLRenderPipelineDescriptor</span>* pipelineStateDescriptor = [[<span class="hljs-built_in">MTLRenderPipelineDescriptor</span> alloc] init];
pipelineStateDescriptor.label = <span class="hljs-string">@&quot;Simple Pipeline&quot;</span>;
pipelineStateDescriptor.vertexFunction = vertexFunction;
pipelineStateDescriptor.fragmentFunction = fragmentFunction;
pipelineStateDescriptor.colorAttachments[<span class="hljs-number">0</span>].pixelFormat = layer.pixelFormat;

<span class="hljs-comment">// 🔣 Input Assembly</span>
<span class="hljs-built_in">MTLVertexDescriptor</span>* vertexDesc = [<span class="hljs-built_in">MTLVertexDescriptor</span> vertexDescriptor];
vertexDesc.attributes[<span class="hljs-number">0</span>].format = <span class="hljs-built_in">MTLVertexFormatFloat3</span>;
vertexDesc.attributes[<span class="hljs-number">0</span>].offset = <span class="hljs-number">0</span>;
vertexDesc.attributes[<span class="hljs-number">0</span>].bufferIndex = <span class="hljs-number">0</span>;
vertexDesc.attributes[<span class="hljs-number">1</span>].format = <span class="hljs-built_in">MTLVertexFormatFloat3</span>;
vertexDesc.attributes[<span class="hljs-number">1</span>].offset = <span class="hljs-keyword">sizeof</span>(<span class="hljs-type">float</span>) * <span class="hljs-number">3</span>;
vertexDesc.attributes[<span class="hljs-number">1</span>].bufferIndex = <span class="hljs-number">0</span>;
vertexDesc.layouts[<span class="hljs-number">0</span>].stepFunction = <span class="hljs-built_in">MTLVertexStepFunctionPerVertex</span>;
vertexDesc.layouts[<span class="hljs-number">0</span>].stride = <span class="hljs-keyword">sizeof</span>(Vertex);

pipelineStateDescriptor.vertexDescriptor = vertexDesc;
<span class="hljs-built_in">NSError</span>* error = <span class="hljs-literal">nil</span>;

<span class="hljs-comment">// 🌟 Create Pipeline State Object</span>
pipelineState = [device
    newRenderPipelineStateWithDescriptor:pipelineStateDescriptor
    error:&amp;error];

<span class="hljs-keyword">if</span> (!pipelineState)
{
  <span class="hljs-built_in">NSLog</span>(<span class="hljs-string">@&quot;Failed to created pipeline state, error %@&quot;</span>, error);
}</code></pre></div>
<h2 id="rendering">Rendering</h2>
<h3 id="render-pass">Render Pass</h3>
<p><img src="https://alain.xyz/blog/raw-metal/assets/render-pass.svg" alt="Render Pass Diagram"/></p>
<p><strong>Render Pass</strong>es are a description of frame buffer layouts (color attachments, depth/stencil attachments) and operations (clear color, load actions) for a given set of render calls.</p>
<div><pre><code class="language-mm"><span class="hljs-comment">// 👋 Declare Handles</span>
<span class="hljs-built_in">CAMetalLayer</span>* layer;

<span class="hljs-comment">// 🤵 Build renderPassDescriptor generated from the view&#x27;s drawable textures</span>
<span class="hljs-built_in">CAMetalDrawable</span>* drawable = layer.nextDrawable;

<span class="hljs-built_in">MTLRenderPassDescriptor</span>* renderPassDescriptor = [<span class="hljs-built_in">MTLRenderPassDescriptor</span> renderPassDescriptor];
renderPassDescriptor.colorAttachments[<span class="hljs-number">0</span>].texture = drawable.texture;
renderPassDescriptor.colorAttachments[<span class="hljs-number">0</span>].loadAction = <span class="hljs-built_in">MTLLoadActionClear</span>;

<span class="hljs-built_in">MTLClearColor</span> clearCol;
clearCol.red = <span class="hljs-number">0.2</span>;
clearCol.green = <span class="hljs-number">0.2</span>;
clearCol.blue = <span class="hljs-number">0.2</span>;
clearCol.alpha = <span class="hljs-number">1.0</span>;
renderPassDescriptor.colorAttachments[<span class="hljs-number">0</span>].clearColor = clearCol;
</code></pre></div>
<h3 id="command-buffer">Command Buffer</h3>
<p><img src="https://alain.xyz/blog/raw-metal/assets/command-buffer.svg" alt="Command Buffer Diagram"/></p>
<p><strong>Command Buffer</strong>s encode all the draw commands you intend to execute, and once you&#x27;re done providing it with calls, can be submitted to the GPU. In that sense a command buffer is analogous to a <em>callback</em> that executes draw functions on the GPU once it&#x27;s submitted to the queue.</p>
<div><pre><code class="language-mm"><span class="hljs-comment">// 👋 Declare Metal Handle</span>
<span class="hljs-built_in">MTLCommandBuffer</span>* commandBuffer;
<span class="hljs-type">unsigned</span> viewportSize[<span class="hljs-number">2</span>];

<span class="hljs-keyword">if</span> (commandBuffer != <span class="hljs-literal">nil</span>)
  { [commandBuffer release]; }
  commandBuffer = [(commandQueue commandBuffer];
  (commandBuffer).label = <span class="hljs-string">@&quot;MyCommand&quot;</span>;


  <span class="hljs-keyword">if</span>(renderPassDescriptor != <span class="hljs-literal">nil</span>)
  {
    <span class="hljs-comment">// Create a render command encoder so we can render into something</span>
    <span class="hljs-built_in">MTLRenderCommandEncoder</span>* renderEncoder =
    [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
    renderEncoder.label = <span class="hljs-string">@&quot;MyRenderEncoder&quot;</span>;

    <span class="hljs-comment">// Set the region of the drawable to which we&#x27;ll draw.</span>
    [renderEncoder setViewport:(<span class="hljs-built_in">MTLViewport</span>){<span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, static_cast&lt;<span class="hljs-type">float</span>&gt;(viewportSize[<span class="hljs-number">0</span>]), static_cast&lt;<span class="hljs-type">float</span>&gt;(viewportSize[<span class="hljs-number">1</span>]), <span class="hljs-number">0.1</span>, <span class="hljs-number">1000.0</span> }];

    [renderEncoder setRenderPipelineState: pipelineState];

    [renderEncoder setCullMode:<span class="hljs-built_in">MTLCullModeNone</span>];

    [renderEncoder setVertexBuffer:positionBuffer offset:<span class="hljs-number">0</span> atIndex:<span class="hljs-number">0</span>];

    [renderEncoder setVertexBuffer:colorBuffer offset:<span class="hljs-number">0</span> atIndex:<span class="hljs-number">1</span>];

    [renderEncoder setVertexBuffer:uniformBuffer offset:<span class="hljs-number">0</span> atIndex:<span class="hljs-number">2</span>];

    [renderEncoder drawIndexedPrimitives:<span class="hljs-built_in">MTLPrimitiveTypeTriangle</span> indexCount:<span class="hljs-number">3</span> indexType:<span class="hljs-built_in">MTLIndexTypeUInt32</span> indexBuffer:indexBuffer indexBufferOffset:<span class="hljs-number">0</span>];

    [renderEncoder endEncoding];

    [commandBuffer presentDrawable:drawable];

    [commandBuffer commit];
}</code></pre></div>
<h2 id="destroying-handles">Destroying Handles</h2>
<p>Like most system level programming languages with managed memory models, <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-BAJHFBGH">in Objective C++ you must destroy any objects that you create</a>. This however isn&#x27;t necessary if automatic reference counting (or ARC) is enabled at some point in your code&#x27;s execution with <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html"><code>@autoreleasepool</code></a>.</p>
<div><pre><code class="language-mm"><span class="hljs-type">void</span> destroyAPI()
{
  <span class="hljs-keyword">if</span> (commandBuffer != <span class="hljs-literal">nil</span>)
  {
    [commandBuffer release];
  }

  [commandQueue release];

  [device release];
}

<span class="hljs-type">void</span> destroyResources()
{
  [fragmentFunction release];
  [vertexFunction release];

  [vertLibrary release];
  [fragLibrary release];

  [positionBuffer release];
  [colorBuffer release];
  [indexBuffer release];
  [uniformBuffer release];

  [pipelineState release];
}</code></pre></div>
<h2 id="conclusion">Conclusion</h2>
<p>Metal is arguably the easiest modern computer graphics API to use, with smart defaults and an intuitive API that maps easily to modern GPUs.</p>
<p>Here&#x27;s a few other resources to further your understanding of the Metal API:</p>
<ul>
<li>
<p><a href="https://developer.apple.com/documentation/metal">Apple Metal&#x27;s Official Documentation</a></p>
</li>
<li>
<p>Apple&#x27;s <a href="https://apps.apple.com/us/app/apple-developer/id640199958">developer app</a> and site featured <a href="https://developer.apple.com/videos/graphics-and-games/metal">WWDC talks discussing Metal best practices</a>.</p>
</li>
<li>
<p><a href="http://redqueencoder.com/">Janie Clayton</a> (<a href="https://twitter.com/RedQueenCoder">@RedQueenCoder</a>) wrote <a href="https://play.google.com/store/books/details?pcampaignid=books_read_action&amp;id=A55BDwAAQBAJ">Metal Programming Guide: Tutorial and Reference via Swift</a>, a really well written guide to the Metal API and related concepts.</p>
</li>
<li>
<p><a href="https://github.com/mhorga">Marius Horga</a> wrote <a href="http://metalkit.org/">MetalKit.org</a>, a collection of examples for Metal.</p>
</li>
<li>
<p>Warren Moore (<a href="https://twitter.com/warrenm">@warrenm</a>)&#x27;s <a href="https://metalbyexample.com">Metal by Example book</a> and <a href="https://github.com/metal-by-example/sample-code">examples</a>.</p>
</li>
<li>
<p>Arseny Kapoulkine (<a href="https://twitter.com/zeuxcg">@zeuxcg</a>) wrote an article on what it was like writing a bringing <a href="https://zeux.io/2016/12/01/metal-retrospective/">Metal to Roblox</a>.</p>
</li>
</ul>
<p>You&#x27;ll find all the source code described in this post in the <a href="https://github.com/alaingalvan/metal-seed">Github repo</a> here.</p>]]></content:encoded>
</item>
<item>
<title>Raw Vulkan</title>
<link>https://alain.xyz/blog/raw-vulkan</link>
<description>An overview on how to program a Hello Triangle Vulkan application from the ground up. Learn the core data structures and modern GPU execution model needed to render raster based graphics.</description>
<guid isPermaLink="true">https://alain.xyz/blog/raw-vulkan</guid>
<pubDate>Mon, 09 Sep 2019 00:30:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>vulkan</category><category>hello</category><category>world</category><category>triangle</category><category>introduction</category><category>beginner</category><category>opengl</category>
<enclosure url="https://alain.xyz/blog/raw-vulkan/assets/cover.jpg" length="266061" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/vulkan-logo.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/geforce3.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/os-window.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/apple-window.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/nintendo-switch.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/extensions-layers.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/hardware.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/logical-device.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/queue.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/command-pool.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/descriptor-pool.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/color-formats.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/swapchain.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/image-view.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/render-pass.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/frame-buffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/semaphore-fence.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/vertex-buffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/descriptor-sets.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/raster-pipeline.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/shader-stages.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-vulkan/assets/command-buffer.svg"/><link rel="preload" as="image" href="https://github.com/SaschaWillems.png"/><link rel="preload" as="image" href="https://github.com/Overv.png"/><link rel="preload" as="image" href="https://github.com/kayvonf.png"/><p><img src="https://alain.xyz/blog/raw-vulkan/assets/vulkan-logo.svg" alt="Vulkan Logo"/></p>
<p>Vulkan is a new low level Graphics API released February 2016 by the <a href="https://khronos.org">Khronos Group</a> that maps directly to the design of modern GPUs.</p>
<p>Vulkan is used by <strong>Game Developers</strong>, <strong>Rendering Engineers</strong> and <strong>Scientists</strong> looking to do <em>real-time rendering</em>, <em>raytracing</em>, <em>data visualization</em>, <em>GPGPU computations</em>, <em>machine learning</em>, <em>physics simulations</em>, etc.</p>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/geforce3.svg" alt="NVIDIA Geforce 256"/></p>
<p><strong>Graphic Processing Units (GPUs)</strong> were originally simple <strong>Application Specific Integrated Circuits (ASICs)</strong>, but since then they have become programmable computational units of their own with a focus on <em>throughput over latency</em> <a href="#ref_fatahalian2018">[Fatahalian 2018]</a>. Older APIs like OpenGL or DirectX 9 and below were designed for hardware that&#x27;s drastically changed since the early 90s when they were first released, so Vulkan was designed from scratch to match the way GPUs are engineered today.</p>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/os-window.svg" alt="Vulkan Hello Triangle on different operating systems"/></p>
<p>Currently Vulkan 1.x supports the following platforms:</p>
<ul>
<li>
<p>🖼️ <strong>Windows</strong></p>
</li>
<li>
<p>🐧 <strong>Linux</strong></p>
</li>
<li>
<p>🤖 <strong>Android</strong></p>
</li>
</ul>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/apple-window.svg" alt="Vulkan Hello Triangle on Apple platforms"/></p>
<p>With <strong>Apple MacOS</strong>, <strong>iOS</strong>, and <strong>iPad OS</strong> supporting Vulkan through <a href="https://moltengl.com/moltenvk/">MoltenVK</a>, a Vulkan-Metal compatibility layer that&#x27;s licensed as <a href="https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)">Apache 2.0</a>.</p>
<ul>
<li>
<p>🍎 <strong>Mac OS</strong></p>
</li>
<li>
<p>📱 <strong>iOS</strong> / <strong>iPad OS</strong></p>
</li>
</ul>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/nintendo-switch.svg" alt="Vulkan Hello Triangle on other platforms"/></p>
<p>In addition to other surprising platforms such as TVs, game consoles, etc.</p>
<ul>
<li>
<p>🎮 <strong>Nintendo Switch</strong></p>
</li>
<li>
<p>📺 <strong>NVIDIA Shield</strong></p>
</li>
<li>
<p>🌐 <strong>Google Stadia</strong></p>
</li>
<li>
<p><a href="https://www.khronos.org/conformance/adopters/conformant-products">And many more!</a></p>
</li>
</ul>
<p>And languages such as:</p>
<ul>
<li>
<p><strong>C</strong> - Through the <a href="https://vulkan.lunarg.com/">official bindings for Vulkan</a>, as C is Vulkan&#x27;s official language.</p>
</li>
<li>
<p><strong>C++</strong> - Through <a href="https://github.com/KhronosGroup/Vulkan-Hpp">Vulkan-Hpp</a> the official Vulkan C++ library.</p>
</li>
<li>
<p><strong>Rust</strong> - Through <a href="https://github.com/tomaka/vulkano">Vulkano</a>, an intuitive Rust wrapper with a heavy focus on compile time safety.</p>
</li>
<li>
<p><strong>JavaScript</strong> - Through <a href="https://github.com/maierfelix/nvk">Node Vulkan</a>, node.js bindings for native web applications.</p>
</li>
<li>
<p><strong>Python</strong> - Through <a href="https://github.com/bglgwyng/pyVulkan">pyVulkan</a>, a Python FFI to the C implementation of Vulkan.</p>
</li>
</ul>
<hr/>
<p>I&#x27;ve prepared a <a href="http://github.com/alaingalvan/vulkan-seed">Github Repo</a> with everything we need to get started. We&#x27;re going to walk through a <em>Hello Triangle</em> app in modern C++ 17, a program that creates a triangle, processes it with a shader, and displays it on a window.</p>
<h2 id="setup">Setup</h2>
<p>First install:</p>
<ul>
<li>
<p><a href="https://git-scm.com/">Git</a></p>
</li>
<li>
<p><a href="https://cmake.org">CMake</a></p>
</li>
<li>
<p>A Text Editor such as <a href="https://code.visualstudio.com/">Visual Studio Code</a>.</p>
</li>
<li>
<p>An IDE such as <a href="https://visualstudio.microsoft.com/downloads/">Visual Studio</a>, <a href="https://developer.apple.com/xcode/">XCode</a>, or a compiler such as <a href="https://gcc.gnu.org/">GCC</a>.</p>
</li>
</ul>
<p>Then type the following in your <a href="https://code.visualstudio.com/docs/editor/integrated-terminal">terminal</a>.</p>
<div><pre><code class="language-bash"><span class="hljs-comment"># 🐑 Clone the repo</span>
git <span class="hljs-built_in">clone</span> https://github.com/alaingalvan/vulkan-seed --recurse-submodules

<span class="hljs-comment"># 💿 go inside the folder</span>
<span class="hljs-built_in">cd</span> vulkan-seed

<span class="hljs-comment"># 👯 If you forget to `recurse-submodules` you can always run:</span>
git submodule update --init

<span class="hljs-comment"># 🖼️ To build your Visual Studio solution on Windows x64</span>
cmake -B build -A x64

<span class="hljs-comment"># 🍎 To build your XCode project on Mac OS / iOS</span>
cmake -B build -G Xcode

<span class="hljs-comment"># 🐧 To build your .make file on Linux</span>
cmake -B build

<span class="hljs-comment"># 🔨 Build on any platform:</span>
cmake --build build</code></pre></div>
<blockquote>
<p>Refer to <a href="https://alain.xyz/blog/designing-a-cpp-library">this blog post on designing C++ libraries and apps</a> for more details on CMake, Git Submodules, etc.</p>
</blockquote>
<h2 id="project-layout">Project Layout</h2>
<p>As your project becomes more complex, you&#x27;ll want to separate files and organize your application to something more akin to a game or renderer, check out this post on <a href="https://alain.xyz/blog/game-engine-architecture">game engine architecture</a> and this one on <a href="https://alain.xyz/blog/realtime-renderer-architectures">real time renderer architecture</a> for more details.</p>
<div><pre><code class="language-bash">├─ 📂 external/                    <span class="hljs-comment"># 👶 Dependencies</span>
│  ├─ 📁 crosswindow/                    <span class="hljs-comment"># 🖼️ OS Windows</span>
│  ├─ 📁 crosswindow-graphics/           <span class="hljs-comment"># 🎨 Vulkan Surface Creation</span>
│  └─ 📁 glm/                            <span class="hljs-comment"># ➕ Linear Algebra</span>
├─ 📂 src/                         <span class="hljs-comment"># 🌟 Source Files</span>
│  ├─ 📄 Utils.h                         <span class="hljs-comment"># ⚙️ Utilities (Load Files, Check Shaders, etc.)</span>
│  ├─ 📄 Renderer.h                      <span class="hljs-comment"># 🔺 Triangle Draw Code</span>
│  ├─ 📄 Renderer.cpp                    <span class="hljs-comment"># -</span>
│  └─ 📄 Main.cpp                        <span class="hljs-comment"># 🏁 Application Main</span>
├─ 📄 .gitignore                   <span class="hljs-comment"># 👁️ Ignore certain files in git repo</span>
├─ 📄 CMakeLists.txt               <span class="hljs-comment"># 🔨 Build Script</span>
├─ 📄 license.md                   <span class="hljs-comment"># ⚖️ Your License (Unlicense)</span>
└─ 📃readme.md                     <span class="hljs-comment"># 📖 Read Me!</span></code></pre></div>
<h3 id="dependencies">Dependencies</h3>
<ul>
<li>
<p><a href="https://github.com/alaingalvan/crosswindow">CrossWindow</a> - A cross platform system abstraction library written in C++ for managing windows and performing OS tasks.</p>
</li>
<li>
<p><a href="https://github.com/alaingalvan/crosswindow-graphics">CrossWindow-Graphics</a> - A library to simplify creating an Vulkan Surface with CrossWindow.</p>
</li>
<li>
<p><a href="https://vulkan.lunarg.com/">Vulkan SDK</a> - The official Vulkan SDK distributed by LunarG. <em>This should be installed separately</em>.</p>
</li>
<li>
<p><a href="http://glm.g-truc.net/0.9.8/index.html">GLM</a> - A C++ library that allows users to write <code>glsl</code> like C++ code, with types for vectors, matrices, etc.</p>
</li>
</ul>
<p>We&#x27;ll be writing our application using Vulkan&#x27;s C++ API through <code>vulkan.hpp</code>, a type safe abstraction of <code>vulkan.h</code>.</p>
<h2 id="overview">Overview</h2>
<p>In this application we will need to do the following:</p>
<ol>
<li>
<p><strong>Initialize the API</strong> - <em>Create</em> a <strong>Vulkan Instance</strong> to access inner functions of the Vulkan API. <em>Pick</em> the best <strong>Physical Device</strong> from every device that supports Vulkan on your machine. <em>Create</em> a <strong>Logical Device</strong> , <strong>Surface</strong>, <strong>Queue</strong>, <strong>Command Pool</strong>, <strong>Semaphores</strong>, <strong>Fences</strong>.</p>
</li>
<li>
<p><strong>Create Commands</strong> - Describe everything that&#x27;ll be rendered on the current frame in your command buffers.</p>
</li>
<li>
<p><strong>Initialize Resources</strong> - <em>Create</em> a <strong>Descriptor Pool</strong>, <strong>Descriptor Set Layout</strong>, <strong>Pipeline Layout</strong>, <strong>Vertex Buffer</strong>/<strong>Index Buffer</strong> and send it to GPU Accessible Memory, <em>describe</em> our <strong>Input Attributes</strong>, <em>create</em> a <strong>Uniform Buffer</strong>, <strong>Render Pass</strong>, <strong>Frame Buffers</strong>, <strong>Shader Modules</strong>, and <strong>Pipeline State</strong>.</p>
</li>
<li>
<p><strong>Setup Commands</strong> for each command buffer to set the GPU state to render the triangles.</p>
</li>
<li>
<p><strong>Render</strong> - Use an <strong>Update Loop</strong> to switch between different frames in your swapchain as well as to poll input devices/window events.</p>
</li>
<li>
<p><strong>Destroy</strong> any data structures once the application is asked to close.</p>
</li>
</ol>
<p>The following will explain snippets that can be found in the <a href="https://github.com/alaingalvan/vulkan-seed">Github repo</a>, with certain parts omitted, and <em>member variables (<code>mMemberVariable</code>) declared inline without the <code>m</code> prefix so their type is easier to see and the examples here can work on their own</em>.</p>
<h2 id="window-creation">Window Creation</h2>
<p>We&#x27;re using CrossWindow to handle cross platform window creation, so creating a window and updating it is very easy:</p>
<div><pre><code class="language-cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&quot;CrossWindow/CrossWindow.h&quot;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&quot;Renderer.h&quot;</span></span>

<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;iostream&gt;</span></span>

<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">xmain</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">const</span> <span class="hljs-type">char</span>** argv)</span>
</span>{
  <span class="hljs-comment">// 🖼 Create Window</span>
  xwin::WindowDesc wdesc;
  wdesc.title = <span class="hljs-string">&quot;Vulkan Seed&quot;</span>;
  wdesc.name = <span class="hljs-string">&quot;MainWindow&quot;</span>;
  wdesc.visible = <span class="hljs-literal">true</span>;
  wdesc.width = <span class="hljs-number">640</span>;
  wdesc.height = <span class="hljs-number">640</span>;
  wdesc.fullscreen = <span class="hljs-literal">false</span>;

  xwin::Window window;
  xwin::EventQueue eventQueue;

  <span class="hljs-keyword">if</span> (!window.<span class="hljs-built_in">create</span>(wdesc, eventQueue))
  { <span class="hljs-keyword">return</span>; };

  <span class="hljs-comment">// 🌋 Create a renderer</span>
  <span class="hljs-function">Renderer <span class="hljs-title">renderer</span><span class="hljs-params">(window)</span></span>;

  <span class="hljs-comment">// 🏁 Engine loop</span>
  <span class="hljs-type">bool</span> isRunning = <span class="hljs-literal">true</span>;
  <span class="hljs-keyword">while</span> (isRunning)
  {
    <span class="hljs-type">bool</span> shouldRender = <span class="hljs-literal">true</span>;

    <span class="hljs-comment">// ♻️ Update the event queue</span>
    eventQueue.<span class="hljs-built_in">update</span>();

    <span class="hljs-comment">// 🎈 Iterate through that queue:</span>
    <span class="hljs-keyword">while</span> (!eventQueue.<span class="hljs-built_in">empty</span>())
    {
      <span class="hljs-comment">//Update Events</span>
      <span class="hljs-type">const</span> xwin::Event&amp; event = eventQueue.<span class="hljs-built_in">front</span>();

      <span class="hljs-comment">// 💗 On Resize:</span>
      <span class="hljs-keyword">if</span> (event.type == xwin::EventType::Resize)
      {
        <span class="hljs-type">const</span> xwin::ResizeData data = event.data.resize;
        renderer.<span class="hljs-built_in">resize</span>(data.width, data.height);
        shouldRender = <span class="hljs-literal">false</span>;
      }

      <span class="hljs-comment">// ❌ On Close:</span>
      <span class="hljs-keyword">if</span> (event.type == xwin::EventType::Close)
      {
        window.<span class="hljs-built_in">close</span>();
        shouldRender = <span class="hljs-literal">false</span>;
        isRunning = <span class="hljs-literal">false</span>;
      }

      eventQueue.<span class="hljs-built_in">pop</span>();
    }

    <span class="hljs-comment">// ✨ Update Visuals</span>
    <span class="hljs-keyword">if</span> (shouldRender)
    {
      renderer.<span class="hljs-built_in">render</span>();
    }
  }
}</code></pre></div>
<p>As an alternative to CrossWindow, you could use another library like GLFW, SFML, SDL, QT, or just interface directly with your OS windowing API.</p>
<h2 id="initialize-api">Initialize API</h2>
<h3 id="instances">Instances</h3>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/extensions-layers.svg" alt="Instance Diagram"/></p>
<p>Similar to the OpenGL context, a Vulkan application begins when you create an <strong>instance</strong>. This instance must be loaded with some information about the program such as its name, engine, and minimum Vulkan version, as well any extensions and layers you want to load.</p>
<div><pre><code class="language-cpp"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">findBestExtensions</span><span class="hljs-params">(<span class="hljs-type">const</span> std::vector&lt;vk::ExtensionProperties&gt;&amp; installed,
                        <span class="hljs-type">const</span> std::vector&lt;<span class="hljs-type">const</span> <span class="hljs-type">char</span>*&gt;&amp; wanted,
                        std::vector&lt;<span class="hljs-type">const</span> <span class="hljs-type">char</span>*&gt;&amp; out)</span>
</span>{
    <span class="hljs-keyword">for</span> (<span class="hljs-type">const</span> <span class="hljs-type">char</span>* <span class="hljs-type">const</span>&amp; w : wanted)
    {
        <span class="hljs-keyword">for</span> (vk::ExtensionProperties <span class="hljs-type">const</span>&amp; i : installed)
        {
            <span class="hljs-keyword">if</span> (std::<span class="hljs-built_in">string</span>(i.extensionName).<span class="hljs-built_in">compare</span>(w) == <span class="hljs-number">0</span>)
            {
                out.<span class="hljs-built_in">emplace_back</span>(w);
                <span class="hljs-keyword">break</span>;
            }
        }
    }
}

<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">findBestLayers</span><span class="hljs-params">(<span class="hljs-type">const</span> std::vector&lt;vk::LayerProperties&gt;&amp; installed,
                    <span class="hljs-type">const</span> std::vector&lt;<span class="hljs-type">const</span> <span class="hljs-type">char</span>*&gt;&amp; wanted,
                    std::vector&lt;<span class="hljs-type">const</span> <span class="hljs-type">char</span>*&gt;&amp; out)</span>
</span>{
    <span class="hljs-keyword">for</span> (<span class="hljs-type">const</span> <span class="hljs-type">char</span>* <span class="hljs-type">const</span>&amp; w : wanted)
    {
        <span class="hljs-keyword">for</span> (vk::LayerProperties <span class="hljs-type">const</span>&amp; i : installed)
        {
            <span class="hljs-keyword">if</span> (std::<span class="hljs-built_in">string</span>(i.layerName).<span class="hljs-built_in">compare</span>(w) == <span class="hljs-number">0</span>)
            {
                out.<span class="hljs-built_in">emplace_back</span>(w);
                <span class="hljs-keyword">break</span>;
            }
        }
    }
}

<span class="hljs-function"><span class="hljs-type">uint32_t</span> <span class="hljs-title">getQueueIndex</span><span class="hljs-params">(vk::PhysicalDevice&amp; physicalDevice,
                       vk::QueueFlagBits flags)</span>
</span>{
    std::vector&lt;vk::QueueFamilyProperties&gt; queueProps =
        physicalDevice.<span class="hljs-built_in">getQueueFamilyProperties</span>();

    <span class="hljs-keyword">for</span> (<span class="hljs-type">size_t</span> i = <span class="hljs-number">0</span>; i &lt; queueProps.<span class="hljs-built_in">size</span>(); ++i)
    {
        <span class="hljs-keyword">if</span> (queueProps[i].queueFlags &amp; flags)
        {
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">uint32_t</span>&gt;(i);
        }
    }

    <span class="hljs-comment">// Default queue index</span>
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}

<span class="hljs-function"><span class="hljs-type">uint32_t</span> <span class="hljs-title">getMemoryTypeIndex</span><span class="hljs-params">(vk::PhysicalDevice&amp; physicalDevice,
                            <span class="hljs-type">uint32_t</span> typeBits,
                            vk::MemoryPropertyFlags properties)</span>
</span>{
    <span class="hljs-keyword">auto</span> gpuMemoryProps = physicalDevice.<span class="hljs-built_in">getMemoryProperties</span>();
    <span class="hljs-keyword">for</span> (<span class="hljs-type">uint32_t</span> i = <span class="hljs-number">0</span>; i &lt; gpuMemoryProps.memoryTypeCount; i++)
    {
        <span class="hljs-keyword">if</span> ((typeBits &amp; <span class="hljs-number">1</span>) == <span class="hljs-number">1</span>)
        {
            <span class="hljs-keyword">if</span> ((gpuMemoryProps.memoryTypes[i].propertyFlags &amp; properties) ==
                properties)
            {
                <span class="hljs-keyword">return</span> i;
            }
        }
        typeBits &gt;&gt;= <span class="hljs-number">1</span>;
    }
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
};</code></pre></div>
<ul>
<li>
<p><strong>Extension</strong> - Anything that adds extra functionality to Vulkan, such as support for Win32 windows, or enabling drawing onto a target.</p>
</li>
<li>
<p><strong>Layer</strong> - Middleware between existing Vulkan functionality, such as checking for errors. Layers can range from runtime debugging checks like LunarG&#x27;s Standard Validation tools to hooks to the Steam renderer so your game can behave better when you <code>Ctrl + Shift</code> to switch to the Steam overlay.</p>
</li>
</ul>
<p>You&#x27;ll want to begin by determining which extensions/layers you want, and compare that with which are available to you by Vulkan.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
vk::Instance instance;

<span class="hljs-comment">// 🔍 Find the best Instance Extensions</span>

std::vector&lt;vk::ExtensionProperties&gt; installedExtensions = vk::<span class="hljs-built_in">enumerateInstanceExtensionProperties</span>();

std::vector&lt;<span class="hljs-type">const</span> <span class="hljs-type">char</span>*&gt; wantedExtensions =
{
  VK_KHR_SURFACE_EXTENSION_NAME,
<span class="hljs-meta">#<span class="hljs-keyword">ifdef</span> VK_USE_PLATFORM_WIN32_KHR</span>
  VK_KHR_WIN32_SURFACE_EXTENSION_NAME
<span class="hljs-meta">#<span class="hljs-keyword">elif</span> VK_USE_PLATFORM_MACOS_MVK</span>
  VK_MVK_MACOS_SURFACE_EXTENSION_NAME
<span class="hljs-meta">#<span class="hljs-keyword">elif</span> VK_USE_PLATFORM_XCB_KHR</span>
  VK_KHR_XCB_SURFACE_EXTENSION_NAME
<span class="hljs-meta">#<span class="hljs-keyword">elif</span> VK_USE_PLATFORM_ANDROID_KHR</span>
  VK_KHR_ANDROID_SURFACE_EXTENSION_NAME
<span class="hljs-meta">#<span class="hljs-keyword">elif</span> VK_USE_PLATFORM_XLIB_KHR</span>
  VK_KHR_XLIB_SURFACE_EXTENSION_NAME
<span class="hljs-meta">#<span class="hljs-keyword">elif</span> VK_USE_PLATFORM_XCB_KHR</span>
  VK_KHR_XCB_SURFACE_EXTENSION_NAME
<span class="hljs-meta">#<span class="hljs-keyword">elif</span> VK_USE_PLATFORM_WAYLAND_KHR</span>
  VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME
<span class="hljs-meta">#<span class="hljs-keyword">elif</span> VK_USE_PLATFORM_MIR_KHR || VK_USE_PLATFORM_DISPLAY_KHR</span>
  VK_KHR_DISPLAY_EXTENSION_NAME
<span class="hljs-meta">#<span class="hljs-keyword">elif</span> VK_USE_PLATFORM_ANDROID_KHR</span>
  VK_KHR_ANDROID_SURFACE_EXTENSION_NAME
<span class="hljs-meta">#<span class="hljs-keyword">elif</span> VK_USE_PLATFORM_IOS_MVK</span>
  VK_MVK_IOS_SURFACE_EXTENSION_NAME
<span class="hljs-meta">#<span class="hljs-keyword">endif</span></span>
};

std::vector&lt;<span class="hljs-type">const</span> <span class="hljs-type">char</span>*&gt; extensions = {};

<span class="hljs-built_in">findBestExtensions</span>(installedExtensions, wantedExtensions, extensions);

<span class="hljs-comment">// 🔎 Find the best Instance Layers</span>

std::vector&lt;vk::LayerProperties&gt; installedLayers =
    vk::<span class="hljs-built_in">enumerateInstanceLayerProperties</span>();

std::vector&lt;<span class="hljs-type">const</span> <span class="hljs-type">char</span>*&gt; wantedLayers = {
<span class="hljs-meta">#<span class="hljs-keyword">ifdef</span> _DEBUG</span>
  <span class="hljs-string">&quot;VK_LAYER_LUNARG_standard_validation&quot;</span>
<span class="hljs-meta">#<span class="hljs-keyword">endif</span></span>
};

std::vector&lt;<span class="hljs-type">const</span> <span class="hljs-type">char</span>*&gt; layers = {};

<span class="hljs-built_in">findBestLayers</span>(installedLayers, wantedLayers, layers);

<span class="hljs-comment">// ⚪ Create an Instance</span>
vk::ApplicationInfo appInfo;
appInfo = {.pApplicationName = <span class="hljs-string">&quot;MyApp&quot;</span>,
            .applicationVersion = <span class="hljs-built_in">VK_MAKE_VERSION</span>(<span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>),
            .pEngineName = <span class="hljs-string">&quot;MyAppEngine&quot;</span>,
            .engineVersion = <span class="hljs-built_in">VK_MAKE_VERSION</span>(<span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>),
            .apiVersion = VK_API_VERSION_1_2};

vk::InstanceCreateInfo ci = vk::<span class="hljs-built_in">InstanceCreateInfo</span>(
    vk::<span class="hljs-built_in">InstanceCreateFlags</span>(), &amp;appInfo, layers, extensions);

vk::Instance instance = vk::<span class="hljs-built_in">createInstance</span>(ci);</code></pre></div>
<h3 id="physical-devices">Physical Devices</h3>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/hardware.svg" alt="Instance Diagram"/></p>
<p>In Vulkan, you have access to all enumerable devices that support it, and can query for information like their name, the number of heaps they support, their manufacturer, etc.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
vk::PhysicalDevice physicalDevice;

<span class="hljs-comment">// 💡 Initialize Devices</span>
std::vector&lt;vk::PhysicalDevice&gt; physicalDevices = instance.<span class="hljs-built_in">enumeratePhysicalDevices</span>();
physicalDevice = physicalDevices[<span class="hljs-number">0</span>];</code></pre></div>
<blockquote>
<p>This is useful for choosing the fastest device to use, however you could use the <code>KHX_device_group</code> extension presented at <a href="https://www.khronos.org/assets/uploads/developers/library/2017-gdc/GDC_Vulkan-on-Desktop_Feb17.pdf">GDC 2017</a> to help with multi-gpu processing.</p>
</blockquote>
<h3 id="logical-devices">Logical Devices</h3>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/logical-device.svg" alt="Logical Devices"/></p>
<p>You can then create a logical device from a physical device handle. A logical device can be loaded with its own extensions/layers, can be set to work with graphics, GPGPU computations, handle sparse memory and/or memory transfers by creating queues for that device.</p>
<p>A logical device is your interface to the GPU, and allows you to allocate data and queue up tasks.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
<span class="hljs-type">uint32_t</span> queueFamilyIndex;
vk::SurfaceKHR surface;
vk::Device device;

<span class="hljs-comment">// 👪 Queue Family</span>
queueFamilyIndex = <span class="hljs-built_in">getQueueIndex</span>(physicalDevice, vk::QueueFlagBits::eGraphics);

<span class="hljs-comment">// ⏹ Get Vulkan Surface with CrossWindowGraphics</span>
surface = xgfx::<span class="hljs-built_in">getSurface</span>(&amp;window, instance);
<span class="hljs-keyword">if</span> (!physicalDevice.<span class="hljs-built_in">getSurfaceSupportKHR</span>(queueFamilyIndex, surface))
{
  <span class="hljs-comment">// Check if queueFamily supports this surface</span>
  <span class="hljs-keyword">return</span>;
}

<span class="hljs-comment">// 📦 Queue Creation</span>
std::vector&lt;vk::DeviceQueueCreateInfo&gt; queueCreateInfos;
<span class="hljs-type">float</span> queuePriority = <span class="hljs-number">0.5f</span>;
vk::DeviceQueueCreateInfo qcinfo;
qcinfo = {.queueFamilyIndex = queueFamilyIndex,
          .queueCount = <span class="hljs-number">1</span>,
          .pQueuePriorities = &amp;queuePriority};
queueCreateInfos.<span class="hljs-built_in">emplace_back</span>(qcinfo);

<span class="hljs-comment">// 🎮 Logical Device</span>
std::vector&lt;vk::ExtensionProperties&gt; installedDeviceExtensions =
    physicalDevice.<span class="hljs-built_in">enumerateDeviceExtensionProperties</span>();

std::vector&lt;<span class="hljs-type">const</span> <span class="hljs-type">char</span>*&gt; wantedDeviceExtensions = {
    VK_KHR_SWAPCHAIN_EXTENSION_NAME
};

std::vector&lt;<span class="hljs-type">const</span> <span class="hljs-type">char</span>*&gt; deviceExtensions = {};

<span class="hljs-built_in">findBestExtensions</span>(installedDeviceExtensions,
                    wantedDeviceExtensions,
                    deviceExtensions);

vk::DeviceCreateInfo dinfo = {{}, queueCreateInfos, deviceExtensions};
device = physicalDevice.<span class="hljs-built_in">createDevice</span>(dinfo);</code></pre></div>
<h3 id="queue">Queue</h3>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/queue.svg" alt="Queue"/></p>
<p>Once you have a virtual device, you can access the queues you requested when you created it:</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
vk::Queue queue;

<span class="hljs-comment">// 📦 We only allocated one queue earlier,</span>
<span class="hljs-comment">//so there&#x27;s only one available on index 0.</span>
queue = device.<span class="hljs-built_in">getQueue</span>(queueFamilyIndex, <span class="hljs-number">0</span>);</code></pre></div>
<p>If your application is idle for too long, the Vulkan API will throw a <code>vk::OutOfDateKHRError</code> error, requiring you to re-initialize your graphics API.</p>
<h3 id="command-pool">Command Pool</h3>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/command-pool.svg" alt="Pool of Commands"/></p>
<p>A <strong>command pool</strong> is a means of allocating command buffers. Any number of command buffers can be made from command pools, with you as the developer responsible for managing when and how they&#x27;re created and what is loaded in each.</p>
<p>A command pool cannot be used in multiple threads, but you can create one for each thread and manage them on a per thread level.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
vk::CommandPool commandPool;

<span class="hljs-comment">// 🏊 Create a command pool</span>
vk::CommandPoolCreateInfo commandPoolInfo = vk::<span class="hljs-built_in">CommandPoolCreateInfo</span>(
  vk::<span class="hljs-built_in">CommandPoolCreateFlags</span>(vk::CommandPoolCreateFlagBits::eResetCommandBuffer),
  queueFamilyIndex
);
commandPool = device.<span class="hljs-built_in">createCommandPool</span>(commandPoolInfo);

<span class="hljs-comment">// Later, once your ⛓️ vk::Swapchain has been created</span>

<span class="hljs-comment">// Lets allocate 1 command buffer for each swapchain image.</span>
std::vector&lt;vk::CommandBuffer&gt; commandBuffers = device.<span class="hljs-built_in">allocateCommandBuffers</span>(
  vk::<span class="hljs-built_in">CommandBufferAllocateInfo</span>(
    commandPool,
    vk::CommandBufferLevel::ePrimary,
    swapchainBuffers.<span class="hljs-built_in">size</span>()
  )
);</code></pre></div>
<h3 id="descriptor-pool">Descriptor Pool</h3>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/descriptor-pool.svg" alt="Descriptor Pool"/></p>
<p>A descriptor pool is a means of allocating Descriptor Sets, a set of data structures containing implementation-specific descriptions of resources. to make a descriptor pool, you need to describe exactly how many of each type of descriptor you need to allocate.</p>
<p>To do that you need to provide a collection of the size of each descriptor type.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
vk::DescriptorPool descriptorPool;

std::vector&lt;vk::DescriptorPoolSize&gt; dpsizes =
{
  vk::<span class="hljs-built_in">DescriptorPoolSize</span>(
  vk::DescriptorType::eUniformBuffer,
  <span class="hljs-number">1</span>
  )
};

<span class="hljs-comment">// 🎱 Create Descriptor Pool</span>
<span class="hljs-function">vk::DescriptorPoolCreateInfo <span class="hljs-title">dpci</span><span class="hljs-params">({}, <span class="hljs-number">1</span>, dpsizes)</span></span>;
descriptorPool = device.<span class="hljs-built_in">createDescriptorPool</span>(dpci);</code></pre></div>
<p>Like command buffers, we&#x27;ll come back to descriptor sets later.</p>
<blockquote>
<p>While these work well enough, using bindless resources is significantly more easy, <a href="https://therealmjp.github.io/">Matt Pettineo</a> (<a href="https://twitter.com/mynameismjp">@MyNameIsMJP</a>) <a href="https://therealmjp.github.io/posts/rtg2-bindless/">wrote a chapter in Ray Tracing Gems 2 about this</a>.</p>
</blockquote>
<h3 id="color-formats">Color Formats</h3>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/color-formats.svg" alt="Diagram of Pixels and Data Encoding"/></p>
<p>Knowing what Color formats your GPU supports will play a crucial role in determining what you can display and what kind of buffers you can allocate.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
vk::SurfaceFormatKHR surfaceColorFormat;
vk::ColorSpaceKHR surfaceColorSpace;
vk::Format surfaceDepthFormat;

<span class="hljs-comment">// 🔴🟢🔵 Check to see if we can display rgb colors.</span>
std::vector&lt;vk::SurfaceFormatKHR&gt; surfaceFormats = physicalDevice.<span class="hljs-built_in">getSurfaceFormatsKHR</span>(surface);

<span class="hljs-keyword">if</span> (surfaceFormats.<span class="hljs-built_in">size</span>() == <span class="hljs-number">1</span> &amp;&amp; surfaceFormats[<span class="hljs-number">0</span>].format == vk::Format::eUndefined)
  surfaceColorFormat = vk::Format::eB8G8R8A8Unorm;
<span class="hljs-keyword">else</span>
  surfaceColorFormat = surfaceFormats[<span class="hljs-number">0</span>].format;

surfaceColorSpace = surfaceFormats[<span class="hljs-number">0</span>].colorSpace;

<span class="hljs-comment">// Since all depth formats may be optional, we need to find a suitable depth format to use</span>
<span class="hljs-comment">// Start with the highest precision packed format</span>
std::vector&lt;vk::Format&gt; depthFormats =
{
  vk::Format::eD32SfloatS8Uint,
  vk::Format::eD32Sfloat,
  vk::Format::eD24UnormS8Uint,
  vk::Format::eD16UnormS8Uint,
  vk::Format::eD16Unorm
};

<span class="hljs-keyword">for</span> (vk::Format&amp; format : depthFormats)
{
  vk::FormatProperties depthFormatProperties = physicalDevice.<span class="hljs-built_in">getFormatProperties</span>(format);

  <span class="hljs-comment">// Format must support depth stencil attachment for optimal tiling</span>
  <span class="hljs-keyword">if</span> (depthFormatProperties.optimalTilingFeatures &amp; vk::FormatFeatureFlagBits::eDepthStencilAttachment)
  {
    surfaceDepthFormat = format;
    <span class="hljs-keyword">break</span>;
  }
}</code></pre></div>
<h3 id="swapchain">Swapchain</h3>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/swapchain.svg" alt="Swapchain Diagram"/></p>
<p>A <strong>Swapchain</strong> is a structure that manages the allocation of frame buffers to be cycled through by your application. It&#x27;s here that your application sets up V-Sync via double buffering or triple buffering.</p>
<blockquote>
<p>One approach to setting this up is to take in a JSON file at the start of your application, say <code>config.json</code>, which determines if you&#x27;ll be using V-Sync, your screen resolution, any any other global data you want to configure.</p>
</blockquote>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
vk::Rect2D renderArea;
vk::Extent2D surfaceSize;
vk::Viewport viewport;
vk::SwapchainKHR swapchain;

<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">setupSwapchain</span><span class="hljs-params">(<span class="hljs-type">unsigned</span> width, <span class="hljs-type">unsigned</span> height)</span>
</span>{
  <span class="hljs-comment">// Setup viewports, vsync</span>
  vk::Extent2D swapchainSize = vk::<span class="hljs-built_in">Extent2D</span>(width, height);

  <span class="hljs-comment">// All framebuffers / attachments will be the same size as the surface</span>
  vk::SurfaceCapabilitiesKHR surfaceCapabilities = physicalDevice.<span class="hljs-built_in">getSurfaceCapabilitiesKHR</span>(surface);
  <span class="hljs-keyword">if</span> (!(surfaceCapabilities.currentExtent.width == <span class="hljs-number">-1</span> || surfaceCapabilities.currentExtent.height == <span class="hljs-number">-1</span>)) {
    swapchainSize = surfaceCapabilities.currentExtent;
    renderArea = vk::<span class="hljs-built_in">Rect2D</span>(vk::<span class="hljs-built_in">Offset2D</span>(), swapchainSize);
    viewport = vk::<span class="hljs-built_in">Viewport</span>(<span class="hljs-number">0.0f</span>, <span class="hljs-number">0.0f</span>, <span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">float</span>&gt;(swapchainSize.width), <span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">float</span>&gt;(swapchainSize.height), <span class="hljs-number">0</span>, <span class="hljs-number">1.0f</span>);
  }

  <span class="hljs-comment">// VSync</span>
  std::vector&lt;vk::PresentModeKHR&gt; surfacePresentModes = physicalDevice.<span class="hljs-built_in">getSurfacePresentModesKHR</span>(surface);
  vk::PresentModeKHR presentMode = vk::PresentModeKHR::eImmediate;

  <span class="hljs-keyword">for</span> (vk::PresentModeKHR&amp; pm : surfacePresentModes) {
    <span class="hljs-keyword">if</span> (pm == vk::PresentModeKHR::eMailbox) {
      presentMode = vk::PresentModeKHR::eMailbox;
      <span class="hljs-keyword">break</span>;
    }
  }

  <span class="hljs-comment">// ⛓️ Create Swapchain, Images, Frame Buffers</span>
  device.<span class="hljs-built_in">waitIdle</span>();
  vk::SwapchainKHR oldSwapchain = swapchain;

  <span class="hljs-comment">// Some devices can support more than 2 buffers,</span>
  <span class="hljs-comment">// but during my tests they would crash on fullscreen</span>
  <span class="hljs-comment">// Tested on an NVIDIA 1080 and 165 Hz 2K display ~ @alainxyz</span>
  <span class="hljs-type">uint32_t</span> backbufferCount = std::<span class="hljs-built_in">clamp</span>(surfaceCapabilities.maxImageCount, <span class="hljs-number">1U</span>, <span class="hljs-number">2U</span>);

  swapchain = device.<span class="hljs-built_in">createSwapchainKHR</span>(
    vk::<span class="hljs-built_in">SwapchainCreateInfoKHR</span>(
      vk::<span class="hljs-built_in">SwapchainCreateFlagsKHR</span>(),
      surface,
      backbufferCount,
      surfaceColorFormat,
      surfaceColorSpace,
      swapchainSize,
      <span class="hljs-number">1</span>,
      vk::ImageUsageFlagBits::eColorAttachment,
      vk::SharingMode::eExclusive,
      <span class="hljs-number">1</span>,
      &amp;queueFamilyIndex,
      vk::SurfaceTransformFlagBitsKHR::eIdentity,
      vk::CompositeAlphaFlagBitsKHR::eOpaque,
      presentMode,
      VK_TRUE,
      oldSwapchain
    )
  );

  surfaceSize = vk::<span class="hljs-built_in">Extent2D</span>(std::<span class="hljs-built_in">clamp</span>(swapchainSize.width, <span class="hljs-number">1U</span>, <span class="hljs-number">8192U</span>), std::<span class="hljs-built_in">clamp</span>(swapchainSize.height, <span class="hljs-number">1U</span>, <span class="hljs-number">8192U</span>));
  renderArea = vk::<span class="hljs-built_in">Rect2D</span>(vk::<span class="hljs-built_in">Offset2D</span>(), surfaceSize);
  viewport = vk::<span class="hljs-built_in">Viewport</span>(<span class="hljs-number">0.0f</span>, <span class="hljs-number">0.0f</span>, <span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">float</span>&gt;(surfaceSize.width), <span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">float</span>&gt;(surfaceSize.height), <span class="hljs-number">0</span>, <span class="hljs-number">1.0f</span>);


  <span class="hljs-comment">// Destroy previous swapchain</span>
  <span class="hljs-keyword">if</span> (oldSwapchain != vk::<span class="hljs-built_in">SwapchainKHR</span>(<span class="hljs-literal">nullptr</span>))
  {
    device.<span class="hljs-built_in">destroySwapchainKHR</span>(oldSwapchain);
  }

  <span class="hljs-comment">// Resize swapchain buffers for use later</span>
  swapchainBuffers.<span class="hljs-built_in">resize</span>(backbufferCount);
}</code></pre></div>
<h3 id="view-structures">View Structures</h3>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/image-view.svg" alt="Image View"/></p>
<p>A <strong>View</strong> in Vulkan is a handle to a particular resource on a GPU, such as an Image or a Buffer, and provides information on how that resource should be processed.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
vk::ImageView depthImageView;

depthImageView = device.<span class="hljs-built_in">createImageView</span>(
  vk::<span class="hljs-built_in">ImageViewCreateInfo</span>(
    vk::<span class="hljs-built_in">ImageViewCreateFlags</span>(),
    depthImage,
    vk::ImageViewType::e2D,
    surfaceDepthFormat,
    vk::<span class="hljs-built_in">ComponentMapping</span>(),
    vk::<span class="hljs-built_in">ImageSubresourceRange</span>(
      vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil,
      <span class="hljs-number">0</span>,
      <span class="hljs-number">1</span>,
      <span class="hljs-number">0</span>,
      <span class="hljs-number">1</span>
    )
  )
);</code></pre></div>
<h3 id="render-pass">Render Pass</h3>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/render-pass.svg" alt="Render Pass Diagram"/></p>
<p>A render pass describes the attachments that are expected to be used when executing a graphics pipeline and their relationship with each other. This can be useful in tile based rendering for having information in advance to better optimize cache flushes.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
vk::RenderPass renderPass;

<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">createRenderPass</span><span class="hljs-params">()</span>
</span>{
  std::vector&lt;vk::AttachmentDescription&gt; attachmentDescriptions =
  {
    vk::<span class="hljs-built_in">AttachmentDescription</span>(
      vk::<span class="hljs-built_in">AttachmentDescriptionFlags</span>(),
      surfaceColorFormat,
      vk::SampleCountFlagBits::e1,
      vk::AttachmentLoadOp::eClear,
      vk::AttachmentStoreOp::eStore,
      vk::AttachmentLoadOp::eDontCare,
      vk::AttachmentStoreOp::eDontCare,
      vk::ImageLayout::eUndefined,
      vk::ImageLayout::ePresentSrcKHR
    ),
    vk::<span class="hljs-built_in">AttachmentDescription</span>(
      vk::<span class="hljs-built_in">AttachmentDescriptionFlags</span>(),
      surfaceDepthFormat,
      vk::SampleCountFlagBits::e1,
      vk::AttachmentLoadOp::eClear,
      vk::AttachmentStoreOp::eDontCare,
      vk::AttachmentLoadOp::eDontCare,
      vk::AttachmentStoreOp::eDontCare,
      vk::ImageLayout::eUndefined,
      vk::ImageLayout::eDepthStencilAttachmentOptimal
    )
  };

  std::vector&lt;vk::AttachmentReference&gt; colorReferences =
  {
    vk::<span class="hljs-built_in">AttachmentReference</span>(<span class="hljs-number">0</span>, vk::ImageLayout::eColorAttachmentOptimal)
  };

  std::vector&lt;vk::AttachmentReference&gt; depthReferences = {
    vk::<span class="hljs-built_in">AttachmentReference</span>(<span class="hljs-number">1</span>, vk::ImageLayout::eDepthStencilAttachmentOptimal)
  };

  std::vector&lt;vk::SubpassDescription&gt; subpasses =
  {
    vk::<span class="hljs-built_in">SubpassDescription</span>(
      vk::<span class="hljs-built_in">SubpassDescriptionFlags</span>(),
      vk::PipelineBindPoint::eGraphics,
      <span class="hljs-number">0</span>,
      <span class="hljs-literal">nullptr</span>,
      <span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">uint32_t</span>&gt;(colorReferences.<span class="hljs-built_in">size</span>()),
      colorReferences.<span class="hljs-built_in">data</span>(),
      <span class="hljs-literal">nullptr</span>,
      depthReferences.<span class="hljs-built_in">data</span>(),
      <span class="hljs-number">0</span>,
      <span class="hljs-literal">nullptr</span>
    )
  };

  std::vector&lt;vk::SubpassDependency&gt; dependencies =
  {
    vk::<span class="hljs-built_in">SubpassDependency</span>(
      ~<span class="hljs-number">0U</span>,
      <span class="hljs-number">0</span>,
      vk::PipelineStageFlagBits::eBottomOfPipe,
      vk::PipelineStageFlagBits::eColorAttachmentOutput,
      vk::AccessFlagBits::eMemoryRead,
      vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite,
      vk::DependencyFlagBits::eByRegion
    ),
    vk::<span class="hljs-built_in">SubpassDependency</span>(
      <span class="hljs-number">0</span>,
      ~<span class="hljs-number">0U</span>,
      vk::PipelineStageFlagBits::eColorAttachmentOutput,
      vk::PipelineStageFlagBits::eBottomOfPipe,
      vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite,
      vk::AccessFlagBits::eMemoryRead,
      vk::DependencyFlagBits::eByRegion
    )
  };

  renderPass = device.<span class="hljs-built_in">createRenderPass</span>(
    vk::<span class="hljs-built_in">RenderPassCreateInfo</span>(
      vk::<span class="hljs-built_in">RenderPassCreateFlags</span>(),
      <span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">uint32_t</span>&gt;(attachmentDescriptions.<span class="hljs-built_in">size</span>()),
      attachmentDescriptions.<span class="hljs-built_in">data</span>(),
      <span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">uint32_t</span>&gt;(subpasses.<span class="hljs-built_in">size</span>()),
      subpasses.<span class="hljs-built_in">data</span>(),
      <span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">uint32_t</span>&gt;(dependencies.<span class="hljs-built_in">size</span>()),
      dependencies.<span class="hljs-built_in">data</span>()
    )
  );
}</code></pre></div>
<h3 id="frame-buffers">Frame Buffers</h3>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/frame-buffer.svg" alt="Frame Buffer Diagram with attachments"/></p>
<p>A frame buffer in Vulkan is a container of Image Views that are bound to a specific render pass.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// ⛓️ The swapchain handles allocating frame images.</span>
std::vector&lt;vk::Image&gt; swapchainImages = device.<span class="hljs-built_in">getSwapchainImagesKHR</span>(swapchain);

<span class="hljs-comment">// ↘️ Create Depth Image Data</span>
vk::Image depthImage = device.<span class="hljs-built_in">createImage</span>(
  vk::<span class="hljs-built_in">ImageCreateInfo</span>(
    vk::<span class="hljs-built_in">ImageCreateFlags</span>(),
    vk::ImageType::e2D,
    surfaceDepthFormat,
    vk::<span class="hljs-built_in">Extent3D</span>(surfaceSize.width, surfaceSize.height, <span class="hljs-number">1</span>),
    <span class="hljs-number">1</span>,
    <span class="hljs-number">1</span>,
    vk::SampleCountFlagBits::e1,
    vk::ImageTiling::eOptimal,
    vk::ImageUsageFlagBits::eDepthStencilAttachment | vk::ImageUsageFlagBits::eTransferSrc,
    vk::SharingMode::eExclusive,
    queueFamilyIndices.<span class="hljs-built_in">size</span>(),
    queueFamilyIndices.<span class="hljs-built_in">data</span>(),
    vk::ImageLayout::eUndefined
  )
);

<span class="hljs-comment">// Search through GPU memory properties to see if this can be device local.</span>

vk::MemoryRequirements depthMemoryReq = device.<span class="hljs-built_in">getImageMemoryRequirements</span>(depthImage);
vk::DeviceMemory depthMemory = device.<span class="hljs-built_in">allocateMemory</span>(vk::<span class="hljs-built_in">MemoryAllocateInfo</span>(
depthMemoryReq.size,
<span class="hljs-built_in">getMemoryTypeIndex</span>(physicalDevice, depthMemoryReq.memoryTypeBits,
                    vk::MemoryPropertyFlagBits::eDeviceLocal)));


device.<span class="hljs-built_in">bindImageMemory</span>(
  depthImage,
  depthMemory,
  <span class="hljs-number">0</span>
);

vk::ImageView depthImageView = device.<span class="hljs-built_in">createImageView</span>(
  vk::<span class="hljs-built_in">ImageViewCreateInfo</span>(
    vk::<span class="hljs-built_in">ImageViewCreateFlags</span>(),
    depthImage,
    vk::ImageViewType::e2D,
    surfaceDepthFormat,
    vk::<span class="hljs-built_in">ComponentMapping</span>(),
    vk::<span class="hljs-built_in">ImageSubresourceRange</span>(
      vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil,
      <span class="hljs-number">0</span>,
      <span class="hljs-number">1</span>,
      <span class="hljs-number">0</span>,
      <span class="hljs-number">1</span>
    )
  )
);

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">SwapChainBuffer</span> {
  vk::Image image;
  std::array&lt;vk::ImageView, 2&gt; views;
  vk::Framebuffer frameBuffer;
};

std::vector&lt;SwapChainBuffer&gt; swapchainBuffers;
swapchainBuffers.<span class="hljs-built_in">resize</span>(swapchainImages.<span class="hljs-built_in">size</span>());

<span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; swapchainImages.<span class="hljs-built_in">size</span>(); i++)
{
  swapchainBuffers[i].image = swapchainImages[i];

  <span class="hljs-comment">// 🌈 Color</span>
  swapchainBuffers[i].views[<span class="hljs-number">0</span>] =
    device.<span class="hljs-built_in">createImageView</span>(
      vk::<span class="hljs-built_in">ImageViewCreateInfo</span>(
        vk::<span class="hljs-built_in">ImageViewCreateFlags</span>(),
        swapchainImages[i],
        vk::ImageViewType::e1D,
        surfaceColorFormat,
        vk::<span class="hljs-built_in">ComponentMapping</span>(),
        vk::<span class="hljs-built_in">ImageSubresourceRange</span>(
          vk::ImageAspectFlagBits::eColor,
          <span class="hljs-number">0</span>,
          <span class="hljs-number">1</span>,
          <span class="hljs-number">0</span>,
          <span class="hljs-number">1</span>
        )
      )
    );

  <span class="hljs-comment">// ↘️ Depth</span>
  swapchainBuffers[i].views[<span class="hljs-number">1</span>] = depthImageView;

  swapchainBuffers[i].frameBuffer = device.<span class="hljs-built_in">createFramebuffer</span>(
    vk::<span class="hljs-built_in">FramebufferCreateInfo</span>(
      vk::<span class="hljs-built_in">FramebufferCreateFlags</span>(),
      renderPass,
      swapchainBuffers[i].views.<span class="hljs-built_in">size</span>(),
      swapchainBuffers[i].views.<span class="hljs-built_in">data</span>(),
      surfaceSize.width,
      surfaceSize.height,
      <span class="hljs-number">1</span>
    )
  );
}</code></pre></div>
<h3 id="synchronization">Synchronization</h3>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/semaphore-fence.svg" alt="Semaphore and Fence Diagram"/></p>
<blockquote>
<p>You could say Pipeline Barriers are the most powerful part of the Vulkan API, since it allows for granular control over preventing data races. ~ Charles Giessen (<a href="https://twitter.com/charlesgiessen">@charlesgiessen</a>)</p>
</blockquote>
<p>Vulkan was designed with concurrency in mind, and features 3 primitives for this, <strong>Semaphores</strong>, <strong>Fences</strong>, and programmable <strong>Barriers</strong>.</p>
<p><strong>Semaphores</strong> coordinate operations within the GPU by introducing dependencies between operations.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 🎌 Semaphore used to ensures that image presentation is complete before starting to submit again</span>
vk::Semaphore presentCompleteSemaphore = device.<span class="hljs-built_in">createSemaphore</span>(vk::<span class="hljs-built_in">SemaphoreCreateInfo</span>());

<span class="hljs-comment">// 🎌 Semaphore used to ensures that all commands submitted have been finished before submitting the image to the queue</span>
vk::Semaphore renderCompleteSemaphore = device.<span class="hljs-built_in">createSemaphore</span>(vk::<span class="hljs-built_in">SemaphoreCreateInfo</span>());</code></pre></div>
<p><strong>Fences</strong> are objects used to synchronize the CPU and GPU, allowing the CPU to be alerted when events have finished such as loading resources.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 🚧 Fence for command buffer completion</span>
std::vector&lt;vk::Fence&gt; waitFences;
waitFences.<span class="hljs-built_in">resize</span>(swapchainBuffers.<span class="hljs-built_in">size</span>());
<span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; waitFences.<span class="hljs-built_in">size</span>(); i++)
{
  waitFences[i] = device.<span class="hljs-built_in">createFence</span>(vk::<span class="hljs-built_in">FenceCreateInfo</span>(vk::FenceCreateFlagBits::eSignaled));
}</code></pre></div>
<p>Synchronization primitives can be used in a variety of queue operations.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 💬 Usage in Command Buffer</span>
vk::Result result;
vk::PipelineStageFlags waitDstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
<span class="hljs-function">vk::SubmitInfo <span class="hljs-title">submitInfo</span><span class="hljs-params">(<span class="hljs-number">1</span>, &amp;presentCompleteSemaphore, &amp;waitDstStageMask,
                          <span class="hljs-number">1</span>, &amp;commandBuffers[currentBuffer], <span class="hljs-number">1</span>,
                          &amp;renderCompleteSemaphore)</span></span>;
result = queue.<span class="hljs-built_in">submit</span>(<span class="hljs-number">1</span>, &amp;submitInfo, waitFences[currentBuffer]);

result = queue.<span class="hljs-built_in">presentKHR</span>(
  vk::<span class="hljs-built_in">PresentInfoKHR</span>(
    <span class="hljs-number">1</span>,
    &amp;renderCompleteSemaphore,
    <span class="hljs-number">1</span>,
    &amp;swapchain,
    &amp;currentBuffer,
    <span class="hljs-literal">nullptr</span>
  )
);</code></pre></div>
<blockquote>
<p>Though there exists 2 primitive objects, there are <em>functions</em> that also help with synchronization as well, such as <strong>pipeline barriers</strong> which offer granular control over synchronization within command buffers. We&#x27;re not using any in this example but keep them in mind for your applications!</p>
</blockquote>
<h2 id="initialize-resources">Initialize Resources</h2>
<h3 id="vertex-buffers">Vertex Buffers</h3>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/vertex-buffer.svg" alt="Vertex Buffer Diagram"/></p>
<p>The fundamental problem of graphics is how to manage large sets of data. A vertex buffer is an array of rows of relevant vertex information, such as its position, normal, color, etc. Unlike OpenGL where it would handle allocation and handling memory for you, in Vulkan, you must:</p>
<ol>
<li><strong>Allocate</strong> all the memory related to your buffer.</li>
<li><strong>Map</strong> that data to a host visible handle.</li>
<li><strong>Copy</strong> that data to your GPU.</li>
<li><strong>Bind</strong> your buffer to that block of memory.</li>
</ol>
<p>For buffers that you want as GPU accessible only, you&#x27;ll need to also copy that buffer to a GPU exclusive buffer.</p>
<h3 id="descriptor-sets">Descriptor Sets</h3>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/descriptor-sets.svg" alt="Descriptor Set"/></p>
<p><strong>Descriptor Sets</strong> describe the resources bound to the binding points in a shader (basically <em>uniforms</em>). They connect the binding points of a shader with the buffers and images used for those bindings.</p>
<p>Descriptor sets are composed of <strong>Descriptor Set Layouts</strong>, which are then composed of <strong>Descriptor Set Bindings</strong>, the individual bindings a uniform has. Often these are organized as different resource types.</p>
<p>In Vulkan, Uniforms must be contiguous structs of data that are multiples of <code>128</code> bits (So SIMD vector sized blocks).</p>
<blockquote>
<p>In <a href="https://github.com/acdlite/react-fiber-architecture">Facebook&#x27;s React Fiber engine</a> there&#x27;s the idea of a frequently updated view and a not frequently updated view. Unreal Engine 4 shares this with two global uniform families for frequently (called variable parameters) and not frequently (constant parameters) updated uniforms. Descriptor Sets are where you would make this distinction in Vulkan.</p>
</blockquote>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
vk::DescriptorBufferInfo descriptor;

<span class="hljs-comment">// Binding 0: Uniform buffer (Vertex shader)</span>
std::vector&lt;vk::DescriptorSetLayoutBinding&gt; descriptorSetLayoutBindings =
{
  vk::<span class="hljs-built_in">DescriptorSetLayoutBinding</span>(
    <span class="hljs-number">0</span>,
    vk::DescriptorType::eUniformBuffer,
    <span class="hljs-number">1</span>,
    vk::ShaderStageFlagBits::eVertex,
    <span class="hljs-literal">nullptr</span>
  )
};

std::vector&lt;vk::DescriptorSetLayout&gt; descriptorSetLayouts = {
  device.<span class="hljs-built_in">createDescriptorSetLayout</span>(
    vk::<span class="hljs-built_in">DescriptorSetLayoutCreateInfo</span>(
      vk::<span class="hljs-built_in">DescriptorSetLayoutCreateFlags</span>(),
      descriptorSetLayoutBindings.<span class="hljs-built_in">size</span>(),
      descriptorSetLayoutBindings.<span class="hljs-built_in">data</span>()
  )
  )
};

std::vector&lt;vk::DescriptorSet&gt; descriptorSets = device.<span class="hljs-built_in">allocateDescriptorSets</span>(
  vk::<span class="hljs-built_in">DescriptorSetAllocateInfo</span>(
    descriptorPool,
    descriptorSetLayouts.<span class="hljs-built_in">size</span>(),
    descriptorSetLayouts.<span class="hljs-built_in">data</span>()
  )
);

<span class="hljs-comment">// 💪 Update c</span>

std::vector&lt;vk::WriteDescriptorSet&gt; descriptorWrites =
{
  vk::<span class="hljs-built_in">WriteDescriptorSet</span>(
    descriptorSets[<span class="hljs-number">0</span>],
    <span class="hljs-number">0</span>,
    <span class="hljs-number">0</span>,
    <span class="hljs-number">1</span>,
    vk::DescriptorType::eUniformBuffer,
    <span class="hljs-literal">nullptr</span>,
    &amp;descriptor,
    <span class="hljs-literal">nullptr</span>
  )
};

<span class="hljs-comment">// Update</span>

device.<span class="hljs-built_in">updateDescriptorSets</span>(descriptorWrites, <span class="hljs-literal">nullptr</span>);

<span class="hljs-comment">// Bind at command buffer generation</span>

cmd.<span class="hljs-built_in">bindDescriptorSets</span>(
   vk::PipelineBindPoint::eGraphics,
   pipelineLayout,
   <span class="hljs-number">0</span>,
   descriptorSets,
   <span class="hljs-literal">nullptr</span>
  );</code></pre></div>
<h3 id="pipeline-layouts">Pipeline Layouts</h3>
<p><strong>Pipeline layouts</strong> are a collection of descriptor sets, the bindings to a shader program. In Vulkan in order to bind a shader to a set of data, you needed to describe how the inputs and outputs are organized as inputs.</p>
<p>Access to descriptor sets from a pipeline is accomplished through a pipeline layout. Zero or more descriptor set layouts and zero or more push constant ranges are combined to form a pipeline layout object which describes the complete set of resources that can be accessed by a pipeline.</p>
<p>A pipeline layout represents a sequence of descriptor sets with each having a specific layout. This sequence of layouts is used to determine the interface between shader stages and shader resources.</p>
<p>A Graphics Pipeline is created using a pipeline layout.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
vk::PipelineLayout pipelineLayout;

vk::PipelineLayoutCreateInfo plci = {{},descriptorSetLayouts, {}};
pipelineLayout = device.<span class="hljs-built_in">createPipelineLayout</span>(plci);

<span class="hljs-comment">// 💪 Usage</span>

cmd.<span class="hljs-built_in">bindDescriptorSets</span>(
  vk::PipelineBindPoint::eGraphics,
  pipelineLayout,
  <span class="hljs-number">0</span>,
  descriptorSets,
  <span class="hljs-literal">nullptr</span>
);
</code></pre></div>
<h3 id="pipeline-state-objects">Pipeline State Objects</h3>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/raster-pipeline.svg" alt="Different Pipeline State Objects"/></p>
<p>Pipelines are basically a mix of hardware and software functions that do a particular task on the GPU, in Vulkan, there&#x27;s 4 types:</p>
<ul>
<li>Graphics Pipelines</li>
<li>Compute Pipelines</li>
<li>Ray-Tracing Pipelines</li>
<li>Tensor Pipelines</li>
</ul>
<h4 id="graphics-pipeline">Graphics Pipeline</h4>
<ul>
<li>
<p><strong>Color Blending</strong> - The function that controls how two objects draw on top of each other.</p>
</li>
<li>
<p><strong>Depth Stencil</strong> - A extra piece of information that describes depth information.</p>
</li>
<li>
<p><strong>Vertex Input</strong> - The actual vertex data you&#x27;ll be using in your shader.</p>
</li>
<li>
<p><strong>Shaders</strong> - What shaders will be loaded in.</p>
</li>
</ul>
<p>And many more. These can even be cached! These particular draw calls are grouped such that in older graphics APIs, they would trigger shader recompilation.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// Create Graphics Pipeline</span>

  std::vector&lt;<span class="hljs-type">char</span>&gt; vertShaderCode = <span class="hljs-built_in">readFile</span>(<span class="hljs-string">&quot;assets/triangle.vert.spv&quot;</span>);
  std::vector&lt;<span class="hljs-type">char</span>&gt; fragShaderCode = <span class="hljs-built_in">readFile</span>(<span class="hljs-string">&quot;assets/triangle.frag.spv&quot;</span>);

  vertModule = device.<span class="hljs-built_in">createShaderModule</span>(
    vk::<span class="hljs-built_in">ShaderModuleCreateInfo</span>(
      vk::<span class="hljs-built_in">ShaderModuleCreateFlags</span>(),
      vertShaderCode.<span class="hljs-built_in">size</span>(),
      (<span class="hljs-type">uint32_t</span>*)vertShaderCode.<span class="hljs-built_in">data</span>()
    )
  );

  fragModule = device.<span class="hljs-built_in">createShaderModule</span>(
    vk::<span class="hljs-built_in">ShaderModuleCreateInfo</span>(
      vk::<span class="hljs-built_in">ShaderModuleCreateFlags</span>(),
      fragShaderCode.<span class="hljs-built_in">size</span>(),
      (<span class="hljs-type">uint32_t</span>*)fragShaderCode.<span class="hljs-built_in">data</span>()
    )
  );

  pipelineCache = device.<span class="hljs-built_in">createPipelineCache</span>(vk::<span class="hljs-built_in">PipelineCacheCreateInfo</span>());

  std::vector&lt;vk::PipelineShaderStageCreateInfo&gt; pipelineShaderStages = {
    vk::<span class="hljs-built_in">PipelineShaderStageCreateInfo</span>(
      vk::<span class="hljs-built_in">PipelineShaderStageCreateFlags</span>(),
      vk::ShaderStageFlagBits::eVertex,
      vertModule,
      <span class="hljs-string">&quot;main&quot;</span>,
      <span class="hljs-literal">nullptr</span>
    ),
    vk::<span class="hljs-built_in">PipelineShaderStageCreateInfo</span>(
      vk::<span class="hljs-built_in">PipelineShaderStageCreateFlags</span>(),
      vk::ShaderStageFlagBits::eFragment,
      fragModule,
      <span class="hljs-string">&quot;main&quot;</span>,
      <span class="hljs-literal">nullptr</span>
    )
  };

  vk::PipelineVertexInputStateCreateInfo pvi = vertices.inputState;

  <span class="hljs-function">vk::PipelineInputAssemblyStateCreateInfo <span class="hljs-title">pia</span><span class="hljs-params">(
    vk::PipelineInputAssemblyStateCreateFlags(),
    vk::PrimitiveTopology::eTriangleList
  )</span></span>;

  <span class="hljs-function">vk::PipelineViewportStateCreateInfo <span class="hljs-title">pv</span><span class="hljs-params">(
    vk::PipelineViewportStateCreateFlagBits(),
    <span class="hljs-number">1</span>,
    &amp;viewport,
    <span class="hljs-number">1</span>,
    &amp;renderArea
  )</span></span>;

  <span class="hljs-function">vk::PipelineRasterizationStateCreateInfo <span class="hljs-title">pr</span><span class="hljs-params">(
    vk::PipelineRasterizationStateCreateFlags(),
    VK_FALSE,
    VK_FALSE,
    vk::PolygonMode::eFill,
    vk::CullModeFlagBits::eNone,
    vk::FrontFace::eCounterClockwise,
    VK_FALSE,
    <span class="hljs-number">0</span>,
    <span class="hljs-number">0</span>,
    <span class="hljs-number">0</span>,
    <span class="hljs-number">1.0f</span>
  )</span></span>;

  <span class="hljs-function">vk::PipelineMultisampleStateCreateInfo <span class="hljs-title">pm</span><span class="hljs-params">(
    vk::PipelineMultisampleStateCreateFlags(),
    vk::SampleCountFlagBits::e1
  )</span></span>;

  <span class="hljs-comment">// Dept and Stencil state for primative compare/test operations</span>

  vk::PipelineDepthStencilStateCreateInfo pds = vk::<span class="hljs-built_in">PipelineDepthStencilStateCreateInfo</span>(
    vk::<span class="hljs-built_in">PipelineDepthStencilStateCreateFlags</span>(),
    VK_TRUE,
    VK_TRUE,
    vk::CompareOp::eLessOrEqual,
    VK_FALSE,
    VK_FALSE,
    vk::<span class="hljs-built_in">StencilOpState</span>(),
    vk::<span class="hljs-built_in">StencilOpState</span>(),
    <span class="hljs-number">0</span>,
    <span class="hljs-number">0</span>
  );

  <span class="hljs-comment">// Blend State - How two primatives should draw on top of each other.</span>
  std::vector&lt;vk::PipelineColorBlendAttachmentState&gt; colorBlendAttachments =
  {
    vk::<span class="hljs-built_in">PipelineColorBlendAttachmentState</span>(
      VK_FALSE,
      vk::BlendFactor::eZero,
      vk::BlendFactor::eOne,
      vk::BlendOp::eAdd,
      vk::BlendFactor::eZero,
      vk::BlendFactor::eZero,
      vk::BlendOp::eAdd,
      vk::<span class="hljs-built_in">ColorComponentFlags</span>(vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA)
    )
  };

  <span class="hljs-function">vk::PipelineColorBlendStateCreateInfo <span class="hljs-title">pbs</span><span class="hljs-params">(
    vk::PipelineColorBlendStateCreateFlags(),
    <span class="hljs-number">0</span>,
    vk::LogicOp::eClear,
    <span class="hljs-keyword">static_cast</span>&lt;<span class="hljs-type">uint32_t</span>&gt;(colorBlendAttachments.size()),
    colorBlendAttachments.data()
  )</span></span>;

  std::vector&lt;vk::DynamicState&gt; dynamicStates =
  {
    vk::DynamicState::eViewport,
    vk::DynamicState::eScissor
  };

  <span class="hljs-function">vk::PipelineDynamicStateCreateInfo <span class="hljs-title">pdy</span><span class="hljs-params">(
    vk::PipelineDynamicStateCreateFlags(),
    <span class="hljs-keyword">static_cast</span>&lt;<span class="hljs-type">uint32_t</span>&gt;(dynamicStates.size()),
    dynamicStates.data()
  )</span></span>;

  pipeline = device.<span class="hljs-built_in">createGraphicsPipeline</span>(
    pipelineCache,
    vk::<span class="hljs-built_in">GraphicsPipelineCreateInfo</span>(
      vk::<span class="hljs-built_in">PipelineCreateFlags</span>(),
      <span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">uint32_t</span>&gt;(pipelineShaderStages.<span class="hljs-built_in">size</span>()),
      pipelineShaderStages.<span class="hljs-built_in">data</span>(),
      &amp;pvi,
      &amp;pia,
      <span class="hljs-literal">nullptr</span>,
      &amp;pv,
      &amp;pr,
      &amp;pm,
      &amp;pds,
      &amp;pbs,
      &amp;pdy,
      pipelineLayout,
      renderPass,
      <span class="hljs-number">0</span>
    )
  );</code></pre></div>
<h4 id="pipeline-cache">Pipeline Cache</h4>
<p>A pipeline cache serves to cache previously created pipelines for reuse later. Since pipelines don&#x27;t change often, this you can quickly create another for use later.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 👋 Declare handles</span>
vk::PipelineCache pipelineCache;

<span class="hljs-comment">// 💵 Create Pipeline Cache</span>
vk::PipelineCacheCreateInfo pcci;
pipelineCache = device.<span class="hljs-built_in">createPipelineCache</span>(pcci);</code></pre></div>
<p>You&#x27;re even able to compile the pipeline down into <em>binary</em>, and write the pipeline to a a file. This is part of the reason why DOOM 2016 takes a while to first start up when running it on Vulkan <a href="#ref_lottes2016">[Lottes 2016]</a>, with Doom Eternal downloading Vulkan binaries separately in Steam.</p>
<h4 id="shaders">Shaders</h4>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/shader-stages.svg" alt="Shader Stages Diagram"/></p>
<p>Shaders must be passed to Vulkan as <em>Standard Portable Intermediate Representation V</em> or SPIR-V binary, so any compiler that can make SPIR-V is allowed. Shaders are pre-compiled, loaded into memory, transferred to a shader module, bundled in a set of pipelineShaderStages, which is then put into a graphics pipeline.</p>
<p>Shaders are compiled using the <code>glslangvalidator</code> bundled with the Vulkan SDK provided by LunarG.</p>
<div><pre><code class="language-bash">glslangvalidator -V shader.vert -o shader.vert.spv
glslangvalidator -V shader.frag -o shader.frag.spv</code></pre></div>
<p>Vulkan&#x27;s GLSL code is the same as OpenGL 4.5:</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// Vertex Shader</span>
<span class="hljs-meta">#version 450</span>

<span class="hljs-meta">#extension GL_ARB_separate_shader_objects : enable</span>
<span class="hljs-meta">#extension GL_ARB_shading_language_420pack : enable</span>

<span class="hljs-comment">// Uniforms now come in the form of input layouts</span>
<span class="hljs-comment">// Each location has a 128 bit alignment,</span>
<span class="hljs-comment">// so matrices/arrays mean larger strides in location.</span>
<span class="hljs-keyword">layout</span> (<span class="hljs-keyword">location</span> = <span class="hljs-number">0</span>) <span class="hljs-keyword">in</span> <span class="hljs-type">vec3</span> inPos;
<span class="hljs-keyword">layout</span> (<span class="hljs-keyword">location</span> = <span class="hljs-number">1</span>) <span class="hljs-keyword">in</span> <span class="hljs-type">vec3</span> inColor;

<span class="hljs-keyword">layout</span> (<span class="hljs-keyword">binding</span> = <span class="hljs-number">0</span>) <span class="hljs-keyword">uniform</span> UBO
{
  <span class="hljs-type">mat4</span> projectionMatrix;
  <span class="hljs-type">mat4</span> modelMatrix;
  <span class="hljs-type">mat4</span> viewMatrix;
} ubo;

<span class="hljs-keyword">layout</span> (<span class="hljs-keyword">location</span> = <span class="hljs-number">0</span>) <span class="hljs-keyword">out</span> <span class="hljs-type">vec3</span> outColor;

<span class="hljs-keyword">out</span> gl_PerVertex
{
  <span class="hljs-type">vec4</span> <span class="hljs-built_in">gl_Position</span>;
};


<span class="hljs-type">void</span> main()
{
  outColor = inColor;
  <span class="hljs-built_in">gl_Position</span> = ubo.projectionMatrix * ubo.viewMatrix * ubo.modelMatrix * <span class="hljs-type">vec4</span>(inPos.xyz, <span class="hljs-number">1.0</span>);
}</code></pre></div>
<div><pre><code class="language-glsl"><span class="hljs-comment">// Fragment Shader</span>
<span class="hljs-meta">#version 450</span>

<span class="hljs-meta">#extension GL_ARB_separate_shader_objects : enable</span>
<span class="hljs-meta">#extension GL_ARB_shading_language_420pack : enable</span>

<span class="hljs-keyword">layout</span> (<span class="hljs-keyword">location</span> = <span class="hljs-number">0</span>) <span class="hljs-keyword">in</span> <span class="hljs-type">vec3</span> inColor;

<span class="hljs-keyword">layout</span> (<span class="hljs-keyword">location</span> = <span class="hljs-number">0</span>) <span class="hljs-keyword">out</span> <span class="hljs-type">vec4</span> outFragColor;

<span class="hljs-type">void</span> main()
{
  outFragColor = <span class="hljs-type">vec4</span>(inColor, <span class="hljs-number">1.0</span>);
}</code></pre></div>
<blockquote>
<p>Your shaders can be pre-compiled at build time with build scripts.</p>
</blockquote>
<p>Shaders are loaded into <strong>Pipeline Layouts</strong> which are then executed by a command buffer.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 📈 Create your shader module handles</span>

vk::ShaderModule vertModule = device.<span class="hljs-built_in">createShaderModule</span>(
  vk::<span class="hljs-built_in">ShaderModuleCreateInfo</span>(
    vk::<span class="hljs-built_in">ShaderModuleCreateFlags</span>(),
    vertexShader.<span class="hljs-built_in">size</span>(),
    vertexShader.<span class="hljs-built_in">data</span>()
  )
);

vk::ShaderModule fragModule = device.<span class="hljs-built_in">createShaderModule</span>(
  vk::<span class="hljs-built_in">ShaderModuleCreateInfo</span>(
    vk::<span class="hljs-built_in">ShaderModuleCreateFlags</span>(),
    fragShader.<span class="hljs-built_in">size</span>(),
    fragShader.<span class="hljs-built_in">data</span>()
    )
);</code></pre></div>
<h3 id="command-buffer">Command Buffer</h3>
<p><img src="https://alain.xyz/blog/raw-vulkan/assets/command-buffer.svg" alt="Command Buffer Diagram"/></p>
<p>A <strong>command buffer</strong> is a container of GPU commands, this is where you would see commands similar to OpenGL&#x27;s state commands:</p>
<ul>
<li><code>bindPipeline</code></li>
<li><code>bindVertexBuffers</code></li>
<li><code>bindIndexBuffer</code></li>
<li><code>setViewport</code></li>
<li><code>setScissor</code></li>
<li><code>blitImage</code></li>
</ul>
<p>A common pattern for building a command buffer is:</p>
<ol>
<li>Start Render Pass</li>
<li>Bind Resources
<ol>
<li>Descriptor Sets</li>
<li>Vertex and Index Buffers</li>
<li>Pipeline State</li>
</ol>
</li>
<li>Modify Dynamic State</li>
<li>Draw</li>
<li>Repeat 2 Through 4 as Needed</li>
<li>End Render Pass</li>
</ol>
<p>Different command buffer pools allow multiple threads performing generating command buffers, thus you could allocate a thread for each core on the CPU, and split rendering tasks across each core. This could be used to distribute rendering individual objects, differed rendering passes, physics calculations with compute buffers, etc.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Vulkan is a pretty complicated API to wrap your head around, and while this post attempts to make it simple, there&#x27;s still a lot to bear in mind that other graphics APIs deal with for you. Aspects of the API like memory management, queue indices, descriptor sets, don&#x27;t exist in other APIs but exist here to make this API much faster at the cost of added complexity to your renderer.</p>
<h3 id="more-resources">More Resources</h3>
<ul>
<li>
<p>The <a href="https://www.khronos.org/registry/vulkan/specs/1.2-khr-extensions/html/">Khronos Vulkan Specification page</a> serves as a great start to all things Vulkan.</p>
</li>
<li>
<img class="profile-picture" alt="Sascha Willems picture" src="https://github.com/SaschaWillems.png"/>
<p>Sascha Willems (<a href="https://twitter.com/SaschaWillems2">@SaschaWillems2</a>)
maintains a very well architected and readable
<a href="https://github.com/SaschaWillems/Vulkan">Vulkan</a> examples page here.</p>
</li>
<li>
<img class="profile-picture" alt="Alexander Overvoorde picture" src="https://github.com/Overv.png"/>
<p>Alexander Overvoorde (<a href="https://twitter.com/Overv">@Overv</a>) wrote the <a href="https://github.com/Overv/VulkanTutorial">Vulkan
Tutorial</a>, a comprehensive overview
of the Vulkan API that goes further into detail than this post.</p>
</li>
<li>
<p>Baldur Karlsson (<a href="https://twitter.com/baldurk">@baldurk</a>) wrote <a href="https://renderdoc.org/vulkan-in-30-minutes.html">Vulkan in 30 minutes</a>, a similar tutorial to this one introducing the API.</p>
</li>
<li>
<p><a href="https://vkguide.dev/">V. Blanco</a>&#x27;s series of articles on Vulkan.</p>
</li>
<li>
<p>The Graphics Virtual Meetup provided video overviews of a variety of <a href="https://nvpro-samples.github.io/vk_mini_path_tracer/index.html#computeshaders/usingcomputeshadersforraytracing">Vulkan</a> tutorials introducing the API.</p>
</li>
<li>
<p><a href="https://vkguide.dev/">VKGuide.dev</a> is a comprehensive guide on writing Vulkan applications.</p>
</li>
<li>
<p><a href="https://www.youtube.com/watch?v=tLwbj9qys18">Vulkan Essentials course by TU Wein</a>.</p>
</li>
<li>
<p>Arseny Kapoulkine (<a href="https://twitter.com/zeuxcg">@zeuxcg</a>) wrote an article on how to write <a href="https://zeux.io/2020/02/27/writing-an-efficient-vulkan-renderer/">an efficient vulkan renderer</a> that goes over the mental model you should have when authoring your renderer.</p>
</li>
</ul>
<p>You&#x27;ll find all the source code described in this post in the <a href="https://github.com/alaingalvan/vulkan-seed">Github repo</a> here.</p>
<table class="markademic-citations"><tbody><tr><td id="ref_fatahalian2018"><span class="markademic-citename">[Fatahalian 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Visual Computing Systems</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/kayvonf.png" alt="Kayvon Fatahalian&#x27;s picture"/><a href="http://graphics.stanford.edu/~kayvonf/">Kayvon Fatahalian</a> (<a href="https://twitter.com/kayvonf">@kayvonf</a>)</span></span><br/><span><span class="markademic-publisher">Stanford 2018</span><br/></span><span><a href="http://graphics.stanford.edu/courses/cs348v-18-winter/">graphics.stanford.edu</a></span></div></td></tr><tr><td id="ref_lottes2016"><span class="markademic-citename">[Lottes 2016]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Vulkan and Doom</span></strong></em><br/><span class="markademic-authors"><span><span>Timothy Lottes</span> (<a href="https://twitter.com/LottesTimothy">@LottesTimothy</a>)</span></span><br/><span><span class="markademic-publisher">AMD 2016</span><br/></span><span><a href="http://gpuopen.com/vulkan-and-doom/">gpuopen.com</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Raw WebGL</title>
<link>https://alain.xyz/blog/raw-webgl</link>
<description>An overview on how to write a WebGL application. Learn what key data structures and types are needed to draw in WebGL.</description>
<guid isPermaLink="true">https://alain.xyz/blog/raw-webgl</guid>
<pubDate>Thu, 05 Sep 2019 00:30:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>webgl</category><category>tutorial</category><category>github</category><category>ts</category><category>js</category>
<enclosure url="https://alain.xyz/blog/raw-webgl/assets/cover.jpg" length="62064" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgl/assets/webgl-logo.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgl/assets/webgl-users.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgl/assets/os-window.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgl/assets/webgl-state-diagram.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgl/assets/canvas.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgl/assets/vertex-buffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgl/assets/index-buffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgl/assets/vertex-shader.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgl/assets/fragment-shader.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgl/assets/program.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgl/assets/uniforms.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgl/assets/textures.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-webgl/assets/triangle-raster.gif"/><iframe height="480" style="width:100%" scrolling="no" title="Raw WebGL" src="https://codepen.io/alaingalvan/embed/preview/OMEqKa?height=480&amp;theme-id=dark&amp;default-tab=js,result" frameBorder="0" allowtransparency="true" allowFullScreen=""><p>See the Pen &lt;a href=&#x27;<a href="https://codepen.io/alaingalvan/pen/OMEqKa&#x27;&amp;gt;Raw">https://codepen.io/alaingalvan/pen/OMEqKa&#x27;&amp;gt;Raw</a>
WebGL&lt;/a&gt; by Alain Galvan (&lt;a
href=&#x27;<a href="https://codepen.io/alaingalvan&#x27;&amp;gt;@alaingalvan&amp;lt;/a">https://codepen.io/alaingalvan&#x27;&amp;gt;@alaingalvan&amp;lt;/a</a>&gt;) on &lt;a
href=&#x27;<a href="https://codepen.io&#x27;&amp;gt;CodePen&amp;lt;/a">https://codepen.io&#x27;&amp;gt;CodePen&amp;lt;/a</a>&gt;.</p></iframe>
<p>While it is possible to render 3D graphics using basic HTML and CSS or with the Canvas API, there&#x27;s an alternative Web API for rendering detailed graphics with the same level of visual fidelity as that of video games.</p>
<p><img src="https://alain.xyz/blog/raw-webgl/assets/webgl-logo.svg" alt="WebGL Logo"/></p>
<p>WebGL is an adaptation of the <a href="https://www.khronos.org/opengles/">OpenGL ES 2.0 Spec</a> to JavaScript, and started off as a collaboration of the <a href="https://www.khronos.org/">Khronos Group</a> and <a href="https://mozilla.org">Mozilla</a>.</p>
<p><img src="https://alain.xyz/blog/raw-webgl/assets/webgl-users.jpg" alt="WebGL Use Cases"/></p>
<p>It&#x27;s been used to showcase procedural art pieces in places like <a href="https://www.shadertoy.com/">ShaderToy</a> and <a href="https://codepen.io/">CodePen</a>, 3D models such as those rendered with <a href="https://marmoset.co/viewer/">Marmoset Viewer</a>, geovisualizations such as those in <a href="https://deck.gl/#/">Uber&#x27;s deck.gl</a>, and much more.</p>
<p><img src="https://alain.xyz/blog/raw-webgl/assets/os-window.svg" alt="OpenGL Hello Triangle on Chrome"/></p>
<p>WebGL is currently supported on all major browsers as <a href="https://caniuse.com/#feat=webgl">far back as year 2012</a>.</p>
<p><img src="https://alain.xyz/blog/raw-webgl/assets/webgl-state-diagram.svg" alt="WebGL State Machine"/></p>
<p>At its core, WebGL is a <em>state machine</em> that lets you as the developer tell it how and where it will draw triangles/points/lines, so it&#x27;s your job as a engine developer to organize when and how the state of the application will change.</p>
<hr/>
<p>I&#x27;ve prepared a <a href="https://github.com/alaingalvan/webgl-seed">Github repo</a> with everything you need to get started. We&#x27;ll walk through writing a WebGL <em>Hello Triangle</em> application in TypeScript (JavaScript with optional type checking).</p>
<blockquote>
<p>Check out my other post on <a href="https://alain.xyz/blog/raw-opengl">OpenGL</a> for writing native C++ applications with nearly the same interface. It&#x27;s possible to export OpenGL apps as WebGL via WebAssembly.
If you would like to try a more recent API, check out my post on <a href="https://alain.xyz/blog/raw-webgpu">WebGPU</a>, an API that aligns more closely to modern graphics APIs such as Metal/Vulkan/DirectX 12.</p>
</blockquote>
<h2 id="setup">Setup</h2>
<p>First install:</p>
<ul>
<li>
<p><a href="https://git-scm.com/">Git</a></p>
</li>
<li>
<p><a href="https://nodejs.org/en/">Node.js</a></p>
</li>
<li>
<p>A Text Editor such as <a href="https://code.visualstudio.com/">Visual Studio Code</a>.</p>
</li>
</ul>
<p>Then type the following in your <a href="https://code.visualstudio.com/docs/editor/integrated-terminal">terminal</a>.</p>
<div><pre><code class="language-bash"><span class="hljs-comment"># 🐑 Clone the repo</span>
git <span class="hljs-built_in">clone</span> https://github.com/alaingalvan/webgl-seed

<span class="hljs-comment"># 💿 go inside the folder</span>
<span class="hljs-built_in">cd</span> webgl-seed

<span class="hljs-comment"># 🔨 Start building the project</span>
npm start</code></pre></div>
<blockquote>
<p>Refer to <a href="https://alain.xyz/blog/designing-a-web-app">this blog post on designing web libraries and apps</a> for more details on Node.js, packages, etc.</p>
</blockquote>
<h2 id="project-layout">Project Layout</h2>
<p>As your project becomes more complex, you&#x27;ll want to separate files and organize your application to something more akin to a game or renderer, check out this post on <a href="https://alain.xyz/blog/game-engine-architecture">game engine architecture</a> and this one on <a href="https://alain.xyz/blog/realtime-renderer-architectures">real time renderer architecture</a> for more details.</p>
<div><pre><code class="language-bash">├─ 📂 node_modules/   <span class="hljs-comment"># 👶 Dependencies</span>
│  ├─ 📁 gl-matrix      <span class="hljs-comment"># ➕ Linear Algebra</span>
│  └─ 📁 ...            <span class="hljs-comment"># 🕚 Other Dependencies (TypeScript, Webpack, etc.)</span>
├─ 📂 src/            <span class="hljs-comment"># 🌟 Source Files</span>
│  ├─ 📄 renderer.ts    <span class="hljs-comment"># 🔺 Triangle Renderer</span>
│  └─ 📄 main.ts        <span class="hljs-comment"># 🏁 Application Main</span>
├─ 📄 .gitignore      <span class="hljs-comment"># 👁️ Ignore certain files in git repo</span>
├─ 📄 package.json    <span class="hljs-comment"># 📦 Node Package File</span>
├─ 📄 license.md      <span class="hljs-comment"># ⚖️ Your License (Unlicense)</span>
└─ 📃readme.md        <span class="hljs-comment"># 📖 Read Me!</span></code></pre></div>
<h3 id="dependencies">Dependencies</h3>
<ul>
<li>
<p><a href="https://github.com/toji/gl-matrix">gl-matrix</a> - A JavaScript library that allows users to write <code>glsl</code> like JavaScript code, with types for vectors, matrices, etc. While not in use in this sample, it&#x27;s incredibly useful for programming more advanced topics such as <a href="https://codepen.io/alaingalvan/details/xxbLzqa">camera matrices</a>.</p>
</li>
<li>
<p><a href="https://github.com/microsoft/typescript">TypeScript</a> - JavaScript with types, makes it significantly easier to program web apps with instant autocomplete and type checking.</p>
</li>
<li>
<p><a href="https://github.com/webpack/webpack">Webpack</a> - A JavaScript compilation tool to build minified outputs and test our apps faster.</p>
</li>
</ul>
<h2 id="overview">Overview</h2>
<p>In this application we will need to do the following:</p>
<ol>
<li>
<p><strong>Initialize the API</strong> - Create your <code>HTMLCanvasElement</code>, either directly on your webpage or dynamically. Then call <code>.getContext(&#x27;webgl&#x27;)</code> to get a handle to the <code>gl</code> state machine which will write directly to that canvas. Then setup any initial state for the <code>gl</code> state machine, such as enabling depth testing, your clear color, etc.</p>
</li>
<li>
<p><strong>Initialize Resources</strong> - Create your <code>WebGLBuffer</code>s for your Vertex, Index data, your Vertex/Fragment <code>WebGLShader</code>s, your <code>WebGLProgram</code>.</p>
</li>
<li>
<p><strong>Render</strong> - Define your vertex layout, bind your <code>WebGLBuffer</code>s and <code>WebGLProgram</code> to the state machine, set any uniform data for that draw call, and <code>drawElements</code>.</p>
</li>
<li>
<p><strong>Destroy</strong> - Destroy any WebGL handles that you&#x27;ve created once you&#x27;re done using them.</p>
</li>
</ol>
<p>The following will explain snippets from that can be found in the <a href="https://github.com/alaingalvan/webgl-seed">Github repo</a>, with certain parts omitted, and <em>member variables (<code>this.memberVariable</code>) declared inline without the <code>this.</code> prefix so their type is easier to see and the examples here can work on their own</em>.</p>
<h2 id="initialize-api">Initialize API</h2>
<p><img src="https://alain.xyz/blog/raw-webgl/assets/canvas.svg" alt="Canvas Element Diagram"/></p>
<p>The key to starting a WebGL application is to call the <code>getContext</code> function on an <code>HTMLCanvasElement</code>. You can then supply a specific version of WebGL (<code>&#x27;webgl&#x27;</code> or <code>&#x27;webgl2&#x27;</code>) along with an optional config object.</p>
<div><pre title="Creating a WebGL Context"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// 👋 Declare handles</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">let</span> <span class="hljs-attr">canvas</span>: <span class="hljs-title">HTMLCanvasElement</span> = <span class="hljs-variable">document</span>.<span class="hljs-title">getElementById</span>(</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3">  <span class="hljs-string">&#x27;webgl&#x27;</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4">) <span class="hljs-keyword">as</span> <span class="hljs-title">HTMLCanvasElement</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"><span class="hljs-comment">// ⚪ Initialization</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7"><span class="hljs-keyword">let</span> <span class="hljs-attr">gl</span>: <span class="hljs-title">WebGLRenderingContext</span> = canvas.<span class="hljs-title">getContext</span>(<span class="hljs-string">&#x27;webgl&#x27;</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9"><span class="hljs-keyword">if</span> (!gl) {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">  <span class="hljs-comment">// This rendering engine failed to start...</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">  <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title">Error</span>(<span class="hljs-string">&#x27;WebGL failed to initialize.&#x27;</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14"><span class="hljs-comment">// Most WebGL Apps will want to enable these settings:</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16"><span class="hljs-comment">// ⚫ Set the default clear color when calling `gl.clear`</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17">gl.<span class="hljs-title">clearColor</span>(<span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18"><span class="hljs-comment">// 🎭 Write to all channels during a clear</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19">gl.<span class="hljs-title">colorMask</span>(<span class="hljs-literal">true</span>, <span class="hljs-literal">true</span>, <span class="hljs-literal">true</span>, <span class="hljs-literal">true</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="20"><div class="hljs-ln-n" data-line-number="20"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="20"><span class="hljs-comment">// 👓 Test if when something is drawn, it&#x27;s in front of what was drawn previously</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="21"><div class="hljs-ln-n" data-line-number="21"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="21">gl.<span class="hljs-title">enable</span>(gl.<span class="hljs-property">DEPTH_TEST</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="22"><div class="hljs-ln-n" data-line-number="22"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="22"><span class="hljs-comment">// ≤ Use this function to test depth values</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="23"><div class="hljs-ln-n" data-line-number="23"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="23">gl.<span class="hljs-title">depthFunc</span>(gl.<span class="hljs-property">LEQUAL</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="24"><div class="hljs-ln-n" data-line-number="24"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="24"><span class="hljs-comment">// 🌒 Hide triangles who&#x27;s normals don&#x27;t face the camera</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="25"><div class="hljs-ln-n" data-line-number="25"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="25">gl.<span class="hljs-title">cullFace</span>(gl.<span class="hljs-property">BACK</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="26"><div class="hljs-ln-n" data-line-number="26"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="26"><span class="hljs-comment">// 🍥 Properly blend images with alpha channels</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="27"><div class="hljs-ln-n" data-line-number="27"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="27">gl.<span class="hljs-title">blendFunc</span>(gl.<span class="hljs-property">SRC_ALPHA</span>, gl.<span class="hljs-property">ONE_MINUS_SRC_ALPHA</span>);</td></tr></tbody></table></code></pre></div>
<h2 id="initialize-resources">Initialize Resources</h2>
<h3 id="vertex-buffer-object">Vertex Buffer Object</h3>
<p><img src="https://alain.xyz/blog/raw-webgl/assets/vertex-buffer.svg" alt="Vertex Buffer Diagram"/></p>
<p>A <strong>Vertex Buffer Object (VBO)</strong> is a block of memory (such as a Typed Array) containing vertex data.</p>
<p>You could describe this data with <em>one big buffer</em> containing everything or with <em>independent arrays</em> for each element in your vertex layout, whichever best fits your use case and performance requirements.</p>
<p>Having them split can be easier to update if you&#x27;re changing your vertex buffer data often, which may be useful for <strong>CPU animations</strong> or <strong>procedurally generated geometry</strong>.</p>
<div><pre title="Creating a WebGL Vertex Buffers"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// 📈 Position Vertex Buffer Data</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">const</span> positions = <span class="hljs-keyword">new</span> <span class="hljs-title">Float32Array</span>([</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3">  <span class="hljs-number">1.0</span>, -<span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>, -<span class="hljs-number">1.0</span>, -<span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4">]);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"><span class="hljs-comment">// 🎨 Color Vertex Buffer Data</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7"><span class="hljs-keyword">const</span> colors = <span class="hljs-keyword">new</span> <span class="hljs-title">Float32Array</span>([</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">  <span class="hljs-number">1.0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">  <span class="hljs-number">0.0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">  <span class="hljs-number">0.0</span>, <span class="hljs-comment">// 🔴</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">  <span class="hljs-number">0.0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12">  <span class="hljs-number">1.0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">  <span class="hljs-number">0.0</span>, <span class="hljs-comment">// 🟢</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14">  <span class="hljs-number">0.0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15">  <span class="hljs-number">0.0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16">  <span class="hljs-number">1.0</span> <span class="hljs-comment">// 🔵</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17">]);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19"><span class="hljs-comment">// ✋ Declare Vertex Buffer Handles</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="20"><div class="hljs-ln-n" data-line-number="20"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="20"><span class="hljs-keyword">let</span> <span class="hljs-attr">positionBuffer</span>: <span class="hljs-title">WebGLBuffer</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="21"><div class="hljs-ln-n" data-line-number="21"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="21"><span class="hljs-keyword">let</span> <span class="hljs-attr">colorBuffer</span>: <span class="hljs-title">WebGLBuffer</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="22"><div class="hljs-ln-n" data-line-number="22"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="22"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="23"><div class="hljs-ln-n" data-line-number="23"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="23"><span class="hljs-comment">// 👋 Helper function for creating WebGLBuffer(s) out of Typed Arrays</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="24"><div class="hljs-ln-n" data-line-number="24"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="24"><span class="hljs-keyword">let</span> <span class="hljs-title">createBuffer</span> = (<span class="hljs-params">arr</span>) =&gt; {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="25"><div class="hljs-ln-n" data-line-number="25"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="25">  <span class="hljs-comment">// ⚪ Create Buffer</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="26"><div class="hljs-ln-n" data-line-number="26"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="26">  <span class="hljs-keyword">let</span> buf = gl.<span class="hljs-title">createBuffer</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="27"><div class="hljs-ln-n" data-line-number="27"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="27">  <span class="hljs-keyword">let</span> bufType =</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="28"><div class="hljs-ln-n" data-line-number="28"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="28">    arr <span class="hljs-keyword">instanceof</span> <span class="hljs-title">Uint16Array</span> || arr <span class="hljs-keyword">instanceof</span> <span class="hljs-title">Uint32Array</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="29"><div class="hljs-ln-n" data-line-number="29"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="29">      ? gl.<span class="hljs-property">ELEMENT_ARRAY_BUFFER</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="30"><div class="hljs-ln-n" data-line-number="30"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="30">      : gl.<span class="hljs-property">ARRAY_BUFFER</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="31"><div class="hljs-ln-n" data-line-number="31"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="31">  <span class="hljs-comment">// 🩹 Bind Buffer to WebGLState</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="32"><div class="hljs-ln-n" data-line-number="32"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="32">  gl.<span class="hljs-title">bindBuffer</span>(bufType, buf);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="33"><div class="hljs-ln-n" data-line-number="33"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="33">  <span class="hljs-comment">// 💾 Push data to VBO</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="34"><div class="hljs-ln-n" data-line-number="34"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="34">  gl.<span class="hljs-title">bufferData</span>(bufType, arr, gl.<span class="hljs-property">STATIC_DRAW</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="35"><div class="hljs-ln-n" data-line-number="35"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="35">  <span class="hljs-keyword">return</span> buf;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="36"><div class="hljs-ln-n" data-line-number="36"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="36">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="37"><div class="hljs-ln-n" data-line-number="37"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="37"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="38"><div class="hljs-ln-n" data-line-number="38"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="38"><span class="hljs-comment">// ⚪ Create VBO</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="39"><div class="hljs-ln-n" data-line-number="39"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="39">positionBuffer = <span class="hljs-title">createBuffer</span>(positions);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="40"><div class="hljs-ln-n" data-line-number="40"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="40">colorBuffer = <span class="hljs-title">createBuffer</span>(colors);</td></tr></tbody></table></code></pre></div>
<h3 id="index-buffer-object">Index Buffer Object</h3>
<p><img src="https://alain.xyz/blog/raw-webgl/assets/index-buffer.svg" alt="Index Buffer Diagram"/></p>
<p>An <strong>Index Buffer Object (IBO)</strong> is a list of vertex indices that&#x27;s used to make triangles, lines, or points. When rendering a set of triangles, Index Buffers allow for the reuse of a given vertex for a different triangle.</p>
<p>Now if you&#x27;re rendering triangles, there should be <em>3 indices per triangle</em> in the index buffer, for <em>lines there should be 2 indices per line</em>, and <em>points refer to just 1 element of an index buffer</em>.</p>
<div><pre title="Creating a WebGL Index Buffers"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// 🗄️ Index Buffer Data</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">const</span> indices = <span class="hljs-keyword">new</span> <span class="hljs-title">Uint16Array</span>([<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>]);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-comment">// ✋ Declare Index Buffer Handle</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"><span class="hljs-keyword">let</span> <span class="hljs-attr">indexBuffer</span>: <span class="hljs-title">WebGLBuffer</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7"><span class="hljs-comment">// ⚪ Create IBO (☝ Refer to helper function above)</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">indexBuffer = <span class="hljs-title">createBuffer</span>(indices);</td></tr></tbody></table></code></pre></div>
<blockquote>
<p>Note the use of a <code>TypedArray</code> for the variable <code>indices</code>. WebGL expects typed arrays for data buffers. We&#x27;re opting to use 16 bit unsigned integers to specify our indices, which is faster than using 32 bit integers, however there&#x27;s a limit to how many vertices you can reference, the maximum value of a 16 bit unsigned int.</p>
</blockquote>
<h3 id="vertex-shader">Vertex Shader</h3>
<p><img src="https://alain.xyz/blog/raw-webgl/assets/vertex-shader.svg" alt="Vertex Shader Diagram"/></p>
<p>A <strong>Vertex Shader</strong> is a GPU program that executes on every vertex of what you&#x27;re currently drawing. Often times developers will place code that handles positioning geometry here.</p>
<div><pre title="Basic Vertex Shader"><code class="language-glsl"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-keyword">attribute</span> <span class="hljs-type">vec3</span> inPosition;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">attribute</span> <span class="hljs-type">vec3</span> inColor;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-keyword">varying</span> <span class="hljs-type">vec3</span> vColor;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"><span class="hljs-type">void</span> main()</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">{</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">    vColor = inColor;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">    <span class="hljs-built_in">gl_Position</span> = <span class="hljs-type">vec4</span>(inPosition, <span class="hljs-number">1.0</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">}</td></tr></tbody></table></code></pre></div>
<p>And do the following to create a vertex shader in JavaScript:</p>
<div><pre title="Creating a WebGL Vertex Shader"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// ✋ Declare Vertex Shader Handle</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">let</span> <span class="hljs-attr">vertModule</span>: <span class="hljs-title">WebGLShader</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-comment">// 🕸️ Vertex Shader Source</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"><span class="hljs-keyword">const</span> vertShaderCode = <span class="hljs-string">`</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6">attribute vec3 inPosition;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">attribute vec3 inColor;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">varying vec3 vColor;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">void main()</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12">{</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">    vColor = inColor;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14">    gl_Position = vec4(inPosition, 1.0);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16">`;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18"><span class="hljs-comment">// 👋 Helper function for creating WebGLShader(s) out of strings</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19"><span class="hljs-keyword">let</span> <span class="hljs-title">createShader</span> = (<span class="hljs-params"><span class="hljs-attr">source</span>: <span class="hljs-built_in">string</span>, stage</span>) =&gt; {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="20"><div class="hljs-ln-n" data-line-number="20"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="20">  <span class="hljs-comment">// ⚪ Create Shader</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="21"><div class="hljs-ln-n" data-line-number="21"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="21">  <span class="hljs-keyword">let</span> s = gl.<span class="hljs-title">createShader</span>(stage);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="22"><div class="hljs-ln-n" data-line-number="22"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="22">  <span class="hljs-comment">// 📰 Pass Vertex Shader String</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="23"><div class="hljs-ln-n" data-line-number="23"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="23">  gl.<span class="hljs-title">shaderSource</span>(s, source);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="24"><div class="hljs-ln-n" data-line-number="24"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="24">  <span class="hljs-comment">// 🔨 Compile Vertex Shader (and check for errors)</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="25"><div class="hljs-ln-n" data-line-number="25"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="25">  gl.<span class="hljs-title">compileShader</span>(s);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="26"><div class="hljs-ln-n" data-line-number="26"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="26">  <span class="hljs-comment">// ❔ Check if shader compiled correctly</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="27"><div class="hljs-ln-n" data-line-number="27"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="27">  <span class="hljs-keyword">if</span> (!gl.<span class="hljs-title">getShaderParameter</span>(s, gl.<span class="hljs-property">COMPILE_STATUS</span>)) {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="28"><div class="hljs-ln-n" data-line-number="28"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="28">    <span class="hljs-variable">console</span>.<span class="hljs-title">error</span>(</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="29"><div class="hljs-ln-n" data-line-number="29"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="29">      <span class="hljs-string">&#x27;An error occurred compiling the shader: &#x27;</span> + gl.<span class="hljs-title">getShaderInfoLog</span>(s)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="30"><div class="hljs-ln-n" data-line-number="30"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="30">    );</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="31"><div class="hljs-ln-n" data-line-number="31"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="31">  }</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="32"><div class="hljs-ln-n" data-line-number="32"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="32">  <span class="hljs-keyword">return</span> s;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="33"><div class="hljs-ln-n" data-line-number="33"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="33">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="34"><div class="hljs-ln-n" data-line-number="34"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="34"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="35"><div class="hljs-ln-n" data-line-number="35"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="35">vertModule = <span class="hljs-title">createShader</span>(vertShaderCode, gl.<span class="hljs-property">VERTEX_SHADER</span>);</td></tr></tbody></table></code></pre></div>
<h3 id="fragment-shader">Fragment Shader</h3>
<p><img src="https://alain.xyz/blog/raw-webgl/assets/fragment-shader.svg" alt="Fragment Shader Diagram"/></p>
<p>A <strong>Fragment Shader</strong> executes on every fragment. A fragment is like a <em>pixel</em>, but not limited to just 8 bit RGB, there could be multiple attachments that you&#x27;re writing to, in multiple encoded formats.</p>
<div><pre title="Basic Fragment Shader"><code class="language-glsl"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-keyword">precision</span> <span class="hljs-keyword">mediump</span> <span class="hljs-type">float</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"><span class="hljs-keyword">varying</span> <span class="hljs-keyword">highp</span> <span class="hljs-type">vec3</span> vColor;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"><span class="hljs-type">void</span> main()</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6">{</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">    <span class="hljs-built_in">gl_FragColor</span> = <span class="hljs-type">vec4</span>(vColor, <span class="hljs-number">1.0</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">}</td></tr></tbody></table></code></pre></div>
<p>And do the following to create a fragment shader in JavaScript:</p>
<div><pre title="Creating a WebGL Fragment Shader"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// ✋ Declare Vertex Shader Handle</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">let</span> <span class="hljs-attr">fragModule</span>: <span class="hljs-title">WebGLShader</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-comment">// 🟦 Fragment Shader Source</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"><span class="hljs-keyword">const</span> fragShaderCode = <span class="hljs-string">`</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6">precision mediump float;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">varying highp vec3 vColor;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">void main()</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">{</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12">    gl_FragColor = vec4(vColor, 1.0);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14">`;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16"><span class="hljs-comment">// ☝️ Refer to vertex sample above</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17">fragModule = <span class="hljs-title">createShader</span>(fragShaderCode, gl.<span class="hljs-property">FRAGMENT_SHADER</span>);</td></tr></tbody></table></code></pre></div>
<blockquote>
<p>For more information on shader languages, check out <a href="https://alain.xyz/blog/a-review-of-shader-languages">this post comparing all shader languages across graphics apis</a>.</p>
</blockquote>
<h3 id="program">Program</h3>
<p><img src="https://alain.xyz/blog/raw-webgl/assets/program.svg" alt="Program Diagram"/></p>
<p>A <strong>Shader Program</strong> binds the vertex and fragment shaders together and sets them up to be used in the WebGL state machine.</p>
<div><pre title="Creating a WebGL Shader Program"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// ✋ Declare Program Handle</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">let</span> <span class="hljs-attr">program</span>: <span class="hljs-title">WebGLProgram</span> = <span class="hljs-literal">null</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-comment">// 👋 Helper function for creating WebGLProgram(s) out of WebGLShader(s)</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"><span class="hljs-keyword">let</span> <span class="hljs-title">createProgram</span> = (<span class="hljs-params"><span class="hljs-attr">stages</span>: <span class="hljs-title">WebGLShader</span>[]</span>) =&gt; {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6">  <span class="hljs-keyword">let</span> p = gl.<span class="hljs-title">createProgram</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> stage <span class="hljs-keyword">of</span> stages) {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">    gl.<span class="hljs-title">attachShader</span>(p, stage);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">  }</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">  gl.<span class="hljs-title">linkProgram</span>(p);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">  <span class="hljs-keyword">return</span> p;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14">program = <span class="hljs-title">createProgram</span>([vertModule, fragModule]);</td></tr></tbody></table></code></pre></div>
<h3 id="uniforms">Uniforms</h3>
<p><img src="https://alain.xyz/blog/raw-webgl/assets/uniforms.svg" alt="Uniform Data Diagram"/></p>
<p><strong>Uniforms</strong> are variables that you send to your shader program to adjust its output.</p>
<p>All you need to do is declare a <code>uniform</code> in your shader:</p>
<div><pre><code class="language-glsl"><span class="hljs-keyword">uniform</span> <span class="hljs-type">vec4</span> uColor;</code></pre></div>
<p>And do the following to send your requested data to your shader:</p>
<div><pre title="Creating a Uniform Buffer in WebGL"><code class="language-js"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// 👔 Uniform Data</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">let</span> color = <span class="hljs-keyword">new</span> <span class="hljs-title">Float32Array</span>([<span class="hljs-number">0.5</span>, <span class="hljs-number">0.5</span>, <span class="hljs-number">0.5</span>, <span class="hljs-number">1.0</span>]);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-comment">// 🔎 Get Uniform Location</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"><span class="hljs-keyword">let</span> uniformLocation = gl.<span class="hljs-title">getUniformLocation</span>(program, <span class="hljs-string">&#x27;uColor&#x27;</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"><span class="hljs-comment">// 🟢 Assign Data</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">gl.<span class="hljs-title">uniform4v</span>(uniformLocation, color);</td></tr></tbody></table></code></pre></div>
<h3 id="textures">Textures</h3>
<p><img src="https://alain.xyz/blog/raw-webgl/assets/textures.svg" alt="Textures Diagram"/></p>
<p><strong>Textures</strong> are image data structures that you can use as inputs to a shader or as frame buffer attachments.</p>
<div><pre title="Loading Textures from Images in WebGL"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// 🖼️ Load Textures</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">function</span> <span class="hljs-title">loadTexture</span>(<span class="hljs-params"><span class="hljs-attr">url</span>: <span class="hljs-built_in">string</span></span>) {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3">  <span class="hljs-keyword">let</span> tex = gl.<span class="hljs-title">createTexture</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4">  gl.<span class="hljs-title">bindTexture</span>(gl.<span class="hljs-property">TEXTURE_2D</span>, tex);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5">  <span class="hljs-keyword">const</span> pixel = <span class="hljs-keyword">new</span> <span class="hljs-title">Uint8Array</span>([<span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>]);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6">  gl.<span class="hljs-title">texImage2D</span>(</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">    gl.<span class="hljs-property">TEXTURE_2D</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">    <span class="hljs-number">0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">    gl.<span class="hljs-property">RGBA</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">    <span class="hljs-number">1</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">    <span class="hljs-number">1</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12">    <span class="hljs-number">0</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">    gl.<span class="hljs-property">RGBA</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14">    gl.<span class="hljs-property">UNSIGNED_BYTE</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15">    pixel</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16">  );</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17">  <span class="hljs-keyword">let</span> img = <span class="hljs-keyword">new</span> <span class="hljs-title">Image</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18">  img.<span class="hljs-property">src</span> = url;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19">  img.<span class="hljs-property">onload</span> = <span class="hljs-function">() =&gt;</span> {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="20"><div class="hljs-ln-n" data-line-number="20"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="20">    gl.<span class="hljs-title">bindTexture</span>(gl.<span class="hljs-property">TEXTURE_2D</span>, tex);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="21"><div class="hljs-ln-n" data-line-number="21"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="21">    gl.<span class="hljs-title">texImage2D</span>(gl.<span class="hljs-property">TEXTURE_2D</span>, <span class="hljs-number">0</span>, gl.<span class="hljs-property">RGBA</span>, gl.<span class="hljs-property">RGBA</span>, gl.<span class="hljs-property">UNSIGNED_BYTE</span>, img);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="22"><div class="hljs-ln-n" data-line-number="22"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="22">    gl.<span class="hljs-title">texParameteri</span>(gl.<span class="hljs-property">TEXTURE_2D</span>, gl.<span class="hljs-property">TEXTURE_MIN_FILTER</span>, gl.<span class="hljs-property">LINEAR</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="23"><div class="hljs-ln-n" data-line-number="23"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="23">    gl.<span class="hljs-title">texParameteri</span>(gl.<span class="hljs-property">TEXTURE_2D</span>, gl.<span class="hljs-property">TEXTURE_MAG_FILTER</span>, gl.<span class="hljs-property">LINEAR</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="24"><div class="hljs-ln-n" data-line-number="24"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="24">    gl.<span class="hljs-title">texParameteri</span>(gl.<span class="hljs-property">TEXTURE_2D</span>, gl.<span class="hljs-property">TEXTURE_WRAP_S</span>, gl.<span class="hljs-property">CLAMP_TO_EDGE</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="25"><div class="hljs-ln-n" data-line-number="25"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="25">    gl.<span class="hljs-title">texParameteri</span>(gl.<span class="hljs-property">TEXTURE_2D</span>, gl.<span class="hljs-property">TEXTURE_WRAP_T</span>, gl.<span class="hljs-property">CLAMP_TO_EDGE</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="26"><div class="hljs-ln-n" data-line-number="26"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="26">  };</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="27"><div class="hljs-ln-n" data-line-number="27"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="27">  <span class="hljs-keyword">return</span> tex;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="28"><div class="hljs-ln-n" data-line-number="28"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="28">}</td></tr></tbody></table></code></pre></div>
<p>And once they&#x27;re created, you can send access them in your shader like so:</p>
<div><pre><code class="language-glsl"><span class="hljs-keyword">uniform</span> <span class="hljs-type">sampler2D</span> tAlbedo;</code></pre></div>
<p>And do the following to send it to your shader:</p>
<div><pre title="Binding textures to a Shader"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// ✋ Declare Uniform Location Handle</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">let</span> <span class="hljs-attr">fragAlbedoUniform</span>: <span class="hljs-built_in">number</span> = -<span class="hljs-number">1</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-comment">// 🔎 Get Uniform Location</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5">fragAlbedoUniform = gl.<span class="hljs-title">getUniformLocation</span>(program, <span class="hljs-string">&#x27;tAlbedo&#x27;</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"><span class="hljs-comment">// 🌳 Set the current active Texture ID</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">gl.<span class="hljs-title">activeTexture</span>(gl.<span class="hljs-property">TEXTURE0</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8"><span class="hljs-comment">// 🟢 Set texture object to GL Texture ID</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">gl.<span class="hljs-title">bindTexture</span>(gl.<span class="hljs-property">TEXTURE_2D</span>, tAlbedo);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10"><span class="hljs-comment">// Set a uniform int as the current texture ID being referenced</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">gl.<span class="hljs-title">uniform1i</span>(fragAlbedoUniform, <span class="hljs-number">0</span>);</td></tr></tbody></table></code></pre></div>
<h2 id="rendering">Rendering</h2>
<p><img src="https://alain.xyz/blog/raw-webgl/assets/triangle-raster.gif" alt="Rasterization Animation"/></p>
<p>To draw, call either <code>gl.drawArrays</code> to draw based off a specified pattern of how your vertex buffer is organized, or <code>gl.drawElements</code> if you&#x27;re using an index buffer. More often than not you&#x27;ll be using <code>gl.drawElements</code>.</p>
<div><pre title="Rendering and Refreshing a Canvas"><code class="language-ts"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// ✋ Declare animation handler</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">let</span> <span class="hljs-attr">animationHandler</span>: <span class="hljs-built_in">number</span> = <span class="hljs-number">0</span>;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-comment">// 🔺 Render triangle</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"><span class="hljs-keyword">const</span> <span class="hljs-title">render</span> = (<span class="hljs-params"></span>) =&gt; {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6">  <span class="hljs-comment">// 🖌️ Encode drawing commands</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">  gl.<span class="hljs-title">clear</span>(gl.<span class="hljs-property">COLOR_BUFFER_BIT</span> | gl.<span class="hljs-property">DEPTH_BUFFER_BIT</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">  gl.<span class="hljs-title">useProgram</span>(program);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">  gl.<span class="hljs-title">viewport</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, canvas.<span class="hljs-property">width</span>, canvas.<span class="hljs-property">height</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">  gl.<span class="hljs-title">scissor</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, canvas.<span class="hljs-property">width</span>, canvas.<span class="hljs-property">height</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12">  <span class="hljs-comment">// Bind Vertex Layout</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">  <span class="hljs-keyword">let</span> <span class="hljs-title">setVertexBuffer</span> = (<span class="hljs-params"><span class="hljs-attr">buf</span>: <span class="hljs-title">WebGLBuffer</span>, <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span></span>) =&gt; {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14">    gl.<span class="hljs-title">bindBuffer</span>(gl.<span class="hljs-property">ARRAY_BUFFER</span>, buf);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15">    <span class="hljs-keyword">let</span> loc = gl.<span class="hljs-title">getAttribLocation</span>(program, name);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16">    gl.<span class="hljs-title">vertexAttribPointer</span>(loc, <span class="hljs-number">3</span>, gl.<span class="hljs-property">FLOAT</span>, <span class="hljs-literal">false</span>, <span class="hljs-number">4</span> * <span class="hljs-number">3</span>, <span class="hljs-number">0</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17">    gl.<span class="hljs-title">enableVertexAttribArray</span>(loc);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18">  };</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="20"><div class="hljs-ln-n" data-line-number="20"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="20">  <span class="hljs-title">setVertexBuffer</span>(positionBuffer, <span class="hljs-string">&#x27;inPosition&#x27;</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="21"><div class="hljs-ln-n" data-line-number="21"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="21">  <span class="hljs-title">setVertexBuffer</span>(colorBuffer, <span class="hljs-string">&#x27;inColor&#x27;</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="22"><div class="hljs-ln-n" data-line-number="22"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="22"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="23"><div class="hljs-ln-n" data-line-number="23"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="23">  gl.<span class="hljs-title">bindBuffer</span>(gl.<span class="hljs-property">ELEMENT_ARRAY_BUFFER</span>, indexBuffer);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="24"><div class="hljs-ln-n" data-line-number="24"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="24">  gl.<span class="hljs-title">drawElements</span>(gl.<span class="hljs-property">TRIANGLES</span>, <span class="hljs-number">3</span>, gl.<span class="hljs-property">UNSIGNED_SHORT</span>, <span class="hljs-number">0</span>);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="25"><div class="hljs-ln-n" data-line-number="25"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="25"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="26"><div class="hljs-ln-n" data-line-number="26"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="26">  <span class="hljs-comment">// ➿ Refresh canvas</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="27"><div class="hljs-ln-n" data-line-number="27"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="27">  animationHandler = <span class="hljs-title">requestAnimationFrame</span>(render);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="28"><div class="hljs-ln-n" data-line-number="28"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="28">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="29"><div class="hljs-ln-n" data-line-number="29"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="29"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="30"><div class="hljs-ln-n" data-line-number="30"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="30"><span class="hljs-comment">// 🏎️ Start the Rendering Engine</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="31"><div class="hljs-ln-n" data-line-number="31"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="31"><span class="hljs-title">initializeAPI</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="32"><div class="hljs-ln-n" data-line-number="32"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="32"><span class="hljs-title">initializeResources</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="33"><div class="hljs-ln-n" data-line-number="33"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="33"><span class="hljs-title">render</span>();</td></tr></tbody></table></code></pre></div>
<h2 id="destroy">Destroy</h2>
<p>Even though the JavaScript runtime features garbage collection, GL Objects will not be garbage collected over the course of an application&#x27;s lifetime (so as <em>long as you&#x27;re on the web page</em>). If you want to get rid of a buffer, texture, frame buffer, etc. You&#x27;ll need to call <code>gl.destroy...(handle)</code>, where the <code>...</code> could be a <code>Buffer</code>, <code>Framebuffer</code>, <code>Program</code>, <code>Renderbuffer</code>, <code>Shader</code>, or <code>Texture</code>.</p>
<div><pre title="Destroying Resources in WebGL"><code class="language-js"><table class="hljs-ln"><tbody><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="1"><div class="hljs-ln-n" data-line-number="1"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="1"><span class="hljs-comment">// 💥 Destroy Buffers, Shaders, Programs</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">function</span> <span class="hljs-title">destroyResources</span>(<span class="hljs-params"></span>) {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3">  gl.<span class="hljs-title">deleteBuffer</span>(positionBuffer);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4">  gl.<span class="hljs-title">deleteBuffer</span>(colorBuffer);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5">  gl.<span class="hljs-title">deleteBuffer</span>(indexBuffer);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6">  gl.<span class="hljs-title">deleteShader</span>(vertModule);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">  gl.<span class="hljs-title">deleteShader</span>(fragModule);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">  gl.<span class="hljs-title">deleteProgram</span>(program);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">}</td></tr></tbody></table></code></pre></div>
<h2 id="conclusion">Conclusion</h2>
<p>While a bit different from other Web APIs, WebGL&#x27;s interface can be surprisingly intuitive, and everything you do in it can translate to other languages such as C++, C, Rust, etc. in the form of <a href="https://alain.xyz/blog/raw-opengl">OpenGL</a>.</p>
<p>Now there were a few things I didn&#x27;t cover in this post as they would have been a beyond the scope of this post, such as:</p>
<ul>
<li>Frame Buffers</li>
<li>Cube Maps</li>
<li><code>glDrawArrays</code> options</li>
<li>Blend Modes</li>
<li>Matrices</li>
</ul>
<p>Not to mention aspects of software engineering such as project organization, WebAssembly implementations, <a href="https://alain.xyz/blog/game-engine-architecture">game engine architecture</a>, <a href="https://alain.xyz/blog/realtime-renderer-architectures">real time renderer architecture</a>, etc.</p>
<h3 id="additional-resources">Additional Resources</h3>
<ul>
<li>
<p><a href="https://webgl2fundamentals.org/webgl/lessons/resources/webgl-state-diagram.html">WebGL State Diagram</a></p>
</li>
<li>
<p><a href="http://tareksherif.net/">Tarek Sherif</a> (<a href="https://twitter.com/thsherif">@thsherif</a>)&#x27;s <a href="https://github.com/tsherif/webgl2examples">WebGL 2 Examples</a></p>
</li>
<li>
<p>Tarek&#x27;s <a href="https://tsherif.github.io/picogl.js/">PicoGL.js</a> library.</p>
</li>
<li>
<p><a href="https://github.com/BabylonJS/Babylon.js/blob/master/src/Engines/engine.ts">Babylon.js Source Code</a></p>
</li>
<li>
<p><a href="https://github.com/mrdoob/three.js/blob/dev/src/renderers/WebGLRenderer.js">Three.js Source Code</a></p>
</li>
<li>
<p><a href="https://github.com/pmndrs/react-three-fiber">React Three Fiber</a>, a Three.js react renderer.</p>
</li>
</ul>
<hr/>
<p>You&#x27;ll find all the source code described in this post in the <a href="http://github.com/alaingalvan/webgl-seed">GitHub repo</a> here.</p>]]></content:encoded>
</item>
<item>
<title>Raw OpenGL</title>
<link>https://alain.xyz/blog/raw-opengl</link>
<description>An overview on how to write modern OpenGL applications. Learn what key data structures are needed for it and how to draw objects.</description>
<guid isPermaLink="true">https://alain.xyz/blog/raw-opengl</guid>
<pubDate>Sat, 17 Aug 2019 00:36:34 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>opengl</category><category>triangle</category><category>hello</category><category>theory</category><category>diagrams</category>
<enclosure url="https://alain.xyz/blog/raw-opengl/assets/cover.jpg" length="222859" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/raw-opengl/assets/opengl-users.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-opengl/assets/os-window.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-opengl/assets/apple-window.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-opengl/assets/gl-state-diagram.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-opengl/assets/opengl-logo.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-opengl/assets/khronos.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-opengl/assets/vertex-buffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-opengl/assets/index-buffer.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-opengl/assets/vertex-shader.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-opengl/assets/fragment-shader.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-opengl/assets/program.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-opengl/assets/uniforms.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-opengl/assets/textures.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-opengl/assets/vao-diagram.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/raw-opengl/assets/triangle-raster.gif"/><link rel="preload" as="image" href="https://github.com/JoeyDeVries.png"/><link rel="preload" as="image" href="https://github.com/Overv.png"/><p><img src="https://alain.xyz/blog/raw-opengl/assets/opengl-users.jpg" alt="OpenGL Users Diagram"/></p>
<p>OpenGL is an easy to use low level graphics API used by <em>game developers</em>, <em>web developers</em> through <a href="https://alain.xyz/blog/raw-webgl">WebGL</a>, <em>data visualization engineers</em>, and more.</p>
<p><img src="https://alain.xyz/blog/raw-opengl/assets/os-window.svg" alt="OpenGL Hello Triangle on different operating systems"/></p>
<p>OpenGL is currently supported on:</p>
<ul>
<li>
<p>🖼️ <strong>Windows</strong> (OpenGL 4.6)</p>
</li>
<li>
<p>🐧 <strong>Linux</strong> (OpenGL 4.6)</p>
</li>
<li>
<p>🤖 <strong>Android</strong> (OpenGL ES 3.1)</p>
</li>
<li>
<p>🌐 <strong>Web</strong> (WebGL 2.0)</p>
</li>
</ul>
<p><img src="https://alain.xyz/blog/raw-opengl/assets/apple-window.svg" alt="OpenGL Hello Triangle on Apple Platforms"/></p>
<p>with <strong>Apple MacOS</strong>, <strong>iOS</strong>, and <strong>iPad OS</strong> supporting it but marking it as depreciated <a href="#ref_openglmac">[Inc. 2012]</a>. Don&#x27;t let that discourage you from learning it though as there are 3rd party solutions such as <a href="https://moltengl.com/moltengl/">MoltenGL</a> that let you program OpenGL code using Apple Metal.</p>
<ul>
<li>
<p>🍎 <strong>Mac OS</strong> (OpenGL 4.1)</p>
</li>
<li>
<p>📱 <strong>iOS</strong> / <strong>iPad OS</strong> (OpenGL ES 3.0)</p>
</li>
</ul>
<p>And practically any language can use it:</p>
<ul>
<li>
<p><strong>C</strong> (Official Language)</p>
</li>
<li>
<p><strong>C++</strong> (Either through the C API or unofficial wrappers)</p>
</li>
<li>
<p><strong>Rust</strong> (Through <a href="https://github.com/glium/glium">Glium</a>)</p>
</li>
<li>
<p><strong>Python</strong> (Through <a href="http://pyopengl.sourceforge.net/">PyOpenGL</a>)</p>
</li>
<li>
<p><strong>JavaScript</strong> (WebGL)</p>
</li>
</ul>
<p><img src="https://alain.xyz/blog/raw-opengl/assets/gl-state-diagram.svg" alt="State Machine Diagram"/></p>
<p>Its API is designed as a <em>state machine</em>. Developers are responsible for setting the state of the driver to decide exactly how to render what they want.</p>
<hr/>
<p>I&#x27;ve prepared a <a href="http://github.com/alaingalvan/opengl-seed">Github Repo</a> with everything we need to get started. We&#x27;re going to walk through a <em>Hello Triangle</em> app in modern C++, a program that creates a triangle, processes it with a shader, and displays it on a window.</p>
<h2 id="setup">Setup</h2>
<p>First install:</p>
<ul>
<li>
<p><a href="https://git-scm.com/">Git</a></p>
</li>
<li>
<p><a href="https://cmake.org">CMake</a></p>
</li>
<li>
<p>An IDE such as <a href="https://visualstudio.microsoft.com/downloads/">Visual Studio</a>, <a href="https://developer.apple.com/xcode/">XCode</a>, or a compiler such as <a href="https://gcc.gnu.org/">GCC</a>.</p>
</li>
</ul>
<p>Then type the following in your <a href="https://hyper.is/">terminal</a>.</p>
<div><pre><code class="language-bash"><span class="hljs-comment"># 🐑 Clone the repo</span>
git <span class="hljs-built_in">clone</span> https://github.com/alaingalvan/opengl-seed --recurse-submodules

<span class="hljs-comment"># 💿 go inside the folder</span>
<span class="hljs-built_in">cd</span> opengl-seed

<span class="hljs-comment"># 👯 If you forget to `recurse-submodules` you can always run:</span>
git submodule update --init

<span class="hljs-comment"># 🖼️ To build your Visual Studio solution on Windows x64</span>
cmake -B build -A x64

<span class="hljs-comment"># 🍎 To build your XCode project on Mac OS</span>
cmake -B build -G Xcode

<span class="hljs-comment"># 🐧 To build your .make file on Linux</span>
cmake -B build

<span class="hljs-comment"># 🔨 Build on any platform:</span>
cmake --build build</code></pre></div>
<blockquote>
<p>Refer to <a href="https://alain.xyz/blog/designing-a-cpp-library">this blog post on designing C++ libraries and apps</a> for more details on CMake, Git Submodules, etc.</p>
</blockquote>
<h2 id="platform-differences">Platform Differences</h2>
<p><img src="https://alain.xyz/blog/raw-opengl/assets/opengl-logo.svg" alt="OpenGL Logo"/></p>
<p><em>OpenGL 4.6</em> is the latest version of the language, but on <strong>Mac OS</strong> <em>4.1</em> is the latest version you can target. In addition, on mobile devices you are required to use <em>OpenGL 3.0 ES (Embeded Systems)</em> if you want to support both <strong>iOS</strong> and <strong>Android</strong> devices, and <em>WebGL 2.0</em> is only supported on versions of <strong>Firefox</strong>, <strong>Chrome</strong>, etc. released after <em>Spring 2017</em>.</p>
<p>Generally, you should target your application according to which platforms you ultimately want to support, and either enable features on a per-platform basis or have all platforms use the same set of features.</p>
<table><thead><tr><th style="text-align:center">Platform</th><th style="text-align:center">Version</th><th style="text-align:center">Latest OpenGL Version</th></tr></thead><tbody><tr><td style="text-align:center">🖼️ <strong>Windows</strong></td><td style="text-align:center">10</td><td style="text-align:center">4.6</td></tr><tr><td style="text-align:center">🖼️ <strong>Windows</strong></td><td style="text-align:center">8.1</td><td style="text-align:center">4.6</td></tr><tr><td style="text-align:center">🖼️ <strong>Windows</strong></td><td style="text-align:center">7</td><td style="text-align:center">4.6</td></tr><tr><td style="text-align:center">🍎 <strong>Mac OS</strong></td><td style="text-align:center">10.14</td><td style="text-align:center">4.1</td></tr><tr><td style="text-align:center">🐧 <strong>Linux</strong></td><td style="text-align:center">10.14</td><td style="text-align:center">4.6</td></tr><tr><td style="text-align:center">📱 <strong>iOS / iPad OS</strong></td><td style="text-align:center">12.4</td><td style="text-align:center">ES 3.0</td></tr><tr><td style="text-align:center">🤖 <strong>Android</strong></td><td style="text-align:center">9.0</td><td style="text-align:center">ES 3.1</td></tr><tr><td style="text-align:center">🌐 <strong>Web</strong></td><td style="text-align:center">-</td><td style="text-align:center">WebGL 2.0</td></tr></tbody></table>
<p>Some features such as tessellation or pre-compilation of shaders are only available in newer versions of OpenGL.</p>
<blockquote>
<p>⚠️ <strong>Note</strong>: The version number of OpenGL ES and WebGL does not correspond with regular OpenGL. OpenGL ES 3.0 is closer to OpenGL 4.0 in features, as is WebGL 2.0.</p>
</blockquote>
<table><thead><tr><th style="text-align:left">OpenGL Version</th><th style="text-align:left">Important Features</th></tr></thead><tbody><tr><td style="text-align:left">OpenGL 4.6</td><td style="text-align:left">Pre-compiled Shaders</td></tr><tr><td style="text-align:left">OpenGL 4.0</td><td style="text-align:left">Tessellation</td></tr><tr><td style="text-align:left">OpenGL 3.0</td><td style="text-align:left">Frame Buffer Objects</td></tr><tr><td style="text-align:left">OpenGL ES 3.0</td><td style="text-align:left">Vertex Array Objects</td></tr><tr><td style="text-align:left">OpenGL ES 2.0</td><td style="text-align:left">Cube Maps</td></tr><tr><td style="text-align:left">WebGL 2.0</td><td style="text-align:left">GLSL ES 3.0, Transform Feedback</td></tr><tr><td style="text-align:left">WebGL 1.0</td><td style="text-align:left">-</td></tr></tbody></table>
<h2 id="project-layout">Project Layout</h2>
<p>As your project becomes more complex, you&#x27;ll want to separate files and organize your application to something more akin to a game or renderer, check out this post on <a href="https://alain.xyz/blog/game-engine-architecture">game engine architecture</a> and this one on <a href="https://alain.xyz/blog/realtime-renderer-architectures">real time renderer architecture</a> for more details.</p>
<div><pre><code class="language-bash">├─ 📂 external/                    <span class="hljs-comment"># 👶 Dependencies</span>
│  ├─ 📁 crosswindow/                    <span class="hljs-comment"># 🖼️ OS Windows</span>
│  ├─ 📁 crosswindow-graphics/           <span class="hljs-comment"># 🎨 GL Context Creation</span>
│  ├─ 📁 glm/                            <span class="hljs-comment"># ➕ Linear Algebra</span>
│  ├─ 📁 opengl-registry/                <span class="hljs-comment"># 📚 GL Headers</span>
│  └─ 📁 glad/                           <span class="hljs-comment"># 🙌 GL Extension Loader</span>
├─ 📂 src/                         <span class="hljs-comment"># 🌟 Source Files</span>
│  ├─ 📄 Utils.h                         <span class="hljs-comment"># ⚙️ Utilities (Load Files, Check Shaders, etc.)</span>
│  ├─ 📄 Triangle.h                      <span class="hljs-comment"># 🔺 Triangle Draw Code</span>
│  ├─ 📄 Triangle.cpp                    <span class="hljs-comment"># -</span>
│  └─ 📄 Main.cpp                        <span class="hljs-comment"># 🏁 Application Main</span>
├─ 📄 .gitignore                   <span class="hljs-comment"># 👁️ Ignore certain files in git repo</span>
├─ 📄 CMakeLists.txt               <span class="hljs-comment"># 🔨 Build Script</span>
├─ 📄 license.md                   <span class="hljs-comment"># ⚖️ Your License (Unlicense)</span>
└─ 📃readme.md                     <span class="hljs-comment"># 📖 Read Me!</span></code></pre></div>
<h3 id="dependencies">Dependencies</h3>
<ul>
<li>
<p><a href="https://github.com/alaingalvan/crosswindow">CrossWindow</a> - A cross platform system abstraction library written in C++ for managing windows and performing OS tasks.</p>
</li>
<li>
<p><a href="https://github.com/alaingalvan/crosswindow-graphics">CrossWindow-Graphics</a> - A library to simplify creating an OpenGL context with CrossWindow.</p>
</li>
<li>
<p><a href="https://github.com/g-truc/glm">GLM</a> - Allows us to use the same <em>linear algebra</em> data structures as the official shading language of OpenGL, GLSL.</p>
</li>
<li>
<p><a href="https://github.com/KhronosGroup/OpenGL-Registry">OpenGL Registry</a> - OpenGL&#x27;s official header files. While operating systems tend to have these headers, it&#x27;s easier to reference them from one dependency rather than on a per OS basis.</p>
</li>
<li>
<p><a href="https://github.com/Dav1dde/glad">Glad</a> - A configurable C OpenGL header generation tool.</p>
</li>
</ul>
<p>OpenGL features a core API, as well as a number of driver specific extensions to handle things like specific texture formats, debugging, etc. In order to use OpenGL you&#x27;ll need <code>gl.h</code> that defines the external calls to the OpenGL driver. This is included by the <strong>OpenGL Registry</strong> dependency.</p>
<p>In addition, if you need any driver specific extensions, you&#x27;ll need an extension loading library, which is where the <strong>Glad</strong> dependency comes in.</p>
<p><img src="https://alain.xyz/blog/raw-opengl/assets/khronos.svg" alt="Khronos Group"/></p>
<p>The <a href="https://www.khronos.org/registry/OpenGL/index_gl.php#headers">Khronos Group</a> distributes official extension headers for OpenGL, but there&#x27;s no guarantee that you need or can even support all the extensions included there. In addition, depending on your target version you may need to dynamically load certain OpenGL functions like <code>glGenVertexArrays</code>. Glad can generate the headers based on the <a href="https://www.opengl.org/registry/">specification</a> and offers you the option of including or omitting certain extensions. the reason you may want this is that every driver has a unique set of extensions to the core OpenGL API, and every developer might want a unique set of extensions:</p>
<p>We&#x27;ll be using <a href="https://github.com/Dav1dde/glad">Glad</a>, with no extensions, though you&#x27;re free to generate your own glad header with whatever extensions you would like.</p>
<h2 id="overview">Overview</h2>
<p>Our program will perform the following:</p>
<ol>
<li>
<p>Create a <strong>Window</strong> for our operating system.</p>
</li>
<li>
<p>Create a <strong>Context</strong> to communicate with OpenGL.</p>
</li>
<li>
<p>Create 2 <strong>Vertex Buffers (VBOs)</strong>, one for positions and one for colors.</p>
</li>
<li>
<p>Create an <strong>Index Buffer (IBO)</strong>, an array specifying which indices of the vertex buffer you should use to render primitives.</p>
</li>
<li>
<p>Create a <strong>Vertex Array Object (VAO)</strong> to describe and bind our vertex layout and buffers to the OpenGL state machine.</p>
</li>
<li>
<p>Write a <strong>Vertex Shader</strong> <code>string</code> that transforms your vertices in 3D Space.</p>
</li>
<li>
<p>Write a <strong>Fragment Shader</strong> <code>string</code> that colors the pixels that the triangle envelops.</p>
</li>
<li>
<p>Create a <strong>Program</strong> that binds the two shaders with <code>glCreateProgram</code> and use it with <code>glUseProgram</code>.</p>
</li>
<li>
<p>Set <strong>Uniforms</strong> from our shader to their values with <code>glUniform[type]</code>.</p>
</li>
<li>
<p><strong>Clear</strong> the canvas once the next frame needs to render with <code>glClear</code>.</p>
</li>
<li>
<p><strong>Draw</strong> the primitive onto OpenGL&#x27;s default Frame Buffer with <code>glDrawElements</code>.</p>
</li>
<li>
<p><strong>Update</strong> the screen and the state of all your objects you want to draw every frame.</p>
</li>
<li>
<p><strong>Destroy</strong> any data structures once the application is asked to close.</p>
</li>
</ol>
<p>The following will explain snippets that can be found in the <a href="https://github.com/alaingalvan/open-seed">Github repo</a>, with certain parts omitted, and <em>member variables (<code>mMemberVariable</code>) declared inline without the <code>m</code> prefix so their type is easier to see and the examples here can work on their own</em>.</p>
<h2 id="window-creation">Window Creation</h2>
<p>We&#x27;re using CrossWindow to handle cross platform window creation, so creating a window and updating it is very easy:</p>
<div><pre><code class="language-cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&quot;CrossWindow/CrossWindow.h&quot;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&quot;glad/glad.h&quot;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&quot;CrossWindow/Graphics.h&quot;</span></span>

<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;iostream&gt;</span></span>

<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&quot;Triangle.h&quot;</span></span>

<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">xmain</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">const</span> <span class="hljs-type">char</span>** argv)</span>
</span>{
  <span class="hljs-comment">// 🖼 Create Window</span>
  xwin::WindowDesc wdesc;
  wdesc.title = <span class="hljs-string">&quot;OpenGL Seed&quot;</span>;
  wdesc.name = <span class="hljs-string">&quot;MainWindow&quot;</span>;
  wdesc.visible = <span class="hljs-literal">true</span>;
  wdesc.width = <span class="hljs-number">640</span>;
  wdesc.height = <span class="hljs-number">640</span>;
  wdesc.fullscreen = <span class="hljs-literal">false</span>;

  xwin::Window window;
  xwin::EventQueue eventQueue;

  <span class="hljs-keyword">if</span> (!window.<span class="hljs-built_in">create</span>(wdesc, eventQueue))
  { <span class="hljs-keyword">return</span>; };

  <span class="hljs-comment">// ⚪ Load OpenGL</span>
  xgfx::OpenGLDesc desc;
  xgfx::OpenGLState oglState = xgfx::<span class="hljs-built_in">createContext</span>(&amp;window, desc);
  xgfx::<span class="hljs-built_in">setContext</span>(oglState);
  <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">loadOpenGL</span>())
  { <span class="hljs-keyword">return</span>; }

  <span class="hljs-comment">// 🌟 Create Triangle</span>
  <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">createTriangle</span>())
  { <span class="hljs-keyword">return</span>; }

  <span class="hljs-comment">// 🏁 Engine loop</span>
  <span class="hljs-type">bool</span> isRunning = <span class="hljs-literal">true</span>;
  <span class="hljs-keyword">while</span> (isRunning)
  {
    <span class="hljs-type">bool</span> shouldRender = <span class="hljs-literal">true</span>;

    <span class="hljs-comment">// ♻️ Update the event queue</span>
    eventQueue.<span class="hljs-built_in">update</span>();

    <span class="hljs-comment">// 🎈 Iterate through that queue:</span>
    <span class="hljs-keyword">while</span> (!eventQueue.<span class="hljs-built_in">empty</span>())
    {
      <span class="hljs-comment">//Update Events</span>
      <span class="hljs-type">const</span> xwin::Event&amp; event = eventQueue.<span class="hljs-built_in">front</span>();

      <span class="hljs-comment">// ❌ On Close:</span>
      <span class="hljs-keyword">if</span> (event.type == xwin::EventType::Close)
      {
        window.<span class="hljs-built_in">close</span>();
        shouldRender = <span class="hljs-literal">false</span>;
        isRunning = <span class="hljs-literal">false</span>;
      }

      eventQueue.<span class="hljs-built_in">pop</span>();
    }

    <span class="hljs-comment">// ✨ Update Visuals</span>
    <span class="hljs-keyword">if</span> (shouldRender)
    {
      <span class="hljs-comment">// 🔁 Swap back buffers so we can write to the next frame buffer</span>
      xgfx::<span class="hljs-built_in">swapBuffers</span>(oglState);
      <span class="hljs-built_in">drawTriangle</span>(wdesc.width, wdesc.height);
    }
  }

  <span class="hljs-comment">// ❌ Destroy OpenGL Data Structures</span>
  <span class="hljs-built_in">destroyTriangle</span>();
  xgfx::<span class="hljs-built_in">destroyContext</span>(oglState);
}</code></pre></div>
<blockquote>
<p><strong>Note</strong>: We created our context using the abstraction <code>xgfx::createContext</code>, which picks the latest version of OpenGL for you. For more details on the specifics of context creation check out <a href="https://github.com/alaingalvan/CrossWindow-Graphics/blob/master/src/CrossWindow/Graphics/OpenGL.h">the <code>CrossWindow-Graphics</code> source</a>.</p>
</blockquote>
<p>As an alternative to CrossWindow, you could use another library like GLFW, SFML, SDL, QT, or just interface directly with your OS windowing API.</p>
<h2 id="initialize-api">Initialize API</h2>
<p>There may be some global settings that you may want enabled prior to working with OpenGL, such as what clear color to use, <em>Depth Testing</em>, <em>Cube Map Support</em>, etc.</p>
<div><pre><code class="language-cpp"><span class="hljs-function"><span class="hljs-type">bool</span> <span class="hljs-title">loadOpenGL</span><span class="hljs-params">()</span>
</span>{
  <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">gladLoadGL</span>())
  {
    std::cout &lt;&lt; <span class="hljs-string">&quot;Failed to load OpenGL.&quot;</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }
  GLenum err = <span class="hljs-built_in">glGetError</span>();
  <span class="hljs-keyword">if</span> (err != GL_NO_ERROR)
  {
    std::cout &lt;&lt; <span class="hljs-string">&quot;OpenGL started with error code: &quot;</span> &lt;&lt; err;
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }

  <span class="hljs-comment">// Most OpenGL Apps will want to enable these settings:</span>

  <span class="hljs-comment">// 🖍️ Set default clear color to gray ⚫</span>
  <span class="hljs-built_in">glClearColor</span>(<span class="hljs-number">0.2f</span>, <span class="hljs-number">0.2f</span>, <span class="hljs-number">0.2f</span>, <span class="hljs-number">1.0f</span>);
  <span class="hljs-comment">// 🔺 Enable depth testing</span>
  <span class="hljs-built_in">glEnable</span>(GL_DEPTH_TEST);
  <span class="hljs-comment">// 🧊 Enable seamless cubemap sampling</span>
  <span class="hljs-built_in">glEnable</span>(GL_TEXTURE_CUBE_MAP_SEAMLESS);
  <span class="hljs-comment">// &lt;= Set Depth Test Function</span>
  <span class="hljs-built_in">glDepthFunc</span>(GL_LEQUAL);

  <span class="hljs-comment">// Enable anything else like blend modes, etc...</span>

  <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}</code></pre></div>
<h2 id="initialize-resources">Initialize Resources</h2>
<h3 id="vertex-buffer-object">Vertex Buffer Object</h3>
<p><img src="https://alain.xyz/blog/raw-opengl/assets/vertex-buffer.svg" alt="Vertex Buffer Diagram"/></p>
<p>A <strong>Vertex Buffer Object (VBO)</strong> is a block of memory containing vertex data.</p>
<p>You could describe this data with <em>one big buffer</em> containing everything or with <em>independent arrays</em> for each element in your vertex layout, whichever best fits your use case and performance requirements.</p>
<p>Having them split can be easier to update if you&#x27;re changing your vertex buffer data often, which may be useful for <strong>CPU animations</strong> or <strong>procedurally generated geometry</strong>.</p>
<p>At the same time, having them together makes it can make it easier for Ray Tracing routines to reference vertex data and use it.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 📈 Describe Position Vertex Buffer Data</span>
<span class="hljs-type">float</span> positions[<span class="hljs-number">3</span>*<span class="hljs-number">3</span>] = { <span class="hljs-number">1.0f</span>,  <span class="hljs-number">1.0f</span>,  <span class="hljs-number">0.0f</span>,
                        <span class="hljs-number">-1.0f</span>,  <span class="hljs-number">1.0f</span>,  <span class="hljs-number">0.0f</span>,
                         <span class="hljs-number">0.0f</span>, <span class="hljs-number">-1.0f</span>,  <span class="hljs-number">0.0f</span> };

<span class="hljs-comment">// 🎨 Describe Color Vertex Buffer Data</span>
<span class="hljs-type">float</span> colors[<span class="hljs-number">3</span>*<span class="hljs-number">3</span>] = { <span class="hljs-number">1.0f</span>,  <span class="hljs-number">0.0f</span>,  <span class="hljs-number">0.0f</span>,
                      <span class="hljs-number">0.0f</span>,  <span class="hljs-number">1.0f</span>,  <span class="hljs-number">0.0f</span>,
                      <span class="hljs-number">0.0f</span>,  <span class="hljs-number">0.0f</span>,  <span class="hljs-number">1.0f</span> };

<span class="hljs-comment">// ✋ Declare Vertex Buffer Handles</span>
GLuint positionVBO = <span class="hljs-number">0</span>;
GLuint colorVBO = <span class="hljs-number">0</span>;

<span class="hljs-comment">// ⚪ Create VBO</span>
<span class="hljs-built_in">glGenBuffers</span>(<span class="hljs-number">1</span>, &amp;positionVBO);
<span class="hljs-comment">// 🩹 Bind VBO to GLState</span>
<span class="hljs-built_in">glBindBuffer</span>(GL_ARRAY_BUFFER, positionVBO);
<span class="hljs-comment">// 💾 Push data to VBO</span>
<span class="hljs-built_in">glBufferData</span>(GL_ARRAY_BUFFER, <span class="hljs-built_in">sizeof</span>(<span class="hljs-type">float</span>) * <span class="hljs-number">3</span> * <span class="hljs-number">3</span>, positions, GL_STATIC_DRAW);

<span class="hljs-comment">// ⚪ Create VBO</span>
<span class="hljs-built_in">glGenBuffers</span>(<span class="hljs-number">1</span>, &amp;colorVBO);
<span class="hljs-comment">// 🩹 Bind VBO to GLState</span>
<span class="hljs-built_in">glBindBuffer</span>(GL_ARRAY_BUFFER, colorVBO);
<span class="hljs-comment">// 💾 Push data to VBO</span>
<span class="hljs-built_in">glBufferData</span>(GL_ARRAY_BUFFER, <span class="hljs-built_in">sizeof</span>(<span class="hljs-type">float</span>) * <span class="hljs-number">3</span> * <span class="hljs-number">3</span>, colors, GL_STATIC_DRAW);</code></pre></div>
<h3 id="index-buffer-object">Index Buffer Object</h3>
<p><img src="https://alain.xyz/blog/raw-opengl/assets/index-buffer.svg" alt="Index Buffer Diagram"/></p>
<p>An <strong>Index Buffer Object (IBO)</strong> is a list of vertex indices that&#x27;s used to make triangles, lines, or points.</p>
<p>If you&#x27;re making triangles, there should be <em>3 points per triangle</em> in the index buffer, for <em>lines there should be 2</em>, and <em>points only need 1</em>.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 🗄️ Describe Index Buffer Data</span>
GLuint indexBufferData[<span class="hljs-number">3</span>] = { <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span> };

<span class="hljs-comment">// ✋ Declare Index Buffer Handle</span>
GLuint ibo;

<span class="hljs-comment">// ⚪ Create IBO</span>
<span class="hljs-built_in">glGenBuffers</span>(<span class="hljs-number">1</span>, &amp;ibo);
<span class="hljs-comment">// 🩹 Bind IBO to GLState</span>
<span class="hljs-built_in">glBindBuffer</span>(GL_ELEMENT_ARRAY_BUFFER, ibo);
<span class="hljs-comment">// 💾 Push data to IBO</span>
<span class="hljs-built_in">glBufferData</span>(GL_ELEMENT_ARRAY_BUFFER, <span class="hljs-built_in">sizeof</span>(GLuint) * <span class="hljs-number">3</span>, indexBufferData, GL_STATIC_DRAW);</code></pre></div>
<h3 id="vertex-shader">Vertex Shader</h3>
<p><img src="https://alain.xyz/blog/raw-opengl/assets/vertex-shader.svg" alt="Shader Program Diagram"/></p>
<p>A <strong>Vertex Shader</strong> is a GPU program that executes on every vertex of what you&#x27;re currently drawing. Often times developers will place code that handles positioning geometry here.</p>
<div><pre><code class="language-glsl"><span class="hljs-meta">#version 310 es</span>

<span class="hljs-keyword">layout</span>(<span class="hljs-keyword">location</span> = <span class="hljs-number">0</span>) <span class="hljs-keyword">in</span> <span class="hljs-type">vec3</span> inPosition;
<span class="hljs-keyword">layout</span>(<span class="hljs-keyword">location</span> = <span class="hljs-number">1</span>) <span class="hljs-keyword">in</span> <span class="hljs-type">vec3</span> inColor;

<span class="hljs-keyword">layout</span>(<span class="hljs-keyword">location</span> = <span class="hljs-number">0</span>) <span class="hljs-keyword">out</span> <span class="hljs-type">vec3</span> outColor;

<span class="hljs-type">void</span> main()
{
    outColor = inColor;
    <span class="hljs-built_in">gl_Position</span> = <span class="hljs-type">vec4</span>(inPosition, <span class="hljs-number">1.0</span>);
}</code></pre></div>
<p>And do the following to create a vertex shader in C++:</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// ✋ Declare Vertex Shader Handle</span>
GLuint vs = <span class="hljs-number">0</span>;

<span class="hljs-comment">// 📂 Load Shader File...</span>

<span class="hljs-comment">// ⚪ Create Vertex Shader</span>
vs = <span class="hljs-built_in">glCreateShader</span>(GL_VERTEX_SHADER);
<span class="hljs-comment">// 📰 Pass Vertex Shader String</span>
<span class="hljs-built_in">glShaderSource</span>(vs, <span class="hljs-number">1</span>, &amp;vertString, &amp;vertStringSize);
<span class="hljs-comment">// 🔨 Compile Vertex Shader (and check for errors)</span>
<span class="hljs-built_in">glCompileShader</span>(vs);

<span class="hljs-comment">// ❔ Check if shader compiled correctly</span></code></pre></div>
<h3 id="fragment-shader">Fragment Shader</h3>
<p><img src="https://alain.xyz/blog/raw-opengl/assets/fragment-shader.svg" alt="Shader Program Diagram"/></p>
<p>A <strong>Fragment Shader</strong> executes on every fragment. A fragment is like a pixel, but not limited to just 8 bit RGB, there could be multiple attachments that you&#x27;re writing to, in multiple encoded formats.</p>
<div><pre><code class="language-glsl"><span class="hljs-meta">#version 310 es</span>
<span class="hljs-keyword">precision</span> <span class="hljs-keyword">mediump</span> <span class="hljs-type">float</span>;
<span class="hljs-keyword">precision</span> <span class="hljs-keyword">highp</span> <span class="hljs-type">int</span>;

<span class="hljs-keyword">layout</span>(<span class="hljs-keyword">location</span> = <span class="hljs-number">0</span>) <span class="hljs-keyword">in</span> <span class="hljs-keyword">highp</span> <span class="hljs-type">vec3</span> inColor;

<span class="hljs-keyword">layout</span>(<span class="hljs-keyword">location</span> = <span class="hljs-number">0</span>) <span class="hljs-keyword">out</span> <span class="hljs-keyword">highp</span> <span class="hljs-type">vec4</span> outFragColor;


<span class="hljs-type">void</span> main()
{
    outFragColor = <span class="hljs-type">vec4</span>(inColor, <span class="hljs-number">1.0</span>);
}</code></pre></div>
<p>And do the following to create a fragment shader in C++:</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// ✋ Declare Fragment Shader Handle</span>
GLuint fs = <span class="hljs-number">0</span>;

<span class="hljs-comment">// 📂 Load Shader File...</span>

<span class="hljs-comment">// ⚪ Create Fragment Shader</span>
fs = <span class="hljs-built_in">glCreateShader</span>(GL_FRAGMENT_SHADER);
<span class="hljs-comment">// 📰 Pass Fragment Shader String</span>
<span class="hljs-built_in">glShaderSource</span>(fs, <span class="hljs-number">1</span>, &amp;fragString, &amp;fragStringSize);
<span class="hljs-comment">// 🔨 Compile Vertex Shader (and check for errors)</span>
<span class="hljs-built_in">glCompileShader</span>(fs);

<span class="hljs-comment">// ❔ Check if shader compiled correctly...</span></code></pre></div>
<blockquote>
<p>For more information on shader languages, check out <a href="https://alain.xyz/blog/a-review-of-shader-languages">this post comparing all shader languages across graphics apis</a>.</p>
</blockquote>
<h3 id="program">Program</h3>
<p><img src="https://alain.xyz/blog/raw-opengl/assets/program.svg" alt="Shader Program Diagram"/></p>
<p>A <strong>Shader Program</strong> binds the vertex and fragment shaders together and sets them up to be used in the OpenGL state machine.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// ✋ Declare Program Handle</span>
GLuint program = <span class="hljs-number">0</span>;

<span class="hljs-comment">// ⚪ Create Shader Program</span>
program = <span class="hljs-built_in">glCreateProgram</span>();
<span class="hljs-comment">// 🩹 Attach Shader Stages to Program</span>
<span class="hljs-built_in">glAttachShader</span>(program, vs);
<span class="hljs-built_in">glAttachShader</span>(program, fs);
<span class="hljs-comment">// 🔗 Link Program (and check for errors)</span>
<span class="hljs-built_in">glLinkProgram</span>(program);

<span class="hljs-comment">// ❔ Check if program linked correctly...</span></code></pre></div>
<h3 id="uniforms">Uniforms</h3>
<p><img src="https://alain.xyz/blog/raw-opengl/assets/uniforms.svg" alt="Uniform Data Diagram"/></p>
<p><strong>Uniforms</strong> are variables that you send to your shader program to adjust its output.</p>
<p>All you need to do is declare a <code>uniform</code> in your shader:</p>
<div><pre><code class="language-glsl"><span class="hljs-keyword">uniform</span> <span class="hljs-type">vec4</span> uColor;</code></pre></div>
<p>And do the following to send your requested data to your shader:</p>
<div><pre><code class="language-cpp">GLfloat color[<span class="hljs-number">4</span>] = {
  <span class="hljs-number">0.5f</span>, <span class="hljs-number">0.5f</span>, <span class="hljs-number">0.5f</span>, <span class="hljs-number">1.0f</span>,
};

<span class="hljs-comment">// 🔎 Get Uniform Location</span>
GLint uniformLocation = <span class="hljs-built_in">glGetUniformLocation</span>(program, <span class="hljs-string">&quot;uColor&quot;</span>);
<span class="hljs-comment">// 🟢 Assign Data</span>
<span class="hljs-built_in">glUniform4fv</span>(uniformLocation, <span class="hljs-number">1</span>, color);</code></pre></div>
<p>Alternatively, you can use <strong>Uniform Buffer Objects (UBOs)</strong> to send data to your shader program, which may be easier if you&#x27;re working with other graphics APIs that also do this:</p>
<div><pre><code class="language-glsl"><span class="hljs-keyword">uniform</span> VertUBO
{
  <span class="hljs-comment">// Computed model * view * projection matrices</span>
  <span class="hljs-type">mat4</span> modelViewProjection;

  <span class="hljs-comment">// Inverse Transpose Model matrix (for normalized vectors like normals)</span>
  <span class="hljs-type">mat4</span> inverseTransposeModel;
} ubo;</code></pre></div>
<p>And do the following to send that buffer to your shader:</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// ✋ Declare UBO/UBI Handles</span>
GLuint vertUbo = <span class="hljs-number">0</span>;
GLuint vertexUbi = <span class="hljs-number">0</span>;

<span class="hljs-comment">// 🌟 Create UBO</span>
<span class="hljs-built_in">glGenBuffers</span>(<span class="hljs-number">1</span>, &amp;vertexUbo);

<span class="hljs-comment">// 🩹 Bind it to GL State</span>
<span class="hljs-built_in">glBindBuffer</span>(GL_UNIFORM_BUFFER, vertexUbo);

<span class="hljs-comment">// 📨 Send initial data to UBO</span>
<span class="hljs-built_in">glBufferData</span>(GL_UNIFORM_BUFFER,
              <span class="hljs-built_in">sizeof</span>(VertexUBO),
              &amp;vertexUboData, GL_STREAM_DRAW);

<span class="hljs-comment">// 🗺️ Map UBO to block binding</span>
vertexUbi =
    <span class="hljs-built_in">glGetUniformBlockIndex</span>(shader.program, <span class="hljs-string">&quot;VertUBO&quot;</span>);
<span class="hljs-built_in">glUniformBlockBinding</span>(shader.program, vertexUbi, <span class="hljs-number">0</span>);

<span class="hljs-built_in">glUniformBlockBinding</span>(shader.program, vertexUbi, <span class="hljs-number">0</span>);
<span class="hljs-built_in">glBindBufferBase</span>(GL_UNIFORM_BUFFER, <span class="hljs-number">0</span>, vertexUbo);


<span class="hljs-comment">// 🔁 Later, to update UBO</span>
<span class="hljs-built_in">glBindBuffer</span>(GL_UNIFORM_BUFFER, <span class="hljs-number">0</span>);

<span class="hljs-comment">// 🗺️ Map Uniform Buffer to CPU accessible memory</span>
GLvoid* p =
<span class="hljs-built_in">glMapBufferRange</span>(GL_UNIFORM_BUFFER, <span class="hljs-number">0</span>,
                  <span class="hljs-built_in">sizeof</span>(VertexUBO),
                  GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);

<span class="hljs-comment">// 📄 Copy Data to buffer</span>
<span class="hljs-built_in">memcpy</span>(p, &amp;vertexUboData, <span class="hljs-built_in">sizeof</span>(vertexUboData));

<span class="hljs-comment">// 😁 Unbind the mapping</span>
<span class="hljs-built_in">glUnmapBuffer</span>(GL_UNIFORM_BUFFER);</code></pre></div>
<h3 id="textures">Textures</h3>
<p><img src="https://alain.xyz/blog/raw-opengl/assets/textures.svg" alt="Textures Diagram"/></p>
<p><strong>Textures</strong> are image data structures that you can use as inputs to a shader or as frame buffer attachments.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// ✋ Declare GLTexture Handle</span>
GLuint tAlbedo = <span class="hljs-number">0</span>;

<span class="hljs-comment">// 🎂 Our Texture&#x27;s Size</span>
<span class="hljs-type">unsigned</span> width = <span class="hljs-number">1</span>;
<span class="hljs-type">unsigned</span> height = <span class="hljs-number">1</span>;

<span class="hljs-comment">// ⚪🔵🟢🔴 ABGR</span>
<span class="hljs-type">unsigned</span> data[<span class="hljs-number">1</span>] = {<span class="hljs-number">0xFFFFFFFF</span>};

<span class="hljs-comment">// 🌟Create Texture</span>
<span class="hljs-built_in">glGenTextures</span>(<span class="hljs-number">1</span>, &amp;tAlbedo);
<span class="hljs-comment">// 🌳 Set the current active Texture ID</span>
<span class="hljs-built_in">glActiveTexture</span>(GL_TEXTURE0);
<span class="hljs-comment">// 🟢 Set texture object to GL Texture ID</span>
<span class="hljs-built_in">glBindTexture</span>(GL_TEXTURE_2D, tAlbedo);
<span class="hljs-comment">// 💾 Assign data to texture</span>
<span class="hljs-built_in">glTexImage2D</span>(GL_TEXTURE_2D, <span class="hljs-number">0</span>, GL_RGBA8, width, height, <span class="hljs-number">0</span>, GL_RGBA,
              GL_UNSIGNED_BYTE, data);

<span class="hljs-comment">// 🔁 Sampler Behavior (Linear vs. Nearest Neighbor, Repeat vs. Clamp)</span>
<span class="hljs-built_in">glTexParameteri</span>(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
<span class="hljs-built_in">glTexParameteri</span>(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
<span class="hljs-built_in">glTexParameteri</span>(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
<span class="hljs-built_in">glTexParameteri</span>(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
</code></pre></div>
<p>And once they&#x27;re created, you can send access them in your shader like so:</p>
<div><pre><code class="language-glsl"><span class="hljs-keyword">uniform</span> <span class="hljs-type">sampler2D</span> tAlbedo;</code></pre></div>
<p>And do the following to send it to your shader:</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// ✋ Declare Uniform Location Handle</span>
<span class="hljs-type">int</span> fragAlbedoUniform = <span class="hljs-number">-1</span>;

<span class="hljs-comment">// 🔎 Get Uniform Location</span>
fragAlbedoUniform =
    <span class="hljs-built_in">glGetUniformLocation</span>(shader.program, <span class="hljs-string">&quot;tAlbedo&quot;</span>);
<span class="hljs-comment">// 🌳 Set the current active Texture ID</span>
<span class="hljs-built_in">glActiveTexture</span>(GL_TEXTURE0);
<span class="hljs-comment">// 🟢 Set texture object to GL Texture ID</span>
<span class="hljs-built_in">glBindTexture</span>(GL_TEXTURE_2D, tAlbedo);
<span class="hljs-comment">// Set a uniform int as the current texture ID being referenced</span>
<span class="hljs-built_in">glUniform1i</span>(fragAlbedoUniform, <span class="hljs-number">0</span>);</code></pre></div>
<blockquote>
<p>As for loading textures from files, we&#x27;ll discuss that in a later post.</p>
</blockquote>
<h3 id="vertex-array-object">Vertex Array Object</h3>
<p><img src="https://alain.xyz/blog/raw-opengl/assets/vao-diagram.svg" alt="Diagram of VAO and coresponding Code"/></p>
<p>A <strong>Vertex Array Object (VAO)</strong> is a method of grouping together different vertex buffers such that they can all be bound as a group.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// ✋ Declare Vertex Array Object Handle</span>
GLuint vao = <span class="hljs-number">0</span>;


<span class="hljs-built_in">glGenVertexArrays</span>(<span class="hljs-number">1</span>, &amp;vao);

<span class="hljs-comment">// 💕 Bind Vertex Array Object</span>
<span class="hljs-built_in">glBindVertexArray</span>(vao);

<span class="hljs-comment">// 🔺 Bind positionVBO to VAO</span>
<span class="hljs-built_in">glBindBuffer</span>(GL_ARRAY_BUFFER, positionVBO);
<span class="hljs-comment">// 🔎 Get position of attribute from shader program</span>
GLint positionAttrib = <span class="hljs-built_in">glGetAttribLocation</span>(program, <span class="hljs-string">&quot;inPosition&quot;</span>);
<span class="hljs-comment">// 🩹 Bind positionVBO to VAO with the index of positionAttrib (0)</span>
<span class="hljs-built_in">glEnableVertexAttribArray</span>(positionAttrib);
<span class="hljs-comment">// 💬 Describe data layout of VBO</span>
<span class="hljs-built_in">glVertexAttribPointer</span>(positionAttrib, <span class="hljs-number">3</span>, GL_FLOAT, GL_FALSE,
                      <span class="hljs-built_in">sizeof</span>(<span class="hljs-type">float</span>) * <span class="hljs-number">3</span>, (<span class="hljs-type">void</span>*)<span class="hljs-number">0</span>);

<span class="hljs-comment">// 🎨 Bind colorVBO to VAO</span>
<span class="hljs-built_in">glBindBuffer</span>(GL_ARRAY_BUFFER, colorVBO);
<span class="hljs-comment">// 🔎 Get position of attribute from shader program</span>
GLint colorAttrib = <span class="hljs-built_in">glGetAttribLocation</span>(program, <span class="hljs-string">&quot;inColor&quot;</span>);
<span class="hljs-comment">// 🩹 Bind positionVBO to VAO with the index of colorAttrib (1)</span>
<span class="hljs-built_in">glEnableVertexAttribArray</span>(colorAttrib);
<span class="hljs-comment">// 💬 Describe data layout of VBO</span>
<span class="hljs-built_in">glVertexAttribPointer</span>(colorAttrib, <span class="hljs-number">3</span>, GL_FLOAT, GL_FALSE,
                      <span class="hljs-built_in">sizeof</span>(<span class="hljs-type">float</span>) * <span class="hljs-number">3</span>, (<span class="hljs-type">void</span>*)<span class="hljs-number">0</span>);

<span class="hljs-comment">// 🗄️ Bind IBO to VAO</span>
<span class="hljs-built_in">glBindBuffer</span>(GL_ELEMENT_ARRAY_BUFFER, ibo);</code></pre></div>
<h2 id="rendering">Rendering</h2>
<p><img src="https://alain.xyz/blog/raw-opengl/assets/triangle-raster.gif" alt="Rasterization Animation"/></p>
<p>To draw, call either <code>glDrawArrays</code> to draw based off a specified pattern of how your vertex buffer is organized, or <code>glDrawElements</code> if you&#x27;re using an index buffer (you&#x27;ll tend to always want to do this).</p>
<div><pre><code class="language-cpp"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">render</span><span class="hljs-params">(<span class="hljs-type">unsigned</span> width, <span class="hljs-type">unsigned</span> height)</span>
</span>{
  <span class="hljs-comment">// 🖼️ Set the Viewport size to where you&#x27;ll be drawing</span>
  <span class="hljs-built_in">glViewport</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, width, height);

  <span class="hljs-comment">// 🧪 Clear</span>
  <span class="hljs-built_in">glClear</span>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  <span class="hljs-comment">// 🎨 Bind Shader</span>
  <span class="hljs-built_in">glUseProgram</span>(program);

  <span class="hljs-comment">// 🩹 Bind VAO</span>
  <span class="hljs-built_in">glBindVertexArray</span>(vao);

  <span class="hljs-comment">// 🛆 Draw</span>
  <span class="hljs-built_in">glDrawElements</span>(GL_TRIANGLES, <span class="hljs-number">3</span>, GL_UNSIGNED_INT, <span class="hljs-number">0</span>);
}</code></pre></div>
<h2 id="destroying-handles">Destroying Handles</h2>
<p>Once you&#x27;ve finished with an OpenGL handle, either because you&#x27;ve switched scenes, or the application is shutting down, it&#x27;s important to destroy it so as to not leak memory:</p>
<div><pre><code class="language-cpp"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">destroy</span><span class="hljs-params">()</span>
</span>{
  <span class="hljs-built_in">glDeleteBuffers</span>(<span class="hljs-number">1</span>, &amp;positionVBO);
  <span class="hljs-built_in">glDeleteBuffers</span>(<span class="hljs-number">1</span>, &amp;colorVBO);
  <span class="hljs-built_in">glDeleteBuffers</span>(<span class="hljs-number">1</span>, &amp;ibo);
  <span class="hljs-built_in">glDeleteShader</span>(vs);
  <span class="hljs-built_in">glDeleteShader</span>(fs);
  <span class="hljs-built_in">glDeleteProgram</span>(program);
  <span class="hljs-built_in">glDeleteVertexArrays</span>(<span class="hljs-number">1</span>, &amp;vao);
}</code></pre></div>
<h2 id="conclusion">Conclusion</h2>
<p>Rendering in OpenGL is incredibly easy, just initialize your buffers, bind those buffers to a VAO, bind that VAO to the GL state, bind a <strong>Shader Program</strong> to the GL State, and <code>glDrawElements</code>.</p>
<p>This is the raster based method of rendering triangles, but there&#x27;s a number of other aspects of the OpenGL API I omitted, such as:</p>
<ul>
<li>Frame Buffer Creation</li>
<li>Compute Shaders</li>
<li>Transform Feedback</li>
<li>Cube Maps</li>
<li><code>glDrawArrays</code> options</li>
<li>Blend Modes</li>
</ul>
<p>With other aspects of an OpenGL app only present in the sample code:</p>
<ul>
<li>Context Creation</li>
<li>Swapping Back buffers</li>
<li>Window Creation</li>
<li>Destroying Core API Data Structures</li>
</ul>
<p>Not to mention aspects of software engineering such as project organization, OS window management, <a href="https://alain.xyz/blog/game-engine-architecture">game engine architecture</a>, <a href="https://alain.xyz/blog/realtime-renderer-architectures">real time renderer architecture</a>, etc. You can look forward to that and much more in future blog posts and e-books.</p>
<p>You&#x27;ll find all the source code described in this post in the <a href="http://github.com/alaingalvan/opengl-seed">Github repo</a> here.</p>
<h3 id="more-resources">More Resources</h3>
<ul>
<li>
<p><a href="https://joeydevries.com/">Joey de Vries</a> (<a href="https://twitter.com/JoeyDeVriez">@JoeyDeVriez</a>) released <a href="https://learnopengl.com/">LearnOpenGL.com</a>, an amazing e-book introducing OpenGL and PBR. <a href="#ref_vries2018">[de Vries 2018]</a></p>
</li>
<li>
<p><a href="https://while.io/">Alexander Overvoorde</a> (<a href="https://twitter.com/Overv">@Overv</a>) released <a href="https://open.gl/">Open.gl</a>, a similar e-book introducing OpenGL. <a href="#ref_overvoorde2015">[Overvoorde 2015]</a></p>
</li>
<li>
<p><a href="http://www.miracleworld.net/">Omar Cornut</a> (<a href="https://twitter.com/ocornut">@ocornut</a>)&#x27;s <a href="https://github.com/ocornut/imgui/tree/master/examples">ImGui&#x27;s OpenGL Examples</a> are great to get UIs up and running in an OpenGL application quickly.</p>
</li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_openglmac"><span class="markademic-citename">[Inc. 2012]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">OpenGL Programming Guide for Mac</span></strong></em><br/><span class="markademic-authors"><span><span>Apple Inc.</span></span></span><br/><span><span class="markademic-publisher">2012</span><br/></span><span><a href="https://developer.apple.com/library/content/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_intro/opengl_intro.html">developer.apple.com</a></span></div></td></tr><tr><td id="ref_vries2018"><span class="markademic-citename">[de Vries 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Learn OpenGL</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/JoeyDeVries.png" alt="Joey de Vries&#x27;s picture"/><a href="https://joeydevries.com">Joey de Vries</a> (<a href="https://twitter.com/JoeyDeVriez">@JoeyDeVriez</a>)</span></span><br/><span><span class="markademic-publisher">2018</span><br/></span><span><a href="https://learnopengl.com/">learnopengl.com</a></span></div></td></tr><tr><td id="ref_overvoorde2015"><span class="markademic-citename">[Overvoorde 2015]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Open.GL</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/Overv.png" alt="Alexander Overvoorde&#x27;s picture"/><a href="https://while.io/">Alexander Overvoorde</a> (<a href="https://twitter.com/Overv">@Overv</a>)</span></span><br/><span><span class="markademic-publisher">2015</span><br/></span><span><a href="https://open.gl/">open.gl</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Noise Generation Survey</title>
<link>https://alain.xyz/blog/noise-generation-survey</link>
<description>A summary of different hashing functions, low-discrepancy sequences, turbulence, convolution, and artistic generation techniques.</description>
<guid isPermaLink="true">https://alain.xyz/blog/noise-generation-survey</guid>
<pubDate>Mon, 01 Jul 2019 12:30:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>shader</category><category>noise</category><category>generation</category>
<enclosure url="https://alain.xyz/blog/noise-generation-survey/assets/cover.jpg" length="590164" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/noise-generation-survey/assets/white-sine-noise.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/noise-generation-survey/assets/halton-noise.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/noise-generation-survey/assets/ashina-simplex-noise.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/noise-generation-survey/assets/gradient-noise.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/noise-generation-survey/assets/convolution-noise.png"/><p>Noise generation is at the heart of procedural texture generation tools such as <a href="https://www.substance3d.com/products/substance-designer">Substance Designer</a>, technical art, computer generated art, procedural mesh generation for terrain, clouds, water, and much more.</p>
<p>It&#x27;s difficult to find a reference of each of these functions and what is possible with them, so the following is a survey similar to that of <a href="#ref_lagae2010">[Lagae et al. 2010]</a> of a variety of noise functions and the operations that can be done to drive interesting and complex effects. This is written for scientists and technical artists to serve as a useful reference for the current state of the art.</p>
<h2 id="codepen-demo">Codepen Demo</h2>
<iframe height="360" style="width:100%" scrolling="no" title="Noise Generation Survey" src="https://codepen.io/alaingalvan/embed/preview/vqdRvW/?height=362&amp;theme-id=dark&amp;default-tab=result" frameBorder="0" allowTransparency="true" allowFullScreen=""></iframe>
<h2 id="white-noise">White Noise</h2>
<p><img src="https://alain.xyz/blog/noise-generation-survey/assets/white-sine-noise.png" alt="White Sine Noise"/></p>
<p>White noise is an example of completely random noise with an equal number of high and low frequency information over a given domain.</p>
<h3 id="deterministic-hashing">Deterministic Hashing</h3>
<p>There&#x27;s a number of different approaches to random number generation.</p>
<p>It&#x27;s common in ShaderToy to use the fractal error of a sine function to approximate noise.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🔺 Sine hash by Patricio Gonzalez Vivo. Public Domain.</span>
<span class="hljs-comment">// http://patriciogonzalezvivo.com/2015/thebookofshaders/10/</span>

<span class="hljs-type">float</span> rand(<span class="hljs-type">float</span> n)
{
    <span class="hljs-keyword">return</span> frac(<span class="hljs-built_in">sin</span>(n) * <span class="hljs-number">43758.5453</span>);
}

<span class="hljs-type">float</span> rand(<span class="hljs-type">vec2</span> uv)
{
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">fract</span>(<span class="hljs-built_in">sin</span>(<span class="hljs-built_in">dot</span>(uv.xy ,<span class="hljs-type">vec2</span>(<span class="hljs-number">12.9898</span>,<span class="hljs-number">78.233</span>))) * <span class="hljs-number">43758.5453</span>);
}

<span class="hljs-type">vec2</span> rand2(<span class="hljs-type">vec2</span> p)
{
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">fract</span>(<span class="hljs-built_in">sin</span>(<span class="hljs-type">vec2</span>(<span class="hljs-built_in">dot</span>(p,<span class="hljs-type">vec2</span>(<span class="hljs-number">127.1</span>, <span class="hljs-number">311.7</span>)), <span class="hljs-built_in">dot</span>(p,<span class="hljs-type">vec2</span>(<span class="hljs-number">269.5</span>, <span class="hljs-number">183.3</span>)))) * <span class="hljs-number">43758.5453</span>);
}</code></pre></div>
<p>These are what&#x27;s known as <strong>Deterministic Hashing Functions</strong>, in that the same input would always return the same output. This is perfect for shaders where such deterministic behavior would be desirable to drive effects based on UV coordinates, uniforms, etc.</p>
<h3 id="non-deterministic-hashing">Non-Deterministic Hashing</h3>
<p>The <strong>Mersenne Twister</strong> is a non-deterministic method of generating random values, built off <a href="#ref_matsumoto1998">[Matsumoto et al. 1998]</a>. The Mersenee Twister currently powers the C++ standard&#x27;s official hashing functions.</p>
<h2 id="low-discrepancy-sequences">Low-Discrepancy Sequences</h2>
<p>Van Der Corpus sequences are an example of low discrepancy pseudo-random generators, designed for use in monte-carlo ray tracers to help reduce repeating sampling values during integration.</p>
<h3 id="halton-sequence">Halton Sequence</h3>
<p><img src="https://alain.xyz/blog/noise-generation-survey/assets/halton-noise.png" alt="EA Halton Sequence"/></p>
<p>Halton sequences produce a useful noise pattern that is somewhat low in frequency, making it useful for real time ray tracing.</p>
<h2 id="perlin-noise">Perlin Noise</h2>
<p>Ken Perlin first introduces an algorithm to generate a white noise image in <a href="#ref_perlin1985">[Perlin 1985]</a>, where he also defines key terms used to this day such as <strong>turbulence</strong>.</p>
<p><strong>Perlin Noise</strong> <a href="#ref_perlin2001">[Perlin 2002]</a> is arguably the best known procedural noise function and has been used for years for simulating clouds, smoke, fire, etc.</p>
<blockquote>
<p>Perlin noise assigns a psudo-random gradient vector to each point of the integer lattice. The value of the function at a point on the 2D plane is then determined by smooth interpolation between the 4 closest gradient vectors projected onto the vectors between the current point and the lattice point each vector is assigned to. The psudo-randomness of successive hashing of the lattice point coordinates with a permuation table.</p>
</blockquote>
<p>Improved perlin noise reduced artifacts in the derivatives and improved overall noise appearance. Moddiied noise [Olano 2005] replaced the permuation table with a hash function based on a <strong>psudo-random number generato</strong> (PRNG). <strong>Better Gradient Noise</strong> <a href="#ref_kensler2008">[Kensler et al. 2008]</a> uses permutation table per dimension and different hashing to better de-correlate the values. it also widened the filter kernel to improve band limitation.</p>
<p>All Perlin noise is periodic determined by the hash function of size of the permuation table.</p>
<p>Better gradient noise uses a 4x4 filter instead of a 2x2.</p>
<h3 id="simplex-noise">Simplex Noise</h3>
<p><img src="https://alain.xyz/blog/noise-generation-survey/assets/ashina-simplex-noise.png" alt="Simplex Noise"/></p>
<p>Simplex is an improvement over <strong>Perlin Noise</strong>, which features a number of public domain variants such as:</p>
<ul>
<li>
<p><a href="https://github.com/ashima/webgl-noise/blob/master/src/noise2D.glsl">Ashima&#x27;s Simplex Noise</a></p>
</li>
<li>
<p><a href="https://www.shadertoy.com/view/Msf3WH">Inigo&#x27;s Simplex Noise</a></p>
</li>
</ul>
<h2 id="voronoi-noise">Voronoi Noise</h2>
<div><pre><code class="language-glsl"><span class="hljs-comment">// Voronoi Noise by Inigo Quilez</span>
<span class="hljs-comment">// https://www.shadertoy.com/view/Xd23Dh</span>
<span class="hljs-comment">// More info here: http://iquilezles.org/www/articles/voronoise/voronoise.htm</span>

<span class="hljs-type">vec3</span> hash3( <span class="hljs-type">vec2</span> p )
{
    <span class="hljs-type">vec3</span> q = <span class="hljs-type">vec3</span>( <span class="hljs-built_in">dot</span>(p,<span class="hljs-type">vec2</span>(<span class="hljs-number">127.1</span>,<span class="hljs-number">311.7</span>)),
                   <span class="hljs-built_in">dot</span>(p,<span class="hljs-type">vec2</span>(<span class="hljs-number">269.5</span>,<span class="hljs-number">183.3</span>)),
                   <span class="hljs-built_in">dot</span>(p,<span class="hljs-type">vec2</span>(<span class="hljs-number">419.2</span>,<span class="hljs-number">371.9</span>)) );
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">fract</span>(<span class="hljs-built_in">sin</span>(q)*<span class="hljs-number">43758.5453</span>);
}

<span class="hljs-type">float</span> iqnoise( <span class="hljs-keyword">in</span> <span class="hljs-type">vec2</span> x, <span class="hljs-type">float</span> u, <span class="hljs-type">float</span> v )
{
    <span class="hljs-type">vec2</span> p = <span class="hljs-built_in">floor</span>(x);
    <span class="hljs-type">vec2</span> f = <span class="hljs-built_in">fract</span>(x);

    <span class="hljs-type">float</span> k = <span class="hljs-number">1.0</span>+<span class="hljs-number">63.0</span>*<span class="hljs-built_in">pow</span>(<span class="hljs-number">1.0</span>-v,<span class="hljs-number">4.0</span>);

    <span class="hljs-type">float</span> va = <span class="hljs-number">0.0</span>;
    <span class="hljs-type">float</span> wt = <span class="hljs-number">0.0</span>;
    <span class="hljs-keyword">for</span>( <span class="hljs-type">int</span> j=<span class="hljs-number">-2</span>; j&lt;=<span class="hljs-number">2</span>; j++ )
    <span class="hljs-keyword">for</span>( <span class="hljs-type">int</span> i=<span class="hljs-number">-2</span>; i&lt;=<span class="hljs-number">2</span>; i++ )
    {
        <span class="hljs-type">vec2</span> g = <span class="hljs-type">vec2</span>( <span class="hljs-type">float</span>(i),<span class="hljs-type">float</span>(j) );
        <span class="hljs-type">vec3</span> o = hash3( p + g )*<span class="hljs-type">vec3</span>(u,u,<span class="hljs-number">1.0</span>);
        <span class="hljs-type">vec2</span> r = g - f + o.xy;
        <span class="hljs-type">float</span> d = <span class="hljs-built_in">dot</span>(r,r);
        <span class="hljs-type">float</span> ww = <span class="hljs-built_in">pow</span>( <span class="hljs-number">1.0</span>-<span class="hljs-built_in">smoothstep</span>(<span class="hljs-number">0.0</span>,<span class="hljs-number">1.414</span>,<span class="hljs-built_in">sqrt</span>(d)), k );
        va += o.z*ww;
        wt += ww;
    }

    <span class="hljs-keyword">return</span> va/wt;
}</code></pre></div>
<h2 id="noise-operations">Noise Operations</h2>
<h3 id="tiling">Tiling</h3>
<p>There&#x27;s a number of techniques to tile noise textures:</p>
<ul>
<li>Use a periodic domain</li>
<li>Wang Non-Periodic Tiling <a href="#ref_kirillov2019">[Kirillov 2019]</a></li>
<li>Statistical Tiling <a href="#ref_deliot2019">[Deliot et al. 2019]</a></li>
</ul>
<h3 id="turbulence">Turbulence</h3>
<p><img src="https://alain.xyz/blog/noise-generation-survey/assets/gradient-noise.png" alt="Turbulance Example"/></p>
<p><strong>Turbulence</strong> is the layering of noise (sometimes referred to as <em>octaves</em> of noise in literature), and can be used to generate interesting volumetric effects such as clouds, dust, water, etc.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 3D gradient Noise</span>
<span class="hljs-comment">// MIT License</span>
<span class="hljs-comment">// Copyright © 2013 Inigo Quilez</span>
<span class="hljs-comment">// https://www.shadertoy.com/view/Xsl3Dl</span>

<span class="hljs-type">vec3</span> hash(<span class="hljs-type">vec3</span> p)
{
  p = <span class="hljs-type">vec3</span>(<span class="hljs-built_in">dot</span>(p, <span class="hljs-type">vec3</span>(<span class="hljs-number">127.1</span>, <span class="hljs-number">311.7</span>, <span class="hljs-number">74.7</span>)), <span class="hljs-built_in">dot</span>(p, <span class="hljs-type">vec3</span>(<span class="hljs-number">269.5</span>, <span class="hljs-number">183.3</span>, <span class="hljs-number">246.1</span>)),
           <span class="hljs-built_in">dot</span>(p, <span class="hljs-type">vec3</span>(<span class="hljs-number">113.5</span>, <span class="hljs-number">271.9</span>, <span class="hljs-number">124.6</span>)));

  <span class="hljs-keyword">return</span> <span class="hljs-number">-1.0</span> + <span class="hljs-number">2.0</span> * <span class="hljs-built_in">fract</span>(<span class="hljs-built_in">sin</span>(p) * <span class="hljs-number">43758.5453123</span>);
}

<span class="hljs-type">float</span> noise(<span class="hljs-keyword">in</span> <span class="hljs-type">vec3</span> p) {
  <span class="hljs-type">vec3</span> i = <span class="hljs-built_in">floor</span>(p);
  <span class="hljs-type">vec3</span> f = <span class="hljs-built_in">fract</span>(p);

  <span class="hljs-type">vec3</span> u = f * f * (<span class="hljs-number">3.0</span> - <span class="hljs-number">2.0</span> * f);

  <span class="hljs-keyword">return</span> <span class="hljs-built_in">mix</span>(
      <span class="hljs-built_in">mix</span>(<span class="hljs-built_in">mix</span>(<span class="hljs-built_in">dot</span>(hash(i + <span class="hljs-type">vec3</span>(<span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>)), f - <span class="hljs-type">vec3</span>(<span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>)),
              <span class="hljs-built_in">dot</span>(hash(i + <span class="hljs-type">vec3</span>(<span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>)), f - <span class="hljs-type">vec3</span>(<span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>)), u.x),
          <span class="hljs-built_in">mix</span>(<span class="hljs-built_in">dot</span>(hash(i + <span class="hljs-type">vec3</span>(<span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>)), f - <span class="hljs-type">vec3</span>(<span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>)),
              <span class="hljs-built_in">dot</span>(hash(i + <span class="hljs-type">vec3</span>(<span class="hljs-number">1.0</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>)), f - <span class="hljs-type">vec3</span>(<span class="hljs-number">1.0</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>)), u.x),
          u.y),
      <span class="hljs-built_in">mix</span>(<span class="hljs-built_in">mix</span>(<span class="hljs-built_in">dot</span>(hash(i + <span class="hljs-type">vec3</span>(<span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>)), f - <span class="hljs-type">vec3</span>(<span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>)),
              <span class="hljs-built_in">dot</span>(hash(i + <span class="hljs-type">vec3</span>(<span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>)), f - <span class="hljs-type">vec3</span>(<span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>)), u.x),
          <span class="hljs-built_in">mix</span>(<span class="hljs-built_in">dot</span>(hash(i + <span class="hljs-type">vec3</span>(<span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">1.0</span>)), f - <span class="hljs-type">vec3</span>(<span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">1.0</span>)),
              <span class="hljs-built_in">dot</span>(hash(i + <span class="hljs-type">vec3</span>(<span class="hljs-number">1.0</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">1.0</span>)), f - <span class="hljs-type">vec3</span>(<span class="hljs-number">1.0</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">1.0</span>)), u.x),
          u.y),
      u.z);
}

<span class="hljs-keyword">const</span> <span class="hljs-type">mat3</span> m = <span class="hljs-type">mat3</span>(<span class="hljs-number">0.00</span>, <span class="hljs-number">0.80</span>, <span class="hljs-number">0.60</span>, <span class="hljs-number">-0.80</span>, <span class="hljs-number">0.36</span>, <span class="hljs-number">-0.48</span>, <span class="hljs-number">-0.60</span>, <span class="hljs-number">-0.48</span>, <span class="hljs-number">0.64</span>);

<span class="hljs-type">float</span> turbulance(<span class="hljs-type">vec3</span> pos, <span class="hljs-type">float</span> lacunarity, <span class="hljs-type">float</span> gain ) {
  <span class="hljs-type">float</span> f;
  <span class="hljs-type">vec3</span> q = <span class="hljs-number">8.0</span> * pos;
  f = gain * noise(q);
  q = m * q * <span class="hljs-number">2.01</span> * lacunarity;
  f += (gain * gain) * noise(q);
  q = m * q * <span class="hljs-number">2.02</span> * lacunarity;
  f += (gain * gain * gain) * noise(q);
  q = m * q * <span class="hljs-number">2.03</span> * lacunarity;
  f += (gain * gain * gain * gain) * noise(q);
  q = m * q * <span class="hljs-number">2.01</span> * lacunarity;
  f = <span class="hljs-number">0.5</span> + <span class="hljs-number">0.5</span> * f;
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">clamp</span>(f, <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>);
}</code></pre></div>
<h3 id="domain-warp-convolutions">Domain Warp Convolutions</h3>
<p><img src="https://alain.xyz/blog/noise-generation-survey/assets/convolution-noise.png" alt="Convolution Noise"/></p>
<p>It&#x27;s possible to feed noise functions into one another, resulting in an effect similar to a double pendulum and thus could be described as lagrangian mechanics on a n-dimensional domain.</p>
<p>With convolutions it&#x27;s possible to generate chaotic, complex brownian motion effects, twisting and turning waves, and beautiful patterns.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// Paint Archipelago by @samlo</span>
<span class="hljs-comment">// https://www.shadertoy.com/view/3lf3z2</span>

<span class="hljs-type">float</span> convolution(<span class="hljs-type">vec3</span> p, <span class="hljs-type">float</span> time)
{
    <span class="hljs-type">vec2</span> <span class="hljs-keyword">offset</span> = <span class="hljs-type">vec2</span>(<span class="hljs-number">-0.5</span>);

    <span class="hljs-type">vec2</span> aPos = <span class="hljs-type">vec2</span>(<span class="hljs-built_in">sin</span>(time * <span class="hljs-number">0.005</span>), <span class="hljs-built_in">sin</span>(time * <span class="hljs-number">0.01</span>)) * <span class="hljs-number">6.</span>;
    <span class="hljs-type">vec2</span> aScale = <span class="hljs-type">vec2</span>(<span class="hljs-number">3.0</span>);
    <span class="hljs-type">float</span> a = turbulance(p * aScale + aPos);

    <span class="hljs-type">vec2</span> bPos = <span class="hljs-type">vec2</span>(<span class="hljs-built_in">sin</span>(time * <span class="hljs-number">0.01</span>), <span class="hljs-built_in">sin</span>(time * <span class="hljs-number">0.01</span>)) * <span class="hljs-number">1.</span>;
    <span class="hljs-type">vec2</span> bScale = <span class="hljs-type">vec2</span>(<span class="hljs-number">0.6</span>);
    <span class="hljs-type">float</span> b = turbulance((p + a) * bScale + bPos);

    <span class="hljs-type">vec2</span> cPos = <span class="hljs-type">vec2</span>(<span class="hljs-number">-0.6</span>, <span class="hljs-number">-0.5</span>) + <span class="hljs-type">vec2</span>(<span class="hljs-built_in">sin</span>(-time * <span class="hljs-number">0.001</span>), <span class="hljs-built_in">sin</span>(time * <span class="hljs-number">0.01</span>)) * <span class="hljs-number">2.</span>;
    <span class="hljs-type">vec2</span> cScale = <span class="hljs-type">vec2</span>(<span class="hljs-number">2.6</span>);
    <span class="hljs-type">float</span> c = turbulance((p + b) * cScale + cPos);
    <span class="hljs-keyword">return</span> c;
}</code></pre></div>
<h2 id="more-resources">More Resources</h2>
<ul>
<li>
<p><a href="https://github.com/Auburn/FastNoise2">FastNoise2</a> is a C++ noise generation library that features a node graph editor for designing 3D noise patterns.</p>
</li>
<li>
<p>The <a href="https://alain.xyz/(https://github.com/rust-random)">Rust Random</a> ecosystem of libraries, with cryptographically secure random functions, and implementations of state of the art research in Rust.</p>
</li>
<li>
<p>Alan Wolfe (<a href="https://twitter.com/Atrix256">@Atrix256</a>)&#x27;s series of articles on <a href="https://blog.demofox.org/2017/10/20/generating-blue-noise-sample-points-with-mitchells-best-candidate-algorithm/">Blue Noise</a>.</p>
</li>
<li>
<p><a href="https://www.pcg-random.org/">PCG&#x27;s family of random functions</a>, with implementations in C and comparisons between different techniques such as ChaCha20, Mersenne Twister, etc.</p>
</li>
<li>
<p><a href="https://github.com/kecho/Noice">Noice</a> by Kleber Garcia (<a href="https://twitter.com/kechogarcia">@kechogarcia</a>) is a CLI tool to generate different types of noise very nicely.</p>
</li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_lagae2010"><span class="markademic-citename">[Lagae et al. 2010]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">State of the Art in Procedural Noise Functions</span></strong></em><br/><span class="markademic-authors"><span><span>Ares Lagae</span></span><span>, <span>Sylvain Lefebvre</span></span><span>, <span>Rob Cook</span></span><span>, <span>Tony DeRose</span></span><span>, <span>George Drettakis</span></span><span>, <span>David S. Ebert</span></span><span>, <span>John P. Lewis</span></span><span>, <span>Ken Perlin</span></span><span> and <span>Matthias Zwicker</span></span></span><br/><span><span class="markademic-publisher">Eurographics 2010</span><br/></span><span><a href="http://graphics.cs.kuleuven.be/publications/LLCDDELPZ10STARPNF/">graphics.cs.kuleuven.be</a></span></div></td></tr><tr><td id="ref_matsumoto1998"><span class="markademic-citename">[Matsumoto et al. 1998]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Mersenne Twister: A 623-Dimensionally Equidistributed Uniform Pseudorandom Number Generator</span></strong></em><br/><span class="markademic-authors"><span><span>Makoto Matsumoto</span></span><span> and <span>Takuji Nishimura</span></span></span><br/><span><span class="markademic-publisher">ACM 1998</span><br/></span><span><a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/ARTICLES/mt.pdf">math.sci.hiroshima-u.ac.jp</a></span></div></td></tr><tr><td id="ref_perlin1985"><span class="markademic-citename">[Perlin 1985]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">An Image Synthesizer</span></strong></em><br/><span class="markademic-authors"><span><span>Ken Perlin</span></span></span><br/><span><span class="markademic-publisher">ACM 1985</span><br/></span><span><a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.220.2248&amp;rep=rep1&amp;type=pdf">citeseerx.ist.psu.edu</a></span></div></td></tr><tr><td id="ref_perlin2001"><span class="markademic-citename">[Perlin 2002]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Improving Noise</span></strong></em><br/><span class="markademic-authors"><span><span>Ken Perlin</span></span></span><br/><span><span class="markademic-publisher">SIGGRAPH 2002</span><br/></span><span><a href="https://mrl.nyu.edu/~perlin/noise/">mrl.nyu.edu</a></span></div></td></tr><tr><td id="ref_kensler2008"><span class="markademic-citename">[Kensler et al. 2008]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Better Gradient Noise</span></strong></em><br/><span class="markademic-authors"><span><span>Andrew Kensler</span></span><span>, <span>Aaron Knoll</span></span><span> and <a href="https://www.petershirley.com/">Peter Shirley</a> (<a href="https://twitter.com/Peter_shirley">@Peter_shirley</a>)</span></span><br/><span><span class="markademic-publisher">SCI Institute 2008</span><br/></span><span><a href="https://www.cs.utah.edu/~aek/research/noise.pdf">cs.utah.edu</a></span></div></td></tr><tr><td id="ref_kirillov2019"><span class="markademic-citename">[Kirillov 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Nonperiodic Tiling of Noise-based Procedural Textures</span></strong></em><br/><span class="markademic-authors"><span><span>Aleksandr Kirillov</span></span></span><br/><span><span class="markademic-publisher">GPU Zen 2 2019</span><br/></span><span><a href="http://gpuzen.blogspot.com/">GPU Zen Website</a></span><span> <a href="https://aleksk01010101.wordpress.com/">High Performance Graphics Paper</a></span></div></td></tr><tr><td id="ref_deliot2019"><span class="markademic-citename">[Deliot et al. 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Procedural Stocastic Textures by Tiling and Blending</span></strong></em><br/><span class="markademic-authors"><span><a href="https://thomasdeliot.wixsite.com/blog">Thomas Deliot</a> (<a href="https://twitter.com/thomasdeliot">@thomasdeliot</a>)</span><span> and <a href="https://eheitzresearch.wordpress.com/">Eric Heitz</a> (<a href="https://twitter.com/eric_heitz">@eric_heitz</a>)</span></span><br/><span><span class="markademic-publisher">GPU Zen 2 2019</span><br/></span><span><a href="https://blogs.unity3d.com/2019/02/14/procedural-stochastic-texturing-in-unity/">blogs.unity3d.com</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Ray Tracing Filtering</title>
<link>https://alain.xyz/blog/ray-tracing-filtering</link>
<description>An overview of image based ray tracing denoising, discussing blurring kernels and spatio-temporal reprojection techniques described in research papers and real time rendering engines.</description>
<guid isPermaLink="true">https://alain.xyz/blog/ray-tracing-filtering</guid>
<pubDate>Fri, 28 Jun 2019 01:54:43 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>denoise</category><category>denoising</category><category>raytracing</category><category>filter</category><category>svgf</category><category>asvgf</category><category>a-trous</category><category>atrous</category><category>blue</category><category>noise</category>
<enclosure url="https://alain.xyz/blog/ray-tracing-filtering/assets/cover.jpg" length="2056333" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-filtering/assets/rxgi.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-filtering/assets/execution-order.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-filtering/assets/bluenoise256.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-filtering/assets/noise-blur-comparison.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-filtering/assets/sobolnoise256.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-filtering/assets/motion-buffer.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-filtering/assets/history-length.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-filtering/assets/normal.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/ray-tracing-filtering/assets/depth.jpg"/><link rel="preload" as="image" href="https://github.com/ingowald.png"/><link rel="preload" as="image" href="https://github.com/morgan3d.png"/><link rel="preload" as="image" href="https://github.com/tunabrain.png"/><link rel="preload" as="image" href="https://github.com/NeoSpark314.png"/><link rel="preload" as="image" href="https://github.com/cschied.png"/><link rel="preload" as="image" href="https://github.com/inversepixel.png"/><link rel="preload" as="image" href="https://github.com/mmp.png"/><link rel="preload" as="image" href="https://github.com/Atrix256.png"/><link rel="preload" as="image" href="https://github.com/belcour.png"/><link rel="preload" as="image" href="https://github.com/behindthepixels.png"/><p>This post is the first version of a series of posts on Ray Tracing denoising. The <a href="https://alain.xyz/blog/ray-tracing-denoising">latest post can be found here</a>.</p>
<hr/>
<blockquote>
<p>There&#x27;s an academic arms race to produce the best real-time denoised image from 1 sample per pixel (spp) path traced images. Every new paper like this brings us closer to an ideal solution that can be baked into silicon (mobile raytacing for the masses!) ~ <a href="https://twitter.com/ddiakopoulos/status/1129223581898985472">Dimitri Diakopoulos (@ddiakopoulos)</a></p>
</blockquote>
<p>Since the advent of monte-carlo ray tracing, there have been attempts to reduce the time it takes to converge rays.</p>
<p>This began with algorithms that focused on <strong>sampling techniques</strong>, using stochastic sampling with biased noise generated by quasi-random sequences, weighing different rays to approximate the best distribution of samples with multiple importance sampling (MIS), Next Event Estimation (NEE), Russian Roulette (RR) <a href="#ref_veach1998">[Veach 1998]</a>. Though not necessarily new, recently the use of low discrepancy sampling <a href="#ref_jarosz2019">[Jarosz et al. 2019]</a> and tillable blue noise <a href="#ref_benyoub2019">[Benyoub 2019]</a> has been used by Unity Technologies, and NVIDIA in real time ray tracers to great success.</p>
<blockquote>
<p>While this post reviews techniques on improving sampling via filters and noise algorithms, it does not touch improving sampling via modifications to your ray tracing routine with Environment MIS, Ray Sorting, etc. For more details on that visit my other post on <a href="https://alain.xyz/blog/real-time-ray-tracing">Real Time Ray Tracing</a>.</p>
</blockquote>
<p><strong>Encoding techniques</strong> have been used by recent literature as a means of improving the performance of ray tracing applications by attempting to encode sparsely sampled outputs such as indirect global illumination as 2 levels of <em>spherical harmonics</em>  to help avoid salt/peppering, <em>upscaling and finding the best candidate</em> for a ray sample thanks to jittering the frame buffer to better match a given sample to it&#x27;s closest hit point based on using feature buffers such as normals, depth <a href="#ref_baktash2018">[Abdollah-shamshir-saz 2018]</a>.</p>
<p><strong>Signal processing techniques</strong> such as <em>Gaussian</em>, <em>Median</em> <a href="#ref_mara2017">[Mara et al. 2017]</a>, <em>Bilateral</em>, <em>À-Trous</em> <a href="#ref_dammertz2010">[Dammertz et al. 2010]</a>, and <em>Guided</em> <a href="#ref_he2012">[He et al. 2012]</a> filters have been used to average out and blend regions with low variance. In particular Guided filters driven by <em>feature buffers</em> such as common G-Buffer attachments like depth and normals have seen much success. Machine learning algorithms such as those employed by Intel&#x27;s <a href="https://github.com/OpenImageDenoise/oidn">Open Image Denoise (OIDN)</a>, NVIDIA&#x27;s Denoising Autoencoder <a href="#ref_chaitanya2017">[R. Alla Chaitanya et al. 2017]</a> were used for offline renders such as <a href="https://wiki.blender.org/wiki/Reference/Release_Notes/2.81/Cycles">Blender 2.81</a>, with NVIDIA <a href="#ref_kalantari2015">[Khademi Kalantari et al. 2015]</a> attempting to use <a href="https://twitter.com/NVBackchannel/status/1156995750477254656">trained neural networks in real-time</a> with <a href="https://developer.nvidia.com/designworks/optix/download">Optix 7</a>.</p>
<blockquote>
<p>Visit my other post on <a href="https://alain.xyz/blog/machine-learning-denoising">Machine Learning Denoising</a> for more details on implementing an AI denoiser, though these filtering techniques could be used in conjunction with a neural network denoising pass.</p>
</blockquote>
<p><strong>Accumulation techniques</strong> have seen a resurgence in new literature. While a naive implementation would be to simply accumulate samples on an unchanging scene, it&#x27;s possible to reuse samples in a moving scene. Examples include <a href="#ref_mara2017">[Mara et al. 2017]</a> Spatio-Temporal Filter, the Spatio-Temporal Variance Guided Filter (SVGF) <a href="#ref_schied2017">[Schied 2017]</a>, Spatial Denoising employed by <a href="#ref_baktash2018">[Abdollah-shamshir-saz 2018]</a>, Adaptive SVGF (A-SVGF) <a href="#ref_schied2018">[Schied 2018]</a>, Blockwise Multi-Order Feature Regression (BMFR) <a href="#ref_koskela2019">[Koskela et al. 2019]</a>), and Temporally Dense Ray Tracing <a href="#ref_andersson2019">[Andersson et al. 2019]</a>.</p>
<p><a href="https://morgan3d.github.io/articles/2019-04-01-ddgi/"><img src="https://alain.xyz/blog/ray-tracing-filtering/assets/rxgi.jpg" alt="RXGI fair use screenshot"/></a></p>
<p>In addition, there&#x27;s <strong>separation techniques</strong> that attempt to fine tune behavior for different aspects of a path tracer. The <em>RXGI</em> paper <a href="#ref_mjercik2019">[Majercik et al. 2019]</a> made famous by <a href="https://twitter.com/casualeffects">Morgan McGuire (@CasualEffects)</a> approximated ray traced global illumination (GI) with light probes which used ray tracing to better determine the proper radiance of each probe and to position probes in the scene to avoid bleeding or inaccurate interiors. Specific techniques that can avoid monte carlo integration entirely also avoid the need for real time denoising routines. In addition there have been attempts to move denoising to earlier in rendering by making sampling less biased through ray hashing <a href="#ref_pantaleoni2020">[Pantaleoni 2020]</a> or using information from spatially and temporally adjacent pixels <a href="#ref_bitterli2020">[Bitterli et al. 2020]</a>.</p>
<p>We&#x27;ll be reviewing techniques that can be used to converge to an image close to ground truth as quickly as possible with real-time ray-tracing renderers. Such denoising techniques can then be applied when rendering shadows, ambient occlusion, global illumination, reflections, refractions, volumetric lighting, and more in your applications.</p>
<hr/>
<h2 id="execution-order">Execution Order</h2>
<p><img src="https://alain.xyz/blog/ray-tracing-filtering/assets/execution-order.svg" alt="Execution Order Diagram"/></p>
<p>While there&#x27;s a variety of ways one could write a ray tracing application and denoiser, the following execution order tends to be the case for the state of the art in real time ray tracing hybrid renderer applications in industry and research:</p>
<ol>
<li>
<p><strong>G-Buffer Pass</strong> - Write to feature buffers used commonly for deferred rendering such as view space depth, normals, velocity, or any other feature sets you intend on using when denoising.</p>
</li>
<li>
<p><strong>Ray Trace</strong> - Either with a unified path tracer or using dedicated passes for shadows/reflection/global illumination/ambient occlusion/subsurface scattering/refraction/translucency. Having each aspect of your ray tracer split however can allow for separate denoising schemes that work best for each type of output, though it could be slower.</p>
</li>
<li>
<p><strong>Accumulation</strong> - by interpolating between previous and current color data. Normally this involves reprojecting previous samples, though <a href="#ref_kristof2019">[Kristof 2019]</a> attempts to reproject previous samples first for reflections (so switching steps 2 and 3), then use that data to guide accumulation.</p>
</li>
<li>
<p><strong>Blit Accumulation History</strong> - write your accumulated buffers to be used by the next frame when spatially reprojecting samples. It&#x27;s important to do this <em>before any filtering</em> to prevent a growing blur effect as every frame blurs the previous.</p>
</li>
<li>
<p><strong>Adaptive Heuristics</strong> - A-SVGF  introduces the <strong>Moment Buffer</strong> that encodes change in color variance/velocity to better avoid using old history on areas that have a lot of changes such as moving lights, mirrors, etc. By estimating the current radiance of your path traced image, you can also take advantage of that data to perform firefly rejection.</p>
</li>
<li>
<p><strong>Guided Filtering</strong> - Blur the weighting buffers using a guided filter such as À-Trous, or an edge stopping Gaussian filter. Most denoisers do this 5 times with a 3x3 filter. This can also be guided by the change in velocity/luminance, history length, roughness, shadow penumbra distance, etc.</p>
</li>
<li>
<p><strong>Blit Prepass History</strong> - Write any important previous data sets (linear depth, normals, velocity), preferably by ping-ponging between two output frame buffers to save a copy operation. These will be used for accumulation and reprojection on the next frame.</p>
</li>
</ol>
<h2 id="sampling-techniques">Sampling Techniques</h2>
<p>One of the easiest and fastest ways to get an image to converge faster is to use low discrepancy noise when performing monte-carlo integration. This is better than using purely random noise due to it having a more uniform distribution. This means that the random samples cover the sample domain evenly, rather than cluster and form holes. <a href="#ref_cornel2014">[Cornel 2014]</a></p>
<p>The faster an image converges, the less variance your data will have when feeding it to a denoiser. However, it&#x27;s important to bare in mind the <strong>Curse of Dimensionality</strong>:</p>
<blockquote>
<p>Once the integral is high dimensional, if the integrand is complex, <strong>Stratified Sampling doesn&#x27;t help</strong>. ~ <a href="https://twitter.com/peter_shirley">Peter Shirley (@peter_shirley)</a></p>
</blockquote>
<p>Thus, try to keep the integrand of your ray trace as minimal or as full of constants as possible. <a href="#ref_shirley2018">[Shirley 2018]</a></p>
<h3 id="blue-noise">Blue Noise</h3>
<p><img src="https://alain.xyz/blog/ray-tracing-filtering/assets/bluenoise256.png" alt="Blue Noise Texture Courtesy of ShaderToy"/></p>
<p><strong>Blue noise</strong> is uniformly distributed noise, that is noise with similar samples spread out as far away as possible over a given domain. This lends itself very well to denoising, as there are far less instances of high frequency error in blue noise, making it easier to blur and accumulate <a href="#ref_schied2019">[Schied et al. 2019]</a>.</p>
<p><a href="https://twitter.com/leonard_ritter">Leonard Ritter (@leonard_ritter)</a> wrote a highly performant blue noise texture generation ShaderToy here:</p>
<p style="text-align:center"><iframe width="256" height="256" frameBorder="0" src="https://www.shadertoy.com/embed/XtdyW2?gui=true&amp;t=10&amp;paused=true&amp;muted=false" allowFullScreen=""></iframe></p>
<p>Once you have a uniform <strong>Blue Noise Look Up Texture (LUT)</strong> (sometimes called a <em>precomputed dither matrix</em> in literature) to work with, it&#x27;s possible to map that texture to important regions to sample during ray tracing, such as the BRDF behavior of a given surface and/or the bright regions of an environment texture in the case of image based lighting.</p>
<p><img src="https://alain.xyz/blog/ray-tracing-filtering/assets/noise-blur-comparison.png" alt="Blue vs. White Noise Comparison"/></p>
<p>Blurring blue noise even a little (in this case a 2x gaussian blur) results in a much more uniform result in comparison with white noise where high frequency noise present in monte-carlo rendering would be much more obvious.</p>
<p>Though it should be noted that it can be difficult to blur across edges, especially with temporal anti-aliasing with the previous history buffer may not be accurate to your current viewport to to the perspective pixel shift involved.</p>
<h3 id="sobol-sequences">Sobol Sequences</h3>
<p>Computing blue noise is a somewhat expensive and thus, is only suitable to compute in a preprocessing step. There do however exist functions that behave similarly to blue noise, known as <strong>quasi-random number sequence</strong> algorithms. Examples include the <em>Worley</em>, <em>Sobol</em>, or <em>Halton</em> Sequences, each of which have benefits and tradeoffs <a href="#ref_wolfe2017">[Wolfe 2017]</a>.</p>
<p><img src="https://alain.xyz/blog/ray-tracing-filtering/assets/sobolnoise256.png" alt="Sobol Noise 256x1 texture"/></p>
<p><strong>Sobol</strong> is more computationally expensive but leads to far less discrepancy <a href="#ref_roberts2018">[Roberts 2018]</a>, while Halton is cheaper but has slightly more discrepancy <a href="#ref_wong1997">[Wong et al. 1997]</a>.</p>
<p><a href="https://twitter.com/auzaiffe">Anis Benyoub (Unity Technologies)</a> for instance has chosen to use a 256x1 Sobol texture and a 256x256 RG Blue Noise texture as a means of generating quasi-random monte-carlo (QMC) samples. <a href="#ref_benyoub2019">[Benyoub 2019]</a> <a href="https://twitter.com/eric_heitz">Eric Heitz (@eric_heitz)</a> et al. took this even further with his paper on distributing monte carlo errors over different frames by using a <code>uint sobolSequence[256*256]</code> to scramble blue noise tiles. <a href="#ref_heitz2019">[Heitz et al. 2019]</a></p>
<p>A C++ implementation of <a href="https://gist.github.com/alaingalvan/af92ddbaf3bb01f5ef29bc431bd37891">Sobol</a> can be found here, courtesy of Professor John Burkardt of Virginia Tech.</p>
<p>Having a low discrepancy domain from which to sample your scene can then be used in combination with other techniques such as <strong>Multiple Importance Sampling (MIS)</strong> of the BRDF (material) behavior and light source behavior (with environment maps and participating media being different cases to consider), <strong>Next Event Estimation</strong>, <strong>Russian Roulette</strong>, all with the goal of reducing the variance of a monte-carlo raytraced image.</p>
<blockquote>
<p>For more details regarding these techniques, visit my post on <a href="https://alain.xyz/blog/real-time-raytracing">Real Time Raytracing</a>.</p>
</blockquote>
<h2 id="encoding-techniques">Encoding Techniques</h2>
<h3 id="formats">Formats</h3>
<p>When managing a real time ray tracing application, the question of what formats to use with your trace output buffers will ultimately come into play. The answer to such a question ultimately boils down to:</p>
<ol>
<li>
<p>Are you targeting an HDR output such as with ACES linearization or an HDR 10 bit display, or a 16 bit or 32 bit output image.</p>
</li>
<li>
<p>How many samples you wish to accumulate, as there&#x27;s a limit to where averaging out samples over time no longer contribute to the output image, and a small enough limit where they introduce bias to your image that can lead to color banding.</p>
</li>
</ol>
<p>This <em>sample count limit</em> can be defined as:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mfrac><mn>1</mn><mrow><mi>S</mi><mi>a</mi><mi>m</mi><mi>p</mi><mi>l</mi><mi>e</mi><mi>s</mi></mrow></mfrac><mo>≥</mo><msub><mi>ϵ</mi><mrow><mi>b</mi><mi>i</mi><mi>t</mi><mi>r</mi><mi>a</mi><mi>t</mi><mi>e</mi></mrow></msub></mrow><annotation encoding="application/x-tex">\frac{1}{Samples} \geq \epsilon_{bitrate}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:2.2019em;vertical-align:-0.8804em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3214em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0576em">S</span><span class="mord mathnormal">am</span><span class="mord mathnormal" style="margin-right:0.0197em">pl</span><span class="mord mathnormal">es</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.8804em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≥</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">ϵ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">bi</span><span class="mord mathnormal mtight">t</span><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span><span class="mord mathnormal mtight">a</span><span class="mord mathnormal mtight">t</span><span class="mord mathnormal mtight">e</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span></span>
<p>The limit where the number of samples you&#x27;ve accumulated is less than the minimum ( \epsilon ) of your floating point bit rate. For <code>RGBA16F</code> that number is about 2048 samples. <a href="#ref_wolfe2017b">[Wolfe 2017]</a></p>
<blockquote>
<p>On a side note, formats such as <code>R11G11B10</code> are not recommended due to any accumulation of samples ultimately having a bias towards turning yellow due to the extra precision on the R and G channels.</p>
</blockquote>
<h3 id="spherical-harmonics">Spherical Harmonics</h3>
<p>Rather than writing sparse samples as radiance in a 1 pixel location, it&#x27;s possible to write such samples as spherical harmonics encodings across a larger radius that does not intersect with other samples, and use filtering to fill in the gaps.</p>
<h3 id="upscaling">Upscaling</h3>
<p>Raytracing is an expensive operation to be done at high resolutions such as Ultra HD 4K, so upscaling from a lower resolution can be employed to take advantage of raytracing at interactive frame rates.</p>
<h2 id="spatio-temporal-techniques">Spatio-Temporal Techniques</h2>
<p><strong>Spatiotemporal reprojection</strong> is simply reusing the data from previous frames, <em>spatially</em> reprojecting them to the current frame. Encoding radiance with spatial information has had its earliest literature in <strong>Irradiance Caching</strong> <a href="#ref_ward1988">[Ward et al. 1988]</a>, however real time raytracing takes this idea a step further by keeping data in view space, but attempting to reuse that data by translating it from one frame to the other by means of a motion buffer which calculates the change in a vertex&#x27;s position between the current and previous frame.</p>
<p>This is <em>extremely effective</em> at reducing noise, but relying on temporal data inevitably introduces a slight lag with changes in a scene such as moving lights, objects, as new data needs to be accumulated over time to reflect those changes.</p>
<h3 id="motion-buffer">Motion Buffer</h3>
<p><img src="https://alain.xyz/blog/ray-tracing-filtering/assets/motion-buffer.jpg" alt="Motion Buffer Example"/></p>
<p>A <strong>Motion Buffer</strong> (otherwise known as a <strong>Velocity Buffer</strong>) encodes the change in position of each vertex. This is at the core of spatio-temporal reprojection. This buffer can be calculated by determining the previous and current NDC space coordinate positions of each vertex being rendered, and taking the difference of the two. This can be encoded in a <code>RG16F</code> render target.</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>V</mi><mo>⃗</mo></mover><mo>=</mo><msub><mover accent="true"><mrow><mi>N</mi><mi>D</mi><mi>C</mi></mrow><mo>⃗</mo></mover><mrow><mi>c</mi><mi>u</mi><mi>r</mi></mrow></msub><mo>−</mo><msub><mover accent="true"><mrow><mi>N</mi><mi>D</mi><mi>C</mi></mrow><mo>⃗</mo></mover><mrow><mi>p</mi><mi>r</mi><mi>e</mi><mi>v</mi></mrow></msub></mrow><annotation encoding="application/x-tex">\vec{V} = \vec{NDC}_{cur} - \vec{NDC}_{prev}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9663em"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9663em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.2222em">V</span></span><span style="top:-3.2523em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.2355em"><span class="overlay" style="height:0.714em;width:0.471em"><svg xmlns="http://www.w3.org/2000/svg" width="0.471em" height="0.714em" style="width:0.471em" viewBox="0 0 471 714" preserveAspectRatio="xMinYMin"><path d="M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z"></path></svg></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.1163em;vertical-align:-0.15em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9663em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.109em">N</span><span class="mord mathnormal" style="margin-right:0.0278em">D</span><span class="mord mathnormal" style="margin-right:0.0715em">C</span></span></span><span style="top:-3.2523em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.2355em"><span class="overlay" style="height:0.714em;width:0.471em"><svg xmlns="http://www.w3.org/2000/svg" width="0.471em" height="0.714em" style="width:0.471em" viewBox="0 0 471 714" preserveAspectRatio="xMinYMin"><path d="M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z"></path></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">c</span><span class="mord mathnormal mtight">u</span><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.2524em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9663em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.109em">N</span><span class="mord mathnormal" style="margin-right:0.0278em">D</span><span class="mord mathnormal" style="margin-right:0.0715em">C</span></span></span><span style="top:-3.2523em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.2355em"><span class="overlay" style="height:0.714em;width:0.471em"><svg xmlns="http://www.w3.org/2000/svg" width="0.471em" height="0.714em" style="width:0.471em" viewBox="0 0 471 714" preserveAspectRatio="xMinYMin"><path d="M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z"></path></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">p</span><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span><span class="mord mathnormal mtight">e</span><span class="mord mathnormal mtight" style="margin-right:0.0359em">v</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span></span>
<p>Therefore, one would need the previous <code>modelViewProjection</code> matrix of an object, as well as that object&#x27;s <em>animation vertex velocity</em>, the difference between the position between the current and previous animation sample.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🏃 NDC space velocity</span>
<span class="hljs-type">vec3</span> ndc = inPosition.xyz / inPosition.w;
<span class="hljs-type">vec3</span> ndcPrev = inPositionPrev.xyz / inPositionPrev.w;
outVelocity = ndc.xy - ndcPrev.xy;</code></pre></div>
<h3 id="history-buffer">History Buffer</h3>
<table><thead><tr><th style="text-align:center"><img src="https://alain.xyz/blog/ray-tracing-filtering/assets/history-length.jpg" alt="Normalized history buffer"/> <strong>History (R16 Normalized [0, 212])</strong></th></tr></thead></table>
<p>When performing spatio-temporal reprojection, having a buffer describing the time for which a given sample had to accumulate is very valuable, a <strong>History Buffer</strong>. It can be used to drive a filter to blur stronger in regions with few accumulated samples or be used to estimate the variance of the current image (higher history would mean less variance).</p>
<p>This buffer would be read when accepting reprojected samples to determine the accumulation factor of a given sample with an exponentially moving average.</p>
<div><pre><code class="language-glsl">outHistoryLength = successfulReprojection ? prevHistoryLength + <span class="hljs-number">1.0</span> : <span class="hljs-number">0.0</span>;</code></pre></div>
<h3 id="exponentially-moving-average">Exponentially Moving Average</h3>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>C</mi><mrow><mi>f</mi><mi>i</mi><mi>n</mi><mi>a</mi><mi>l</mi></mrow></msub><mo>=</mo><mo stretchy="false">(</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>α</mi><mo stretchy="false">)</mo><mo>∗</mo><msub><mi>C</mi><mrow><mi>p</mi><mi>r</mi><mi>e</mi><mi>v</mi><mi>i</mi><mi>o</mi><mi>u</mi><mi>s</mi></mrow></msub><mo stretchy="false">)</mo><mo>+</mo><mo stretchy="false">(</mo><mi>α</mi><mo>∗</mo><msub><mi>C</mi><mrow><mi>c</mi><mi>u</mi><mi>r</mi><mi>r</mi><mi>e</mi><mi>n</mi><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">C_{final} = ( (1 - \alpha ) \ast C_{previous} ) + ( \alpha \ast C_{current} )</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9694em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0715em">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0715em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.1076em">f</span><span class="mord mathnormal mtight">ina</span><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">((</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0037em">α</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.0361em;vertical-align:-0.2861em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0715em">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:-0.0715em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">p</span><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span><span class="mord mathnormal mtight">e</span><span class="mord mathnormal mtight" style="margin-right:0.0359em">v</span><span class="mord mathnormal mtight">i</span><span class="mord mathnormal mtight">o</span><span class="mord mathnormal mtight">u</span><span class="mord mathnormal mtight">s</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0037em">α</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0715em">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.2806em"><span style="top:-2.55em;margin-left:-0.0715em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">c</span><span class="mord mathnormal mtight">u</span><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span><span class="mord mathnormal mtight">e</span><span class="mord mathnormal mtight">n</span><span class="mord mathnormal mtight">t</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span>
<div><pre><code class="language-glsl">outColor = <span class="hljs-built_in">mix</span>(colorPrevious, colorCurrent, accumulationFactor);</code></pre></div>
<p>Having a history buffer available to you gives your algorithm a perspective of how many samples a given region of the screen have been accumulated, which you can feed to a shader to determine an accumulation factor ( \alpha ).</p>
<p>While this can encode spatial information thanks to the motion buffer allowing for the translation of previous samples, <em>A-SVGF</em> takes this a step further.</p>
<h3 id="moment-buffer">Moment Buffer</h3>
<p>By using the change in luminosity and velocity over correlated samples to help drive the accumulation factor of a spatio-temporal filter, it&#x27;s possible to reduce ghosting significantly.</p>
<p>This difference in temporal variance and velocity is then used to determine the rate of change on a sample of the screen, encoding that information in what it calls a <strong>Moment Buffer</strong>. <a href="#ref_schied2019">[Schied et al. 2019]</a></p>
<p>We&#x27;ll discuss an implementation of a moment buffer while discussing variance estimation later in the post.</p>
<h3 id="caveats">Caveats</h3>
<p>Temporal lag is a problem with this technique. There can be differences in brightness between regions with a high number of accumulated samples and new regions. This is especially evident in darker regions of a raytraced scene such as interiors. To mitigate this, rather than using 1 sample per pixel, it&#x27;s best to use 2 or more samples per pixel in dark areas of a scene.</p>
<p>In addition, there can be significant differences between view positions and their ray traced outputs. While shadows, global illumination, ambient occlusion, subsurface scattering, and translucency can be reprojected spatially without much issue, ray traced outputs that are not lambertian in nature such as reflections and refraction cannot be reprojected (at least, without taking into account bounce velocity, which may be more computational trouble than its worth and not yield many reprojected samples). Thus, these buffers would rely much more on filtering techniques.</p>
<h2 id="signal-processing-techniques">Signal Processing Techniques</h2>
<p>Monte-carlo integration starts with a noisy image which gradually becomes clearer as time goes on. In an attempt to reduce the amount of time it takes to compute a clear final image, attempts have been made to use a variety of filtering techniques when rendering such as <strong>Gaussian</strong>, <strong>Median</strong>, <strong>Bilateral</strong>, and <strong>À-Trous</strong> filters. These have been used in combination with variance estimation across neighboring samples to better reject fireflies and drive the accumulation of samples in regions that change in variance/velocity.</p>
<h3 id="bluring-kernels">Bluring Kernels</h3>
<p>Filtering is an expensive operation to do in a real-time renderer, as texture lookup operations introduces latency when executing a shader. <strong>À-Trous (With holes)</strong> is a fast approximation of a bilateral filter currently used by <strong>Spatio-temporal Variance Guided Filtering (SVGF)</strong> and it&#x27;s <strong>Adaptive (A-SVGF)</strong> counterpart <a href="#ref_schied2017">[Schied 2017]</a> <a href="#ref_schied2018">[Schied 2018]</a>, first introduced in <a href="#ref_dammertz2010">[Dammertz et al. 2010]</a>, in combination with spatio-temporal reprojection and variance estimation to perform a cheap, fast, and high quality edge avoiding blur.</p>
<table><thead><tr><th style="text-align:center"><img src="https://alain.xyz/blog/ray-tracing-filtering/assets/normal.jpg" alt="Ground Truth"/> <strong>Normals (RG16F)</strong></th><th style="text-align:center"><img src="https://alain.xyz/blog/ray-tracing-filtering/assets/depth.jpg" alt="View Space Depth Buffer"/> <strong>View Space Depth (R32F)</strong></th></tr></thead></table>
<p>A-Trous avoids sampling in a slightly dithered pattern to cover a wider radius than would normally be possible in a 3x3 or 5x5 guassian kernel while at the same time having the ability to be repeated multiple times, and avoid bluring across edges thanks number of different inputs <a href="#ref_he2012">[He et al. 2012]</a> <a href="#ref_li2012">[Li et al. 2012]</a> <a href="#ref_dammertz2010">[Dammertz et al. 2010]</a>, such as a <strong>Normals</strong>, <strong>Depth</strong>, <strong>Squared World Space Positions</strong>, derivatives of said attachments <a href="#ref_koskela2019">[Koskela et al. 2019]</a>, the change in variance of luminosity, and change in velocity of those buffers <a href="#ref_schied2018">[Schied 2018]</a>.</p>
<p>This can be done in combination with:</p>
<ul>
<li>
<p>Subsampling according to a dithering pattern, thus reducing the number of samples in your bluring kernel even more.</p>
</li>
<li>
<p>Drive your blur with more information such as surface roughness <a href="#ref_baktash2018">[Abdollah-shamshir-saz 2018]</a>, the aproximate Specular BRDF lobe <a href="#ref_tokuyoshi2015">[Tokuyoshi 2015]</a>, shadow penumbras <a href="#ref_liu2019">[Liu et al. 2019]</a>, etc.</p>
</li>
</ul>
<h3 id="caveats-1">Caveats</h3>
<ul>
<li>
<p>Noise Dancing is more prominant and visible when it&#x27;s blured, especially if your integrand is complex enough.</p>
</li>
<li>
<p>Balancing your blurring size with keeping high frequency details is a diffcult problem, with Machine Learning kernels doing better than variance guided filters.</p>
</li>
</ul>
<h3 id="variance-estimation">Variance Estimation</h3>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi>σ</mi><mn>2</mn></msup><mo>=</mo><mfrac><mrow><mi mathvariant="normal">Σ</mi><mo stretchy="false">(</mo><mi>x</mi><mo>−</mo><mover accent="true"><mi>x</mi><mo>^</mo></mover><msup><mo stretchy="false">)</mo><mn>2</mn></msup></mrow><mi>n</mi></mfrac></mrow><annotation encoding="application/x-tex">\sigma^2 = \frac{\Sigma(x - \hat{x})^2}{n}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8641em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">σ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.1771em;vertical-align:-0.686em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.4911em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal">n</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">Σ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">x</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.2222em"><span class="mord">^</span></span></span></span></span></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span>
<p><strong>Variance</strong> is the squared difference of a signal&#x27;s average (mean). One can take the average of the current signal and that signal squared with a 3x3 gaussian kernel, then taking the difference of the two.</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi>σ</mi><mn>2</mn></msup><mo>=</mo><mfrac><mrow><mi mathvariant="normal">Σ</mi><msup><mi>x</mi><mn>2</mn></msup></mrow><mi>n</mi></mfrac><mo>−</mo><msup><mover accent="true"><mi>x</mi><mo>^</mo></mover><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">\sigma^2 = \frac{\Sigma x^2}{n} - \hat{x}^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8641em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">σ</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.1771em;vertical-align:-0.686em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.4911em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord mathnormal">n</span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord">Σ</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.8641em"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">x</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.2222em"><span class="mord">^</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em"><span style="top:-3.113em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span></span>
<div><pre><code class="language-glsl"><span class="hljs-keyword">const</span> <span class="hljs-type">float</span> radius = <span class="hljs-number">2</span>; <span class="hljs-comment">//5x5 kernel</span>
<span class="hljs-type">vec2</span> sigmaVariancePair = <span class="hljs-type">vec2</span>(<span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>);
<span class="hljs-type">float</span> sampCount = <span class="hljs-number">0.0</span>;
<span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> y = -radius; y &lt;= radius; ++y)
{
    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x = -radius; x &lt;= radius; ++x)
    {
        <span class="hljs-comment">// ☝️ We already have the center data</span>
        <span class="hljs-keyword">if</span> (xx != <span class="hljs-number">0</span> || yy != <span class="hljs-number">0</span>) { <span class="hljs-keyword">continue</span>; }

        <span class="hljs-comment">// ⬇️ Sample current point data with current uv</span>
        <span class="hljs-type">ivec2</span> p = ipos + <span class="hljs-type">ivec2</span>(xx, yy);
        <span class="hljs-type">vec4</span> curColor = <span class="hljs-built_in">texelFetch</span>(tColor, p, <span class="hljs-number">0</span>);

        <span class="hljs-comment">// 💡 Determine the average brightness of this sample</span>
        <span class="hljs-comment">// 🌎 Using International Telecommunications Union&#x27;s ITU BT.601 encoding params</span>
        <span class="hljs-type">float</span> samp = luminance(curColor);
        <span class="hljs-type">float</span> sampSquared = samp * samp;
        sigmaVariancePair += <span class="hljs-type">vec2</span>(samp, sampSquared);

        sampCount += <span class="hljs-number">1.0</span>;
    }
}
sigmaVariancePair /= sampCount;
<span class="hljs-type">float</span> variance = <span class="hljs-built_in">max</span>(<span class="hljs-number">0.0</span>, sigmaVariancePair.y - sigmaVariancePair.x * sigmaVariancePair.x);</code></pre></div>
<p><a href="https://twitter.com/c_schied">Christoph Schied (@c_schied)</a> does this in A-SVGF estimating the spatial variance as a combination of an edge avoiding guassian filter (just like in the a-trous guided filter) and using this in a feedback loop to drive the <code>accumulationFactor</code> during spatio-temporal reprojection:</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">/**
 * Variance Estimation
 * Copyright (c) 2018, Christoph Schied
 * All rights reserved.
 * 🎓 Slightly simplified for this example:
 */</span>

<span class="hljs-comment">// ⛏️ Setup</span>
<span class="hljs-type">float</span> weightSum = <span class="hljs-number">1.0</span>;
<span class="hljs-type">int</span> radius = <span class="hljs-number">3</span>; <span class="hljs-comment">// ⚪ 7x7 Gaussian Kernel</span>
<span class="hljs-type">vec2</span> moment = <span class="hljs-built_in">texelFetch</span>(tMomentPrev, ipos, <span class="hljs-number">0</span>).rg;
<span class="hljs-type">vec4</span> c = <span class="hljs-built_in">texelFetch</span>(tColor, ipos, <span class="hljs-number">0</span>);
<span class="hljs-type">float</span> histlen = <span class="hljs-built_in">texelFetch</span>(tHistoryLength, ipos, <span class="hljs-number">0</span>).r;

<span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> yy = -radius; yy &lt;= radius; ++yy)
{
    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> xx = -radius; xx &lt;= radius; ++xx)
    {
        <span class="hljs-comment">// ☝️ We already have the center data</span>
        <span class="hljs-keyword">if</span> (xx != <span class="hljs-number">0</span> || yy != <span class="hljs-number">0</span>) { <span class="hljs-keyword">continue</span>; }

        <span class="hljs-comment">// ⬇️ Sample current point data with current uv</span>
        <span class="hljs-type">ivec2</span> p = ipos + <span class="hljs-type">ivec2</span>(xx, yy);
        <span class="hljs-type">vec4</span> curColor = <span class="hljs-built_in">texelFetch</span>(tColor, p, <span class="hljs-number">0</span>);
        <span class="hljs-type">float</span> curDepth = <span class="hljs-built_in">texelFetch</span>(tDepth, p, <span class="hljs-number">0</span>).x;
        <span class="hljs-type">vec3</span> curNormal = <span class="hljs-built_in">texelFetch</span>(tNormal, p, <span class="hljs-number">0</span>).xyz;

        <span class="hljs-comment">// 💡 Determine the average brightness of this sample</span>
        <span class="hljs-comment">// 🌎 Using International Telecommunications Union&#x27;s ITU BT.601 encoding params</span>
        <span class="hljs-type">float</span> l = luminance(curColor.rgb);

        <span class="hljs-type">float</span> weightDepth = <span class="hljs-built_in">abs</span>(curDepth - depth.x) / (depth.y * <span class="hljs-built_in">length</span>(<span class="hljs-type">vec2</span>(xx, yy)) + <span class="hljs-number">1.0e-2</span>);
        <span class="hljs-type">float</span> weightNormal = <span class="hljs-built_in">pow</span>(<span class="hljs-built_in">max</span>(<span class="hljs-number">0</span>, <span class="hljs-built_in">dot</span>(curNormal, normal)), <span class="hljs-number">128.0</span>);

        <span class="hljs-type">uint</span> curMeshID =  <span class="hljs-built_in">floatBitsToUint</span>(<span class="hljs-built_in">texelFetch</span>(tMeshID, p, <span class="hljs-number">0</span>).r);

        <span class="hljs-type">float</span> w = <span class="hljs-built_in">exp</span>(-weightDepth) * weightNormal * (meshID == curMeshID ? <span class="hljs-number">1.0</span> : <span class="hljs-number">0.0</span>);

        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">isnan</span>(w))
            w = <span class="hljs-number">0.0</span>;

        weightSum += w;

        moment += <span class="hljs-type">vec2</span>(l, l * l) * w;
        c.rgb += curColor.rgb * w;
    }
}

moment /= weightSum;
c.rgb /= weightSum;

varianceSpatial = (<span class="hljs-number">1.0</span> + <span class="hljs-number">2.0</span> * (<span class="hljs-number">1.0</span> - histlen)) * <span class="hljs-built_in">max</span>(<span class="hljs-number">0.0</span>, moment.y - moment.x * moment.x);
outFragColor = <span class="hljs-type">vec4</span>(c.rgb, (<span class="hljs-number">1.0</span> + <span class="hljs-number">3.0</span> * (<span class="hljs-number">1.0</span> - histlen)) * <span class="hljs-built_in">max</span>(<span class="hljs-number">0.0</span>, moment.y - moment.x * moment.x));</code></pre></div>
<h3 id="firefly-rejection">Firefly Rejection</h3>
<p>Firefly rejection can be done in a variety of ways, from adjusting how you&#x27;re sampling during raytracing, to using filtering techniques or huristics about your output radiance.</p>
<h4 id="increase-roughness-per-bounce">Increase Roughness Per Bounce</h4>
<div><pre><code class="language-glsl"><span class="hljs-comment">//https://twitter.com/YuriyODonnell/status/1199253959086612480</span>
<span class="hljs-comment">//http://cg.ivd.kit.edu/publications/p2013/PSR_Kaplanyan_2013/PSR_Kaplanyan_2013.pdf</span>
<span class="hljs-comment">//http://jcgt.org/published/0007/04/01/paper.pdf</span>
<span class="hljs-type">float</span> oldRoughness = payload.roughness;
payload.roughness = <span class="hljs-built_in">min</span>(<span class="hljs-number">1.0</span>, payload.roughness + roughnessBias);
roughnessBias += oldRoughness * <span class="hljs-number">0.75</span>f;</code></pre></div>
<h4 id="clamp-rejection">Clamp Rejection</h4>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🗜️ Ray Tracing Gems Chapter 17</span>
<span class="hljs-type">vec3</span> fireflyRejectionClamp(<span class="hljs-type">vec3</span> radiance, <span class="hljs-type">vec3</span> maxRadiance)
{
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">max</span>(radiance, maxRadiance);
}</code></pre></div>
<h4 id="variance-rejection">Variance Rejection</h4>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🧯 Ray Tracing Gems Chapter 25</span>
<span class="hljs-type">vec3</span> fireflyRejectionVariance(<span class="hljs-type">vec3</span> radiance, <span class="hljs-type">vec3</span> variance, <span class="hljs-type">vec3</span> shortMean, <span class="hljs-type">vec3</span> dev)
{
    <span class="hljs-type">vec3</span> dev = <span class="hljs-built_in">sqrt</span>(<span class="hljs-built_in">max</span>(<span class="hljs-number">1.0e-5</span>, variance));
    <span class="hljs-type">vec3</span> highThreshold = <span class="hljs-number">0.1</span> + shortMean + dev * <span class="hljs-number">8.0</span>;
    <span class="hljs-type">vec3</span> overflow = <span class="hljs-built_in">max</span>(<span class="hljs-number">0.0</span>, radiance - highThreshold);
    <span class="hljs-keyword">return</span> radiance - overflow;
}</code></pre></div>
<h2 id="conclusion">Conclusion</h2>
<p>Denoising results in a drastic difference in the quality of a given image. By using a combination of advanced sampling, encoding, accumulation, and filtering techniques, it&#x27;s possible to achieve very high quality images for the small price of a few <code>ms</code>.</p>
<p>Aspects of this can also be driven by machine learning algorithms, which can serve as a smarter bluring tool than a bilateral filter, or trained to temporally accumulate samples faster. There&#x27;s plenty of room for future work here!</p>
<p>Here&#x27;s a few additional example implementations of real time ray tracing denoisers worth reviewing in no particular order:</p>
<ul>
<li>
<p><a href="https://www.linkedin.com/in/peterkristof/">Peter Kristof</a> of Microsoft made a really robust RTX Ambient Occlusion example with a robust implementation of SVGF <a href="https://github.com/microsoft/DirectX-Graphics-Samples/tree/master/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingRealTimeDenoisedAmbientOcclusion">here</a>.</p>
</li>
<li>
<p>The Apple Metal Team released an example of using <a href="https://developer.apple.com/documentation/metalperformanceshaders/mpssvgfdenoiser">Metal Performance Shaders, Temporal Anti-Aliasing (MPSTAA), and SVGF (MPSSVGF)</a> to <a href="https://developer.apple.com/documentation/metalperformanceshaders/animating_and_denoising_a_raytraced_scene">denoise a ray traced scene</a>.</p>
</li>
<li>
<p>Christof Shied (<a href="https://twitter.com/c_schied">@c_schied</a>) and Alexey Panteleev of NVIDIA wrote the denoiser for Quake 2 RTX (which is referenced heavily in this post) which is on Github <a href="https://github.com/NVIDIA/Q2RTX/blob/master/src/refresh/vkpt/shader/">here</a>.</p>
</li>
<li>
<p><img class="profile-picture" src="https://github.com/ingowald.png" alt="Ingo Wald&#x27;s picture"/> Ingo Wald (<a href="https://alain.xyz/(https://twitter.com/IngoWald)">@IngoWald</a>) released a Optix course providing examples for using the Optix denoiser <a href="https://github.com/ingowald/optix7course/blob/master/example12_denoiseSeparateChannels/SampleRenderer.cpp#L794">here</a>.</p>
</li>
<li>
<p>In the <a href="https://cis565-fall-2019.github.io/">University of Pennsylvania&#x27;s CIS565 2019 course</a>, a number of students made awesome projects that implement the latest in denoising research:</p>
<ul>
<li><a href="https://github.com/gztong/BMFR-DXR-Denoiser">Blockwise Multi-Order Feature Regression for Real-Time Path Tracing Reconstruction (BMFR)</a> by Jiangping Xu, Gangzheng Tong, &amp; Tianming Xu</li>
</ul>
</li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_veach1998"><span class="markademic-citename">[Veach 1998]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Robust Monte Carlo Methods for Light Transport Simulation</span></strong></em><br/><span class="markademic-authors"><span><span>Eric Veach</span></span></span><br/><span><span class="markademic-publisher">Stanford University 1998</span><br/></span><span><a href="http://graphics.stanford.edu/papers/veach_thesis/">graphics.stanford.edu</a></span></div></td></tr><tr><td id="ref_jarosz2019"><span class="markademic-citename">[Jarosz et al. 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Orthogonal array sampling for Monte Carlo rendering</span></strong></em><br/><span class="markademic-authors"><span><a href="https://cs.dartmouth.edu/~wjarosz/">Wojciech Jarosz</a> (<a href="https://twitter.com/wkjarosz">@wkjarosz</a>)</span><span>, <span>Afnan Enayet</span></span><span>, <span>Andrew Kensler</span></span><span>, <span>Charlie Kilpatrick</span></span><span> and <span>Per Christensen</span></span></span><br/><span><span class="markademic-publisher">EGSR 2019</span><br/></span><span><a href="https://cs.dartmouth.edu/~wjarosz/publications/jarosz19orthogonal.html">cs.dartmouth.edu</a></span></div></td></tr><tr><td id="ref_benyoub2019"><span class="markademic-citename">[Benyoub 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Leveraging Ray Tracing Hardware Acceleration In Unity</span></strong></em><br/><span class="markademic-authors"><span><a href="https://auzaiffe.wordpress.com/blog/">Anis Benyoub</a> (<a href="https://twitter.com/Auzaiffe">@Auzaiffe</a>)</span></span><br/><span><span class="markademic-publisher">Digital Dragons 2019</span><br/></span><span><a href="https://auzaiffe.files.wordpress.com/2019/05/digital-dragons-leveraging-ray-tracing-hardware-acceleration-in-unity.pdf">auzaiffe.files.wordpress.com</a></span></div></td></tr><tr><td id="ref_baktash2018"><span class="markademic-citename">[Abdollah-shamshir-saz 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Voxel Based Hybrid Path Tracing with Spatial Denoising</span></strong></em><br/><span class="markademic-authors"><span><span>Baktash Abdollah-shamshir-saz</span></span></span><br/><span><span class="markademic-publisher">i3D 2018</span><br/></span><span><a href="http://toomuchvoltage.com/pub/vbhptwstd/abstract.pdf">toomuchvoltage.com</a></span></div></td></tr><tr><td id="ref_mara2017"><span class="markademic-citename">[Mara et al. 2017]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">An Efficient Denoising Algorithm for Global Illumination</span></strong></em><br/><span class="markademic-authors"><span><span>Michael Mara</span></span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/morgan3d.png" alt="Morgan McGuire&#x27;s picture"/><a href="https://casual-effects.com/">Morgan McGuire</a> (<a href="https://twitter.com/CasualEffects">@CasualEffects</a>)</span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/tunabrain.png" alt="Benedikt Bitterli&#x27;s picture"/><a href="https://benedikt-bitterli.me">Benedikt Bitterli</a> (<a href="https://twitter.com/tunabrain">@tunabrain</a>)</span><span> and <a href="https://cs.dartmouth.edu/~wjarosz/">Wojciech Jarosz</a> (<a href="https://twitter.com/wkjarosz">@wkjarosz</a>)</span></span><br/><span><span class="markademic-publisher">ACM, High Performance Graphics 2017</span><br/></span><span><a href="https://cs.dartmouth.edu/~wjarosz/publications/mara17towards.html">cs.dartmouth.edu</a></span></div></td></tr><tr><td id="ref_dammertz2010"><span class="markademic-citename">[Dammertz et al. 2010]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Edge-Avoiding À-Trous Wavelet Transform for fast Global Illumination Filtering</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/NeoSpark314.png" alt="Holger Dammertz&#x27;s picture"/><a href="http://holger.dammertz.org/">Holger Dammertz</a> (<a href="https://twitter.com/NeoSpark314">@NeoSpark314</a>)</span><span>, <span>Daniel Sewtz</span></span><span>, <span>Johannes Hanika</span></span><span> and <span>Hendrik P.A. Lensch</span></span></span><br/><span><span class="markademic-publisher">High Performance Graphics 2010</span><br/></span><span><a href="https://jo.dreggn.org/home/2010_atrous.pdf">jo.dreggn.org</a></span></div></td></tr><tr><td id="ref_he2012"><span class="markademic-citename">[He et al. 2012]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Guided Image Filtering</span></strong></em><br/><span class="markademic-authors"><span><span>Kaiming He</span></span><span>, <span>Jian Sun</span></span><span> and <span>Xiaoou Tang</span></span></span><br/><span><span class="markademic-publisher">2012</span><br/></span><span><a href="http://kaiminghe.com/publications/pami12guidedfilter.pdf">kaiminghe.com</a></span></div></td></tr><tr><td id="ref_chaitanya2017"><span class="markademic-citename">[R. Alla Chaitanya et al. 2017]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Interactive Reconstruction of Monte Carlo Image Sequences using a Recurrent Denoising Autoencoder</span></strong></em><br/><span class="markademic-authors"><span><span>Chakravarty R. Alla Chaitanya</span></span><span>, <span>Anton S. Kaplanyan</span></span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/cschied.png" alt="Christoph Schied&#x27;s picture"/><a href="https://github.com/cschied">Christoph Schied</a> (<a href="https://twitter.com/c_schied">@c_schied</a>)</span><span>, <span>Marco Salvi</span> (<a href="https://twitter.com/marcosalvi">@marcosalvi</a>)</span><span>, <span>Aaron Lefohn</span></span><span>, <span>Derek Nowrouzezahrai</span></span><span> and <span>Timo Aila</span></span></span><br/><span><span class="markademic-publisher">ACM 2017</span><br/></span><span><a href="https://research.nvidia.com/sites/default/files/publications/dnn_denoise_author.pdf">research.nvidia.com</a></span></div></td></tr><tr><td id="ref_kalantari2015"><span class="markademic-citename">[Khademi Kalantari et al. 2015]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">A Machine Learning Approach for Filtering Monte Carlo Noise</span></strong></em><br/><span class="markademic-authors"><span><span>Nima Khademi Kalantari</span></span><span>, <span>Steve Bako</span></span><span> and <span>Pradeep Sen</span></span></span><br/><span><span class="markademic-publisher">ACM Transactions on Graphics (TOG) 2015</span><br/></span><span><a href="http://cvc.ucsb.edu/graphics/Papers/SIGGRAPH2015_LBF/">cvc.ucsb.edu</a></span></div></td></tr><tr><td id="ref_schied2017"><span class="markademic-citename">[Schied 2017]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Spatiotemporal Variance-Guided Filtering</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/cschied.png" alt="Christoph Schied&#x27;s picture"/><a href="https://github.com/cschied">Christoph Schied</a> (<a href="https://twitter.com/c_schied">@c_schied</a>)</span></span><br/><span><span class="markademic-publisher">2017</span><br/></span><span><a href="http://research.nvidia.com/publication/2017-07_Spatiotemporal-Variance-Guided-Filtering%3A">research.nvidia.com</a></span></div></td></tr><tr><td id="ref_schied2018"><span class="markademic-citename">[Schied 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Gradient Estimation for Real-Time Adaptive Temporal Filtering</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/cschied.png" alt="Christoph Schied&#x27;s picture"/><a href="https://github.com/cschied">Christoph Schied</a> (<a href="https://twitter.com/c_schied">@c_schied</a>)</span></span><br/><span><span class="markademic-publisher">ACM 2018</span><br/></span><span><a href="https://cg.ivd.kit.edu/atf.php">cg.ivd.kit.edu</a></span></div></td></tr><tr><td id="ref_koskela2019"><span class="markademic-citename">[Koskela et al. 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Blockwise Multi-Order Feature Regression for Real-Time Path Tracing Reconstruction</span></strong></em><br/><span class="markademic-authors"><span><span>Matias Koskela</span></span><span> and <span>Kalle Immonen</span></span></span><br/><span><span class="markademic-publisher">ACM Transactions on Graphics (TOG) 2019</span><br/></span><span><a href="http://www.tut.fi/vga/publications/Blockwise_Multi-Order_Feature_Regression_for_Real-Time_Path_Tracing_Reconstruction.html">tut.fi</a></span></div></td></tr><tr><td id="ref_andersson2019"><span class="markademic-citename">[Andersson et al. 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Temporally Dense Ray Tracing</span></strong></em><br/><span class="markademic-authors"><span><span>Pontus Andersson</span></span><span>, <span>Jim Nilsson</span></span><span>, <span>Josef Spjut</span></span><span>, <span>Marco Salvi</span> (<a href="https://twitter.com/marcosalvi">@marcosalvi</a>)</span><span> and <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/inversepixel.png" alt="Tomas Akenine-Möller&#x27;s picture"/><a href="http://cs.lth.se/tomas-akenine-moller/">Tomas Akenine-Möller</a> (<a href="https://twitter.com/inversepixel">@inversepixel</a>)</span></span><br/><span><span class="markademic-publisher">High Performance Graphics 2019</span><br/></span><span><a href="https://research.nvidia.com/sites/default/files/pubs/2019-07_Temporally-Dense-Ray//temporally-dense-ray-tracing.pdf">research.nvidia.com</a></span></div></td></tr><tr><td id="ref_mjercik2019"><span class="markademic-citename">[Majercik et al. 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Dynamic Diffuse Global Illumination with Ray-Traced Irradiance Fields</span></strong></em><br/><span class="markademic-authors"><span><span>Zander Majercik</span></span><span>, <span>Jean-Philippe Guertin</span></span><span>, <span>Derek Nowrouzezahrai</span></span><span> and <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/morgan3d.png" alt="Morgan McGuire&#x27;s picture"/><a href="https://casual-effects.com/">Morgan McGuire</a> (<a href="https://twitter.com/CasualEffects">@CasualEffects</a>)</span></span><br/><span><span class="markademic-publisher">2019</span><br/></span><span><a href="http://jcgt.org/published/0008/02/01/">jcgt.org</a></span></div></td></tr><tr><td id="ref_pantaleoni2020"><span class="markademic-citename">[Pantaleoni 2020]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Online Path Sampling Control with Progressive Spatio-Temporal Filtering</span></strong></em><br/><span class="markademic-authors"><span><span>Jacopo Pantaleoni</span></span></span><br/><span><span class="markademic-publisher">NVIDIA 2020</span><br/></span><span><a href="https://arxiv.org/pdf/2005.07547.pdf">arxiv.org</a></span></div></td></tr><tr><td id="ref_bitterli2020"><span class="markademic-citename">[Bitterli et al. 2020]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Spatiotemporal Reservoir Resampling for Real-Time Ray Tracing With Dynamic Direct Lighting</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/tunabrain.png" alt="Benedikt Bitterli&#x27;s picture"/><a href="https://benedikt-bitterli.me">Benedikt Bitterli</a> (<a href="https://twitter.com/tunabrain">@tunabrain</a>)</span><span>, <a href="http://cwyman.org/">Chris Wyman</a> (<a href="https://twitter.com/_cwyman_">@_cwyman_</a>)</span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/mmp.png" alt="Matt Pharr&#x27;s picture"/><a href="https://pharr.org/matt/">Matt Pharr</a></span><span>, <a href="https://www.petershirley.com/">Peter Shirley</a> (<a href="https://twitter.com/Peter_shirley">@Peter_shirley</a>)</span><span>, <span>Aaron Lefohn</span></span><span> and <a href="https://cs.dartmouth.edu/~wjarosz/">Wojciech Jarosz</a> (<a href="https://twitter.com/wkjarosz">@wkjarosz</a>)</span></span><br/><span><span class="markademic-publisher">ACM Transaction on Graphics 2020</span><br/></span><span><a href="https://research.nvidia.com/publication/2020-07_Spatiotemporal-reservoir-resampling">research.nvidia.com</a></span></div></td></tr><tr><td id="ref_kristof2019"><span class="markademic-citename">[Kristof 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Real-Time Denoised Raytraced Ambient Occlusion</span></strong></em><br/><span class="markademic-authors"><span><span>Peter Kristof</span></span></span><br/><span><span class="markademic-publisher">Microsoft 2019</span><br/></span><span><a href="https://youtu.be/3EdE38iRn2A">youtu.be</a></span><span> <a href="https://github.com/microsoft/DirectX-Graphics-Samples/tree/master/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingRealTimeDenoisedAmbientOcclusion">Github</a></span></div></td></tr><tr><td id="ref_cornel2014"><span class="markademic-citename">[Cornel 2014]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Analysis of Forced Random Sampling</span></strong></em><br/><span class="markademic-authors"><span><a href="http://drivenbynostalgia.com/">Daniel Cornel</a> (<a href="https://twitter.com/nostalgiadriven">@nostalgiadriven</a>)</span></span><br/><span><span class="markademic-publisher">2014</span><br/></span><span><a href="http://drivenbynostalgia.com/files/DA.pdf">drivenbynostalgia.com</a></span></div></td></tr><tr><td id="ref_shirley2018"><span class="markademic-citename">[Shirley 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Flavors of Sampling in Ray Tracing</span></strong></em><br/><span class="markademic-authors"><span><span>Pete Shirley</span></span></span><br/><span><span class="markademic-publisher">BlogSpot 2018</span><br/></span><span><a href="http://psgraphics.blogspot.com/2018/10/flavors-of-sampling-in-ray-tracing.html">psgraphics.blogspot.com</a></span></div></td></tr><tr><td id="ref_schied2019"><span class="markademic-citename">[Schied et al. 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Real-Time Path Tracing and Denoising in &#x27;Quake 2&#x27;</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/cschied.png" alt="Christoph Schied&#x27;s picture"/><a href="https://github.com/cschied">Christoph Schied</a> (<a href="https://twitter.com/c_schied">@c_schied</a>)</span><span> and <span>Alexey Panteleev</span></span></span><br/><span><span class="markademic-publisher">GDC 2019</span><br/></span><span><a href="https://www.gdcvault.com/play/1026185/">gdcvault.com</a></span><span> <a href="https://youtu.be/FewqoJjHR0A">youtu.be</a></span></div></td></tr><tr><td id="ref_wolfe2017"><span class="markademic-citename">[Wolfe 2017]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">When Random Numbers Are Too Random: Low Discrepancy Sequences</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/Atrix256.png" alt="Alan Wolfe&#x27;s picture"/><a href="http://demofox.org/">Alan Wolfe</a> (<a href="https://twitter.com/Atrix256">@Atrix256</a>)</span></span><br/><span><span class="markademic-publisher">The Blog at the Bottom of the Sea 2017</span><br/></span><span><a href="https://blog.demofox.org/2017/05/29/when-random-numbers-are-too-random-low-discrepancy-sequences/">blog.demofox.org</a></span></div></td></tr><tr><td id="ref_roberts2018"><span class="markademic-citename">[Roberts 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">The Unreasonable Effectiveness of Quasirandom Sequences</span></strong></em><br/><span class="markademic-authors"><span><span>Martin Roberts</span></span></span><br/><span><span class="markademic-publisher">2018</span><br/></span><span><a href="http://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/">extremelearning.com.au</a></span></div></td></tr><tr><td id="ref_wong1997"><span class="markademic-citename">[Wong et al. 1997]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Sampling with Hammersley and Halton Points</span></strong></em><br/><span class="markademic-authors"><span><span>Tien-Tsin Wong</span></span><span>, <span>Wai-Shing Luk</span></span><span> and <span>Pheng-Ann Heng</span></span></span><br/><span><span class="markademic-publisher">1997</span><br/></span><span><a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.552.864&amp;rep=rep1&amp;type=pdf">citeseerx.ist.psu.edu</a></span></div></td></tr><tr><td id="ref_heitz2019"><span class="markademic-citename">[Heitz et al. 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Distributing Monte Carlo Errors as a Blue Noise in Screen Space by Permuting Pixel Seeds Between Frames</span></strong></em><br/><span class="markademic-authors"><span><a href="https://eheitzresearch.wordpress.com/">Eric Heitz</a> (<a href="https://twitter.com/eric_heitz">@eric_heitz</a>)</span><span> and <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/belcour.png" alt="Laurent Belcour&#x27;s picture"/><a href="https://belcour.github.io/blog/">Laurent Belcour</a> (<a href="https://twitter.com/_Laurent">@_Laurent</a>)</span></span><br/><span><span class="markademic-publisher">EGSR 2019</span><br/></span><span><a href="https://eheitzresearch.wordpress.com/772-2/">eheitzresearch.wordpress.com</a></span></div></td></tr><tr><td id="ref_wolfe2017b"><span class="markademic-citename">[Wolfe 2017]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Demystifying Floating Point Precision</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/Atrix256.png" alt="Alan Wolfe&#x27;s picture"/><a href="http://demofox.org/">Alan Wolfe</a> (<a href="https://twitter.com/Atrix256">@Atrix256</a>)</span></span><br/><span><span class="markademic-publisher">The Blog at the Bottom of the Sea 2017</span><br/></span><span><a href="https://blog.demofox.org/2017/11/21/floating-point-precision/">blog.demofox.org</a></span></div></td></tr><tr><td id="ref_ward1988"><span class="markademic-citename">[Ward et al. 1988]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">A Ray Tracing Solution for Diffuse Interreflection</span></strong></em><br/><span class="markademic-authors"><span><span>Greg Ward</span></span><span>, <span>Francis Rubinstein</span></span><span> and <span>Robert Clear</span></span></span><br/><span><span class="markademic-publisher">Siggraph 1988</span><br/></span><span><a href="https://floyd.lbl.gov/radiance/papers/sg88/paper.html">floyd.lbl.gov</a></span></div></td></tr><tr><td id="ref_li2012"><span class="markademic-citename">[Li et al. 2012]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">SURE-based Optimization for Adaptive Sampling and Reconstruction</span></strong></em><br/><span class="markademic-authors"><span><span>Tzu-Mao Li</span></span><span>, <span>Yu-Ting Wu</span></span><span> and <span>Yung-Yu Chang</span></span></span><br/><span><span class="markademic-publisher">Siggraph Asia 2012</span><br/></span><span><a href="http://cseweb.ucsd.edu/~ravir/274/15/papers/a194-li.pdf">cseweb.ucsd.edu</a></span></div></td></tr><tr><td id="ref_tokuyoshi2015"><span class="markademic-citename">[Tokuyoshi 2015]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Specular Lobe-Aware Filtering and Upsampling for Interactive Indirect Illumination</span></strong></em><br/><span class="markademic-authors"><span><span>Yusuke Tokuyoshi</span></span></span><br/><span><span class="markademic-publisher">Eurographics 2015</span><br/></span><span><a href="http://www.jp.square-enix.com/tech/library/pdf/Specular%20Lobe-Aware%20Filtering%20and%20Upsampling%20for%20Interactive%20Indirect%20Illumination.pdf">jp.square-enix.com</a></span></div></td></tr><tr><td id="ref_liu2019"><span class="markademic-citename">[Liu et al. 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Cinematic Rendering in UE4 with Real-Time Ray Tracing and Denoising</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/behindthepixels.png" alt="Edward Liu&#x27;s picture"/><a href="http://behindthepixels.io/">Edward Liu</a> (<a href="https://twitter.com/edliu1105">@edliu1105</a>)</span><span>, <span>Ignacio Llamas</span> (<a href="https://twitter.com/ignaciollamas">@ignaciollamas</a>)</span><span>, <span>Juan Cañada</span> (<a href="https://twitter.com/jcanadagarcia">@jcanadagarcia</a>)</span><span> and <span>Patrick Kelly</span> (<a href="https://twitter.com/_pmkelly">@_pmkelly</a>)</span></span><br/><span><span class="markademic-publisher">Ray Tracing Gems 2019</span><br/></span><span><a href="http://www.realtimerendering.com/raytracinggems/">realtimerendering.com</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Real Time Renderer Architectures</title>
<link>https://alain.xyz/blog/realtime-renderer-architectures</link>
<description>A review of the state of the art in real time renderer architectures.</description>
<guid isPermaLink="true">https://alain.xyz/blog/realtime-renderer-architectures</guid>
<pubDate>Thu, 27 Jun 2019 12:30:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>shader</category><category>glsl</category><category>hlsl</category><category>vulkan</category><category>directx</category><category>opengl</category><category>spir-v</category><category>metal</category>
<enclosure url="https://alain.xyz/blog/realtime-renderer-architectures/assets/cover.jpg" length="94374" type="image/jpeg"/>
<content:encoded><![CDATA[<p>There are a variety of architectures in 3D real time rendering that are applicable to a variety of use cases, each with their own benefits and trade offs in performance and capability. The following is a summary of each as well as a detailed description of their implementation.</p>
<p>Though each implementation can be done in any graphics API, all code in this post will be using <strong>Vulkan</strong> with no dependencies, as Vulkan offers the best perspective of computer graphics APIs and the most compatibility across operating systems. In addition, though some concepts are applicable with 2D renderers (in particular the immediate mode model used often in UI frameworks like <a href="https://twitter.com/ocornut">Aymar Cornut&#x27;s (@ocornut)</a> <a href="https://github.com/ocornut/imgui">imgui</a> and <a href="https://github.com/wjakob/nanogui">Wenzel Jacob&#x27;s NanoGUI</a>. This post will primarily focus on writing a renderer for a 3D application.</p>
<h2 id="data-organization">Data Organization</h2>
<p>The <strong>glTF Specification</strong> <a href="#ref_cozzi2016">[Cozzi et al. 2016]</a> offers a useful perspective on the organization of data in a real time rendering application. Data in a real-time application can be described as being composed of:</p>
<ul>
<li>
<p>A tree of <strong>Nodes</strong>, each with their own transformation matrix.</p>
</li>
<li>
<p>A collection of <strong>Primitives</strong> that those nodes point to, each of which represents 1 draw call in a graphics API. Each Primitive points to mesh buffers and a technique with its own parameters.</p>
</li>
<li>
<p>A collection of <strong>Material Implementations (Techniques)</strong>, each with their own parameters set and each pointing to a reference material that handles the shader logic, uniforms, etc.</p>
</li>
<li>
<p>A collection of <strong>Materials</strong> with shaders programs and aspects of the graphics pipeline such as blend mode, stencil tests, etc. set for that material.</p>
</li>
<li>
<p>A collection of <strong>Mesh Data Buffers</strong> that can contain CPU and/or GPU accessible data buffers that will be used when rendering that primitive.</p>
</li>
</ul>
<p>As well as state that is used by the renderer that all draw calls will share such as:</p>
<ul>
<li>
<p><strong>Camera Matrices</strong> (Perspective and View matrices to generate the current <code>modelViewProjection</code> matrix for that draw call)</p>
</li>
<li>
<p><strong>View Surface Data</strong> such as the position the output buffer takes on the rendering window in the case of split screen games or Picture in Picture (pip).</p>
</li>
<li>
<p><strong>Light Data</strong> such as their position, direction, type, brightness, and color.</p>
</li>
<li>
<p><strong>Skybox Data</strong> for irradiance/radiance maps.</p>
</li>
<li>
<p><strong>Postprocessing Parameters</strong> such as depth of field focus points, tone mapping algorithm, etc.</p>
</li>
</ul>
<p>And of course any other arbitrary information such as terrain data, voxel data for GI, ray marched sign distance fields for clouds, etc.</p>
<p>Most rendering architectures organize their data in this format, and indeed most model files organize themselves in this way as well. This consistent data design makes it easier for applications to interoperable and work together to help design, model, and render a given scene.</p>
<h2 id="immediate-mode-context">Immediate Mode Context</h2>
<p><strong>Immediate Mode Context Rendering</strong> uses explicit draw calls every frame to render a scene.</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">/**
 * Example of ImGui rendering a debug window:
 */</span>

<span class="hljs-type">unsigned</span> frameRate;
<span class="hljs-type">float</span> ms;

 <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">renderDebugData</span><span class="hljs-params">()</span>
 </span>{
     ImGui::<span class="hljs-built_in">Text</span>(<span class="hljs-string">&quot;Frame Rate: %d fps&quot;</span>, frameRate);
     ImGui::<span class="hljs-built_in">Text</span>(<span class="hljs-string">&quot;Frame Time: %.3f ms&quot;</span>, ms);
 }

 <span class="hljs-comment">// Later in an Unreal Engine 4 Actor:</span>

 <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">AMyActor::tick</span><span class="hljs-params">(<span class="hljs-type">float</span> deltaTime)</span>
 </span>{
     <span class="hljs-comment">// Execute every frame</span>
     <span class="hljs-built_in">renderDebugData</span>();
 }</code></pre></div>
<p>Immediate Mode Context Rendering normally uses a singleton stack containing draw calls, and when updating the frame it pops that stack until it&#x27;s empty with each draw command. Afterwords the Immediate Mode Context Renderer has a list of command buffers that it executes in a given Graphics API. Due to the nature of this architecture it is difficult it can be less performant than an inturupt, subscriber based architecture where elements are explicitly added/removed from a graphics state. As such, this architecture is normally relegated to UIs or 2D applications where such performance concerns aren&#x27;t an issue.</p>
<h2 id="forward">Forward</h2>
<p><strong>Forward Rendering</strong> is arguably the first and most common implementation of real time rendering. It is capable of practically every effect you can ask for, from transparency, refraction, reflection, MSAA, and much more.</p>
<h2 id="deferred">Deferred</h2>
<p><strong>Deffered Rendering</strong> is arguably the most intuitive. Instead of rendering lighting information on a per mesh level, you render it on a per pixel level of the current surface. This can result in easier to architect post-processing effects and faster lighting for cases of scenes with a lot of lights. At the same time, it can be somewhat slower than forward rendering since there can be more divergance in an image resulting in stalls.</p>
<p>This involves the use of a Geometric Buffer (G-Buffer) <a href="#ref_saito1990">[Saito et al. 1990]</a>, though such a buffer can also be used in forward rendering as a prepass.</p>
<p>Deffered Rendering tends to be executed using raster based screen space buffers, with a blend mode set to add between lights to accumulate lighting information in the scene.</p>
<ol>
<li>
<p>Render Diffuse Irradiance information (Gobal Illumination, irradiance skybox texture/spherical harmonics/dice harmonics, etc.)</p>
</li>
<li>
<p>Render Specular Radiance Information (Reflections, Light specular terms, etc.)</p>
</li>
<li>
<p>Repeat for each light.</p>
</li>
</ol>
<h3 id="trade-offs">Trade Offs</h3>
<ul>
<li>
<p>Anti-Aliasing is limited to SSAA, FXAA, TAA.</p>
</li>
<li>
<p>Transparency is much more difficult to compute.</p>
</li>
</ul>
<h2 id="tile-deferred">Tile Deferred</h2>
<p><strong>Tiled Deffered</strong> takes advantage of the fact that GPUs execute shaders in &quot;tiles&quot; and leverages that to automate the addition of such tiles rather than using blend modes. Keeping this data away from the shader author and making it automatic has the benefit of reducing complexity while at the same time increasing performance.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In the same vain as the days where per vertex Gouraud shading was the state of the art, it&#x27;s now possible to limit shading to an object in texture space, reducing the amount of expensive lighting calculations on that object.</p>
<p>By using <strong>Sampler Feedback</strong> along side other modern graphics techniques such as compute shaders, mesh shaders, and variable rate shading, this technique can allow for more efficient processing of expensive lighting operations and thus more work distributed across a variety of tasks in your graphics application. This was featured in <a href="https://devblogs.microsoft.com/directx/announcing-directx-12-ultimate/">DirectX 12 Ultimate&#x27;s Announcement</a>.</p>
<ul>
<li>
<p><a href="http://stack.gl/">Stack.gl</a> has a number of great examples of rendering architectures, including a great WebGL Forward Rendering <a href="https://github.com/stackgl/glsl-lighting-walkthrough">example here</a>.</p>
</li>
<li>
<p><a href="https://cis565-fall-2019.github.io/">University of Pennsylvania&#x27;s CIS565 course</a> has an example of <a href="https://github.com/CIS565-Fall-2016/Project5-WebGL-Deferred-Shading-with-glTF">WebGL Defered Shading with gLTF</a></p>
</li>
<li>
<p><a href="https://bisqwit.iki.fi/">Joel Yliluoma</a> <a href="https://bisqwit.iki.fi/jutut/kuvat/programming_examples/polytut/">wrote a number of articles and YouTube videos discussing offline rasterization</a>, a useful reference to what a software renderer might look like.</p>
</li>
<li>
<p><a href="https://dickyjim.wordpress.com/2013/09/04/spherical-harmonics-for-beginners/">Here&#x27;s a really nice Spherical Harmonics summary with an example ShaderToy</a>.</p>
</li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_cozzi2016"><span class="markademic-citename">[Cozzi et al. 2016]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">GL Transmission Format (glTF) Specification</span></strong></em><br/><span class="markademic-authors"><span><span>Patrick Cozzi</span></span><span> and <span>Tony Parisi</span></span></span><br/><span><span class="markademic-publisher">Khronos Group 2016</span><br/></span><span><a href="https://github.com/KhronosGroup/glTF/tree/master/specification/1.0">github.com</a></span></div></td></tr><tr><td id="ref_saito1990"><span class="markademic-citename">[Saito et al. 1990]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Comprehensible rendering of 3-D shapes</span></strong></em><br/><span class="markademic-authors"><span><span>Takafumi Saito</span></span><span> and <span>Tokiichiro Takahashi</span></span></span><br/><span><span class="markademic-publisher">SIGGRAPH 1990</span><br/></span><span><a href="https://dl.acm.org/citation.cfm?doid=97880.97901">dl.acm.org</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Real Time Ray Tracing</title>
<link>https://alain.xyz/blog/real-time-ray-tracing</link>
<description>An overview of various ray operations, their similarities, differences, and how to implement each type of ray operation in real time rendering.</description>
<guid isPermaLink="true">https://alain.xyz/blog/real-time-ray-tracing</guid>
<pubDate>Thu, 20 Jun 2019 12:30:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>shader</category><category>glsl</category><category>hlsl</category><category>vulkan</category><category>directx</category><category>opengl</category><category>spir-v</category><category>metal</category>
<enclosure url="https://alain.xyz/blog/real-time-ray-tracing/assets/cover.jpg" length="80063" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/real-time-ray-tracing/assets/cook1984.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/real-time-ray-tracing/assets/raytrace-execution-steps.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/real-time-ray-tracing/assets/camera-ray-diagram.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/real-time-ray-tracing/assets/ray-casting-diagram.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/real-time-ray-tracing/assets/ray-marching-diagram.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/real-time-ray-tracing/assets/snail-by-inigo-quilez.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/real-time-ray-tracing/assets/ray-tracing-diagram.svg"/><link rel="preload" as="image" href="https://alain.xyz/blog/real-time-ray-tracing/assets/path-tracing-diagram.svg"/><link rel="preload" as="image" href="https://github.com/IngoWald.png"/><link rel="preload" as="image" href="https://github.com/sebh.png"/><p>Real time ray-tracing <em>was</em> a goal graphics software engineers have sought after ever since the first papers on light simulations were published back in the late 1970s to early 1980s <a href="#ref_whitted1980">[Whitted 1980]</a> <a href="#ref_cook1984">[Cook et al. 1984]</a> <a href="#ref_veach1998">[Veach 1998]</a>. As of now this is no longer a dream, but a reality that is available commercially in modern graphics processing units (GPU) and game consoles such as the PS5 and Xbox Series X.</p>
<p><img src="https://alain.xyz/blog/real-time-ray-tracing/assets/cook1984.jpg" alt="Cook&#x27;s 1984 Billiard Render"/></p>
<p>Real time ray-tracing continues to be a heavily researched topic. From early papers and projects from the demo-scene using <em>shader model 3 to perform simple ray tracing</em> <a href="#ref_quilez2005">[Quílez 2005]</a>, to recent papers running into the issue of <em>branch coherency</em> during acceleration structure traversals <a href="#ref_wald2018">[Benthin et al. 2018]</a>, attempts to reduce sample counts for rays by means of <a href="https://alain.xyz/blog/raytracing-denoising">denoising algorithms</a>. <em>GPU architectural papers</em> such as those released by NVIDIA&#x27;s Pascal architecture, <em>viewport based quad-tree systems</em> to reduce samples in unimportant areas, <a href="https://alain.xyz/blog/advances-in-material-models">BRDF papers for analytically modeling a variety of materials</a>, and so much more, the subject of ray tracing contains a variety of complex sub domains.</p>
<p>While GPUs were originally meant for storing frame buffers, simple rasterization and shading operations, they&#x27;ve since become powerful co-processors that can perform non-specialized calculations, from compute to tensor and ray-tracing routines with varying levels of hardware acceleration. <a href="#ref_arafa2019">[Arafa 2019]</a></p>
<p>Thanks to an immense effort on the part of hardware designers, researchers and industry manufacturers, <strong>real time hardware accelerated ray tracing</strong> is now possible in modern high end graphics cards, with compute based falloffs available on select devices.</p>
<p>Now ray tracing has become an ambiguous term <a href="#ref_shirley2018">[Shirley 2018]</a> over the years, so let&#x27;s define terms based on the research papers they were first used:</p>
<ol>
<li>
<p><strong>Ray Casting</strong> - a simple collision algorithm, rays can hit a acceleration data structure such as a discrete shape or set of triangles, and that ray returns the data regarding that hit point.</p>
</li>
<li>
<p><strong>Ray Marching</strong> - A method of ray casting that works by &quot;marching&quot; towards a density function&#x27;s threshold &quot;solid&quot; value <a href="#ref_perlin1989">[Perlin et al. 1989]</a> by means of a sphere tracing algorithm <a href="#ref_hart1989">[Hart et al. 1989]</a>. Similarly, a hierarchical Z Buffer can also be marched, such as in Pixel-projected reflections <a href="#ref_cichocki2017">[Cichocki 2017]</a>. Ray marching has recently been made popular by the <a href="https://www.pouet.net/">DemoScene</a> and <a href="https://www.shadertoy.com/">ShaderToy</a>.</p>
</li>
<li>
<p><strong>Ray Tracing</strong> - otherwise known as <em>Whitted Path Tracing</em> <a href="#ref_whitted1980">[Whitted 1980]</a> or <em>Recursive Ray Casting</em>, rays can continue past their original collision point, thus allowing for surface behaviors like reflections. <em>Cook Style Ray Tracing</em> (distribution ray tracing) <a href="#ref_cook1984">[Cook et al. 1984]</a> involves the use of monte-carlo integration (introducing randomness) on surfaces to introduce soft shadows. The billiard table figure above was rendered with this technique in <strong>1984</strong>!</p>
</li>
<li>
<p><strong>Path Tracing</strong> - Kajiya expanded on ray tracing with with the inclusion of diffuse inter-reflection (global illumination) and even further with his light integration formula <a href="#ref_kajiya1986">[Kajiya 1986]</a>. Veach took this even further with his Ph.D dissertation on path tracing which introduced ideas like <em>Multiple Importance Sampling (MIPS)</em>, <em>Next Event Estimation (NEE)</em>, and <em>Russian Roulette</em> <a href="#ref_veach1998">[Veach 1998]</a>.</p>
</li>
</ol>
<hr/>
<p>Ray tracing is a subject that can encompass entire books and courses, so it&#x27;s important to make clear what we <em>won&#x27;t</em> be discussing in detail:</p>
<ol>
<li>
<p><strong>Existing Renderers</strong> - There&#x27;s <a href="https://www.mitsuba-renderer.org/">Mitsuba 2</a>, <a href="https://developer.nvidia.com/optix">NVIDIA OptiX</a>, <a href="https://github.com/embree/embree/tree/master/tutorials">Intel Embree</a>, <a href="https://www.blender.org/">Blender Cycles</a>, <a href="https://autodesk.com">Autodesk Arnold</a>, <a href="https://renderman.pixar.com/">Pixar RenderMan</a>, <a href="https://github.com/mmp/pbrt-v3">PBRT&#x27;s reference renderer</a>. The differences between each of these backends are complex enough to warrant their own blog post or paper. That being said, it&#x27;s highly valuable to review the source of any one of these projects!</p>
</li>
<li>
<p><strong>Acceleration Structures</strong> - While KD-Trees and Bounding Volume Hierarchies are important things to know, these are abstracted by real-time Ray Tracing APIs <a href="#ref_stich2018">[Stich 2018]</a> (though you could always implement your own, <a href="https://www.scratchapixel.com/lessons/advanced-rendering/introduction-acceleration-structure/bounding-volume-hierarchy-BVH-part1">Scratch A Pixel</a> has a brilliant article on the subject, and the subject is detailed in <a href="http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration.html">Physically Based Rendering Chapter 4</a>).</p>
</li>
<li>
<p><strong>Linear Algebra</strong> - You should know how to multiply matrices to describe a camera, scene hierarchy, etc. Here&#x27;s a <a href="https://developer.nvidia.com/rtx/raytracing/dxr/DX12-Raytracing-tutorial/Extra/dxr_tutorial_extra_perspective">refresher</a> on the subject.</p>
</li>
</ol>
<h2 id="overview">Overview</h2>
<p><img src="https://alain.xyz/blog/real-time-ray-tracing/assets/raytrace-execution-steps.svg" alt="Ray Tracing Execution Steps"/></p>
<p>The pattern by which any ray based algorithm follows is:</p>
<ol>
<li>
<p><strong>For Each Pixel</strong> that you intend to get <em>radiance</em> (light value) information from, cast a ray based on a given camera matrix. This ray can be calculated by getting the NDC coordinate of that pixel, then multiplying that by the inverse view-projection matrix to form a world based vector from the camera to the scene.</p>
</li>
<li>
<p><strong>Test for Collisions</strong> with simple math functions for simple shapes, or more complex methods like a bounding volume hierarchy for traversing the scene, and a low level acceleration structure to test each triangle of the objects in the scene such as a KD-Tree.</p>
</li>
<li>
<p><strong>Calculate Radiance</strong> by testing that surface for its material function (or Bidirectional Reflectance/Transmission Distribution Function, <em>BxDF</em>).</p>
</li>
<li>
<p><strong>Cast Rays</strong> - by bouncing off that surface according to the behavior expected by that material or what you intend to record (Global Illumination, Reflections, etc.).</p>
</li>
<li>
<p><strong>Stop, Average, and Write</strong> at some pre-determined level of quality such as 4096 samples, then average out the radiance values of each of those samples to get a final color. Finally, write that to your output memory, be it a frame buffer attachment in a graphics API or a <code>.png</code> image, etc.</p>
</li>
</ol>
<h2 id="camera-ray">Camera Ray</h2>
<p><img src="https://alain.xyz/blog/real-time-ray-tracing/assets/camera-ray-diagram.svg" alt="Ray Camera Diagram"/></p>
<p>At the start of every ray tracing routine is the initial camera ray. This can be computed using a <code>View</code> and <code>Projection</code> matrix. If you want to add other effects to your routine such as anti-aliasing or depth of field you can adjust the ray direction with a random offset in its origin and direction.</p>
<p>These matrices can easily be found in linear algebra libraries that include a <code>lookAt</code> and <code>perspective</code> matrix calculation, but the basic idea is to build a basis matrix based on the camera orientation, and a scaling matrix based on its field of view and near/far planes.</p>
<p>Here&#x27;s an example that uses <a href="https://glm.g-truc.net/0.9.9/index.html">GLM</a>:</p>
<div><pre><code class="language-cpp"><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> glm;

<span class="hljs-comment">// 📷 Declare Image Size</span>
<span class="hljs-type">const</span> <span class="hljs-type">unsigned</span> width = <span class="hljs-number">1920</span>;
<span class="hljs-type">const</span> <span class="hljs-type">unsigned</span> height = <span class="hljs-number">1080</span>;

<span class="hljs-comment">// 👧 First Person Camera</span>
<span class="hljs-comment">// 🧍‍♀️ Standing at 1.65 meters</span>
<span class="hljs-comment">// 👆 Pointing at the zenith at PI/2</span>
<span class="hljs-type">float</span> theta = <span class="hljs-number">1.570796f</span>, phi = <span class="hljs-number">-.1</span>f;
vec3 origin = <span class="hljs-built_in">vec3</span>(<span class="hljs-number">-10.0f</span>, <span class="hljs-number">1.65f</span>, <span class="hljs-number">-0.5f</span>);
vec3 firstPersonDirection =
    <span class="hljs-built_in">vec3</span>(<span class="hljs-built_in">sin</span>(theta) * <span class="hljs-built_in">cos</span>(phi), <span class="hljs-built_in">sin</span>(phi), <span class="hljs-built_in">cos</span>(theta) * <span class="hljs-built_in">cos</span>(phi));
vec3 firstPersonUp = <span class="hljs-built_in">vec3</span>(<span class="hljs-built_in">sin</span>(theta) * <span class="hljs-built_in">sin</span>(phi) * <span class="hljs-number">-1.0f</span>, <span class="hljs-built_in">cos</span>(phi),
                          <span class="hljs-built_in">cos</span>(theta) * <span class="hljs-built_in">sin</span>(phi) * <span class="hljs-number">-1.0f</span>);
mat4 view = <span class="hljs-built_in">lookAt</span>(origin, origin + firstPersonDirection, firstPersonUp);

<span class="hljs-comment">// 🦍 aprox. 103 degrees FoV horizontal like Overwatch</span>
<span class="hljs-type">float</span> fovVertical = <span class="hljs-number">1.24f</span>;
<span class="hljs-type">float</span> aspectRatio = <span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">float</span>&gt;(width) / <span class="hljs-built_in">static_cast</span>&lt;<span class="hljs-type">float</span>&gt;(height);
<span class="hljs-type">float</span> near = <span class="hljs-number">.1</span>f, far = <span class="hljs-number">300.0f</span>;
mat4 projection = <span class="hljs-built_in">perspective</span>(fovVertical, aspectRatio, near, far);

<span class="hljs-comment">// 🌎 Unproject camera for world space ray</span>
mat4 inversinvProjectionView = <span class="hljs-built_in">inverse</span>(projection * view);</code></pre></div>
<p>Then you can use that data as a uniform in a compute shader, ray dispatch shader, fragment shader, etc:</p>
<div><pre><code class="language-glsl"><span class="hljs-meta">#version 430</span>

<span class="hljs-keyword">precision</span> <span class="hljs-keyword">mediump</span> <span class="hljs-type">float</span>;

<span class="hljs-comment">/**************************************************************
 * 🎛️ Inputs
 **************************************************************/</span>

<span class="hljs-keyword">layout</span>(<span class="hljs-keyword">local_size_x</span> = <span class="hljs-number">1</span>, <span class="hljs-keyword">local_size_y</span> = <span class="hljs-number">1</span>) <span class="hljs-keyword">in</span>;

<span class="hljs-comment">/**************************************************************
 * 🖼️ Outputs
 **************************************************************/</span>

<span class="hljs-keyword">layout</span>(<span class="hljs-keyword">rgba16f</span>, <span class="hljs-keyword">binding</span> = <span class="hljs-number">0</span>) <span class="hljs-keyword">uniform</span> <span class="hljs-type">image2D</span> outColor;

<span class="hljs-comment">/**************************************************************
 * 👔 Uniforms
 **************************************************************/</span>

<span class="hljs-keyword">layout</span>(<span class="hljs-keyword">binding</span> = <span class="hljs-number">1</span>) <span class="hljs-keyword">uniform</span> FragUBO
{
    <span class="hljs-type">mat4</span> invProjectionView;
    <span class="hljs-type">float</span> near;
    <span class="hljs-type">float</span> far;
}
ubo;

<span class="hljs-comment">/**************************************************************
 * 👋 Main
 **************************************************************/</span>

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">Ray</span>
{
  <span class="hljs-type">vec3</span> origin;
  <span class="hljs-type">vec3</span> direction;
};

<span class="hljs-type">void</span> main()
{
    <span class="hljs-comment">// 🟥 Pixel Coordinates</span>
    <span class="hljs-type">ivec2</span> pixelCoords = <span class="hljs-type">ivec2</span>(<span class="hljs-built_in">gl_GlobalInvocationID</span>.xy);
    <span class="hljs-type">ivec2</span> dims = <span class="hljs-built_in">imageSize</span>(outColor); <span class="hljs-comment">// fetch image dimensions</span>

    <span class="hljs-comment">// 🌄 Texture Coordinates</span>
    <span class="hljs-type">vec2</span> texCoord = <span class="hljs-type">vec2</span>(<span class="hljs-type">float</span>(pixelCoords.x) / <span class="hljs-type">float</span>(dims.x),
                         <span class="hljs-type">float</span>(pixelCoords.y) / <span class="hljs-type">float</span>(dims.y));

    <span class="hljs-comment">// 🎥 Normalized Device Coordinates (NDC)</span>
    <span class="hljs-type">vec2</span> ndc = (<span class="hljs-number">2.0</span> * texCoord - <span class="hljs-number">1.0</span>);
    <span class="hljs-type">float</span> ndcDepth = ubo.far - ubo.near;
    <span class="hljs-type">float</span> ndcSum = ubo.far + ubo.near;

    <span class="hljs-comment">// 🌏 World Space Ray</span>
    <span class="hljs-type">vec4</span> camRay = ubo.inverseViewProjection * <span class="hljs-type">vec4</span>(ndc * ndcDepth, ndcSum, ndcDepth);
    <span class="hljs-type">vec4</span> camOrigin = ubo.inverseViewProjection * <span class="hljs-type">vec4</span>( ndc, <span class="hljs-number">-1.0</span>, <span class="hljs-number">1.0</span> );

    <span class="hljs-comment">// 🧪 Test ray with scene...</span>
    Ray ray;
    ray.origin = camOrigin;
    ray.direction = <span class="hljs-built_in">normalize</span>(camRay).xyz;
    <span class="hljs-comment">//vec4 radiance = calculateRadiance(ray);</span>

    <span class="hljs-comment">// ✍ Write to final color output</span>
    <span class="hljs-built_in">imageStore</span>(outColor, pixelCoords, radiance);
}</code></pre></div>
<h2 id="ray-casting">Ray Casting</h2>
<p><img src="https://alain.xyz/blog/real-time-ray-tracing/assets/ray-casting-diagram.svg" alt="Ray Casting Diagram"/></p>
<p><strong>Ray Casting</strong> is casting a ray from the camera to a collision point and <em>stop the operation</em> there. Early iterations of ray casting were extremely simple, opting to try drawing simple lines or lighting effects.</p>
<p>This is generally a simple operation, and functions as a <em>basic primitive</em> by which all other Ray Tracing methods are built off of.</p>
<p>There&#x27;s a variety of different operations for triangles and basic primitives, acceleration structures like bounding volume hierarchies, KD-Trees, etc. <a href="https://www.geometrictools.com/Samples/Intersection.html">available here</a>.</p>
<h2 id="ray-marching">Ray Marching</h2>
<p><img src="https://alain.xyz/blog/real-time-ray-tracing/assets/ray-marching-diagram.svg" alt="Ray Marching Diagram"/></p>
<p><strong>Ray Marching</strong> is a form of raycasting that iterates over discrete steps to determine collisions, with <strong>Sphere Marching</strong> taking that idea a step further, integrating a distance field function to determine collisions (rather than a collision acceleration data structure such as a K-D Tree or Bounding Volume Hierarchy (BVH)). <a href="#ref_hart1989">[Hart et al. 1989]</a> <a href="#ref_hart1996">[Hart 1996]</a> <a href="#ref_keinert2014">[Keinert et al. 2014]</a></p>
<p><a href="https://www.shadertoy.com/view/ld3Gz2" title="Snail by Inigo Quilez"><img src="https://alain.xyz/blog/real-time-ray-tracing/assets/snail-by-inigo-quilez.jpg" alt="ShaderToy by Inigo Quilez"/></a></p>
<p>Ray Marching (both terms are used interchangeably) is very useful for rendering real-time volumes, the game Fortnite by Epic Games published a <a href="https://www.fxguide.com/featured/a-fortnite-at-epic/">post on FxGuide</a> detailing their technique, and prior to that, Sebastien Hillaire published a paper describing raymarching voxels for volumetric rendering <a href="#ref_hillaire2015">[Hillaire 2015]</a>. Practically every <a href="http://shadertoy.com">ShaderToy</a> and project in the demoscene uses some form of ray marching.</p>
<blockquote>
<p>Note: As ray marching is more of a method of testing collisions, it is possible to use ray marching to drive a ray caster or a complex path tracer.</p>
</blockquote>
<div><pre><code class="language-glsl"><span class="hljs-keyword">const</span> <span class="hljs-type">int</span> marchSteps = <span class="hljs-number">64</span>;
<span class="hljs-keyword">const</span> <span class="hljs-type">float</span> epsilon = <span class="hljs-number">1.0e-5</span>;

<span class="hljs-comment">// ⭕ Sphere Marching</span>
<span class="hljs-type">float</span> raymarch(<span class="hljs-type">vec3</span> rayOrigin, <span class="hljs-type">vec3</span> rayDirection, <span class="hljs-type">float</span> near, <span class="hljs-type">float</span> far)
{
  <span class="hljs-comment">// 🏁 Starting integrated distance</span>
  <span class="hljs-type">float</span> dist = near + epsilon;

  <span class="hljs-comment">// 📏 Final distance Value</span>
  <span class="hljs-type">float</span> t = <span class="hljs-number">0.0</span>;

  <span class="hljs-comment">// 🥁 March</span>
  <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; marchSteps; i++)
  {
    <span class="hljs-comment">// 👩‍🌾 Near/Far Planes</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">abs</span>(dist) &lt; near || t &gt; far)
      <span class="hljs-keyword">break</span>;

    <span class="hljs-comment">// 🥊 Advance the distance of the last lookup</span>
    <span class="hljs-comment">// `dist` approaches values below 0.</span>
    t += dist;
    <span class="hljs-type">vec3</span> p = rayOrigin + t * rayDirection;
    <span class="hljs-comment">//dist = scenedf(p); // 👈 your scene distance field</span>
  }

  <span class="hljs-keyword">return</span> t;
}</code></pre></div>
<p>This can easily be done in a <em>compute</em> or <em>fragment</em> shader with no ray tracing cores necessary.</p>
<p><a href="https://twitter.com/iquilezles">Inigo Quilez (@iquilezles)</a> has an entire blog full of articles on ray marching <a href="https://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm">here</a>.</p>
<h2 id="ray-tracing">Ray Tracing</h2>
<p><img src="https://alain.xyz/blog/real-time-ray-tracing/assets/ray-tracing-diagram.svg" alt="Ray Tracing Diagram"/></p>
<p><strong>Ray Tracing</strong> is when a ray continues past its original point in a <em>recursive manner</em>. The word tends to be used to describe everything here, recursive ray casts, cook style ray tracing, path tracing, etc.</p>
<p>This is useful to calculate information based on an object&#x27;s neighbors such as Reflections, Global Illumination or Ambient Occlusion.</p>
<h2 id="path-tracing">Path Tracing</h2>
<p><img src="https://alain.xyz/blog/real-time-ray-tracing/assets/path-tracing-diagram.svg" alt="Ray Tracing Diagram"/></p>
<p><strong>Path Tracing</strong> is the process of tracing a ray&#x27;s path, such as from the camera to a light source. Path tracing is basically ray tracing that uses material functions (BxDFs) to model light interactions and the behavior of rays that interact with those materials.</p>
<p>There are several types of path tracing techniques described in literature, here&#x27;s a few:</p>
<ul>
<li>
<p><strong>Backwards Path Tracing</strong> - Tracing a scene by casting rays from the camera to the scene until they hit light objects. Also called <em>Unidirectional Path Tracing</em> though that title can also apply to only tracing from lights.</p>
</li>
<li>
<p><strong>Bidirectional Path Tracing</strong> - Traditional path tracing, Bidirectional Path Tracing (BPT) where sub paths are started at the camera and the lights (in our case the environment map), and vertices from the sub paths are connected to form a full path.</p>
</li>
</ul>
<h2 id="importance-sampling">Importance Sampling</h2>
<p>When sampling an environment map, Light, or BxDF, there are regions which are more likely to hit highlights or bright concentrated spots than others, and if you focus your rays to target these regions, you&#x27;ll see far less variance when sampling these regions.</p>
<blockquote>
<p>By varying the local sample density according to the importance function, the error of the approximation can be significantly reduced. The decision whether to place an ink dot is the result of a threshold comparison of each image pixel with the corresponding dither matrix element. <a href="#ref_cornel2014">[Cornel 2014]</a></p>
</blockquote>
<p>How this is implemented can vary, taking into account <em>Next Event Estimation (NEE)</em>, <em>Russian Roulette</em>, and more:</p>
<ul>
<li>
<p><strong>Multiple Importance Sampling (MIPS)</strong> - When estimating an integral, we should draw samples from multiple distributions, hoping one matches reasonably well, and weigh the samples from each technique to reduce variance spikes.</p>
</li>
<li>
<p><strong>Probability Density Function (PDF)</strong> - denotes the probability area of a given interaction, which for most of path tracing, is the outgoing direction of a ray according to a BxDF.</p>
</li>
<li>
<p><strong>Cumulative Distribution Function (CDF)</strong> - Usually denoted as ( P(x) ), it&#x27;s the probability that a uniformly distributed sample ( u &lt; x \int_-\inf^x p(y)dy ). It basically defines the integral of all possible points of a given random variable, and should tend to be <code>1.0</code>.</p>
</li>
<li>
<p><strong>Russian Roulette</strong> - A random chance that if the luminance of a ray is less than a given ( \epsilon ) the path will be discarded. Reduces variance by accepting stronger rays more often.</p>
</li>
<li>
<p><strong>Next Event Estimation (NEE)</strong> - Tracing shadow rays to the light source on each bounce to see if you can terminate the current path. This involves shooting a shadow ray towards light sources, if it&#x27;s occluded, terminate the ray.</p>
</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>The differences between <em>ray casting</em>, <em>ray marching</em>, <em>ray tracing</em>, and <em>path tracing</em> are somewhat subtle, it can be useful to imagine each in that order of complexity, with ray casting serving as an operation, ray marching as a method of managing collisions of density functions, and ray tracing serving as an overall description of all techniques.</p>
<p>For more information information on material models, check out <a href="https://alain.xyz/blog/advances-in-material-models">this blog post where I review the state of the art in materials</a>.</p>
<h3 id="books">Books</h3>
<ul>
<li>
<p><a href="http://raytracinggems.com">Ray Tracing Gems</a></p>
</li>
<li>
<p><a href="http://www.pbr-book.org/">Physically Based Rendering</a></p>
</li>
<li>
<p><a href="https://raytracing.github.io/">Ray Tracing in One Weekend</a></p>
</li>
<li>
<p>The set of books recommended by <a href="http://www.realtimerendering.com/raytracing.html">Real Time Rendering</a></p>
</li>
<li>
<p>Professor Morgan McGuire (<a href="https://twitter.com/CasualEffects">@CasualEffects</a>) released <a href="https://graphicscodex.courses.nvidia.com/app.html">The Graphics Codex</a>, a massive repository of articles, equations, diagrams, and programming projects.</p>
</li>
</ul>
<h3 id="articles">Articles</h3>
<ul>
<li>
<p><a href="http://demofox.org/">Alan Wolfe</a> (<a href="https://twitter.com/Atrix256">@Atrix256</a>) released a series of articles <a href="https://blog.demofox.org/2020/05/25/casual-shadertoy-path-tracing-1-basic-camera-diffuse-emissive/">introducing the concept of path tracing in ShaderToy</a>.</p>
</li>
<li>
<p><a href="https://www.scratchapixel.com/lessons/3d-basic-rendering/global-illumination-path-tracing">Scratch A Pixel&#x27;s articles on Ray Tracing</a></p>
</li>
<li>
<p><a href="https://twitter.com/j_bikker">Jacco Bikker (@j_bikker)</a>&#x27;s <a href="https://jacco.ompf2.com/2019/12/11/probability-theory-for-physically-based-rendering/">articles on Probability Theory for Physically Based Rendering</a>.</p>
</li>
<li>
<p>Kai Burjack wrote a series of articles on <a href="https://github.com/LWJGL/lwjgl3-wiki/wiki/2.6.1.-Ray-tracing-with-OpenGL-Compute-Shaders-%28Part-I%29">Ray tracing with OpenGL Compute Shaders</a> with <a href="https://github.com/LWJGL/lwjgl3">LWJGL</a>, Java&#x27;s most popular OpenGL implementation.</p>
</li>
</ul>
<h3 id="courses">Courses</h3>
<ul>
<li>
<p><a href="https://twitter.com/karoly_zsolnai">Károly Zsolnai-Fehér&#x27;s (@karoly_zsolnai)</a> <a href="https://www.youtube.com/watch?v=pjc1QAI6zS0&amp;list=PLujxSBD-JXgnGmsn7gEyN28P1DnRZG7qi">TU Wien Rendering Course</a></p>
</li>
<li>
<p><a href="https://twitter.com/kayvonf">Kayvon Fatahalian&#x27;s (@kayvonf)</a> <a href="http://graphics.stanford.edu/courses/cs348v-18-winter/">Visual Computing Systems</a></p>
</li>
<li>
<p>Alexander Keller et al.&#x27;s Siggraph 2020 course <a href="http://www.iliyan.com/publications/Siggraph2020Course">Advances in Monte Carlo Rendering: The Legacy of Jaroslav Křivánek</a>.</p>
</li>
</ul>
<h3 id="source-code">Source Code</h3>
<ul>
<li>
<p>Chris Wyman of NVIDIA&#x27;s <a href="https://github.com/NVIDIAGameWorks/GettingStartedWithRTXRayTracing">Getting Started with RTX Ray Tracing</a> tutorials.</p>
</li>
<li>
<p><a href="https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR">NVIDIA&#x27;s Vulkan Ray Tracing Tutorials</a> now support the standard Vulkan <code>KHR</code> extensions.</p>
</li>
<li>
<p><a href="https://github.com/GPSnoopy/RayTracingInVulkan">Ray Tracing in Vulkan</a></p>
</li>
</ul>
<p>For CPU based ray tracing:</p>
<ul>
<li>
<p><a href="https://github.com/mmp/pbrt-v3">PBRT Reference Renderer</a></p>
</li>
<li>
<p><a href="https://github.com/mitsuba-renderer/mitsuba">Mitsuba</a></p>
</li>
<li>
<p><a href="https://github.com/blender/blender/tree/master/intern/cycles">Blender Cycles</a></p>
</li>
<li>
<p><a href="https://github.com/RayTracing/raytracing.github.io/tree/master/src">Ray Tracing in One Weekend, Next Week, and the Rest of Your Life</a></p>
</li>
<li>
<p><a href="https://www.shadertoy.com/view/4sSXWt">Direct Light using MIS ShaderToy</a></p>
</li>
<li>
<p><a href="https://users.cg.tuwien.ac.at/zsolnai/gfx/smallpaint/">SmallPaint</a></p>
</li>
<li>
<p><a href="https://github.com/ssloy/tinyrenderer/wiki/Lesson-0-getting-started">Write a CPU rasterizer with tinyrenderer</a></p>
</li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_whitted1980"><span class="markademic-citename">[Whitted 1980]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">An Improved Illumination Model for Shaded Display</span></strong></em><br/><span class="markademic-authors"><span><span>Turner Whitted</span></span></span><br/><span><span class="markademic-publisher">ACM, Communications of the ACM 1980</span><br/></span><span><a href="http://doi.acm.org/10.1145/358876.358882">doi.acm.org</a></span></div></td></tr><tr><td id="ref_cook1984"><span class="markademic-citename">[Cook et al. 1984]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Distributed Ray Tracing</span></strong></em><br/><span class="markademic-authors"><span><span>Robert Cook</span></span><span>, <span>Loren Carpenter</span></span><span> and <span>Thomas Porter</span></span></span><br/><span><span class="markademic-publisher">ACM, SIGGRAPH Computer Graphics 1984</span><br/></span><span><a href="http://doi.acm.org/10.1145/800031.808590">doi.acm.org</a></span></div></td></tr><tr><td id="ref_veach1998"><span class="markademic-citename">[Veach 1998]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Robust Monte Carlo Methods for Light Transport Simulation</span></strong></em><br/><span class="markademic-authors"><span><span>Eric Veach</span></span></span><br/><span><span class="markademic-publisher">Stanford 1998</span><br/></span><span><a href="http://graphics.stanford.edu/papers/veach_thesis/">graphics.stanford.edu</a></span></div></td></tr><tr><td id="ref_quilez2005"><span class="markademic-citename">[Quílez 2005]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Simple GPU Raytracing</span></strong></em><br/><span class="markademic-authors"><span><a href="https://www.iquilezles.org">Íñigo Quílez</a> (<a href="https://twitter.com/iquilezles">@iquilezles</a>)</span></span><br/><span><span class="markademic-publisher">ShaderToy 2005</span><br/></span><span><a href="https://www.iquilezles.org/www/articles/simplegpurt/simplegpurt.htm">iquilezles.org</a></span></div></td></tr><tr><td id="ref_wald2018"><span class="markademic-citename">[Benthin et al. 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Compressed-Leaf Bounding Volume Hierarchies</span></strong></em><br/><span class="markademic-authors"><span><span>Carsten Benthin</span></span><span>, <img class="markademic-profile" style="margin-right:.25em" src="https://github.com/IngoWald.png" alt="Ingo Wald&#x27;s picture"/><a href="http://www.sci.utah.edu/~wald/">Ingo Wald</a> (<a href="https://twitter.com/IngoWald">@IngoWald</a>)</span><span>, <span>Sven Woop</span></span><span> and <span>Attila T. Afra</span></span></span><br/><span><span class="markademic-publisher">High Performance Graphics 2018</span><br/></span><span><a href="https://ingowald.blog/2018/06/06/preprint-of-our-hpg2018-compressed-leaf-bvh-short-paper/">ingowald.blog</a></span></div></td></tr><tr><td id="ref_arafa2019"><span class="markademic-citename">[Arafa 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Instructions’ Latencies Characterization for NVIDIA GPGPUs</span></strong></em><br/><span class="markademic-authors"><span><span>Yehia Arafa</span></span></span><br/><span><span class="markademic-publisher">NVIDIA 2019</span><br/></span><span><a href="https://arxiv.org/pdf/1905.08778.pdf">arxiv.org</a></span></div></td></tr><tr><td id="ref_shirley2018"><span class="markademic-citename">[Shirley 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Overview of Ray Tracing</span></strong></em><br/><span class="markademic-authors"><span><a href="https://www.petershirley.com/">Peter Shirley</a> (<a href="https://twitter.com/Peter_shirley">@Peter_shirley</a>)</span></span><br/><span><span class="markademic-publisher">Siggraph 2018</span><br/></span><span><a href="https://youtu.be/Q1cuuepVNoY?t=774">youtu.be</a></span></div></td></tr><tr><td id="ref_perlin1989"><span class="markademic-citename">[Perlin et al. 1989]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Hypertexture</span></strong></em><br/><span class="markademic-authors"><span><a href="https://cs.nyu.edu/~perlin/">Ken Perlin</a></span><span> and <span>Eric M. Hoffert</span></span></span><br/><span><span class="markademic-publisher">ACM, SIGGRAPH Computer Graphics 1989</span><br/></span><span><a href="http://doi.acm.org/10.1145/74334.74359">doi.acm.org</a></span></div></td></tr><tr><td id="ref_hart1989"><span class="markademic-citename">[Hart et al. 1989]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Ray Tracing Deterministic 3-D Fractals</span></strong></em><br/><span class="markademic-authors"><span><a href="https://graphics.cs.illinois.edu/jch/">John Hart</a></span><span>, <span>Daniel Sandin</span></span><span> and <span>Louis Koffman</span></span></span><br/><span><span class="markademic-publisher">Siggraph 1989</span><br/></span><span><a href="https://dl.acm.org/doi/10.1145/74334.74363">dl.acm.org</a></span></div></td></tr><tr><td id="ref_cichocki2017"><span class="markademic-citename">[Cichocki 2017]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Optimized pixel-projected reflections for planar reflectors</span></strong></em><br/><span class="markademic-authors"><span><span>Adam Cichocki</span></span></span><br/><span><span class="markademic-publisher">Siggraph, SIGGRAPH Computer Graphics 2017</span><br/></span><span><a href="http://advances.realtimerendering.com/s2017/PixelProjectedReflectionsAC_v_1.92.pdf">advances.realtimerendering.com</a></span></div></td></tr><tr><td id="ref_kajiya1986"><span class="markademic-citename">[Kajiya 1986]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">The Rendering Equation</span></strong></em><br/><span class="markademic-authors"><span><span>James Kajiya</span></span></span><br/><span><span class="markademic-publisher">ACM, SIGGRAPH Computer Graphics 1986</span><br/></span><span><a href="http://doi.acm.org/10.1145/15886.15902">doi.acm.org</a></span></div></td></tr><tr><td id="ref_stich2018"><span class="markademic-citename">[Stich 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Introduction to NVIDIA RTX and DirectX Ray Tracing</span></strong></em><br/><span class="markademic-authors"><span><span>Martin Stich</span></span></span><br/><span><span class="markademic-publisher">NVIDIA Developer Blog 2018</span><br/></span><span><a href="https://devblogs.nvidia.com/introduction-nvidia-rtx-directx-ray-tracing/">devblogs.nvidia.com</a></span></div></td></tr><tr><td id="ref_hart1996"><span class="markademic-citename">[Hart 1996]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Sphere Tracing: A Geometric Method for the Antialiased Ray Tracing of Implicit Surfaces</span></strong></em><br/><span class="markademic-authors"><span><a href="https://graphics.cs.illinois.edu/jch/">John Hart</a></span></span><br/><span><span class="markademic-publisher">The Visual Computer 1996</span><br/></span></div></td></tr><tr><td id="ref_keinert2014"><span class="markademic-citename">[Keinert et al. 2014]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Enhanced Sphere Rendering</span></strong></em><br/><span class="markademic-authors"><span><span>Benjamin Keinert</span></span><span>, <span>Henry Schäfer</span></span><span>, <span>Johann Korndörfer</span></span><span>, <span>Urs Ganse</span></span><span> and <span>Marc Stamminger</span></span></span><br/><span><span class="markademic-publisher">Smart Tools and Apps for Graphics (STAG) 2014</span><br/></span><span><a href="http://erleuchtet.org/~cupe/permanent/enhanced_sphere_tracing.pdf">erleuchtet.org</a></span></div></td></tr><tr><td id="ref_hillaire2015"><span class="markademic-citename">[Hillaire 2015]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Physically Based and Unified Volumetric Rendering in Frostbite</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/sebh.png" alt="Sebastien Hillaire&#x27;s picture"/><a href="https://sebh.github.io/">Sebastien Hillaire</a> (<a href="https://twitter.com/SebHillaire">@SebHillaire</a>)</span></span><br/><span><span class="markademic-publisher">EA 2015</span><br/></span><span><a href="https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite">ea.com</a></span></div></td></tr><tr><td id="ref_cornel2014"><span class="markademic-citename">[Cornel 2014]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Analysis of Forced Random Sampling</span></strong></em><br/><span class="markademic-authors"><span><a href="http://drivenbynostalgia.com/">Daniel Cornel</a> (<a href="https://twitter.com/nostalgiadriven">@nostalgiadriven</a>)</span></span><br/><span><span class="markademic-publisher">2014</span><br/></span><span><a href="http://drivenbynostalgia.com/files/DA.pdf">drivenbynostalgia.com</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Graphics Debugging</title>
<link>https://alain.xyz/blog/graphics-debugging</link>
<description>Graphics API debuggers have grown in complexity over the years as they expose more tools to developers looking to optimize their applications. The following is an overview of new graphics API debuggers and how you could use them to optimize your graphical applications.</description>
<guid isPermaLink="true">https://alain.xyz/blog/graphics-debugging</guid>
<pubDate>Wed, 12 Jun 2019 00:36:34 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>graphics</category><category>api</category><category>debugging</category>
<enclosure url="https://alain.xyz/blog/graphics-debugging/assets/cover.jpg" length="197232" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/graphics-debugging/assets/pix.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/graphics-debugging/assets/renderdoc.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/graphics-debugging/assets/radeon-gpu-profiler.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/graphics-debugging/assets/nsight-graphics.png"/><link rel="preload" as="image" href="https://github.com/aras-p.png"/><p>Graphics debuggers have become much more capable of educating developers. They can be used to generate code so developers can better understand what&#x27;s needed to execute commands, provide key insights for developers to better optimize their titles by showing shader disassembly/ISA, and show the memory layout of buffers in a given application to better detect errors. They&#x27;ve even enabled individuals to perform renderer studies like those done by Adrian Courrèges <a href="#ref_courreges2017">[Courrèges 2017]</a> and myself, and feature a variety of incredibly useful insights and features:</p>
<ul>
<li>
<p>Generated C++ from NVIDIA NSight Graphics showcases not just an example implementation of an application&#x27;s rendering architecture but also how to write such an architecture in most modern graphics APIs (Vulkan, DirectX 11/12, and OpenGL).</p>
</li>
<li>
<p>Shaders can be edited in the debugger with the results propagating instantly saving you a shader compilation refresh in your application.</p>
</li>
<li>
<p>Profiling tools help developers see more of their application&#x27;s shortcomings, from what&#x27;s slowing down the execution of a single <code>Dispatch</code> command the most to getting high level insights of the timing of render passes for given frame.</p>
</li>
</ul>
<p>The following is an attempt at summarizing shared aspects of a variety of graphics debuggers, and how you can use these features to optimize and debug your graphics applications.</p>
<h2 id="overview">Overview</h2>
<table><thead><tr><th style="text-align:center"><img src="https://alain.xyz/blog/graphics-debugging/assets/pix.png" alt="Microsoft PIX"/></th><th style="text-align:center"><img src="https://alain.xyz/blog/graphics-debugging/assets/renderdoc.png" alt="RenderDoc"/></th></tr></thead><tbody><tr><td style="text-align:center"><img src="https://alain.xyz/blog/graphics-debugging/assets/radeon-gpu-profiler.png" alt="Radeon GPU Profiler"/></td><td style="text-align:center"><img src="https://alain.xyz/blog/graphics-debugging/assets/nsight-graphics.png" alt="NVIDIA Nsight Graphics"/></td></tr></tbody></table>
<p>Currently there exists the following graphics API debuggers (with a few depreciated ones not listed):</p>
<ul>
<li>
<p><a href="https://devblogs.microsoft.com/pix/download/">Microsoft PIX (DirectX 11/12)</a></p>
</li>
<li>
<p><a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/display/using-gpuview">Microsoft GPUView</a></p>
</li>
<li>
<p><a href="https://gpuopen.com/tools/">AMD Radeon GPU Developer Tools Suite (AMD GPUs)</a></p>
</li>
<li>
<p><a href="https://developer.nvidia.com/nsight-graphics">NVIDIA NSight Graphics (NVIDIA GPUs)</a></p>
</li>
<li>
<p><a href="https://software.intel.com/en-us/gpa">Intel Graphics Performance Analyser (Intel GPUs)</a></p>
</li>
<li>
<p><a href="https://developer.qualcomm.com/software/snapdragon-profiler">Qualcom Snapdragon Profiler</a></p>
</li>
<li>
<p><a href="https://renderdoc.org/">RenderDoc</a></p>
</li>
<li>
<p><a href="https://developer.apple.com/xcode/">XCode Graphics Debugger (Metal)</a></p>
</li>
<li>
<p><a href="https://github.com/BabylonJS/Spector.js#browser-extension">Spector.js (WebGL)</a></p>
</li>
<li>
<p><a href="https://chrome.google.com">Chrome&#x27;s Structural Profiler (WebGL)</a> <a href="#ref_olmstead2014">[Olmstead 2014]</a></p>
</li>
</ul>
<p>Each of which for the most part share all the same features:</p>
<ul>
<li>
<p><em>Per Command Analysis</em> with grouping support with the <a href="https://devblogs.microsoft.com/pix/winpixeventruntime/">PIX event runtime</a> or vendor specific markers.</p>
</li>
<li>
<p><em>Frame Buffer Attachment Analysis</em> with the ability to adjust color weights for better visualization.</p>
</li>
<li>
<p><em>Mesh visualizations</em>, be it Bottom Level Acceleration Data Structures (BLAS&#x27;) or how verticies are transformed by the vertex shader.</p>
</li>
<li>
<p><em>Buffer Exploration</em> (uniforms, vertex, structured, textures, etc.)</p>
</li>
<li>
<p><em>Profiling</em> every aspect of rendering, from execution time for render passes to individual calls.</p>
</li>
<li>
<p><em>Shader Editing</em> and seeing those changes propagate in the frame capture.</p>
</li>
</ul>
<p>Though <em>GPU Vendor specific tools</em> such as AMD&#x27;s Radeon GPU Profiler, NVIDIA NSight Graphics, or Intel GPA tend to share more information about their platform. NVIDIA NSight Graphics also features a C++ project generator, perfect for learning the rendering architecture of a given application. Microsoft PIX is one of the few debugging tools that can be used for Windows 10 UWP applications.</p>
<p>Having information on individual shader execution, execution times for draw calls, render passes, etc. is extremely valuable since it will help you with achieving the maximum throughput of your GPU (otherwise known as <em>Speed of Light</em> or <strong>SOL</strong>) <a href="#ref_bavoil2018">[Bavoil 2018]</a>, and if your application isn&#x27;t running close to <strong>SOL</strong> then you&#x27;re wasting cycles and leaving performance on the table.</p>
<h2 id="gpu-architecture">GPU Architecture</h2>
<p>Graphics Processing Units (GPU)s typically execute thousands of threads concurrently (also known as executing <em>work items</em>) in groups called thread groups (or <em>work-groups</em>). Such bundles of threads are known as <strong>Warps</strong> (NVIDIA) or <strong>WaveFronts</strong> (AMD) <a href="#ref_moth2012">[Moth 2012]</a>. If your shader code does anything to prevent the threads of a work group from executing concurrently, this can introduce <em>divergence</em> by keeping threads from being able to continue processing that code during a given cycle.</p>
<p>In addition to simply shader processing, the GPU must execute a given <strong>Pipeline</strong> (be it Raster, Compute, Tensor, or Ray Trace), and depending on the complexity of a pipeline, a graphics API call can take longer to execute on the GPU.</p>
<p>Each stage of a graphics pipeline could have varying amounts of:</p>
<ul>
<li>
<p><strong>Stall Drain</strong> - Active warps that are stalled after exit for all memory instructions to complete so warp resources can be freed.</p>
</li>
<li>
<p><strong>Texture Stall Cycles</strong> - The number of cycles a texture request (<code>TEX TEXIN</code>) is stalled awaiting data from texture header or sampler L2 (<code>TSL2</code>) cache.</p>
</li>
</ul>
<p>With NVIDIA NSight Graphics you&#x27;ll want to analyze these statistics, or as a general rule of thumb refer to <strong>Shader Model Throughput for Active Cycles</strong>, which is how close the current API call is to achieving SOL.</p>
<h2 id="capture">Capture</h2>
<ul>
<li>
<p><strong>PIX</strong> - Press the green GPU Capture button on the app or <code>Print Screen</code>.</p>
</li>
<li>
<p><strong>Radeon GPU Profiler</strong> - Requires having Radeon Developer Panel open, Press <em>Capture Profile</em> or <code>CTRL+ALT+C</code>.</p>
</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>In NVIDIA NSight Graphics, the best summary of performance is its <strong>Top SOLs</strong> metric. Here&#x27;s a checklist of different aspects of your application that you should be paying attention to when optimizing your graphics application:</p>
<ol>
<li>
<p>is your app <em>CPU limited</em>? This means that you&#x27;re unable to send the GPU new work fast enough due to something CPU bound stalling the execution of API calls in your app.</p>
</li>
<li>
<p>Are there <em>Wait for Idle</em> commands due to pipeline switches? Changing the pipeline of a given application is inevitable, but if you could keep changes to a minimum, that can improve performance.</p>
</li>
<li>
<p>Is there too much <em>Texture fetching</em>? Texture memory is fast but depending on the computation, it could be faster to procedurally compute a given value you&#x27;re reading from a texture (though this is rare).</p>
</li>
<li>
<p>Are you using 16 bit index buffers for raster rendering? This can massively increase the performance of your app!</p>
</li>
<li>
<p>How big are your render pass attachments? Could you encode normals for example in 2 channels using <em>Sphere Map Transform Encoding</em> by Martin Mittring <a href="#ref_pran2010">[Pranckevičius 2010]</a>. Uncharted 4 by Naughty Dog features a more granular packing of render attachments, by packing various attachments into 2 <code>RGBA16</code> G-Buffers <a href="#ref_garawany2016">[El Garawany 2016]</a>.</p>
</li>
</ol>
<h3 id="more-resources">More Resources</h3>
<ul>
<li>
<p><a href="https://www.lei.chat">Lei Zhang</a> wrote an article discussing <a href="https://www.lei.chat/posts/sampling-performance-counters-from-gpu-drivers/">Sampling Performance Counters from GPU Drivers</a>.</p>
</li>
<li>
<p>Jason Booth wrote an article describing what factors contribute to <a href="https://medium.com/@jasonbooth_86226/stalling-a-gpu-7faac66b11b9">stalling on a GPU</a>.</p>
</li>
<li>
<p>AMD specific resources:</p>
<ul>
<li>
<p>Adam Sawicki (<a href="https://twitter.com/Reg__">@Reg__</a>) released an article describing how to use <a href="https://asawicki.info/news_1747_understanding_graphs_in_gpuview_and_rgp">Radeon GPU Profiler</a>.</p>
</li>
<li>
<p>AMD released an article explaining how to use <a href="https://gpuopen.com/learn/rga-dxr/">Radeon GPU Analyzer with DXR</a>.</p>
</li>
<li>
<p>AMD released a talk titled <a href="https://www.youtube.com/watch?v=_ilAL-1-moA&amp;list=PL-ZzkbB7kcKjoE3-q_-6eL49XRqqcWZeJ&amp;index=19">Trip Down the Shader Compiler Pipeline</a>, which helps you author shaders better by understanding how shaders become vendor bytecode.</p>
</li>
<li>
<p>The <a href="https://developer.amd.com/wp-content/resources/RDNA2_Shader_ISA_November2020.pdf">RDNA2 ISA</a> reference guide will help you better architect your shaders by showing what instructions DXIL and SPIR-V ultimately become in AMD hardware.</p>
</li>
<li>
<p><a href="https://www.amd.com/system/files/documents/rdna-whitepaper.pdf">AMD RDNA White Paper</a> is a useful guide to the design of recent RDNA cards.</p>
</li>
</ul>
</li>
<li>
<p>NVIDIA specific resources:</p>
<ul>
<li>
<p>NVIDIA&#x27;s <a href="https://docs.nvidia.com/cuda/pdf/ptx_isa_7.6.pdf">Parallel Thread Execution (PTX) ISA</a> shows what instructions CUDA cores can execute, and describes the execution model of NVIDIA hardware.</p>
</li>
<li>
<p>Wessam Bahnassi (<a href="https://twitter.com/wbahnassi">@wbahnassi</a>) released an article discussing ways to improve <a href="https://developer.nvidia.com/blog/advanced-api-performance-command-buffers/">Command Buffer Performance</a>.</p>
</li>
<li>
<p>Louis Bavoil (<a href="https://twitter.com/louisbavoil">@louisbavoil</a>) released a blog post detailing how to <a href="https://developer.nvidia.com/blog/the-peak-performance-analysis-method-for-optimizing-any-gpu-workload/">optimize your applications for peak performance on NVIDIA GPUs</a>.</p>
</li>
<li>
<p>The <a href="https://images.nvidia.com/aem-dam/en-zz/Solutions/design-visualization/technologies/turing-architecture/NVIDIA-Turing-Architecture-Whitepaper.pdf">NVIDIA Turing White Paper</a>.</p>
</li>
</ul>
</li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_courreges2017"><span class="markademic-citename">[Courrèges 2017]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Metal Gear Solid V - Graphics Study</span></strong></em><br/><span class="markademic-authors"><span><a href="https://www.adriancourreges.com/">Adrian Courrèges</a> (<a href="https://twitter.com/ado_tan">@ado_tan</a>)</span></span><br/><span><span class="markademic-publisher">2017</span><br/></span><span><a href="http://www.adriancourreges.com/blog/2017/12/15/mgs-v-graphics-study/">adriancourreges.com</a></span></div></td></tr><tr><td id="ref_olmstead2014"><span class="markademic-citename">[Olmstead 2014]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Optimizing WebGL Applications</span></strong></em><br/><span class="markademic-authors"><span><span>Dom Olmstead</span></span></span><br/><span><span class="markademic-publisher">Google Developers 2014</span><br/></span><span><a href="https://www.youtube.com/watch?v=QVvHtWePQdA">youtube.com</a></span></div></td></tr><tr><td id="ref_bavoil2018"><span class="markademic-citename">[Bavoil 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">The Peak-Performance-Percentage Analysis Method for Optimizing Any GPU Workload</span></strong></em><br/><span class="markademic-authors"><span><span>Louis Bavoil</span></span></span><br/><span><span class="markademic-publisher">NVIDIA DevBlogs 2018</span><br/></span><span><a href="https://devblogs.nvidia.com/the-peak-performance-analysis-method-for-optimizing-any-gpu-workload/">devblogs.nvidia.com</a></span></div></td></tr><tr><td id="ref_moth2012"><span class="markademic-citename">[Moth 2012]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Warp or Wavefront of GPU threads</span></strong></em><br/><span class="markademic-authors"><span><span>Daniel Moth</span></span></span><br/><span><span class="markademic-publisher">Microsoft 2012</span><br/></span><span><a href="https://blogs.msdn.microsoft.com/nativeconcurrency/2012/03/26/warp-or-wavefront-of-gpu-threads/">blogs.msdn.microsoft.com</a></span></div></td></tr><tr><td id="ref_pran2010"><span class="markademic-citename">[Pranckevičius 2010]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Compact Normal Storage for small G-Buffers</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/aras-p.png" alt="Aras Pranckevičius&#x27;s picture"/><a href="https://aras-p.info/">Aras Pranckevičius</a> (<a href="https://twitter.com/aras_p">@aras_p</a>)</span></span><br/><span><span class="markademic-publisher">2010</span><br/></span><span><a href="https://aras-p.info/texts/CompactNormalStorage.html">aras-p.info</a></span></div></td></tr><tr><td id="ref_garawany2016"><span class="markademic-citename">[El Garawany 2016]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Deferred Lighting in Uncharted 4</span></strong></em><br/><span class="markademic-authors"><span><span>Ramy El Garawany</span></span></span><br/><span><span class="markademic-publisher">Siggraph 2016</span><br/></span><span><a href="http://advances.realtimerendering.com/s2016/index.html">advances.realtimerendering.com</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Advances in Material Models</title>
<link>https://alain.xyz/blog/advances-in-material-models</link>
<description>A review of material models used in real-time renderers and ray tracing engines.</description>
<guid isPermaLink="true">https://alain.xyz/blog/advances-in-material-models</guid>
<pubDate>Wed, 05 Jun 2019 11:30:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>shader</category><category>glsl</category><category>hlsl</category><category>vulkan</category><category>directx</category><category>opengl</category><category>spir-v</category><category>metal</category>
<enclosure url="https://alain.xyz/blog/advances-in-material-models/assets/cover.jpg" length="840652" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://github.com/belcour.png"/><p><strong>Materials</strong> in computer graphics can be described with a <em>Material Model</em>, a function describing the output luminance (light energy) in the direction of the viewer of a given surface or volume. Such a function uses the environment around a given point, the lights that are visible to that point, the shadows cast by those lights at that point, and the observer&#x27;s location to determine an output luminance and such interactions are modeled with <strong>Shading Models</strong>.</p>
<p>An Analytical <strong>Shading Model</strong> is an mathematical approximation of a surface/volume to light interaction. <a href="#ref_eisemann2012">[Eisemann et al. 2012]</a></p>
<p>Such models are expressed for a given surface normal <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal">n</span></span></span></span>, unit view vector <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ω</mi></mrow><annotation encoding="application/x-tex">\omega</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0359em">ω</span></span></span></span>, unit light vector <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>l</mi></mrow><annotation encoding="application/x-tex">l</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal" style="margin-right:0.0197em">l</span></span></span></span>. For a given surface point <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi></mrow><annotation encoding="application/x-tex">p</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal">p</span></span></span></span>, the view vector is defined as the direction from <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi></mrow><annotation encoding="application/x-tex">p</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal">p</span></span></span></span> to the observer. <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>l</mi></mrow><annotation encoding="application/x-tex">l</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal" style="margin-right:0.0197em">l</span></span></span></span> defines the direction from <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi></mrow><annotation encoding="application/x-tex">p</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal">p</span></span></span></span> towards the light.</p>
<p>By using measured data it&#x27;s possible to build a mathematical model of a given material, or a model that can simulate a variety of materials. <a href="#ref_matusik2003">[Matusik et al. 2003]</a> <a href="#ref_belcour2013">[Belcour et al. 2013]</a></p>
<p>Shading models can describe surfaces, volumes, or a combination of the two. The term <strong>Bidirectional (Scattering, Surface, Reflectance, Transmission) Distribution Function (BSDF)</strong> is a formal term for describing a shading model on a given surface. It&#x27;s commonly abbreviated as <em>BRDF</em>, though <em>BSDF</em> is sometimes used as well, <em>BxDF</em> has been used by <a href="https://github.com/mmp/pbrt-v3">pbrt</a> and <a href="https://github.com/EpicGames/UnrealEngine/blob/release/Engine/Shaders/Private/ShadingModels.ush">Unreal Engine 4</a> to describe models that can serve for both surfaces and volumes.</p>
<p>A BRDF gives the amount of <em>incoming light energy from direction</em> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>ω</mi><mo>^</mo></mover></mrow><annotation encoding="application/x-tex">\hat{\omega}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0359em">ω</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.25em"><span class="mord">^</span></span></span></span></span></span></span></span></span></span> that turns into <em>outgoing light energy in direction</em> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ω</mi></mrow><annotation encoding="application/x-tex">\omega</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0359em">ω</span></span></span></span>. It is written with respect to a surface patch whose normal points upward.</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>f</mi><mi mathvariant="normal">_</mi><mrow><mi>X</mi><mo separator="true">,</mo><mover accent="true"><mi>x</mi><mo>^</mo></mover></mrow><mo stretchy="false">(</mo><mover accent="true"><mi>ω</mi><mo>^</mo></mover><mi mathvariant="normal">_</mi><mi>i</mi><mo separator="true">,</mo><mover accent="true"><mi>ω</mi><mo>^</mo></mover><mi mathvariant="normal">_</mi><mi>o</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">f\_{X,\hat{x}}(\hat{\omega}\_i,\hat{\omega}\_o)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.06em;vertical-align:-0.31em"></span><span class="mord mathnormal" style="margin-right:0.1076em">f</span><span class="mord" style="margin-right:0.0278em">_</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0785em">X</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">x</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.2222em"><span class="mord">^</span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0359em">ω</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.25em"><span class="mord">^</span></span></span></span></span></span></span><span class="mord" style="margin-right:0.0278em">_</span><span class="mord mathnormal">i</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal" style="margin-right:0.0359em">ω</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.25em"><span class="mord">^</span></span></span></span></span></span></span><span class="mord" style="margin-right:0.0278em">_</span><span class="mord mathnormal">o</span><span class="mclose">)</span></span></span></span></p>
<p>This models the excitant radiance <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>W</mi><mi mathvariant="normal">/</mi><msup><mi>m</mi><mn>2</mn></msup><mi>s</mi><mi>r</mi></mrow><annotation encoding="application/x-tex">W / m^2 sr</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0641em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.1389em">W</span><span class="mord">/</span><span class="mord"><span class="mord mathnormal">m</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mord mathnormal" style="margin-right:0.0278em">sr</span></span></span></span> of a Surface, essentially what math is involved in a given surface that adds light intensity <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>L</mi><mi>o</mi></msub></mrow><annotation encoding="application/x-tex">L_o</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">o</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> to the image.</p>
<p>BRDFs obey the following rules:</p>
<ul>
<li>
<p><strong>Helmhotz-reciprocity</strong> - The direction of the ray of light can be reversed (important for Bidirectional Path Tracing).</p>
</li>
<li>
<p><strong>Positivity</strong> - an exit direction cannot have a negative probability. <code>max(lighting, 0.0)</code></p>
</li>
<li>
<p><strong>Energy Conserving</strong> - does not create energy, it is impossible for more light to come out than what has come in (unless of course it&#x27;s a light source).</p>
</li>
<li>
<p>All incident light must be reflected when surface reflectivity is 1 (white furnace test).</p>
</li>
</ul>
<p>It functions as the ratio of scattered radiance (specular light) and irradiance (diffuse light).</p>
<p>Over the years many different types of BRDFs have been developed:</p>
<p>Some of the more popular are:</p>
<ul>
<li>Lambertian <a href="#ref_lambert1982">[Heinrich Lambert 1982]</a></li>
<li>Phong <a href="#ref_phong1975">[Tuong Phong 1975]</a></li>
<li>Blinn-Phong <a href="#ref_blinn1977">[F. Blinn 1977]</a></li>
<li>Schlick <a href="#ref_schlick1994">[Schlick 1994]</a></li>
<li>GGX <a href="#ref_walter2007">[Walter et al. 2007]</a> <a href="#ref_karis2013">[Karis 2013]</a></li>
<li>Disney <a href="#ref_burley2012">[Burley 2012]</a></li>
<li>Smith Microfacet <a href="#ref_heitz2016">[Heitz et al. 2016]</a></li>
</ul>
<p>In addition to:</p>
<ul>
<li>Cook-Torrance <a href="#ref_cook1982">[L. Cook et al. 1982]</a></li>
<li>Oren-Nayar <a href="#ref_oren1994">[Oren et al. 1994]</a></li>
<li>Neumann-Neumann <a href="#ref_neumann1999">[Neumann et al. 1999]</a></li>
<li>Ward <a href="#ref_ward1992">[Ward 1992]</a> <a href="#ref_walter2005">[Walter 2005]</a></li>
<li>Kurt <a href="#ref_kurt2010">[Kurt et al. 2010]</a></li>
<li>Ashikhmin-Shirley <a href="#ref_ashikhmin2000">[Ashikhmin et al. 2000]</a></li>
<li>Beckmann</li>
<li>Kelemen</li>
<li>Lafortune</li>
<li>Wolff</li>
<li>Walter</li>
<li>Nishir</li>
<li>Minnaert</li>
<li>Gaussian</li>
<li>Modified-Phong</li>
<li>Stretched Phong</li>
<li>Stretched Blinn</li>
</ul>
<p>Interpolation methods such as:</p>
<ul>
<li>Flat Shading</li>
<li>Gourand <a href="#ref_gouraud1971">[Gouraud 1971]</a></li>
<li>Phong <a href="#ref_phong1975">[Tuong Phong 1975]</a></li>
</ul>
<p>And many more described in literature. While each is fascinating in their own right, each has trade-offs in performance, accuracy, and complexity.</p>
<p>We&#x27;ll be focusing on describing the most popular models in this list, reviewing how to use each of these models to describe metals (Gold, Steel, Aluminum), dialectics (plastics, rocks), anisotropic <a href="#ref_ashikhmin2000">[Ashikhmin et al. 2000]</a> (velvet), subsurface (skin, jello), refractive (glass, water), and translucent (stained Glass, rough glass) materials. We&#x27;ll conclude with more literature on the subject of material models. <a href="#ref_georgiev2019">[Georgiev et al. 2019]</a> <a href="#ref_langlands2017">[Langlands 2017]</a></p>
<h2 id="statistics-in-brdfs">Statistics in BRDFs</h2>
<p><strong>Normal Distribution Function (NDF)</strong> - The likelihood of a given facet to be oriented in a given direction.</p>
<p><strong>Probability Distribution Function (PDF)</strong> - A model of the distribution of radiance in a given direction.</p>
<h2 id="analytical-shading-models">Analytical Shading Models</h2>
<p>An analytical model approximates a path traced model of a given BRDF, giving a valid output luminosity with 1 sample. These are useful for real time rendering techniques where only one sample is expected per fragment.</p>
<h3 id="lambertian">Lambertian</h3>
<p>One of the simplest BRDF models is <strong>Lambertian</strong> shading. Assuming that the surface is a completely diffuse reflection, meaning that light is scattered uniformly in all directions independent of the observer&#x27;s view point, the model can be described as:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>L</mi><mn>0</mn><mi>d</mi></msubsup><mo stretchy="false">(</mo><mi>p</mi><mo separator="true">,</mo><mi>ω</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi>κ</mi><mi>d</mi></msub><mi>m</mi><mi>a</mi><mi>x</mi><mo stretchy="false">(</mo><mn>0</mn><mo separator="true">,</mo><mi>n</mi><mo>⋅</mo><mi>l</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">L_0^d (p, \omega) = \kappa_d max(0, n \cdot l)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0991em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8491em"><span style="top:-2.4519em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">d</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2481em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">p</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">ω</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal">κ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">d</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord mathnormal">ma</span><span class="mord mathnormal">x</span><span class="mopen">(</span><span class="mord">0</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0197em">l</span><span class="mclose">)</span></span></span></span></p>
<div><pre><code class="language-glsl"><span class="hljs-type">float</span> lambertianPDF(<span class="hljs-type">vec3</span> normal, <span class="hljs-type">vec3</span> lightDirection)
{
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">max</span>(<span class="hljs-number">0.0</span>, <span class="hljs-built_in">dot</span>(normal, lightDirection)) * ( <span class="hljs-number">1.0</span> / PI );
}

<span class="hljs-type">vec3</span> lambertianLight(SurfaceData s, Light light)
{
    <span class="hljs-keyword">return</span> s.albedo * lambertianPDF(s.normal, light.direction);
}</code></pre></div>
<p>Note the use of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mn>1</mn><mi>π</mi></mfrac></mrow><annotation encoding="application/x-tex">\frac{1}{\pi}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1901em;vertical-align:-0.345em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8451em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em">π</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.394em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span>, this is sometimes omitted in real time renderers. <a href="#ref_seblagarde2012">[Seblagarde 2012]</a></p>
<p>Where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>κ</mi><mi>d</mi></msub></mrow><annotation encoding="application/x-tex">\kappa_d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">κ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">d</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> is the reflectance that encodes how much energy is absorbed by the surface (albedo).</p>
<p>Dot product implicitly encodes that <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>L</mi><mi>o</mi></msub></mrow><annotation encoding="application/x-tex">L_o</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">o</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> will be lower when the light arrives at a grazing angle, similar to real life. In real life, lambertian interactions are minimal, and most objects show specular interactions in some capacity.</p>
<p>Such reflections can even show more of the color of a light than of the material during reflection, dialectric materials such as water, plastic, and metal vs. metallic objects such as gold or steel.</p>
<h3 id="phong">Phong</h3>
<p><strong>Phong</strong> is based on the informal observation that highlights appear when the view vector aligns with the direction of the reflected light vector <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo>=</mo><mn>1</mn><mo>−</mo><mn>2</mn><mo stretchy="false">(</mo><mi>n</mi><mo>⋅</mo><mi>l</mi><mo stretchy="false">)</mo><mi>n</mi></mrow><annotation encoding="application/x-tex"> r = 1 - 2(n \cdot l )n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">2</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0197em">l</span><span class="mclose">)</span><span class="mord mathnormal">n</span></span></span></span>. As soon as the view vector deviates from <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi></mrow><annotation encoding="application/x-tex">r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span></span></span></span>, light starts disappearing. Phone modeled the specular component of the material with the following function:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>L</mi><mi>o</mi><mi>s</mi></msubsup><mo stretchy="false">(</mo><mi>p</mi><mo separator="true">,</mo><mi>ω</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi>κ</mi><mi>a</mi></msub><mo>+</mo><msub><mi>κ</mi><mi>d</mi></msub><msubsup><mi>L</mi><mi>o</mi><mi>d</mi></msubsup><mo stretchy="false">(</mo><mi>p</mi><mo separator="true">,</mo><mi>ω</mi><mo stretchy="false">)</mo><mo>+</mo><msub><mi>κ</mi><mi>s</mi></msub><mi>m</mi><mi>a</mi><mi>x</mi><mo stretchy="false">(</mo><mn>0</mn><mo separator="true">,</mo><mi>ω</mi><mo>⋅</mo><mi>r</mi><msup><mo stretchy="false">)</mo><mi>a</mi></msup></mrow><annotation encoding="application/x-tex">L_o^s (p, \omega) = \kappa_a + \kappa_d L_o^d (p, \omega) + \kappa_s max(0, \omega \cdot r)^a</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6644em"><span style="top:-2.453em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">o</span></span></span><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">s</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">p</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">ω</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.7333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">κ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">a</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.0991em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal">κ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">d</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8491em"><span style="top:-2.453em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">o</span></span></span><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">d</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">p</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">ω</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal">κ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">s</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord mathnormal">ma</span><span class="mord mathnormal">x</span><span class="mopen">(</span><span class="mord">0</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">ω</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0278em">r</span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">a</span></span></span></span></span></span></span></span></span></span></span></p>
<p>Where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>κ</mi><mi>s</mi></msub></mrow><annotation encoding="application/x-tex">\kappa_s</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">κ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">s</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> is the specular-reflection constant that defines the ratio of reflection and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>κ</mi><mi>d</mi></msub></mrow><annotation encoding="application/x-tex">\kappa_d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">κ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">d</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> the ratio of the diffuse part <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>L</mi><mi>o</mi><mi>d</mi></msubsup></mrow><annotation encoding="application/x-tex">L_o^d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0961em;vertical-align:-0.247em"></span><span class="mord"><span class="mord mathnormal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8491em"><span style="top:-2.453em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">o</span></span></span><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">d</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em"><span></span></span></span></span></span></span></span></span></span> usually <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>κ</mi><mi>s</mi></msub><mi mathvariant="normal">∣</mi><msub><mi>κ</mi><mi>d</mi></msub><mo>&lt;</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\kappa_s | \kappa_d &lt; 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal">κ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">s</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">∣</span><span class="mord"><span class="mord mathnormal">κ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">d</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">&lt;</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">1</span></span></span></span>. The constant term <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>κ</mi><mi>a</mi></msub></mrow><annotation encoding="application/x-tex">\kappa_a</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">κ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">a</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> ambient can be used to emulate indirect illumination, but is considered a hack. <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi></mrow><annotation encoding="application/x-tex">\alpha</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.0037em">α</span></span></span></span> defines the shininess of a material, the larger the value, the smaller and more focused the highlights of the surface will be.</p>
<div><pre><code class="language-glsl"><span class="hljs-type">float</span> phongSpecular(<span class="hljs-type">vec3</span> normal, <span class="hljs-type">vec3</span> lightDirection, <span class="hljs-type">vec3</span> viewDirection, <span class="hljs-type">float</span> shininess)
{
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">pow</span>(<span class="hljs-built_in">max</span>(<span class="hljs-number">0.0</span>, <span class="hljs-built_in">dot</span>(<span class="hljs-built_in">reflect</span>(normal, lightDirection), viewDirection)), shininess);
}</code></pre></div>
<h3 id="blinn-phong">Blinn-Phong</h3>
<p>A modification of the Phong term suggested by Blinn. Instead of relying on a reflected light vector, the define a unit halfway vector between the view and the light <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>h</mi><mo>=</mo><mo stretchy="false">(</mo><mi>ω</mi><mo>+</mo><mi>l</mi><mo stretchy="false">)</mo><mi mathvariant="normal">/</mi><mi mathvariant="normal">∣</mi><mi>ω</mi><mo>+</mo><mi>l</mi><mi mathvariant="normal">∣</mi></mrow><annotation encoding="application/x-tex">h = (\omega + l) / |\omega + l|</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal">h</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0359em">ω</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0197em">l</span><span class="mclose">)</span><span class="mord">/∣</span><span class="mord mathnormal" style="margin-right:0.0359em">ω</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0197em">l</span><span class="mord">∣</span></span></span></span> which is cheaper to compute. This new model is also more accurate at reproducing measured BRDF surfaces. <a href="#ref_gotanda2012">[Gotanda]</a></p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>L</mi><mi>o</mi><mi>s</mi></msubsup><mo stretchy="false">(</mo><mi>p</mi><mo separator="true">,</mo><mi>ω</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi>κ</mi><mi>a</mi></msub><mo>+</mo><msub><mi>κ</mi><mi>d</mi></msub><msubsup><mi>L</mi><mi>o</mi><mi>d</mi></msubsup><mo stretchy="false">(</mo><mi>p</mi><mo separator="true">,</mo><mi>ω</mi><mo stretchy="false">)</mo><mo>+</mo><msub><mi>κ</mi><mi>s</mi></msub><mi>m</mi><mi>a</mi><mi>x</mi><mo stretchy="false">(</mo><mn>0</mn><mo separator="true">,</mo><mi>h</mi><mo>⋅</mo><mi>n</mi><msup><mo stretchy="false">)</mo><mi>a</mi></msup></mrow><annotation encoding="application/x-tex">L_o^s (p, \omega) = \kappa_a + \kappa_d L_o^d (p, \omega) + \kappa_s max(0, h \cdot n)^a</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6644em"><span style="top:-2.453em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">o</span></span></span><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">s</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">p</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">ω</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.7333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">κ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">a</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.0991em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal">κ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">d</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal">L</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8491em"><span style="top:-2.453em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">o</span></span></span><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">d</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.247em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">p</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">ω</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal">κ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">s</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord mathnormal">ma</span><span class="mord mathnormal">x</span><span class="mopen">(</span><span class="mord">0</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal">h</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal">n</span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">a</span></span></span></span></span></span></span></span></span></span></span></p>
<div><pre><code class="language-glsl"><span class="hljs-type">float</span> blinPhongSpecular(<span class="hljs-type">vec3</span> normal, <span class="hljs-type">vec3</span> lightDirection, <span class="hljs-type">vec3</span> viewDirection, <span class="hljs-type">float</span> shininess)
{
    <span class="hljs-type">vec3</span> halfDirection = <span class="hljs-built_in">normalize</span>(viewDirection + lightDirection);
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">pow</span>(<span class="hljs-built_in">max</span>(<span class="hljs-number">0.0</span>, <span class="hljs-built_in">dot</span>(halfDirection, normal)), shininess);
}</code></pre></div>
<h3 id="schlick">Schlick</h3>
<p><strong>Schlick</strong> describes a fresnel approximation <a href="#ref_schlick1994">[Schlick 1994]</a> that&#x27;s used throughout computer graphics, first described as follows:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>R</mi><mo>∗</mo><mi>λ</mi><mo stretchy="false">(</mo><mi>t</mi><mo separator="true">,</mo><mi>u</mi><mo separator="true">,</mo><mi>v</mi><mo separator="true">,</mo><msup><mi>v</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo separator="true">,</mo><mi>w</mi><mo stretchy="false">)</mo><mo>=</mo><mi>S</mi><mo>∗</mo><mi>λ</mi><mo stretchy="false">(</mo><mi>u</mi><mo stretchy="false">)</mo><mi>D</mi><mo stretchy="false">(</mo><mi>t</mi><mo separator="true">,</mo><mi>v</mi><mo separator="true">,</mo><msup><mi>v</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo separator="true">,</mo><mi>w</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">R*\lambda(t, u, v, v&#x27;, w) = S*\lambda(u) D(t, v, v&#x27;, w)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0077em">R</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em"></span><span class="mord mathnormal">λ</span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal">u</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">v</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">v</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0269em">w</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0576em">S</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em"></span><span class="mord mathnormal">λ</span><span class="mopen">(</span><span class="mord mathnormal">u</span><span class="mclose">)</span><span class="mord mathnormal" style="margin-right:0.0278em">D</span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0359em">v</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em">v</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.0269em">w</span><span class="mclose">)</span></span></span></span></p>
<p>Where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi><mi mathvariant="normal">_</mi><mi>λ</mi></mrow><annotation encoding="application/x-tex">S\_\lambda</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0044em;vertical-align:-0.31em"></span><span class="mord mathnormal" style="margin-right:0.0576em">S</span><span class="mord" style="margin-right:0.0278em">_</span><span class="mord mathnormal">λ</span></span></span></span> is the spectral factor:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi><mo>∗</mo><mi>λ</mi><mo stretchy="false">(</mo><mi>u</mi><mo stretchy="false">)</mo><mo>=</mo><mi>C</mi><mo>∗</mo><mi>λ</mi><mo>+</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>C</mi><mi mathvariant="normal">_</mi><mi>λ</mi><mo stretchy="false">)</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>u</mi><msup><mo stretchy="false">)</mo><mn>5</mn></msup></mrow><annotation encoding="application/x-tex">S*\lambda(u) = C*\lambda + (1 - C\_\lambda)(1 - u)^5</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0576em">S</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal">λ</span><span class="mopen">(</span><span class="mord mathnormal">u</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6833em"></span><span class="mord mathnormal" style="margin-right:0.0715em">C</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.7778em;vertical-align:-0.0833em"></span><span class="mord mathnormal">λ</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.06em;vertical-align:-0.31em"></span><span class="mord mathnormal" style="margin-right:0.0715em">C</span><span class="mord" style="margin-right:0.0278em">_</span><span class="mord mathnormal">λ</span><span class="mclose">)</span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.0641em;vertical-align:-0.25em"></span><span class="mord mathnormal">u</span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">5</span></span></span></span></span></span></span></span></span></span></span></p>
<div><pre><code class="language-glsl"><span class="hljs-type">float</span> SchlickFresnel(<span class="hljs-type">float</span> u)
{
    <span class="hljs-type">float</span> m = <span class="hljs-built_in">clamp</span>(<span class="hljs-number">1.0</span> - u, <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>);
    <span class="hljs-type">float</span> m2 = m * m;
    <span class="hljs-keyword">return</span> m2 * m2 * m; <span class="hljs-comment">// pow(m,5.0)</span>
}</code></pre></div>
<h3 id="oren-nayar">Oren-Nayar</h3>
<p><strong>Oren-Nayar</strong> uses a ratio of lambertian to better approximate diffuse surfaces, better passing the <em>white furnace test</em>. <a href="#ref_oren1994">[Oren et al. 1994]</a></p>
<div><pre><code class="language-glsl"><span class="hljs-type">float</span> orenNayarDiffuse(
  <span class="hljs-type">vec3</span> normal,
  <span class="hljs-type">vec3</span> lightDirection,
  <span class="hljs-type">vec3</span> viewDirection,
  <span class="hljs-type">float</span> roughness,
  <span class="hljs-type">float</span> albedo)
{

  <span class="hljs-type">float</span> LdotV = <span class="hljs-built_in">dot</span>(lightDirection, viewDirection);
  <span class="hljs-type">float</span> NdotL = <span class="hljs-built_in">dot</span>(lightDirection, normal);
  <span class="hljs-type">float</span> NdotV = <span class="hljs-built_in">dot</span>(normal, viewDirection);

  <span class="hljs-type">float</span> s = LdotV - NdotL * NdotV;
  <span class="hljs-type">float</span> t = <span class="hljs-built_in">mix</span>(<span class="hljs-number">1.0</span>, <span class="hljs-built_in">max</span>(NdotL, NdotV), <span class="hljs-built_in">step</span>(<span class="hljs-number">0.0</span>, s));

  <span class="hljs-type">float</span> sigma2 = roughness * roughness;
  <span class="hljs-type">float</span> A = <span class="hljs-number">1.0</span> + sigma2 * (albedo / (sigma2 + <span class="hljs-number">0.13</span>) + <span class="hljs-number">0.5</span> / (sigma2 + <span class="hljs-number">0.33</span>));
  <span class="hljs-type">float</span> B = <span class="hljs-number">0.45</span> * sigma2 / (sigma2 + <span class="hljs-number">0.09</span>);

  <span class="hljs-keyword">return</span> albedo * <span class="hljs-built_in">max</span>(<span class="hljs-number">0.0</span>, NdotL) * (A + B * s / t) / <span class="hljs-number">3.14159265</span>;
}
</code></pre></div>
<h3 id="ggx">GGX</h3>
<p>GGX (Ground Glass Unknown) is an analytic BSDF model first proposed by <a href="#ref_walter2007">[Walter et al. 2007]</a> that took into account the micro-facet distribution of an underlying material.</p>
<p>Unreal Engine 4 expanded on this definition by defining a <em>split sum approximation</em> to GGX that used pre-convoluted cube map textures, and a pre-integrated LUT that maps <code>vec2(cosTheta, roughness)</code> to the specular fresnel contribution of a given material. [^karis2013] [^hammon2017] [^carpentier2017] [^heitz2017]</p>
<div><pre><code class="language-glsl"><span class="hljs-type">vec3</span> ImportanceSampleGGX(<span class="hljs-type">vec2</span> Xi, <span class="hljs-type">float</span> Roughness, <span class="hljs-type">vec3</span> N)
{
  <span class="hljs-type">float</span> a = Roughness * Roughness;
  <span class="hljs-type">float</span> Phi = <span class="hljs-number">2</span> * PI * Xi.x;
  <span class="hljs-type">float</span> CosTheta = <span class="hljs-built_in">sqrt</span>((<span class="hljs-number">1</span> - Xi.y) / (<span class="hljs-number">1</span> + (a * a - <span class="hljs-number">1</span>) * Xi.y));
  <span class="hljs-type">float</span> SinTheta = <span class="hljs-built_in">sqrt</span>(<span class="hljs-number">1</span> - CosTheta * CosTheta);
  <span class="hljs-type">vec3</span> H;
  H.x = SinTheta * <span class="hljs-built_in">cos</span>(Phi);
  H.y = SinTheta * <span class="hljs-built_in">sin</span>(Phi);
  H.z = CosTheta;
  <span class="hljs-type">vec3</span> UpVector = <span class="hljs-built_in">abs</span>(N.z) &lt; <span class="hljs-number">0.999</span> ? <span class="hljs-type">vec3</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>) : <span class="hljs-type">vec3</span>(<span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
  <span class="hljs-type">vec3</span> TangentX = <span class="hljs-built_in">normalize</span>(<span class="hljs-built_in">cross</span>(UpVector, N));
  <span class="hljs-type">vec3</span> TangentY = <span class="hljs-built_in">cross</span>(N, TangentX);
  <span class="hljs-comment">// Tangent to world space</span>
  <span class="hljs-keyword">return</span> TangentX * H.x + TangentY * H.y + N * H.z;
}
<span class="hljs-type">vec3</span> SpecularIBL(<span class="hljs-type">vec3</span> SpecularColor, <span class="hljs-type">float</span> Roughness, <span class="hljs-type">vec3</span> N, <span class="hljs-type">vec3</span> V)
{
  <span class="hljs-type">vec3</span> SpecularLighting = <span class="hljs-number">0</span>;
  <span class="hljs-keyword">const</span> <span class="hljs-type">uint</span> NumSamples = <span class="hljs-number">1024</span>;
  <span class="hljs-keyword">for</span> (<span class="hljs-type">uint</span> i = <span class="hljs-number">0</span>; i &lt; NumSamples; i++) {
    <span class="hljs-type">vec2</span> Xi = Hammersley(i, NumSamples);
    <span class="hljs-number">4</span> <span class="hljs-type">vec3</span> H = ImportanceSampleGGX(Xi, Roughness, N);
    <span class="hljs-type">vec3</span> L = <span class="hljs-number">2</span> * <span class="hljs-built_in">dot</span>(V, H) * H - V;
    <span class="hljs-type">float</span> NoV = saturate(<span class="hljs-built_in">dot</span>(N, V));
    <span class="hljs-type">float</span> NoL = saturate(<span class="hljs-built_in">dot</span>(N, L));
    <span class="hljs-type">float</span> NoH = saturate(<span class="hljs-built_in">dot</span>(N, H));
    <span class="hljs-type">float</span> VoH = saturate(<span class="hljs-built_in">dot</span>(V, H));
    <span class="hljs-keyword">if</span> (NoL &gt; <span class="hljs-number">0</span>)
    {
      <span class="hljs-type">vec3</span> SampleColor = EnvMap.SampleLevel(EnvMapSampler, L, <span class="hljs-number">0</span>).rgb;
      <span class="hljs-type">float</span> G = G_Smith(Roughness, NoV, NoL);
      <span class="hljs-type">float</span> Fc = <span class="hljs-built_in">pow</span>(<span class="hljs-number">1</span> - VoH, <span class="hljs-number">5</span>);
      <span class="hljs-type">vec3</span> F = (<span class="hljs-number">1</span> - Fc) * SpecularColor + Fc;
      <span class="hljs-comment">// Incident light = SampleColor * NoL</span>
      <span class="hljs-comment">// Microfacet specular = D*G*F / (4*NoL*NoV)</span>
      <span class="hljs-comment">// pdf = D * NoH / (4 * VoH)</span>
      SpecularLighting += SampleColor * F * G * VoH / (NoH * NoV);
    }
  }
  <span class="hljs-keyword">return</span> SpecularLighting / NumSamples;
}

<span class="hljs-type">vec3</span> PrefilterEnvMap(<span class="hljs-type">float</span> Roughness, <span class="hljs-type">vec3</span> R)
{
  <span class="hljs-type">vec3</span> N = R;
  <span class="hljs-type">vec3</span> V = R;
  <span class="hljs-type">vec3</span> PrefilteredColor = <span class="hljs-number">0</span>;
  <span class="hljs-keyword">const</span> <span class="hljs-type">uint</span> NumSamples = <span class="hljs-number">1024</span>;
  <span class="hljs-keyword">for</span> (<span class="hljs-type">uint</span> i = <span class="hljs-number">0</span>; i &lt; NumSamples; i++) {
    <span class="hljs-type">vec2</span> Xi = Hammersley(i, NumSamples);
    <span class="hljs-type">vec3</span> H = ImportanceSampleGGX(Xi, Roughness, N);
    <span class="hljs-type">vec3</span> L = <span class="hljs-number">2</span> * <span class="hljs-built_in">dot</span>(V, H) * H - V;
    <span class="hljs-type">float</span> NoL = saturate(<span class="hljs-built_in">dot</span>(N, L));
    <span class="hljs-keyword">if</span> (NoL &gt; <span class="hljs-number">0</span>)
    {
      PrefilteredColor += EnvMap.SampleLevel(EnvMapSampler, L, <span class="hljs-number">0</span>).rgb * NoL;
      TotalWeight += NoL;
    }
  }
  <span class="hljs-keyword">return</span> PrefilteredColor / TotalWeight;
}
<span class="hljs-type">vec2</span> IntegrateBRDF(<span class="hljs-type">float</span> Roughness, <span class="hljs-type">float</span> NoV)
{
  <span class="hljs-type">vec3</span> V;
  V.x = <span class="hljs-built_in">sqrt</span>(<span class="hljs-number">1.0</span>f - NoV * NoV); <span class="hljs-comment">// sin</span>
  V.y = <span class="hljs-number">0</span>;
  V.z = NoV; <span class="hljs-comment">// cos</span>
  <span class="hljs-type">float</span> A = <span class="hljs-number">0</span>;
  <span class="hljs-type">float</span> B = <span class="hljs-number">0</span>;
  <span class="hljs-keyword">const</span> <span class="hljs-type">uint</span> NumSamples = <span class="hljs-number">1024</span>;
  <span class="hljs-keyword">for</span> (<span class="hljs-type">uint</span> i = <span class="hljs-number">0</span>; i &lt; NumSamples; i++) {
    <span class="hljs-type">vec2</span> Xi = Hammersley(i, NumSamples);
    <span class="hljs-type">vec3</span> H = ImportanceSampleGGX(Xi, Roughness, N);
    <span class="hljs-type">vec3</span> L = <span class="hljs-number">2</span> * <span class="hljs-built_in">dot</span>(V, H) * H - V;
    <span class="hljs-type">float</span> NoL = saturate(L.z);
    <span class="hljs-type">float</span> NoH = saturate(H.z);
    <span class="hljs-type">float</span> VoH = saturate(<span class="hljs-built_in">dot</span>(V, H));
    <span class="hljs-keyword">if</span> (NoL &gt; <span class="hljs-number">0</span>)
    {
      <span class="hljs-type">float</span> G = G_Smith(Roughness, NoV, NoL);
      <span class="hljs-type">float</span> G_Vis = G * VoH / (NoH * NoV);
      <span class="hljs-type">float</span> Fc = <span class="hljs-built_in">pow</span>(<span class="hljs-number">1</span> - VoH, <span class="hljs-number">5</span>);
      A += (<span class="hljs-number">1</span> - Fc) * G_Vis;
      B += Fc * G_Vis;
    }
  }
  <span class="hljs-keyword">return</span> <span class="hljs-type">vec2</span>(A, B) / NumSamples;
}
<span class="hljs-type">vec3</span> ApproximateSpecularIBL(<span class="hljs-type">vec3</span> SpecularColor, <span class="hljs-type">float</span> Roughness, <span class="hljs-type">vec3</span> N,
                            <span class="hljs-type">vec3</span> V)
{
  <span class="hljs-type">float</span> NoV = saturate(<span class="hljs-built_in">dot</span>(N, V));
  <span class="hljs-type">vec3</span> R = <span class="hljs-number">2</span> * <span class="hljs-built_in">dot</span>(V, N) * N - V;
  <span class="hljs-type">vec3</span> PrefilteredColor = PrefilterEnvMap(Roughness, R);
  <span class="hljs-type">vec2</span> EnvBRDF = IntegrateBRDF(Roughness, NoV);
  <span class="hljs-keyword">return</span> PrefilteredColor * (SpecularColor * EnvBRDF.x + EnvBRDF.y);
}</code></pre></div>
<h2 id="conclusion-and-sample-code">Conclusion and Sample Code</h2>
<ul>
<li>
<p>Jakub Boksansky (<a href="https://twitter.com/boksajak">@boksajak</a>) wrote a <a href="https://boksajak.github.io/blog/BRDF">Crash Course in BRDF Implementation</a> here.</p>
</li>
<li>
<p>Brian Karris (<a href="https://twitter.com/briankaris">@BrianKaris</a>) wrote great reference to different specular BRDFs <a href="http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html">here</a>.</p>
</li>
</ul>
<hr/>
<p>For code samples refer to the following:</p>
<ul>
<li>
<p><a href="https://twitter.com/JoeyDeVriez">Joey de Vries @JoeyDeVriez</a> provides an amazing summary of image based lighting for PBR in his <a href="https://learnopengl.com/PBR/IBL/Specular-IBL">LearnOpenGL.com</a> blog.</p>
</li>
<li>
<p>Disney BRDF Explorer features examples of every BRDF you could possibly want to know about <a href="https://github.com/wdas/brdf/tree/master/src/brdfs">here</a>.</p>
</li>
<li>
<p>Unity provides the source code to their shaders <a href="https://unity3d.com/get-unity/download/archive">here in their archive</a>, simply click on the arrow and select <strong>Built In Shaders</strong>, and check out their <code>UnityStandardBRDF.cginc</code> file.</p>
</li>
<li>
<p><a href="https://github.com/EpicGames/UnrealEngine/tree/release/Engine/Shaders/Private">Unreal Engine 4</a> also provides the source code to their shaders if you link your Unreal account to your Github account. You&#x27;ll find BRDF shaders in <code>Engine/Shaders/Private/BRDF.ush</code>.</p>
</li>
<li>
<p><a href="https://casual-effects.com/g3d/www/index.html#sourcecode">Morgan McGuire&#x27;s G3D Engine</a> features a material models example in <code>G3D10/samples/minimalOpenGL/min.pix</code>.</p>
</li>
<li>
<p>If you install <a href="https://marmoset.co/toolbag/">Marmoset Toolbag</a>, you&#x27;ll find its material model shaders in <code>data/shader/mat</code>, with examples of Lambertian, GGX, Phong, Anisotropic, etc.</p>
</li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_eisemann2012"><span class="markademic-citename">[Eisemann et al. 2012]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Real-Time Shadows</span></strong></em><br/><span class="markademic-authors"><span><span>Elmar Eisemann</span></span><span>, <span>Michael Schwarz</span></span><span>, <span>Ulf Assarsson</span></span><span> and <span>Michael Wimmer</span></span></span><br/><span><span class="markademic-publisher">CRC Press 2012</span><br/></span><span><a href="https://www.realtimeshadows.com/">realtimeshadows.com</a></span></div></td></tr><tr><td id="ref_matusik2003"><span class="markademic-citename">[Matusik et al. 2003]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">A Data-Driven Reflectance Model</span></strong></em><br/><span class="markademic-authors"><span><span>Wojciech Matusik</span></span><span>, <span>Hanspeter Pfister</span></span><span>, <span>Matt Brand</span></span><span> and <span>Leonard McMillan</span></span></span><br/><span><span class="markademic-publisher">ACM Transaction on Graphics 2003</span><br/></span><span><a href="http://www.merl.com/brdf/">merl.com</a></span></div></td></tr><tr><td id="ref_belcour2013"><span class="markademic-citename">[Belcour et al. 2013]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">ALTA: A BRDF Analysis Library</span></strong></em><br/><span class="markademic-authors"><span><img class="markademic-profile" style="margin-right:.25em" src="https://github.com/belcour.png" alt="Laurent Belcour&#x27;s picture"/><a href="https://belcour.github.io/blog/">Laurent Belcour</a> (<a href="https://twitter.com/_Laurent">@_Laurent</a>)</span><span>, <span>Ludovic Courtes</span></span><span> and <span>Romain Pacanowski</span></span></span><br/><span><span class="markademic-publisher">2013</span><br/></span><span><a href="http://alta.gforge.inria.fr/">alta.gforge.inria.fr</a></span></div></td></tr><tr><td id="ref_lambert1982"><span class="markademic-citename">[Heinrich Lambert 1982]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Photometria, sive De mensura et gradibus luminus, colorum et umbrae</span></strong></em><br/><span class="markademic-authors"><span><span>Johann Heinrich Lambert</span></span></span><br/><span><span class="markademic-publisher">1982</span><br/></span><span><a href="https://archive.org/details/lambertsphotome00lambgoog/page/n7">archive.org</a></span></div></td></tr><tr><td id="ref_phong1975"><span class="markademic-citename">[Tuong Phong 1975]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Illumination for Computer Generated Pictures</span></strong></em><br/><span class="markademic-authors"><span><span>Bui Tuong Phong</span></span></span><br/><span><span class="markademic-publisher">ACM 1975</span><br/></span><span><a href="http://users.cs.northwestern.edu/~ago820/cs395/Papers/Phong_1975.pdf">users.cs.northwestern.edu</a></span></div></td></tr><tr><td id="ref_blinn1977"><span class="markademic-citename">[F. Blinn 1977]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Models of Light Reflection for Computer Synthesized Pictures</span></strong></em><br/><span class="markademic-authors"><span><span>J F. Blinn</span></span></span><br/><span><span class="markademic-publisher">ACM Computer Graphics 1977</span><br/></span><span><a href="https://www.microsoft.com/en-us/research/publication/models-of-light-reflection-for-computer-synthesized-pictures/">microsoft.com</a></span></div></td></tr><tr><td id="ref_schlick1994"><span class="markademic-citename">[Schlick 1994]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">An Inexpesnive BRDF Model for Physically-Based Rendering</span></strong></em><br/><span class="markademic-authors"><span><span>Christophe Schlick</span></span></span><br/><span><span class="markademic-publisher">1994</span><br/></span><span><a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.50.2297&amp;rep=rep1&amp;type=pdf">citeseerx.ist.psu.edu</a></span></div></td></tr><tr><td id="ref_walter2007"><span class="markademic-citename">[Walter et al. 2007]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Microfacet Models for Refraction through Rough Surfaces</span></strong></em><br/><span class="markademic-authors"><span><span>Bruce Walter</span></span><span>, <span>Stephen R. Marschner</span></span><span>, <span>Hongsong Li</span></span><span> and <span>Kenneth E. Torrance</span></span></span><br/><span><span class="markademic-publisher">EGSR 2007</span><br/></span><span><a href="http://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.html">cs.cornell.edu</a></span></div></td></tr><tr><td id="ref_karis2013"><span class="markademic-citename">[Karis 2013]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Real Shading in Unreal Engine 4</span></strong></em><br/><span class="markademic-authors"><span><span>Brian Karis</span></span></span><br/><span><span class="markademic-publisher">2013</span><br/></span><span><a href="https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf">cdn2.unrealengine.com</a></span></div></td></tr><tr><td id="ref_burley2012"><span class="markademic-citename">[Burley 2012]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Physically-Based Shading at Disney</span></strong></em><br/><span class="markademic-authors"><span><span>Brent Burley</span></span></span><br/><span><span class="markademic-publisher">2012</span><br/></span><span><a href="https://disney-animation.s3.amazonaws.com/library/s2012_pbs_disney_brdf_notes_v2.pdf">disney-animation.s3.amazonaws.com</a></span></div></td></tr><tr><td id="ref_heitz2016"><span class="markademic-citename">[Heitz et al. 2016]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Multiple-Scattering Microfacet BSDFs with the Smith Model</span></strong></em><br/><span class="markademic-authors"><span><a href="https://eheitzresearch.wordpress.com/">Eric Heitz</a> (<a href="https://twitter.com/eric_heitz">@eric_heitz</a>)</span><span>, <span>Johannes Hanika</span></span><span> and <span>Carsten Dachsbacher</span></span></span><br/><span><span class="markademic-publisher">2016</span><br/></span><span><a href="https://eheitzresearch.wordpress.com/240-2/">eheitzresearch.wordpress.com</a></span></div></td></tr><tr><td id="ref_cook1982"><span class="markademic-citename">[L. Cook et al. 1982]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">A reflectance model for computer graphics</span></strong></em><br/><span class="markademic-authors"><span><span>Robert L. Cook</span></span><span> and <span>Kenneth E. Torrance</span></span></span><br/><span><span class="markademic-publisher">ACM Transactions on Graphics 1982</span><br/></span><span><a href="http://inst.cs.berkeley.edu/~cs294-13/fa09/lectures/cookpaper.pdf">inst.cs.berkeley.edu</a></span></div></td></tr><tr><td id="ref_oren1994"><span class="markademic-citename">[Oren et al. 1994]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Generalization of Lambert&#x27;s Reflectance Model</span></strong></em><br/><span class="markademic-authors"><span><span>Michael Oren</span></span><span> and <span>Shree K. Nayar</span></span></span><br/><span><span class="markademic-publisher">1994</span><br/></span><span><a href="http://www1.cs.columbia.edu/CAVE/projects/oren/">www1.cs.columbia.edu</a></span></div></td></tr><tr><td id="ref_neumann1999"><span class="markademic-citename">[Neumann et al. 1999]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Compact Metallic Reflectance Models</span></strong></em><br/><span class="markademic-authors"><span><span>László Neumann</span></span><span> and <span>Attila Neumannn</span></span></span><br/><span><span class="markademic-publisher">1999</span><br/></span><span><a href="https://www.researchgate.net/publication/227631231_Compact_Metallic_Reflectance_Models">researchgate.net</a></span></div></td></tr><tr><td id="ref_ward1992"><span class="markademic-citename">[Ward 1992]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Measuring and Modeling Anisotropic Reflection</span></strong></em><br/><span class="markademic-authors"><span><span>Greg Ward</span></span></span><br/><span><span class="markademic-publisher">1992</span><br/></span><span><a href="https://cseweb.ucsd.edu/~ravir/6998/papers/p265-ward.pdf">cseweb.ucsd.edu</a></span></div></td></tr><tr><td id="ref_walter2005"><span class="markademic-citename">[Walter 2005]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Notes on the Ward BRDF</span></strong></em><br/><span class="markademic-authors"><span><span>Bruce Walter</span></span></span><br/><span><span class="markademic-publisher">Cornell 2005</span><br/></span><span><a href="https://pdfs.semanticscholar.org/330e/59117d7da6c794750730a15f9a178391b9fe.pdf">pdfs.semanticscholar.org</a></span></div></td></tr><tr><td id="ref_kurt2010"><span class="markademic-citename">[Kurt et al. 2010]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">An Anisotropic BRDF Model for Fitting and Monte Carlo Rendering</span></strong></em><br/><span class="markademic-authors"><span><span>Murat Kurt</span></span><span>, <span>László Szirmay-Kalos</span></span><span> and <span>Jaroslav Křivánek</span></span></span><br/><span><span class="markademic-publisher">2010</span><br/></span><span><a href="https://cgg.mff.cuni.cz/~jaroslav/papers/2010-anisobrdf/2010-anisobrdf.pdf">cgg.mff.cuni.cz</a></span></div></td></tr><tr><td id="ref_ashikhmin2000"><span class="markademic-citename">[Ashikhmin et al. 2000]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">An Anisotropic Phong BRDF Model</span></strong></em><br/><span class="markademic-authors"><span><span>Michael Ashikhmin</span></span><span> and <a href="https://www.petershirley.com/">Peter Shirley</a> (<a href="https://twitter.com/Peter_shirley">@Peter_shirley</a>)</span></span><br/><span><span class="markademic-publisher">2000</span><br/></span><span><a href="https://www.cs.utah.edu/~shirley/papers/jgtbrdf.pdf">cs.utah.edu</a></span></div></td></tr><tr><td id="ref_gouraud1971"><span class="markademic-citename">[Gouraud 1971]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Continuous Shading of Curved Surfaces</span></strong></em><br/><span class="markademic-authors"><span><span>Henri Gouraud</span></span></span><br/><span><span class="markademic-publisher">1971</span><br/></span><span><a href="https://ieeexplore.ieee.org/document/1671906">ieeexplore.ieee.org</a></span></div></td></tr><tr><td id="ref_georgiev2019"><span class="markademic-citename">[Georgiev et al. 2019]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Autodesk Standard Surface</span></strong></em><br/><span class="markademic-authors"><span><span>Iliyan Georgiev</span></span><span>, <span>Jamie Portsmouth</span></span><span>, <span>Zap Andersson</span></span><span>, <span>Adrien Herubel</span></span><span>, <span>Alan King</span></span><span>, <span>Shinji Ogaki</span></span><span> and <span>Frederic Servant</span></span></span><br/><span><span class="markademic-publisher">Autodesk 2019</span><br/></span><span><a href="https://autodesk.github.io/standard-surface/">autodesk.github.io</a></span></div></td></tr><tr><td id="ref_langlands2017"><span class="markademic-citename">[Langlands 2017]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Physically Based Shader Design in Arnold</span></strong></em><br/><span class="markademic-authors"><span><span>Anders Langlands</span></span></span><br/><span><span class="markademic-publisher">Solid Angle 2017</span><br/></span><span><a href="https://www.arnoldrenderer.com/research/physically_based_shader_design_in_arnold.pdf">arnoldrenderer.com</a></span></div></td></tr><tr><td id="ref_seblagarde2012"><span class="markademic-citename">[Seblagarde 2012]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">PI or not to PI in game lighting equation</span></strong></em><br/><span class="markademic-authors"><span><span>Sébastien Seblagarde</span></span></span><br/><span><span class="markademic-publisher">2012</span><br/></span><span><a href="https://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/">seblagarde.wordpress.com</a></span></div></td></tr><tr><td id="ref_gotanda2012"><span class="markademic-citename">[Gotanda]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Beyond a Simple Physically Based Blinn-Phong Model in Real-Time</span></strong></em><br/><span class="markademic-authors"><span><span>Yoshiharu Gotanda</span></span></span><br/><span><a href="https://blog.selfshadow.com/publications/s2012-shading-course/">blog.selfshadow.com</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Image Editor Effects</title>
<link>https://alain.xyz/blog/image-editor-effects</link>
<description>A review of image adjustment / effects shader logic found in Photoshop and other image editors and game engines. We&apos;ll discuss layer effects, filters, color correction, and how to include these effects in your own applications.</description>
<guid isPermaLink="true">https://alain.xyz/blog/image-editor-effects</guid>
<pubDate>Sun, 11 Nov 2018 11:30:00 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>shader</category><category>glsl</category><category>hlsl</category><category>vulkan</category><category>directx</category><category>opengl</category><category>spir-v</category><category>metal</category><category>photoshop</category><category>layer</category><category>effects</category><category>logic</category>
<enclosure url="https://alain.xyz/blog/image-editor-effects/assets/cover.jpg" length="400396" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/disolve.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/darken.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/lighten.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/multiply.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/divide.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/screen.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/overlay.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/colordodge.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/lineardodge.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/colorburn.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/linearburn.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/exclusion.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/difference.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/curves.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/levels.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/brightnesscontrast.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/gamma.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/hsl.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/gradient.png"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/gaussian.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/pixelate.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/sobel.jpg"/><link rel="preload" as="image" href="https://alain.xyz/blog/image-editor-effects/assets/sharpen.jpg"/><p>Image processing effects used in image editors like Adobe Photoshop are used to composite different layers and adjust the overall appearance of an image. These effects build upon research in areas such as signal processing, statistics, and mathematical analysis, and applies them to visual effects and postprocessing. Let&#x27;s review how some of the blend modes and effects found in image editors work.</p>
<blockquote>
<p>You can find a number of sample shaders that feature these effects in my repository on <a href="https://github.com/alaingalvan/image-editor-effects">Image Editor Shader</a>.</p>
</blockquote>
<h2 id="webgl-example">WebGL Example</h2>
<h2 id="blend-modes">Blend Modes</h2>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi><munder><mo><mi>C</mi></mo><mstyle scriptlevel="1"><mtable rowspacing="0.1em" columnalign="center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="1" displaystyle="false"><mrow><mi>r</mi><mi>e</mi><mi>s</mi><mi>u</mi><mi>l</mi><mi>t</mi></mrow></mstyle></mtd></mtr></mtable></mstyle></munder></mi><mo>=</mo><mi>B</mi><mo stretchy="false">(</mo><mi><munder><mo><mi>C</mi></mo><mstyle scriptlevel="1"><mtable rowspacing="0.1em" columnalign="center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="1" displaystyle="false"><mrow><mi>b</mi><mi>a</mi><mi>c</mi><mi>k</mi><mi>d</mi><mi>r</mi><mi>o</mi><mi>p</mi></mrow></mstyle></mtd></mtr></mtable></mstyle></munder></mi><mo separator="true">,</mo><mi><munder><mo><mi>C</mi></mo><mstyle scriptlevel="1"><mtable rowspacing="0.1em" columnalign="center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="1" displaystyle="false"><mrow><mi>s</mi><mi>o</mi><mi>u</mi><mi>r</mi><mi>c</mi><mi>e</mi></mrow></mstyle></mtd></mtr></mtable></mstyle></munder></mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\underset{\substack{result}}{C} = B(\underset{\substack{backdrop}}{C}, \underset{\substack{source}}{C})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.5614em;vertical-align:-0.8781em"></span><span class="mord"><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6833em"><span style="top:-2.3529em;margin-left:0em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6872em"><span style="top:-2.6928em"><span class="pstrut" style="height:2.7em"></span><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span><span class="mord mathnormal mtight">es</span><span class="mord mathnormal mtight">u</span><span class="mord mathnormal mtight" style="margin-right:0.0197em">l</span><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1872em"><span></span></span></span></span></span></span></span></span></span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span><span class="mop mathnormal" style="margin-right:0.0715em">C</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.8781em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.6382em;vertical-align:-0.8882em"></span><span class="mord mathnormal" style="margin-right:0.0502em">B</span><span class="mopen">(</span><span class="mord"><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6833em"><span style="top:-2.3479em;margin-left:0em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-2.7em"><span class="pstrut" style="height:2.7em"></span><span class="mord mtight"><span class="mord mathnormal mtight">ba</span><span class="mord mathnormal mtight">c</span><span class="mord mathnormal mtight" style="margin-right:0.0315em">k</span><span class="mord mathnormal mtight">d</span><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span><span class="mord mathnormal mtight">o</span><span class="mord mathnormal mtight">p</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em"><span></span></span></span></span></span></span></span></span></span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span><span class="mop mathnormal" style="margin-right:0.0715em">C</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.8882em"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6833em"><span style="top:-2.4em;margin-left:0em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.5553em"><span style="top:-2.8247em"><span class="pstrut" style="height:2.7em"></span><span class="mord mtight"><span class="mord mathnormal mtight">so</span><span class="mord mathnormal mtight">u</span><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span><span class="mord mathnormal mtight">ce</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.0553em"><span></span></span></span></span></span></span></span></span></span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span><span class="mop mathnormal" style="margin-right:0.0715em">C</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.7387em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span>
<p>At their core, <strong>blend modes</strong> are different functions ( B(C<em>b, C_s) ) used in the _color compositing formula</em>: <a href="#ref_adobe2001">[Systems Incorporated 2001]</a></p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>C</mi><mi>r</mi></msub><mo>=</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mfrac><msub><mi>α</mi><mi>s</mi></msub><msub><mi>α</mi><mi>r</mi></msub></mfrac><mo stretchy="false">)</mo><mo>×</mo><msub><mi>C</mi><mi>b</mi></msub><mo>+</mo><mfrac><msub><mi>α</mi><mi>s</mi></msub><msub><mi>α</mi><mi>r</mi></msub></mfrac><mo>×</mo><mo stretchy="false">[</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><msub><mi>α</mi><mi>b</mi></msub><mo stretchy="false">)</mo><mo>×</mo><msub><mi>C</mi><mi>s</mi></msub><mo>+</mo><msub><mi>α</mi><mi>b</mi></msub><mo>×</mo><mi>B</mi><mo stretchy="false">(</mo><msub><mi>C</mi><mi>b</mi></msub><mo separator="true">,</mo><msub><mi>C</mi><mi>s</mi></msub><mo stretchy="false">)</mo><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">C_r = ( 1 - \frac{\alpha_s}{\alpha_r} ) \times C_b + \frac{\alpha_s}{\alpha_r} \times [(1 - \alpha_b) \times C_s + \alpha_b \times B(C_b, C_s)]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0715em">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0715em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.9436em;vertical-align:-0.836em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.1076em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0037em">α</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0037em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0037em">α</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0037em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">s</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.836em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0715em">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0715em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">b</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.9436em;vertical-align:-0.836em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.1076em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0037em">α</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0037em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0278em">r</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0037em">α</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0037em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">s</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.836em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">[(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0037em">α</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0037em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">b</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0715em">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0715em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">s</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.7333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0037em">α</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0037em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">b</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal" style="margin-right:0.0502em">B</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0715em">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-left:-0.0715em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">b</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0715em">C</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0715em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">s</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)]</span></span></span></span></span>
<p>With simple variations on how alpha or values are interpreted.</p>
<h3 id="dissolve">Dissolve</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/disolve.jpg" alt="Dissolve Screenshot"/></p>
<p>A dissolve filter is dependant on a noise texture.</p>
<p>Dissolve is commonly used in deferred rendering as a way to reduce the number of samples of objects far away (such as the pattern dissolve of Grand Theft Auto V <a href="#ref_courreges2015">[Courrèges 2015]</a> ), or as a way of simulating alpha such as the dither effect in Marmoset Toolbag.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">//🔀 iq&#x27;s random functions:</span>
<span class="hljs-type">float</span> rand(<span class="hljs-type">float</span> seed)
{
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">abs</span>(<span class="hljs-built_in">fract</span>(<span class="hljs-built_in">sin</span>(seed) * <span class="hljs-number">43758.5453123</span>));
}

<span class="hljs-type">float</span> rand(<span class="hljs-type">vec2</span> seed)
{
    <span class="hljs-keyword">return</span> rand(<span class="hljs-built_in">dot</span>(seed, <span class="hljs-type">vec2</span>(<span class="hljs-number">12.9898</span>, <span class="hljs-number">78.233</span>)));
}

<span class="hljs-comment">// 🥤 Dissolve</span>
<span class="hljs-type">vec4</span> dissolve(<span class="hljs-type">vec4</span> col, <span class="hljs-type">vec4</span> blend, <span class="hljs-type">vec2</span> uv)
{
  <span class="hljs-keyword">return</span> <span class="hljs-type">vec4</span>(<span class="hljs-built_in">mix</span>(col.rgb, blend.rgb, blend.a &gt; rand(uv) ? <span class="hljs-number">1.0</span> : <span class="hljs-number">0.0</span>), col.a);
}</code></pre></div>
<h3 id="darken">Darken</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/darken.jpg" alt="Darken Screenshot"/></p>
<p>Darken will only decrease the brightness of areas where the brightness of the source image is higher than the overlaid image.</p>
<p>This can be useful as a way of only affecting the foreground of an image:</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🌘 Darken</span>
<span class="hljs-type">vec4</span> darken(<span class="hljs-type">vec4</span> col, <span class="hljs-type">vec4</span> blend)
{
  <span class="hljs-keyword">return</span> <span class="hljs-type">vec4</span>(<span class="hljs-built_in">mix</span>(col.rgb, <span class="hljs-built_in">min</span>(col.rgb, blend.rgb), blend.a), col.a);
}</code></pre></div>
<h3 id="lighten">Lighten</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/lighten.jpg" alt="Lighten Screenshot"/></p>
<p>Lighten will only increase the brightness of areas where the brightness of the source image is lower than the overlaid image.</p>
<p>This can be useful to only affect the background of an image.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🌔 Lighten</span>
<span class="hljs-type">vec4</span> lighten(<span class="hljs-type">vec4</span> col, <span class="hljs-type">vec4</span> blend)
{
  <span class="hljs-keyword">return</span> <span class="hljs-type">vec4</span>(<span class="hljs-built_in">mix</span>(col.rgb, <span class="hljs-built_in">max</span>(col.rgb, blend.rgb), blend.a), col.a);
}</code></pre></div>
<h3 id="multiply">Multiply</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/multiply.jpg" alt="Multiply Screenshot"/></p>
<p>Named after the operation it performs, multiply results in darker images, since two numbers smaller than 1.0 multiplied together results in an even smaller number.</p>
<p>This is useful for recoloring grayscale images.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// ✴️ Multiply</span>
<span class="hljs-type">vec4</span> multiply(<span class="hljs-type">vec4</span> col, <span class="hljs-type">vec4</span> blend)
{
  <span class="hljs-keyword">return</span> <span class="hljs-type">vec4</span>(<span class="hljs-built_in">mix</span>(col.rgb, col.rgb * blend.rgb, blend.a), col.a);
}</code></pre></div>
<h3 id="divide">Divide</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/divide.jpg" alt="Divide Screenshot"/></p>
<p>I&#x27;m using <code>0.00001</code> as an (\epsilon) epsilon value (approximately ( 1 / (2^16) ) ), which will work for most situations, though you&#x27;ll want to change this to suit your target bitrate.</p>
<p>Division is great for making images brighter with dark inputs.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🗂️ Divide</span>
<span class="hljs-type">vec4</span> divide(<span class="hljs-type">vec4</span> col, <span class="hljs-type">vec4</span> blend)
{
  <span class="hljs-keyword">return</span> <span class="hljs-type">vec4</span>(<span class="hljs-built_in">mix</span>(col.rgb, <span class="hljs-built_in">sign</span>(blend.rgb) * col.rgb / <span class="hljs-built_in">clamp</span>(blend.rgb, <span class="hljs-number">0.00001</span>, <span class="hljs-number">1.0</span>), blend.a), col.a);
}</code></pre></div>
<h3 id="screen">Screen</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/screen.jpg" alt="Screen Screenshot"/></p>
<p>Screen results in a gentle brightening of an image.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🔅 Screen</span>
<span class="hljs-type">vec4</span> screen(<span class="hljs-type">vec4</span> col, <span class="hljs-type">vec4</span> blend)
{
  <span class="hljs-keyword">return</span> <span class="hljs-type">vec4</span>(<span class="hljs-built_in">mix</span>(col.rgb, <span class="hljs-number">1.0</span> - (<span class="hljs-number">1.0</span> - col.rgb) * (<span class="hljs-number">1.0</span> - blend.rgb), blend.a), col.a);
}</code></pre></div>
<h3 id="overlay">Overlay</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/overlay.jpg" alt="Overlay Screenshot"/></p>
<p>Overlay results in a beautiful mix between dark and bright areas in an image.</p>
<p>This works great to give the source image some contrast.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🌦️ Overlay</span>
<span class="hljs-type">vec4</span> overlay(<span class="hljs-type">vec4</span> col, <span class="hljs-type">vec4</span> blend)
{
    <span class="hljs-type">vec4</span> outColor = <span class="hljs-type">vec4</span>(<span class="hljs-number">0.</span>, <span class="hljs-number">0.</span>, <span class="hljs-number">0.</span>, col.a);

    <span class="hljs-keyword">if</span> (col.r &gt; <span class="hljs-number">0.5</span>)
    {
        outColor.r = (<span class="hljs-number">1.0</span> - (<span class="hljs-number">1.0</span> - <span class="hljs-number">2.0</span> * (col.r - <span class="hljs-number">0.5</span>)) * (<span class="hljs-number">1.0</span> - blend.r));
    }
    <span class="hljs-keyword">else</span>
    {
        outColor.r = ((<span class="hljs-number">2.0</span> * col.r) * blend.r);
    }

    <span class="hljs-keyword">if</span> (col.g &gt; <span class="hljs-number">0.5</span>)
    {
        outColor.g = (<span class="hljs-number">1.0</span> - (<span class="hljs-number">1.0</span> - <span class="hljs-number">2.0</span> * (col.g - <span class="hljs-number">0.5</span>)) * (<span class="hljs-number">1.0</span> - blend.g));
    }
    <span class="hljs-keyword">else</span>
    {
        outColor.g = ((<span class="hljs-number">2.0</span> * col.g) * blend.g);
    }

    <span class="hljs-keyword">if</span> (col.b &gt; <span class="hljs-number">0.5</span>)
    {
        outColor.b = (<span class="hljs-number">1.0</span> - (<span class="hljs-number">1.0</span> - <span class="hljs-number">2.0</span> * (col.b - <span class="hljs-number">0.5</span>)) * (<span class="hljs-number">1.0</span> - blend.b));
    }
    <span class="hljs-keyword">else</span>
    {
        outColor.b = ((<span class="hljs-number">2.0</span> * col.b) * blend.b);
    }

    <span class="hljs-keyword">return</span> <span class="hljs-built_in">mix</span>(col, outColor, blend.a);
}</code></pre></div>
<h3 id="color-dodge">Color Dodge</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/colordodge.jpg" alt="Color Dodge Screenshot"/></p>
<p>Color dodge tends to result in saturated bright areas.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🌅 Color Dodge</span>
<span class="hljs-type">vec4</span> colorDodge(<span class="hljs-type">vec4</span> col, <span class="hljs-type">vec4</span> blend)
{
  <span class="hljs-keyword">return</span> <span class="hljs-type">vec4</span>(<span class="hljs-built_in">mix</span>(col.rgb, col.rgb / <span class="hljs-built_in">clamp</span>(<span class="hljs-number">1.0</span> - blend.rgb, <span class="hljs-number">0.00001</span>, <span class="hljs-number">1.0</span>), blend.a), col.a);
}</code></pre></div>
<h3 id="linear-dodge-add">Linear Dodge (Add)</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/lineardodge.jpg" alt="Linear Dodge Screenshot"/></p>
<p>Linear dodge functions as a simple addition of two colors, similar to to how lighting works in rendering engines.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// ☀️ Linear Dodge</span>
<span class="hljs-type">vec4</span> linearDodge(<span class="hljs-type">vec4</span> col, <span class="hljs-type">vec4</span> blend)
{
  <span class="hljs-keyword">return</span> <span class="hljs-type">vec4</span>(<span class="hljs-built_in">mix</span>(col.rgb, col.rgb + blend.rgb, blend.a), col.a);
}</code></pre></div>
<h3 id="color-burn">Color Burn</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/colorburn.jpg" alt="Color Burn Screenshot"/></p>
<p>Color burn tends to result in saturated dark areas.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🕯️ Color Burn</span>
<span class="hljs-type">vec4</span> colorBurn(<span class="hljs-type">vec4</span> col, <span class="hljs-type">vec4</span> blend)
{
  <span class="hljs-keyword">return</span> <span class="hljs-type">vec4</span>(<span class="hljs-built_in">mix</span>(col.rgb, <span class="hljs-number">1.0</span> - (<span class="hljs-number">1.0</span> - col.rgb) / <span class="hljs-built_in">clamp</span>(blend.rgb, <span class="hljs-number">0.00001</span>, <span class="hljs-number">1.0</span>), blend.a), col.a);
}</code></pre></div>
<h3 id="linear-burn-subtract">Linear Burn (Subtract)</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/linearburn.jpg" alt="Linear Burn Screenshot"/></p>
<p>Linear burn is the equivalent of subtracting an image by another image, similar to linear dodge working as addition.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🔥 Linear Burn</span>
<span class="hljs-type">vec4</span> linearBurn(<span class="hljs-type">vec4</span> col, <span class="hljs-type">vec4</span> blend)
{
  <span class="hljs-keyword">return</span> <span class="hljs-type">vec4</span>(<span class="hljs-built_in">mix</span>(col.rgb, <span class="hljs-built_in">max</span>(col.rgb - (<span class="hljs-number">1.0</span> - blend.rgb), <span class="hljs-number">0.0</span>), blend.a), col.a);
}</code></pre></div>
<h3 id="exclusion">Exclusion</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/exclusion.jpg" alt="Exclusion Screenshot"/></p>
<p>Exclusion is a great way to help determine the difference between two images, as wraps around color spaces:</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🥑 Exclusion</span>
<span class="hljs-type">vec4</span> exclusion(<span class="hljs-type">vec4</span> col, <span class="hljs-type">vec4</span> blend)
{
  <span class="hljs-keyword">return</span> <span class="hljs-type">vec4</span>(<span class="hljs-built_in">mix</span>(col.rgb, <span class="hljs-number">0.5</span> - <span class="hljs-number">2.0</span> * (col.rgb - <span class="hljs-number">0.5</span>) * (blend.rgb - <span class="hljs-number">0.5</span>), blend.a), col.a);
}</code></pre></div>
<h3 id="difference">Difference</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/difference.jpg" alt="Difference Screenshot"/></p>
<p>Difference works similarly to exclusion and serves a similar function of determining the difference between two images:</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 👁️ Difference</span>
<span class="hljs-type">vec4</span> difference(<span class="hljs-type">vec4</span> col, <span class="hljs-type">vec4</span> blend)
{
  <span class="hljs-keyword">return</span> <span class="hljs-type">vec4</span>(<span class="hljs-built_in">mix</span>(col.rgb, <span class="hljs-built_in">abs</span>(col.rgb - blend.rgb), blend.a), col.a);
}</code></pre></div>
<h2 id="color-adjustments">Color Adjustments</h2>
<h3 id="curves">Curves</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/curves.jpg" alt="Curves Screenshot"/></p>
<p>Color curves involves mapping the input image colors to a curve, represented in this case as a texture where the left is dark, and the right is bright. <a href="#ref_bjorke2007">[Bjorke 2007]</a></p>
<p>For the example above, it uses a classic purple complementary colors curve, where green is low for dark colors, high for lights.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// ⤴️ Curves</span>
<span class="hljs-type">vec4</span> curves(<span class="hljs-type">vec4</span> inColor, <span class="hljs-type">sampler2D</span> texCurve)
{
    <span class="hljs-keyword">return</span> <span class="hljs-type">vec4</span>(<span class="hljs-built_in">texture2D</span>(texCurve, <span class="hljs-type">vec2</span>(inColor.r, <span class="hljs-number">0.5</span>)).r, <span class="hljs-built_in">texture2D</span>(texCurve, <span class="hljs-type">vec2</span>(inColor.g, <span class="hljs-number">0.5</span>)).g, <span class="hljs-built_in">texture2D</span>(texCurve, <span class="hljs-type">vec2</span>(inColor.b, <span class="hljs-number">0.5</span>)).b, inColor.a);
}</code></pre></div>
<h3 id="levels">Levels</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/levels.jpg" alt="Levels Screenshot"/></p>
<p>Levels are like curves, but are much more simple to control as they only require 3 parameters, a minima where everything below it is considered black, a maxima where values above are white, and a midpoint which can scale those ranges.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🏃‍♀️ Levels</span>
<span class="hljs-type">vec4</span> levels(<span class="hljs-type">vec4</span> inColor, <span class="hljs-type">vec4</span> minima, <span class="hljs-type">vec4</span> midpoint, <span class="hljs-type">vec4</span> maxima)
{
    <span class="hljs-type">vec4</span> range = <span class="hljs-built_in">max</span>(<span class="hljs-built_in">abs</span>(maxima - minima), <span class="hljs-number">1.0</span> / <span class="hljs-number">255.0</span>);
    <span class="hljs-type">vec4</span> col = (inColor - minima) / range;
    <span class="hljs-type">float</span> gamma = midpoint * <span class="hljs-number">2.0</span>;
    <span class="hljs-keyword">if</span> (gamma &gt; <span class="hljs-number">0.5</span>)
    {
        gamma = (midpoint - <span class="hljs-number">0.5</span>) * <span class="hljs-number">2.0</span>;
    }
    col = <span class="hljs-built_in">pow</span>(col, <span class="hljs-number">1.0</span> / gamma);
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">clamp</span>(col, <span class="hljs-type">vec4</span>(<span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>), <span class="hljs-type">vec4</span>(<span class="hljs-number">1.0</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">1.0</span>))
}</code></pre></div>
<h3 id="brightness--contrast">Brightness / Contrast</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/brightnesscontrast.jpg" alt="Brightness / Contrast Screenshot"/></p>
<p>Brightness and Contrast are used to help increase or decrease the overall spread of colors in an image.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 💡 Brightness / Contrast</span>
<span class="hljs-type">vec4</span> brightnessContrast(<span class="hljs-type">vec4</span> inColor, <span class="hljs-type">float</span> brightness, <span class="hljs-type">float</span> contrast)
{
  <span class="hljs-keyword">return</span> <span class="hljs-type">vec4</span>((inColor.rgb - <span class="hljs-number">0.5</span>) * contrast + <span class="hljs-number">0.5</span> + brightness, inColor.a);
}</code></pre></div>
<h3 id="gamma">Gamma</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/gamma.jpg" alt="Gamma Screenshot"/></p>
<p>Gamma is a basic power function.</p>
<p>It&#x27;s often used as a way of converting between <strong>linear</strong> and <strong>sRGB</strong> encoded colors ( linear to sRGB is ( \gamma \approx 2.2 ), sRGB to linear is ( \gamma \approx \dfrac12.2 ) ), as textures loaded into graphics programs are read as linear. It&#x27;s also used as a way to control the exposure of a final image.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// γ Gamma</span>
<span class="hljs-type">vec4</span> gamma(<span class="hljs-type">vec4</span> inColor, <span class="hljs-type">float</span> g)
{
  <span class="hljs-keyword">return</span> <span class="hljs-type">vec4</span>(<span class="hljs-built_in">pow</span>(<span class="hljs-built_in">abs</span>(inColor.rgb), g), inColor.a);
}</code></pre></div>
<h3 id="hue--saturation--lightness">Hue / Saturation / Lightness</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/hsl.jpg" alt="Hue Saturation Lightness Screenshot"/></p>
<p>It may be necessary to convert an image to a different working space, such as hue / saturation / lightness, to better control the image. There also exists other image spaces such as XYZ space, Yuv, etc. that may be worth looking into.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">/*
 * Max, Min Functions.
 */</span>
<span class="hljs-type">float</span> maxCom(<span class="hljs-type">vec4</span> col)
{
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">max</span>(col.r, <span class="hljs-built_in">max</span>(col.g,col.b));
}

<span class="hljs-type">float</span> minCom(<span class="hljs-type">vec4</span> col)
{
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">min</span>(col.r, <span class="hljs-built_in">min</span>(col.g,col.b));
}

<span class="hljs-comment">/*
 * Returns a vec4 with components h,s,l,a.
 */</span>
<span class="hljs-type">vec4</span> rgbToHsl(<span class="hljs-type">vec4</span> col)
{
    <span class="hljs-type">float</span> maxComponent = maxCom(col);
    <span class="hljs-type">float</span> minComponent = minCom(col);
    <span class="hljs-type">float</span> dif = maxComponent - minComponent;
    <span class="hljs-type">float</span> add = maxComponent + minComponent;
    <span class="hljs-type">vec4</span> outColor = <span class="hljs-type">vec4</span>(<span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, col.a);

    <span class="hljs-keyword">if</span> (minComponent == maxComponent)
    {
        outColor.r = <span class="hljs-number">0.0</span>;
    }
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (col.r == maxComponent)
    {
        outColor.r = <span class="hljs-built_in">mod</span>(((<span class="hljs-number">60.0</span> * (col.g - col.b) / dif) + <span class="hljs-number">360.0</span>), <span class="hljs-number">360.0</span>);
    }
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (col.g == maxComponent)
    {
        outColor.r = (<span class="hljs-number">60.0</span> * (col.b - col.r) / dif) + <span class="hljs-number">120.0</span>;
    }
    <span class="hljs-keyword">else</span>
    {
        outColor.r = (<span class="hljs-number">60.0</span> * (col.r - col.g) / dif) + <span class="hljs-number">240.0</span>;
    }

    outColor.b = <span class="hljs-number">0.5</span> * add;

    <span class="hljs-keyword">if</span> (outColor.b == <span class="hljs-number">0.0</span>)
    {
        outColor.g = <span class="hljs-number">0.0</span>;
    }
    <span class="hljs-comment">/*else if (outColor.b == 1.0)
    {
        outColor.g = 0.0;
    }*/</span>
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (outColor.b &lt;= <span class="hljs-number">0.5</span>)
    {
        outColor.g = dif / add;
    }
    <span class="hljs-keyword">else</span>
    {
        outColor.g = dif / (<span class="hljs-number">2.0</span> - add);
    }

    outColor.r /= <span class="hljs-number">360.0</span>;

    <span class="hljs-keyword">return</span> outColor;
}

<span class="hljs-comment">/*
 * Returns a vec4 with components r,g,b,a, based off vec4 col with components h,s,l,a.
 */</span>
<span class="hljs-type">float</span> hueToRgb(<span class="hljs-type">float</span> p, <span class="hljs-type">float</span> q, <span class="hljs-type">float</span> h)
{
    <span class="hljs-keyword">if</span> (h &lt; <span class="hljs-number">0.0</span>)
    {
        h += <span class="hljs-number">1.0</span>;
    }
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (h &gt; <span class="hljs-number">1.0</span>)
    {
        h -= <span class="hljs-number">1.0</span>;
    }
    <span class="hljs-keyword">if</span> ((h * <span class="hljs-number">6.0</span>) &lt; <span class="hljs-number">1.0</span>)
    {
        <span class="hljs-keyword">return</span> p + (q - p) * h * <span class="hljs-number">6.0</span>;
    }
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> ((h * <span class="hljs-number">2.0</span>) &lt; <span class="hljs-number">1.0</span>)
    {
        <span class="hljs-keyword">return</span> q;
    }
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> ((h * <span class="hljs-number">3.0</span>) &lt; <span class="hljs-number">2.0</span>)
    {
        <span class="hljs-keyword">return</span> p + (q - p) * ((<span class="hljs-number">2.0</span> / <span class="hljs-number">3.0</span>) - h) * <span class="hljs-number">6.0</span>;
    }
    <span class="hljs-keyword">else</span>
    {
        <span class="hljs-keyword">return</span> p;
    }
}

<span class="hljs-comment">/*
 * Returns a vec4 with components r,g,b,a, based off vec4 col with components h,s,l,a.
 */</span>
<span class="hljs-type">vec4</span> hslToRgb(<span class="hljs-type">vec4</span> col)
{
    <span class="hljs-type">vec4</span> outColor = <span class="hljs-type">vec4</span>(<span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, col.a);
    <span class="hljs-type">float</span> p, q, tr, tg, tb;
    <span class="hljs-keyword">if</span> (col.b &lt;= <span class="hljs-number">0.5</span>)
    {
        q = col.b * (<span class="hljs-number">1.0</span> + col.g);
    }
    <span class="hljs-keyword">else</span>
    {
        q = col.b + col.g - (col.b * col.g);
    }

    p = <span class="hljs-number">2.0</span> * col.b - q;
    tr = col.r + (<span class="hljs-number">1.0</span> / <span class="hljs-number">3.0</span>);
    tg = col.r;
    tb = col.r - (<span class="hljs-number">1.0</span> / <span class="hljs-number">3.0</span>);

    outColor.r = hueToRgb(p, q, tr);
    outColor.g = hueToRgb(p, q, tg);
    outColor.b = hueToRgb(p, q, tb);

    <span class="hljs-keyword">return</span> outColor;
}</code></pre></div>
<h3 id="gradient">Gradient</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/gradient.png" alt="Hue Saturation Lightness Screenshot"/></p>
<p>So a gradient is made of a series of 1D points. Each point has a color, and depending on the tool, can also include a center point, an easing function, as well as a pair of 2D points where the gradient originates.</p>
<p>Let&#x27;s start with the simplest gradient type, a horizontal linear gradient:</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// ⛰️ Gradient (Horizontal)</span>
<span class="hljs-type">vec4</span> gradientU(<span class="hljs-type">vec2</span> uv, <span class="hljs-type">vec4</span> colora, <span class="hljs-type">vec4</span> colorb)
{
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">mix</span>(colora, colorb, uv.x);
}</code></pre></div>
<p>And now let&#x27;s expand that by letting you control the angle (in radians) of the gradient:</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🚞 Gradient (Angle)</span>
<span class="hljs-type">vec4</span> gradientAngle(<span class="hljs-type">vec2</span> uv, <span class="hljs-type">vec4</span> colora, <span class="hljs-type">vec4</span> colorb, <span class="hljs-type">float</span> angle)
{
  <span class="hljs-type">vec2</span> rotation = <span class="hljs-type">vec2</span>(<span class="hljs-built_in">cos</span>(angle), <span class="hljs-built_in">sin</span>(angle));
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">mix</span>(colora, colorb, <span class="hljs-built_in">dot</span>(uv, rotation));
}</code></pre></div>
<p>Now let&#x27;s make that two points in UV space:</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🚠 Gradient (Points)</span>
<span class="hljs-type">vec4</span> gradientPoints(<span class="hljs-type">vec2</span> uv, <span class="hljs-type">vec4</span> colora, <span class="hljs-type">vec4</span> colorb, <span class="hljs-type">vec2</span> p1, <span class="hljs-type">vec2</span> p2)
{
  <span class="hljs-type">vec2</span> dir = p2 - p1;
  <span class="hljs-type">float</span> angle = <span class="hljs-built_in">atan</span>(dir.x, dir.y);
  <span class="hljs-type">vec2</span> rotation = <span class="hljs-type">vec2</span>(<span class="hljs-built_in">cos</span>(angle), <span class="hljs-built_in">sin</span>(angle));
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">mix</span>(colora, colorb, <span class="hljs-built_in">dot</span>(uv, rotation) * <span class="hljs-built_in">length</span>(dir));
}</code></pre></div>
<p>And now let&#x27;s make a radial gradient:</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// ⭕ Gradient (Radial)</span>
<span class="hljs-type">vec4</span> gradientRadial(<span class="hljs-type">vec2</span> uv, <span class="hljs-type">vec4</span> colora, <span class="hljs-type">vec4</span> colorb, <span class="hljs-type">vec2</span> p1, <span class="hljs-type">vec2</span> p2)
{
  <span class="hljs-type">vec2</span> center = p2 - p1;
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">mix</span>(colora, colorb, <span class="hljs-built_in">dot</span>(uv, center) * <span class="hljs-built_in">length</span>(center * <span class="hljs-number">2.0</span>));
}</code></pre></div>
<h2 id="blurs">Blurs</h2>
<h3 id="gaussian-blur">Gaussian Blur</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/gaussian.jpg" alt="Gaussian Blur Screenshot"/></p>
<p>A Gaussian blur is an average of all colors around a given point. It&#x27;s useful to make an image appear unclear, however it&#x27;s also quite expensive.</p>
<p>Often times the gaussian blur you see in real time rendering is an approximation, a <strong>3x3 Gaussian Blur</strong>.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 👓 3x3 Gaussian Blur</span>
<span class="hljs-type">vec4</span> gaussianBlur3x3(<span class="hljs-type">vec2</span> uv, <span class="hljs-type">sampler2D</span> tex, <span class="hljs-type">float</span> r)
{
    <span class="hljs-type">vec4</span> outColor = <span class="hljs-built_in">texture2D</span>(img, uv);
    <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> y &lt; <span class="hljs-number">-1</span>; y &lt; <span class="hljs-number">2</span>; ++y)
    {
        <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> x &lt; <span class="hljs-number">-1</span>; x &lt; <span class="hljs-number">2</span>; ++x)
        {
            <span class="hljs-type">vec2</span> <span class="hljs-keyword">offset</span> = <span class="hljs-type">vec2</span>(<span class="hljs-type">float</span>(x), <span class="hljs-type">float</span>(y)) * r;
            outColor += <span class="hljs-built_in">texture2D</span>(tex, uv + <span class="hljs-keyword">offset</span>);
        }
    }
    outColor *= <span class="hljs-number">1.0</span> / <span class="hljs-number">10.0</span>;

    <span class="hljs-keyword">return</span> outColor;
}</code></pre></div>
<h3 id="pixelate">Pixelate</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/pixelate.jpg" alt="Pixelate Screenshot"/></p>
<p>Pixelation is often used as a transition effect or to emulate a sprite like image in a 3D space. One place it was used tastefully was in the <a href="https://www.ign.com/wikis/ratchet-and-clank-ps4/Pixelizer">PS4 remake of Rachet and Clank</a>.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🔲 Pixelate</span>
<span class="hljs-type">vec4</span> pixelate(<span class="hljs-type">vec2</span> uv, <span class="hljs-type">sampler2D</span> tex, <span class="hljs-type">int</span> r)
{
    <span class="hljs-type">ivec2</span> texSize = <span class="hljs-built_in">textureSize</span>(tex, <span class="hljs-number">0</span>);
    uv *= texSize / r;
    uv = <span class="hljs-built_in">floor</span>(uv);
    uv /= texSize / r;

    <span class="hljs-type">vec4</span> outColor = <span class="hljs-built_in">texture2D</span>(tex, uv);

    <span class="hljs-keyword">return</span> outColor;
}</code></pre></div>
<h2 id="edge-effects">Edge Effects</h2>
<h3 id="sobel">Sobel</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/sobel.jpg" alt="Sobel Screenshot"/></p>
<p>Sobel is a basic edge detection algorithm. It&#x27;s often used to generate outlines for objects in combination with a depth/stencil buffer.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// ✏️ 3x3 Sobel</span>
<span class="hljs-type">vec4</span> sobel(<span class="hljs-type">vec2</span> uv, <span class="hljs-type">sampler2D</span> tex, <span class="hljs-type">vec2</span> pixelSize, <span class="hljs-type">float</span> r)
{
    <span class="hljs-type">vec4</span> outColor = <span class="hljs-type">vec4</span>(<span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>);
    <span class="hljs-type">vec4</span> center = <span class="hljs-built_in">texture2D</span>(tex, uv);

    <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> xx = <span class="hljs-number">-1</span>; xx &lt; <span class="hljs-number">2</span>; ++xx)
    {
        <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> yy = <span class="hljs-number">-1</span>; yy &lt; <span class="hljs-number">2</span>; ++yy)
        {
            <span class="hljs-type">float</span> dif = <span class="hljs-built_in">length</span>(center - <span class="hljs-built_in">texture2D</span>(tex, uv + pixelSize * <span class="hljs-type">vec2</span>(<span class="hljs-type">float</span>(xx), <span class="hljs-type">float</span>(yy))));
            outColor.xyz += dif;
        }
    }

    <span class="hljs-keyword">return</span> outColor;
}</code></pre></div>
<h3 id="sharpen">Sharpen</h3>
<p><img src="https://alain.xyz/blog/image-editor-effects/assets/sharpen.jpg" alt="Sharpen Screenshot"/></p>
<p>Sharpen amplifies difference between colors across edges. Similar to sobel, it works by detecting differences in neighboring samples.</p>
<div><pre><code class="language-glsl"><span class="hljs-comment">// 🗡️ 3x3 Sharpen</span>
<span class="hljs-type">vec4</span> sharpen(<span class="hljs-type">vec2</span> uv, <span class="hljs-type">sampler2D</span> tex, <span class="hljs-type">vec2</span> pixelSize, <span class="hljs-type">float</span> weight)
{
    <span class="hljs-type">vec4</span> outColor = <span class="hljs-built_in">texture</span>(tex, uv);
    <span class="hljs-type">vec3</span> diffSum = <span class="hljs-type">vec3</span>(<span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>);

    <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> xx = <span class="hljs-number">-1</span>; xx &lt; <span class="hljs-number">2</span>; ++xx)
    {
        <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> yy = <span class="hljs-number">-1</span>; yy &lt; <span class="hljs-number">2</span>; ++yy)
        {
            <span class="hljs-type">vec3</span> dif = outColor.xyz - <span class="hljs-built_in">texture</span>(tex, uv + pixelSize * <span class="hljs-type">vec2</span>(<span class="hljs-type">float</span>(xx), <span class="hljs-type">float</span>(yy))).xyz;
            diffSum += dif;
        }
    }

    diffSum /= <span class="hljs-number">9.0</span>;

    outColor.xyz = outColor.xyz + (weight * diffSum);

    <span class="hljs-keyword">return</span> outColor;
}</code></pre></div>
<h2 id="conclusion">Conclusion</h2>
<p>Post-processing techniques such as layer effects, color correction, and filter effects are one of the many pillars of image editing and authoring software such as PhotoShop, Gimp, etc. While this article reviews most effects, there&#x27;s still a number of other effects that weren&#x27;t mentioned here such as:</p>
<ul>
<li>
<p>High Dynamic Range (HDR) to Low Dynamic Range (LDR) conversion, <a href="https://renderstuff.com/tutorials/hdri-rendering-and-post-processing-tutorial-238/">Renderstuff.com features a blog post detailing this</a>.</p>
</li>
<li>
<p>Noise Generation, I wrote a <a href="https://alain.xyz/blog/noise-generation-survey">separate blog post about this</a>.</p>
</li>
<li>
<p>Perceptual Gradients, Björn Ottosson (<a href="https://twitter.com/bjornornorn">@bjornornorn</a>) wrote a <a href="https://bottosson.github.io/posts/oklab/">blog post</a> detailing the Oklab color space gradient and compares with with other color space gradients.</p>
</li>
<li>
<p>Steven Hill (<a href="https://twitter.com/self_shadow">@self_shadow</a>) details how to approach blending normals in his blog post: <a href="https://blog.selfshadow.com/publications/blending-in-detail/">Blending in Detail</a>.</p>
</li>
</ul>
<p>And much more, falling into the domains of statistical analysis, signal processing, computer graphics, and much more.</p>
<h3 id="more-resources">More Resources</h3>
<ul>
<li><a href="https://dunnbypaul.net/blends/">Paul Dunn released a blog post analyzing blend modes like this one.</a></li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_adobe2001"><span class="markademic-citename">[Systems Incorporated 2001]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">PDF Reference Third Edition - 7.2.4 Blend Mode</span></strong></em><br/><span class="markademic-authors"><span><span>Adobe Systems Incorporated</span></span></span><br/><span><span class="markademic-publisher">Addison-Wesley 2001</span><br/></span><span><a href="https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/pdfreference1.4.pdf">opensource.adobe.com</a></span></div></td></tr><tr><td id="ref_courreges2015"><span class="markademic-citename">[Courrèges 2015]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">GTA V - Graphics Study</span></strong></em><br/><span class="markademic-authors"><span><a href="https://www.adriancourreges.com/">Adrian Courrèges</a> (<a href="https://twitter.com/ado_tan">@ado_tan</a>)</span></span><br/><span><span class="markademic-publisher">2015</span><br/></span><span><a href="http://www.adriancourreges.com/blog/2015/11/02/gta-v-graphics-study/">adriancourreges.com</a></span></div></td></tr><tr><td id="ref_bjorke2007"><span class="markademic-citename">[Bjorke 2007]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">GPU Gems - Chapter 22 Color Controls</span></strong></em><br/><span class="markademic-authors"><span><span>Kevin Bjorke</span></span></span><br/><span><span class="markademic-publisher">Addison-Wesley Professional 2007</span><br/></span><span><a href="https://developer.nvidia.com/gpugems/gpugems/part-iv-image-processing/chapter-22-color-controls">developer.nvidia.com</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Game Engine Architecture</title>
<link>https://alain.xyz/blog/game-engine-architecture</link>
<description>A review of modern game engine architectures, discussing different approaches to modeling high and low level systems, actors, entity component systems, data oriented design, and current best practices.</description>
<guid isPermaLink="true">https://alain.xyz/blog/game-engine-architecture</guid>
<pubDate>Sat, 27 Oct 2018 00:36:34 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>game engine</category>
<enclosure url="https://alain.xyz/blog/game-engine-architecture/assets/cover.jpg" length="676055" type="image/jpeg"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/game-engine-architecture/assets/runtime-engine-architecture.svg"/><link rel="preload" as="image" href="https://github.com/morgan3d.png"/><link rel="preload" as="image" href="https://github.com/wolfgangfengel.png"/><link rel="preload" as="image" href="https://github.com/raysan5.png"/><link rel="preload" as="image" href="https://github.com/matt77hias.png"/><link rel="preload" as="image" href="https://github.com/kecho.png"/><div style="perspective:960px;transform-style:preserve-3d;transform-origin:0vw 0px 0px"><div style="width:400px;overflow:hidden;transform:translateX(calc(50% + 102.5px)) translateZ(-270px) rotateX(45deg) rotateZ(45deg);box-shadow:4px 4px 16px rgba(0,0,0,0.2)"><div><pre><code class="language-c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdlib.h&gt;</span>   <span class="hljs-comment">// card &gt; aek.ppm</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;math.h&gt;</span></span>
<span class="hljs-keyword">typedef</span> <span class="hljs-type">int</span> i;<span class="hljs-keyword">typedef</span> <span class="hljs-type">float</span> f;<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">v</span>{</span>
f x,y,z;v operator+(v r){<span class="hljs-keyword">return</span> v(x+r.x
,y+r.y,z+r.z);}v operator*(f r){<span class="hljs-keyword">return</span>
v(x*r,y*r,z*r);}f operator%(v r){<span class="hljs-keyword">return</span>
x*r.x+y*r.y+z*r.z;}v(){}v operator^(v r
){<span class="hljs-keyword">return</span> v(y*r.z-z*r.y,z*r.x-x*r.z,x*r.
y-y*r.x);}v(f a,f b,f c){x=a;y=b;z=c;}v
operator!(){<span class="hljs-keyword">return</span>*this*(<span class="hljs-number">1</span>/<span class="hljs-built_in">sqrt</span>(*this%*
this));}};i G[]={<span class="hljs-number">247570</span>,<span class="hljs-number">280596</span>,<span class="hljs-number">280600</span>,
<span class="hljs-number">249748</span>,<span class="hljs-number">18578</span>,<span class="hljs-number">18577</span>,<span class="hljs-number">231184</span>,<span class="hljs-number">16</span>,<span class="hljs-number">16</span>};f <span class="hljs-title function_">R</span><span class="hljs-params">()</span>{
<span class="hljs-keyword">return</span>(f)rand()/RAND_MAX;}i <span class="hljs-title function_">T</span><span class="hljs-params">(v o,v d,f
&amp;t,v&amp;n)</span>{t=<span class="hljs-number">1e9</span>;i m=<span class="hljs-number">0</span>;f p=-o.z/d.z;<span class="hljs-keyword">if</span>(.<span class="hljs-number">01</span>
&lt;p)t=p,n=v(<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span>),m=<span class="hljs-number">1</span>;<span class="hljs-keyword">for</span>(i k=<span class="hljs-number">19</span>;k--;)
<span class="hljs-keyword">for</span>(i j=<span class="hljs-number">9</span>;j--;)<span class="hljs-keyword">if</span>(G[j]&amp;<span class="hljs-number">1</span>&lt;&lt;k){v p=o+v(-k
,<span class="hljs-number">0</span>,-j<span class="hljs-number">-4</span>);f b=p%d,c=p%p<span class="hljs-number">-1</span>,q=b*b-c;<span class="hljs-keyword">if</span>(q&gt;<span class="hljs-number">0</span>
){f s=-b-<span class="hljs-built_in">sqrt</span>(q);<span class="hljs-keyword">if</span>(s&lt;t&amp;&amp;s&gt;.<span class="hljs-number">01</span>)t=s,n=!(
p+d*t),m=<span class="hljs-number">2</span>;}}<span class="hljs-keyword">return</span> m;}v <span class="hljs-title function_">S</span><span class="hljs-params">(v o,v d)</span>{f t
;v n;i m=T(o,d,t,n);<span class="hljs-keyword">if</span>(!m)<span class="hljs-keyword">return</span> v(.<span class="hljs-number">7</span>,
.<span class="hljs-number">6</span>,<span class="hljs-number">1</span>)*<span class="hljs-built_in">pow</span>(<span class="hljs-number">1</span>-d.z,<span class="hljs-number">4</span>);v h=o+d*t,l=!(v(<span class="hljs-number">9</span>+R(
),<span class="hljs-number">9</span>+R(),<span class="hljs-number">16</span>)+h*<span class="hljs-number">-1</span>),r=d+n*(n%d*<span class="hljs-number">-2</span>);f b=l%
n;<span class="hljs-keyword">if</span>(b&lt;<span class="hljs-number">0</span>||T(h,l,t,n))b=<span class="hljs-number">0</span>;f p=<span class="hljs-built_in">pow</span>(l%r*(b
&gt;<span class="hljs-number">0</span>),<span class="hljs-number">99</span>);<span class="hljs-keyword">if</span>(m&amp;<span class="hljs-number">1</span>){h=h*.<span class="hljs-number">2</span>;<span class="hljs-keyword">return</span>((i)(<span class="hljs-built_in">ceil</span>(
h.x)+<span class="hljs-built_in">ceil</span>(h.y))&amp;<span class="hljs-number">1</span>?v(<span class="hljs-number">3</span>,<span class="hljs-number">1</span>,<span class="hljs-number">1</span>):v(<span class="hljs-number">3</span>,<span class="hljs-number">3</span>,<span class="hljs-number">3</span>))*(b
*.<span class="hljs-number">2</span>+.<span class="hljs-number">1</span>);}<span class="hljs-keyword">return</span> v(p,p,p)+S(h,r)*.<span class="hljs-number">5</span>;}i
<span class="hljs-title function_">main</span><span class="hljs-params">()</span>{<span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;P6 512 512 255 &quot;</span>);v g=!v
(<span class="hljs-number">-6</span>,<span class="hljs-number">-16</span>,<span class="hljs-number">0</span>),a=!(v(<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span>)^g)*.<span class="hljs-number">002</span>,b=!(g^a
)*.<span class="hljs-number">002</span>,c=(a+b)*<span class="hljs-number">-256</span>+g;<span class="hljs-keyword">for</span>(i y=<span class="hljs-number">512</span>;y--;)
<span class="hljs-keyword">for</span>(i x=<span class="hljs-number">512</span>;x--;){v <span class="hljs-title function_">p</span><span class="hljs-params">(<span class="hljs-number">13</span>,<span class="hljs-number">13</span>,<span class="hljs-number">13</span>)</span>;<span class="hljs-keyword">for</span>(i r
=<span class="hljs-number">64</span>;r--;){v t=a*(R()-.<span class="hljs-number">5</span>)*<span class="hljs-number">99</span>+b*(R()-.<span class="hljs-number">5</span>)*
<span class="hljs-number">99</span>;p=S(v(<span class="hljs-number">17</span>,<span class="hljs-number">16</span>,<span class="hljs-number">8</span>)+t,!(t*<span class="hljs-number">-1</span>+(a*(R()+x)+b
*(y+R())+c)*<span class="hljs-number">16</span>))*<span class="hljs-number">3.5</span>+p;}<span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;%c%c%c&quot;</span>
,(i)p.x,(i)p.y,(i)p.z);}}</code></pre></div></div></div>
<p>The definition of an <em>Engine</em> can be ambiguous. It could be an application engine driven by views such as <a href="https://developer.apple.com/documentation/uikit">Apple&#x27;s UIKit</a>, <a href="https://reactnative.dev/">React Native</a> or the modern web platform (though one might want to call those <em>application engines</em>), a rendering engine without complex logic for input devices and built for real time rendering research such as <a href="https://developer.nvidia.com/falcor">NVIDIA Falcor</a> or <a href="https://github.com/microsoft/DirectX-Graphics-Samples/tree/master/MiniEngine">Microsoft&#x27;s MiniEngine</a>, a <a href="https://www.shadertoy.com/view/lsKfWd">shadertoy</a>, <a href="http://awards.scene.org/">demoscene</a> app, <a href="https://github.com/mzucker/miniray">Cook raytracer</a> written onto a business card <a href="#ref_sanglard2013">[Sanglard 2013]</a>, or a massive framework <a href="#ref_gregory2018">[Gregory 2018]</a> made of a variety of components such as <a href="https://www.unrealengine.com">Unreal</a>, <a href="https://unity3d.com/">Unity</a>, <a href="https://godotengine.org/">Godot</a>, etc.</p>
<p><img src="https://alain.xyz/blog/game-engine-architecture/assets/runtime-engine-architecture.svg" alt="Runtime Engine Architecture"/></p>
<p>Overall however, a <strong>Game Engine is a platform by which a team of individuals can all contribute and build a product</strong>. We&#x27;ll be reviewing <strong>Game Engine Architectures</strong>, their design, best practices when designing engines, and current state of the art tools that you can build off of.</p>
<h2 id="systems">Systems</h2>
<p>Game engines are made of <em>top level systems</em> built as:</p>
<ul>
<li>
<p>An <strong>Application Hierarchy</strong>, with a <strong>Scene</strong> (or <em>view</em>) of <strong>Actors</strong> (or collection of <em>view components</em>), and components of those actors such as <em>logic controllers</em>, <em>meshes</em>, <em>image textures</em>, <em>audio</em>, etc. This is often called the <em>Actor Design Pattern</em>, where independent actors execute their own tasks.</p>
</li>
<li>
<p><strong>Singletons</strong> objects accessible throughout an application that store state or control overarching systems like <em>input</em>, <em>application state</em>, <em>UIs</em>, etc.</p>
</li>
<li>
<p><strong>Middleware</strong> that can subscribe to other middleware (eg. <em>Renderer</em> middleware subscribing to <em>Windowing</em> middleware), and low level systems such as actors to subscribe to them to access <em>application state</em>.</p>
</li>
</ul>
<p>Or any combination of those designs.</p>
<p>Actors can be built with:</p>
<ul>
<li>
<p><strong>Classical Hierarchies</strong> where components such as <em>transformations</em>, <em>Meshes</em>, etc. are described through inheritance.</p>
</li>
<li>
<p>An <strong>Entity Component System</strong> where declarative components hold the state of their data and have external systems such as <em>middleware</em> or <em>singletons</em> to process that state.</p>
</li>
</ul>
<p>Or even a combination of these designs (though using two different schools of thought may end up making code more complex and difficult to understand). In the end these constraints and methods of organization only serve as a way of keeping concerns separate and making your application easier to understand as a whole.</p>
<hr/>
<p>Let&#x27;s review each approach along with their strengths and weaknesses:</p>
<h2 id="singleton">Singleton</h2>
<p>Singletons are incredibly useful when architecting applications, as any system can immediately access data from a singleton. Unity for instance makes heavy use of singletons, which can be seen for instance, in the way they handle their <code>Input</code> singleton:</p>
<div><pre><code class="language-cs"><span class="hljs-comment">// Source: https://docs.unity3d.com/ScriptReference/Input.GetKeyDown.html</span>
<span class="hljs-keyword">using</span> UnityEngine;
<span class="hljs-keyword">using</span> System.Collections;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">ExampleClass</span> : <span class="hljs-title">MonoBehaviour</span>
{
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Update</span>()</span>
    {
        <span class="hljs-keyword">if</span> (Input.GetKeyDown((KeyCode.Space))
        {
            print(<span class="hljs-string">&quot;space key was pressed&quot;</span>);
        }
    }
}</code></pre></div>
<p><a href="https://github.com/ocornut/imgui">Dear ImGui</a> uses a singleton to draw immediate mode graphics user interfaces:</p>
<div><pre><code class="language-cpp"><span class="hljs-comment">// 💬 Inside the ImGui namespace, all state is maintained in `imgui.cpp`.</span>
ImGui::<span class="hljs-built_in">Text</span>(<span class="hljs-string">&quot;Hello, world %d&quot;</span>, <span class="hljs-number">123</span>);

<span class="hljs-keyword">if</span> (ImGui::<span class="hljs-built_in">Button</span>(<span class="hljs-string">&quot;Save&quot;</span>))
{
    <span class="hljs-comment">// 💾 Save the application</span>
}

<span class="hljs-type">const</span> <span class="hljs-type">char</span>* buf[<span class="hljs-number">1024</span>];
ImGui::<span class="hljs-built_in">InputText</span>(<span class="hljs-string">&quot;string&quot;</span>, buf, <span class="hljs-built_in">IM_ARRAYSIZE</span>(buf));

ImGui::<span class="hljs-built_in">SliderFloat</span>(<span class="hljs-string">&quot;float&quot;</span>, &amp;f, <span class="hljs-number">0.0f</span>, <span class="hljs-number">1.0f</span>);</code></pre></div>
<h3 id="writing-a-singleton">Writing a Singleton</h3>
<p>There&#x27;s a variety of ways to approach writing a singleton, each offers a different level of control of when a singleton is created, in exchange for slightly more lines of code. Most of the time you will want to decide, since some systems may depend on other systems.</p>
<div><pre><code class="language-cpp"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Singleton</span>
{
<span class="hljs-keyword">public</span>:
  <span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">int</span> <span class="hljs-title">getData</span><span class="hljs-params">()</span></span>;

  <span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">int</span> <span class="hljs-title">setData</span><span class="hljs-params">(<span class="hljs-type">int</span> data)</span></span>;
<span class="hljs-keyword">private</span>:
  <span class="hljs-type">int</span> mData;
};

<span class="hljs-comment">// ♦️ In your .cpp file:</span>
<span class="hljs-keyword">namespace</span>
{
  MyCreatedSingleton* gSingleton = <span class="hljs-literal">nullptr</span>;
}

<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">createSingleton</span><span class="hljs-params">()</span>
</span>{
  <span class="hljs-keyword">if</span>(!gSingleton)
  {
    gSingleton = <span class="hljs-keyword">new</span> <span class="hljs-built_in">MyCreatedSingleton</span>();
  }
}

<span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">MyCreatedSingleton::getData</span><span class="hljs-params">()</span>
</span>{
  <span class="hljs-keyword">return</span> gSingleton-&gt;mData;
}

<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">MyCreatedSingleton::setData</span><span class="hljs-params">(<span class="hljs-type">int</span> data)</span>
</span>{
  gSingleton-&gt;mData = data;
}

<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">useSingleton</span><span class="hljs-params">()</span>
</span>{
  <span class="hljs-type">int</span> data = Singleton::<span class="hljs-built_in">getData</span>();
  data++;
  Singleton::<span class="hljs-built_in">setData</span>(data);
}
</code></pre></div>
<p>Singleton architectures make it very difficult to know where dependencies are, which can make it difficult to unit test your code or draw dependency graphs. They are easier to use as for the most part, you would only need to <code>#include &quot;YourSingleton.h&quot;</code> and you&#x27;re good to go), but you as an application architect will need to decide if having a single controlling instance fits the needs of your application.</p>
<h2 id="middleware">Middleware</h2>
<p>One approach to modeling high level systems is the use of <strong>middleware</strong>, the idea is to keep the core of an engine small and allow middleware to fill in the gaps in state that would be missing by default. <code>Actor</code>s can subscribe to middleware if it exists, and from there read data from OS windows, the GUI state, or anything developers would feel need to be shared across <code>Actor</code>s.</p>
<h3 id="writing-middleware">Writing Middleware</h3>
<div><pre><code class="language-cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&quot;Engine.h&quot;</span></span>

<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">Engine::start</span><span class="hljs-params">(<span class="hljs-type">const</span> EngineDesc&amp; desc)</span>
</span>{
  <span class="hljs-comment">// 🌟 Initialize middleware</span>
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> itr = mMiddleware.<span class="hljs-built_in">begin</span>(); itr != mMiddleware.<span class="hljs-built_in">end</span>(); ++itr)
  {
    <span class="hljs-function">std::vector&lt;IMiddlewarePtr&gt; <span class="hljs-title">createdMiddleware</span><span class="hljs-params">(mMiddleware.begin(), itr)</span></span>;
    IMiddleware&amp; m = **itr;
    <span class="hljs-keyword">if</span> (!m.<span class="hljs-built_in">create</span>(desc, createdMiddleware))
    {
      mMiddleware.<span class="hljs-built_in">erase</span>(itr);
      itr = mMiddleware.<span class="hljs-built_in">begin</span>();
      <span class="hljs-keyword">continue</span>;
    }
  }

  <span class="hljs-comment">// 🏎️ Run engine loop</span>
  <span class="hljs-keyword">while</span> (mRunning)
  {
    mRunning = <span class="hljs-literal">false</span>;

    <span class="hljs-keyword">for</span> (IMiddlewarePtr&amp; ware : mMiddleware)
    {
      <span class="hljs-type">bool</span> shouldUpdate = ware-&gt;<span class="hljs-built_in">shouldUpdate</span>();

      <span class="hljs-keyword">if</span> (shouldUpdate)
      {
        mRunning |= shouldUpdate;
        ware-&gt;<span class="hljs-built_in">update</span>(<span class="hljs-keyword">this</span>);
      }
    }
  }
}</code></pre></div>
<p>Middleware would be subscribed to by actors which need access to the state of the various systems running the application. The benefits of this are that the global namespace is no longer used, leading to more isolated code, but this comes at the cost of subscription overhead on the part of actors, in addition to local pointers to subscribed data. There&#x27;s also a level of &quot;technical overhead&quot; on the part of an engineer, as more isolated code means being aware of what middleware they would need to subscribe to in order to solve their problems.</p>
<div><pre><code class="language-cpp"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">Player::setupState</span><span class="hljs-params">(<span class="hljs-type">const</span> std::vector&lt;MiddlewarePtr&gt;&amp; middleware)</span>
</span>{
    input = <span class="hljs-built_in">findMiddleware</span>&lt;InputMiddleware&gt;(middleware);
}

<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">Player::update</span><span class="hljs-params">(<span class="hljs-type">float</span> deltaTime)</span>
</span>{
      <span class="hljs-keyword">if</span> (input-&gt;<span class="hljs-built_in">checkKey</span>(Key::Right).<span class="hljs-built_in">active</span>())
    {
      <span class="hljs-comment">//move right</span>
    }
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (input-&gt;<span class="hljs-built_in">checkKey</span>(Key::Left).<span class="hljs-built_in">active</span>())
    {
      <span class="hljs-comment">//move left</span>
    }
    <span class="hljs-keyword">else</span>
    {
      <span class="hljs-comment">//slow down</span>
    }
}</code></pre></div>
<p>Middleware follows the <strong>subscriber</strong> design pattern, and as such, there&#x27;s an initial setup cost for each actor that wants to subscribe to middleware outside the scene hierarchy (on the order of ( O(N) ) ). While this cost can be mitigated (by <code>constexpr</code> based pointer resolving at compile time), it&#x27;s still code that must be included by subscribers.</p>
<h2 id="actor-model">Actor Model</h2>
<h3 id="inheritance-hierarchy">Inheritance Hierarchy</h3>
<p>Actors have the option of being built with an Inheritance Hierarchy. This makes it very easy to design actors that share the same functionality instantly. Issues arise with this approach such as the Diamond Problem, what if an actor needs to inherit multiple objects, each of which inherits from the same source object? That&#x27;s not to mention an endless chain of inheritance that you see in APIs like Java, Inheritance Soup if you will.</p>
<p>Then there&#x27;s the idea of compile time inheritance though the use of magic methods, compile time virtual function calls, alongside <a href="https://github.com/iboB/dynamix">mixins</a>, a concept popularized by languages like Ruby.</p>
<h3 id="entity-component-system">Entity Component System</h3>
<p>The Entity Component Architecture lets entities hold components, which typically just contain data. That data is then processed with high level systems managing rendering, collisions, path-finding, physics, etc.</p>
<p>This fits very well with the idea of <em>Data Oriented Design</em>. Basically, it&#x27;s in an application&#x27;s best interest to group similar data together to help the CPU&#x27;s cache miss less often.</p>
<h2 id="more-resources">More Resources</h2>
<ul>
<li>
<p><a href="https://github.com/GPUOpen-LibrariesAndSDKs/Cauldron">AMD&#x27;s Cauldron</a> Engine is a real time PBR renderer that uses the GLTF specification, ImGui, and a backend powered by either Vulkan or DirectX 12.</p>
</li>
<li>
<p>NVIDIA&#x27;s GameWorks team released <a href="https://developer.nvidia.com/falcor">Falcor</a>, a realtime research renderer used in a variety of research papers.</p>
</li>
<li>
<p>János Turánszki&#x27;s (<a href="https://github.com/turanszkij/WickedEngine">@wickedengine</a>) is an incredibly well written game engine with tons of examples.</p>
</li>
<li>
<p><img class="profile-picture" src="https://github.com/morgan3d.png" alt="Morgan McGuire&#x27;s picture"/> Professor Morgan McGuire (<a href="https://twitter.com/CasualEffects">@CasualEffects</a>)
released <a href="https://casual-effects.com/g3d/www/index.html">G3D Engine</a>, his open
source rendering engine.</p>
</li>
<li>
<p>Sebastian Merry of Microsoft and Microsoft&#x27;s DirectX 12 Examples team released <a href="https://github.com/microsoft/DirectX-Graphics-Samples/tree/master/MiniEngine">MiniEngine</a>, a sample engine for rendering DirectX 12 applications.</p>
</li>
<li>
<p>Epic Games&#x27; Unreal Engine is open source and available on GitHub <a href="https://github.com/EpicGames">here</a>, though this requires you to <a href="https://www.unrealengine.com/en-US/ue4-on-github">register an Epic Games account and link it to your GitHub</a>.</p>
</li>
<li>
<p><img class="profile-picture" src="https://github.com/wolfgangfengel.png" alt="Wolfgang Engel&#x27;s picture"/> Wolfgang Engel (<a href="https://twitter.com/wolfgangengel">@wolfgangengel</a>) released
a cross platform renderer called <a href="https://github.com/ConfettiFX/The-Forge">The Forge</a>,
his <a href="https://github.com/wolfgangfengel">GitHub</a> is also full of samples and examples
of rendering engines.</p>
</li>
<li>
<p><a href="https://twitter.com/raysan5"><img class="profile-picture" src="https://github.com/raysan5.png" alt="Raylib picture"/> Ray</a> and a team of open source contributors built the <a href="https://github.com/raysan5/raylib">raylib</a> series of libraries, cross platform game libraries inspired by the XNA framework.</p>
</li>
</ul>
<p>There&#x27;s also a variety of individual engineers that have open sourced their personal engines:</p>
<ul>
<li>
<p><a href="https://matt77hias.github.io/"><img class="profile-picture" src="https://github.com/matt77hias.png" alt="Matthias Moulin picture"/> Matthias Moulin</a> (<a href="https://twitter.com/matt77hias">@matt77hias</a>)&#x27;s <a href="https://github.com/matt77hias/MAGE">MAGE - Matthias Advanced Game Engine</a>.</p>
</li>
<li>
<p><a href="http://www.kecho.me/"><img class="profile-picture" src="https://github.com/kecho.png" alt="Kleber Garcia picture"/> Kleber Garcia</a> (<a href="https://twitter.com/kechogarcia">@kechogarcia</a>)&#x27;s <a href="https://github.com/PegasusEngine/Pegasus">Pegasus Engine</a>.</p>
</li>
</ul>
<p>Finally, there&#x27;s a variety of articles and books on game engine architecture, including:</p>
<ul>
<li>
<p><a href="https://www.gameenginebook.com/">Game Engine Architecture</a> by Jason Gregory is the de-facto standard in the subject of game engines, along with similar books on rendering architectures like <a href="https://www.realtimerendering.com/">Real Time Rendering</a>.</p>
</li>
<li>
<p><a href="https://playoverwatch.com/">Blizzard Entertainment&#x27;s Overwatch</a> game released an article discussing their editor, which <em>appears</em> (but don&#x27;t quote me on this) to be a a Qt powered scene editor called <a href="https://playoverwatch.com/en-us/news/23453344/what-it-s-like-to-work-on-overwatch-s-tools-and-engine/">TED</a>.</p>
</li>
<li>
<p><a href="https://godotengine.org/">Godot Engine</a> has a <a href="https://godotengine.org/devblog">blog</a> where they discuss rendering and architecture changes they&#x27;ve been working on, such as their <a href="https://godotengine.org/article/vulkan-progress-report-7">Vulkan renderer</a>.</p>
</li>
<li>
<p>Sean Middleditch (<a href="https://twitter.com/seanmiddleditch">@seanmiddleditch</a>) released a <a href="https://seanmiddleditch.github.io/makes-games-not-engines-to-learn-engines/">blog post that focused on never forgetting that game engines are for games</a>, not magical structures in and of themselves, so engineering them in the context of an underlying purpose rather than for the sake of engineering is incredibly important.</p>
</li>
</ul>
<table class="markademic-citations"><tbody><tr><td id="ref_sanglard2013"><span class="markademic-citename">[Sanglard 2013]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Decyphering the Business Card Raytracer</span></strong></em><br/><span class="markademic-authors"><span><a href="https://fabiensanglard.net/">Fabien Sanglard</a> (<a href="https://twitter.com/fabynou">@fabynou</a>)</span></span><br/><span><span class="markademic-publisher">2013</span><br/></span><span><a href="http://fabiensanglard.net/rayTracing_back_of_business_card/">fabiensanglard.net</a></span></div></td></tr><tr><td id="ref_gregory2018"><span class="markademic-citename">[Gregory 2018]</span><br/><div style="padding-left:1em"><em><strong><span class="markademic-title">Game Engine Architecture</span></strong></em><br/><span class="markademic-authors"><span><span>Jason Gregory</span></span></span><br/><span><span class="markademic-publisher">2018</span><br/></span><span><a href="https://www.gameenginebook.com/">gameenginebook.com</a></span></div></td></tr></tbody></table>]]></content:encoded>
</item>
<item>
<title>Pixel Art Design for Game Development</title>
<link>https://alain.xyz/blog/pixel-art-design-for-game-dev</link>
<description>Everything you need to know to get started making pixel art for indie game development.</description>
<guid isPermaLink="true">https://alain.xyz/blog/pixel-art-design-for-game-dev</guid>
<pubDate>Sun, 28 Aug 2016 02:34:20 GMT</pubDate>
<author>hi@alain.xyz (Alain Galvan)</author>
<category>blog</category><category>pixel</category><category>art</category><category>game</category><category>development</category><category>sprite</category><category>sprites</category><category>gba</category><category>snes</category><category>nintendo</category><category>gameboy</category><category>design</category>
<enclosure url="https://alain.xyz/blog/pixel-art-design-for-game-dev/assets/cover.gif" length="91301" type="image/gif"/>
<content:encoded><![CDATA[<link rel="preload" as="image" href="https://alain.xyz/blog/pixel-art-design-for-game-dev/assets/chaos-zero.gif"/><link rel="preload" as="image" href="https://alain.xyz/blog/pixel-art-design-for-game-dev/assets/blursilprogress.gif"/><link rel="preload" as="image" href="https://alain.xyz/blog/pixel-art-design-for-game-dev/assets/layermask.gif"/><link rel="preload" as="image" href="https://alain.xyz/blog/pixel-art-design-for-game-dev/assets/perfect-chaos.gif"/><link rel="preload" as="image" href="https://alain.xyz/blog/pixel-art-design-for-game-dev/assets/curvesphotoshop.gif"/><link rel="preload" as="image" href="https://alain.xyz/blog/pixel-art-design-for-game-dev/assets/levelsphotoshop.gif"/><link rel="preload" as="image" href="https://alain.xyz/blog/pixel-art-design-for-game-dev/assets/colorbalancephotoshop.gif"/><link rel="preload" as="image" href="https://alain.xyz/blog/pixel-art-design-for-game-dev/assets/colorreplacegimp.gif"/><link rel="preload" as="image" href="https://alain.xyz/blog/pixel-art-design-for-game-dev/assets/colorcountgimp.gif"/><p>Pixel art is a large part of the legacy of game development. Every Pokemon game up until their X/Y series was rendered entirely with pixel art, Ragnarok Online (2000) was one of the first commercial works to feature 3D rendering along side pixel art characters. Even when early games tried to push the limits of 3D rendering such as Doom or Quake, they relied on Pixel Art based billboards to render their characters.</p>
<h2 id="limitations--guidelines">Limitations &amp; Guidelines</h2>
<blockquote>
<p>“The absence of limitations is the enemy of art.” ― <a href="https://www.goodreads.com/quotes/2350-the-absence-of-limitations-is-the-enemy-of-art">Orson Welles</a></p>
</blockquote>
<p>Colors have <em>4 channels</em>, <strong>R</strong>, <strong>G</strong>, <strong>B</strong>, and <strong>Alpha</strong> (transparency), and most modern devices use an 8 bit per channel (for a maximum of <em>256 possible brightness levels per channel</em>) for image files and displays. On old game consoles like the Gameboy, Super Nintendo, Genesis, and even the GBA, developers had to deal with tighter color requirements. The Gameboy only had <em>4 shades</em> to choose from, the Gameboy Color had a 5-bit (for a max of 32 possible brightness levels) per channel display, and could display a maximum of <em>56 colors</em>, though normally it was far less. Pixel art was born from these limitations.</p>
<p>Older devices had limited amounts of ram. The Commodore 64&#x27;s resolution was <em>320 by 200 pixels</em>, and so if you wanted a <em>4 bit display</em>, you would need <em>4 bits</em> for every pixel (64,000) on the screen, for a grand total of _((64000 _ 4)/8)/1000 = 32kb of ram*, which was often times more than the amount of ram in the entire computer!</p>
<p>In addition, on the Nes, sprites were very small images, so often times you would be limited by the size a sprite could be, constrained to a 8x8 grid. You could only have 64 of these sprites on a screen at any given time.</p>
<p>We&#x27;re now free to use all 256^3 (16.3 million) colors if we want, make pixel art at any size, but bear in mind <em>restrictions</em> are what makes pixel art distinct from other forms of art. Even to this day we have communities like PixelJoint keeping these restrictions alive.</p>
<h2 id="pixel-art-vs-digital-paintings">Pixel Art Vs. Digital Paintings</h2>
<p><strong>Pixel Art</strong> is an image that was designed pixel by pixel.</p>
<p>It&#x27;s different from other forms of art like digital painting or line art in that every pixel is important, whereas those forms of art avoid that level of precision.</p>
<h2 id="palette">Palette</h2>
<p>A <strong>palette</strong> is a color ramp, from low to high, and should be characterized by complex, hue shifting tones. You shouldn&#x27;t have a pallette that&#x27;s linear but rather, have one that mixes colors together.</p>
<p>Most artists start with an average color for the piece that they&#x27;re doing, then add highlights and shadows to that.</p>
<p><img src="https://alain.xyz/blog/pixel-art-design-for-game-dev/assets/chaos-zero.gif" alt="Chaos Zero Timelapse by Alain Galvan"/></p>
<p>A good idea is to use complementary colors to help blend colors together. Purple or Teal work well for shadows, whereas yellows and whites work for specular highlights.</p>
<p>It&#x27;s best to have a maximum of 15 colors per sprite (with transparency being the 16th color).</p>
<h2 id="silhouette">Silhouette</h2>
<p><img src="https://alain.xyz/blog/pixel-art-design-for-game-dev/assets/blursilprogress.gif" alt="Silhouettes"/></p>
<p>The lead cartoonist for Spongebob Squarepants, Sherm Cohen, suggested that silhouettes should communicate the idea of a character&#x27;s action. This applies not only to animation, but also to all forms of art, including pixel art, the silhouette is what makes or breaks a piece.</p>
<p>You want your silhouette to be solid, with no jagged lines, and would paint on top of that silhouette.</p>
<p><img src="https://alain.xyz/blog/pixel-art-design-for-game-dev/assets/layermask.gif" alt="Layer Masking In Photoshop"/></p>
<p>One technique to make this easier is to use <strong>Layer masks</strong>, in Photoshop, this is done by <em>Layer &gt; Layer Mask &gt; From Transparency</em>. This will make painting easier by letting you draw strokes without worrying about the silhouette you worked so hard on.</p>
<h2 id="anti-aliasing-and-readability">Anti-Aliasing and Readability</h2>
<p>Since pixel art is normally small, it can become difficult to communicate intricate details within a small space, so an artist is fighting between anti-aliasing and readability.</p>
<p><strong>Anti-Aliasing</strong> is the process of making edges smoother by blending them with their surrounding pixels.</p>
<p><strong>Readability</strong> is how easily you can distinguish details in an image. Generally, The more anti-aliased an image, the less readable it is.</p>
<p>One way to improve readability is to add <strong>outlines</strong> to your sprite. That&#x27;s not always a good idea, such as for environments, but for characters and objects, outlines are a great stylistic choice.</p>
<h2 id="shading">Shading</h2>
<p><img src="https://alain.xyz/blog/pixel-art-design-for-game-dev/assets/perfect-chaos.gif" alt="Perfect Chaos"/></p>
<p><strong>Dithering</strong> is to gradate colors together via a pattern. Generally you want to avoid mechanical dithering and op for a pattern based effect instead.</p>
<p>In this sprite of Perfect Chaos from Sonic Adventure DX, I dithered the image with intricate water simulation. Proper anti-aliasing and careful shapes and edges made the water look almost photo-realistic.</p>
<h2 id="post-processing">Post-Processing</h2>
<p>Often times your original choice of colors wasn&#x27;t the best one, so post-processing could serve as a way to correct your colors and make them stand out.</p>
<p><img src="https://alain.xyz/blog/pixel-art-design-for-game-dev/assets/curvesphotoshop.gif" alt="Curves in Photoshop"/></p>
<p><strong>Curves</strong> - The curve tool is a method of adjusting the ratio of colors on an image. On Photoshop this can be found at <em>Image &gt; Adjustments &gt; Curves</em>, and on Gimp at <em>Colors &gt; Curves</em>.</p>
<p><img src="https://alain.xyz/blog/pixel-art-design-for-game-dev/assets/levelsphotoshop.gif" alt="Levels in Photoshop"/></p>
<p><strong>Levels</strong> - The levels tool is a simple method of adjusting the brightness of colors. The <strong>middle handle</strong> adjust the mid color balance, the <strong>left handle</strong> adjusts the darks, the <strong>right handle</strong> adjusts the highlights. Curves and levels are interchangeable so use whichever you prefer.</p>
<p><img src="https://alain.xyz/blog/pixel-art-design-for-game-dev/assets/colorbalancephotoshop.gif" alt="Color Balance in Photoshop"/></p>
<p><strong>Color Balance</strong> - A method of changing the general tonalities of an image. Movie makers often adjust the color balance to have cyan shadows and red orange highlights. Look at any movie and notice just how blue it is!</p>
<p><img src="https://alain.xyz/blog/pixel-art-design-for-game-dev/assets/colorreplacegimp.gif" alt="Color Replacement in Gimp"/></p>
<p><strong>Color Replacement</strong> - Sometimes what you really need is to manually replace colors. You can do this by using the color selection tool in Gimp (There&#x27;s no equivalent functionality in photoshop), just click the icon with a hand and a blue, red, and green square, set your threshold to 0, and select the colors you want to replace. Then use the fill tool and fill the whole selection.</p>
<p><img src="https://alain.xyz/blog/pixel-art-design-for-game-dev/assets/colorcountgimp.gif" alt="Color Count in Gimp"/></p>
<p>To measure the number of colors on your sprite, you can use a tool like Gimp&#x27;s <strong>Colorcube Analysis</strong>.</p>
<h2 id="more-resources">More Resources</h2>
<ul>
<li>
<p><a href="https://pixeljoint.com">PixelJoint.com</a> remains the top pixel art community.</p>
</li>
<li>
<p>The Twitter account <a href="https://twitter.com/Pixel_Dailies">@PixelDailies</a> is a forum for individuals who want to share pixel art with each other.</p>
</li>
<li>
<p>Mara Gagiu (<a href="https://twitter.com/MaraGrfx">@MaraGrfx</a>) wrote a paper on style preserving filter for pixel art <a href="https://twitter.com/MaraGrfx/status/1354524491519430657">here</a></p>
</li>
<li>
<p><a href="https://ephtracy.github.io/">MagicaVoxel</a> is a tool that can turn pixel art into 3D models easily.</p>
</li>
</ul>]]></content:encoded>
</item>
</channel>
</rss>
