<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[GM Shaders]]></title><description><![CDATA[A series of mini shader tutorials for game developers and beyond!]]></description><link>https://mini.gmshaders.com</link><image><url>https://substackcdn.com/image/fetch/$s_!Y0OL!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf69737-7d50-4dc8-9cce-aad9b9019215_512x512.png</url><title>GM Shaders</title><link>https://mini.gmshaders.com</link></image><generator>Substack</generator><lastBuildDate>Thu, 16 Apr 2026 13:25:24 GMT</lastBuildDate><atom:link href="https://mini.gmshaders.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Xor]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[xordev@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[xordev@substack.com]]></itunes:email><itunes:name><![CDATA[Xor]]></itunes:name></itunes:owner><itunes:author><![CDATA[Xor]]></itunes:author><googleplay:owner><![CDATA[xordev@substack.com]]></googleplay:owner><googleplay:email><![CDATA[xordev@substack.com]]></googleplay:email><googleplay:author><![CDATA[Xor]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[FragCoord: The Ultimate Tool]]></title><description><![CDATA[Building my ulimate shader editor]]></description><link>https://mini.gmshaders.com/p/fragcoord</link><guid isPermaLink="false">https://mini.gmshaders.com/p/fragcoord</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Sat, 07 Mar 2026 20:14:49 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/0f14c3da-d0dc-4aa4-a7ce-df622d3a2e08_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hey everyone! It&#8217;s been too long! Since the last time <a href="https://x.com/XorDev/status/1995342198947102983">I helped Shopify put shaders on the Vegas Sphere</a> on Black Friday, I have been working on VFX for <a href="https://www.orderofthesinkingstar.com/en/">Order of the Sinking Star</a> and some other exciting projects I can&#8217;t talk about yet.</p><p>I&#8217;ve also started building <a href="https://fragcoord.xyz/">FragCoord.xyz</a>, my take on shader editing, debugging, and analysis in your browser. None of the existing tools fit my needs; some are stagnant, others are niche. I wanted to make something capable for professionals and hobbyists. Today, I&#8217;d like to show you where it&#8217;s at currently and the cool things you can do with it now.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lK7E!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb4ade5f-030a-4966-b0ac-88cecb7a7fc3_2191x1333.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lK7E!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb4ade5f-030a-4966-b0ac-88cecb7a7fc3_2191x1333.png 424w, https://substackcdn.com/image/fetch/$s_!lK7E!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb4ade5f-030a-4966-b0ac-88cecb7a7fc3_2191x1333.png 848w, https://substackcdn.com/image/fetch/$s_!lK7E!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb4ade5f-030a-4966-b0ac-88cecb7a7fc3_2191x1333.png 1272w, https://substackcdn.com/image/fetch/$s_!lK7E!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb4ade5f-030a-4966-b0ac-88cecb7a7fc3_2191x1333.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lK7E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb4ade5f-030a-4966-b0ac-88cecb7a7fc3_2191x1333.png" width="1456" height="886" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eb4ade5f-030a-4966-b0ac-88cecb7a7fc3_2191x1333.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:886,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:281044,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/188095219?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb4ade5f-030a-4966-b0ac-88cecb7a7fc3_2191x1333.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lK7E!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb4ade5f-030a-4966-b0ac-88cecb7a7fc3_2191x1333.png 424w, https://substackcdn.com/image/fetch/$s_!lK7E!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb4ade5f-030a-4966-b0ac-88cecb7a7fc3_2191x1333.png 848w, https://substackcdn.com/image/fetch/$s_!lK7E!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb4ade5f-030a-4966-b0ac-88cecb7a7fc3_2191x1333.png 1272w, https://substackcdn.com/image/fetch/$s_!lK7E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb4ade5f-030a-4966-b0ac-88cecb7a7fc3_2191x1333.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Screenshot of the editor</figcaption></figure></div><h1>Introduction</h1><p>When you first visit FragCoord.xyz, you are greeted with a template shader screen and a quick overview of the current features (subject to changes in the near future):</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Xncj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94d64450-2ff7-4705-81ce-0ab77c2d190d_1917x1400.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Xncj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94d64450-2ff7-4705-81ce-0ab77c2d190d_1917x1400.png 424w, https://substackcdn.com/image/fetch/$s_!Xncj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94d64450-2ff7-4705-81ce-0ab77c2d190d_1917x1400.png 848w, https://substackcdn.com/image/fetch/$s_!Xncj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94d64450-2ff7-4705-81ce-0ab77c2d190d_1917x1400.png 1272w, https://substackcdn.com/image/fetch/$s_!Xncj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94d64450-2ff7-4705-81ce-0ab77c2d190d_1917x1400.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Xncj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94d64450-2ff7-4705-81ce-0ab77c2d190d_1917x1400.png" width="1456" height="1063" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/94d64450-2ff7-4705-81ce-0ab77c2d190d_1917x1400.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1063,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:442791,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/188095219?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94d64450-2ff7-4705-81ce-0ab77c2d190d_1917x1400.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Xncj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94d64450-2ff7-4705-81ce-0ab77c2d190d_1917x1400.png 424w, https://substackcdn.com/image/fetch/$s_!Xncj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94d64450-2ff7-4705-81ce-0ab77c2d190d_1917x1400.png 848w, https://substackcdn.com/image/fetch/$s_!Xncj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94d64450-2ff7-4705-81ce-0ab77c2d190d_1917x1400.png 1272w, https://substackcdn.com/image/fetch/$s_!Xncj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94d64450-2ff7-4705-81ce-0ab77c2d190d_1917x1400.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The <strong>Editor</strong> is where you start and where you edit shader code with a shader display on the right. The editor is designed to be flexible with resizing and layout options (additional options on the settings page).</p><p>The <strong>Explore</strong> page shows you the top shaders and feed if you follow shader creators. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ht3u!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff32ec92-6026-4bdf-81ea-185f871837a5_1265x909.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ht3u!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff32ec92-6026-4bdf-81ea-185f871837a5_1265x909.png 424w, https://substackcdn.com/image/fetch/$s_!Ht3u!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff32ec92-6026-4bdf-81ea-185f871837a5_1265x909.png 848w, https://substackcdn.com/image/fetch/$s_!Ht3u!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff32ec92-6026-4bdf-81ea-185f871837a5_1265x909.png 1272w, https://substackcdn.com/image/fetch/$s_!Ht3u!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff32ec92-6026-4bdf-81ea-185f871837a5_1265x909.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ht3u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff32ec92-6026-4bdf-81ea-185f871837a5_1265x909.png" width="1265" height="909" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ff32ec92-6026-4bdf-81ea-185f871837a5_1265x909.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:909,&quot;width&quot;:1265,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:813429,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/188095219?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff32ec92-6026-4bdf-81ea-185f871837a5_1265x909.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Ht3u!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff32ec92-6026-4bdf-81ea-185f871837a5_1265x909.png 424w, https://substackcdn.com/image/fetch/$s_!Ht3u!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff32ec92-6026-4bdf-81ea-185f871837a5_1265x909.png 848w, https://substackcdn.com/image/fetch/$s_!Ht3u!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff32ec92-6026-4bdf-81ea-185f871837a5_1265x909.png 1272w, https://substackcdn.com/image/fetch/$s_!Ht3u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff32ec92-6026-4bdf-81ea-185f871837a5_1265x909.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The Explore page with the shader of the day</figcaption></figure></div><p>Here you can find other shader examples, like, comment, and follow.</p><p>The <strong>Docs</strong> are documentation for FragCoord, editor tools, uniforms, rendering, import/export, and other technical details for FC.</p><p>Let&#8217;s take a closer look at the editor and the inspector tools.</p><h1>Inspector Modes</h1><p>Under the shader preview, you&#8217;ll see 5 inspector modes (Tuner, Inspect, Errors, Frames, and Heatmap). Each of these gives you a different lens to examine the shader.</p><h2>Tuner - Adjust inputs</h2><p>When nothing is selected, the tuner shows all the built-in uniforms supported in FragCoord, such as u_resolution for the shader resolution and u_time for the time elapsed in seconds. The uniforms used in the code are highlighted (the rest are grayed out). The live values of those uniforms are shown on the right side:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Hs3Q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36210f0c-479e-4d78-8044-91920ff553fd_946x471.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Hs3Q!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36210f0c-479e-4d78-8044-91920ff553fd_946x471.png 424w, https://substackcdn.com/image/fetch/$s_!Hs3Q!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36210f0c-479e-4d78-8044-91920ff553fd_946x471.png 848w, https://substackcdn.com/image/fetch/$s_!Hs3Q!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36210f0c-479e-4d78-8044-91920ff553fd_946x471.png 1272w, https://substackcdn.com/image/fetch/$s_!Hs3Q!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36210f0c-479e-4d78-8044-91920ff553fd_946x471.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Hs3Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36210f0c-479e-4d78-8044-91920ff553fd_946x471.png" width="946" height="471" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/36210f0c-479e-4d78-8044-91920ff553fd_946x471.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:471,&quot;width&quot;:946,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:29528,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/188095219?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36210f0c-479e-4d78-8044-91920ff553fd_946x471.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Hs3Q!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36210f0c-479e-4d78-8044-91920ff553fd_946x471.png 424w, https://substackcdn.com/image/fetch/$s_!Hs3Q!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36210f0c-479e-4d78-8044-91920ff553fd_946x471.png 848w, https://substackcdn.com/image/fetch/$s_!Hs3Q!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36210f0c-479e-4d78-8044-91920ff553fd_946x471.png 1272w, https://substackcdn.com/image/fetch/$s_!Hs3Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36210f0c-479e-4d78-8044-91920ff553fd_946x471.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>You can add your own uniforms, such as RGB colors, textures, or anything else, with the Add uniform button. This lets you pick the data type, and then you can adjust it with the sliders needed. The tuner can also be used to adjust any selected value in the code. This makes it easy to tune magic numbers and see what each number does.</p><h2>Inspect - Visualize expressions</h2><p>The Inspector lets you look at each stage of the shader. You can select a variable or expression in the main function see what the output looks like in the shader port. Hover with the mouse to see the values at the cursor.</p><p>In the Inspect panel, you&#8217;ll see the histogram of the selected expression, the minimum and maximum values (the display is normalized to the 0.0 to 1.0 range). If your shader is producing unexpected results, it can help to look through step by step.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JYOb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362f3c7a-e316-4d36-aea1-07a9c5901392_961x409.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JYOb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362f3c7a-e316-4d36-aea1-07a9c5901392_961x409.png 424w, https://substackcdn.com/image/fetch/$s_!JYOb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362f3c7a-e316-4d36-aea1-07a9c5901392_961x409.png 848w, https://substackcdn.com/image/fetch/$s_!JYOb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362f3c7a-e316-4d36-aea1-07a9c5901392_961x409.png 1272w, https://substackcdn.com/image/fetch/$s_!JYOb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362f3c7a-e316-4d36-aea1-07a9c5901392_961x409.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JYOb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362f3c7a-e316-4d36-aea1-07a9c5901392_961x409.png" width="961" height="409" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/362f3c7a-e316-4d36-aea1-07a9c5901392_961x409.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:409,&quot;width&quot;:961,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:92012,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/188095219?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362f3c7a-e316-4d36-aea1-07a9c5901392_961x409.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!JYOb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362f3c7a-e316-4d36-aea1-07a9c5901392_961x409.png 424w, https://substackcdn.com/image/fetch/$s_!JYOb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362f3c7a-e316-4d36-aea1-07a9c5901392_961x409.png 848w, https://substackcdn.com/image/fetch/$s_!JYOb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362f3c7a-e316-4d36-aea1-07a9c5901392_961x409.png 1272w, https://substackcdn.com/image/fetch/$s_!JYOb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362f3c7a-e316-4d36-aea1-07a9c5901392_961x409.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Inspect histogram and range</figcaption></figure></div><h2>Errors - Find NaN and Inf</h2><p>Sometimes a shader produces an unexpected black screen or black pixels. This is often caused by Not-a-Number errors (for example, division by 0). You can toggle individual checks for NaNs, Infs, and Out of Range tests for colors outside of the 0 to 1 range.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rccA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5117f452-1f6d-49ee-95fb-fbf833ae0d3a_831x409.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rccA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5117f452-1f6d-49ee-95fb-fbf833ae0d3a_831x409.png 424w, https://substackcdn.com/image/fetch/$s_!rccA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5117f452-1f6d-49ee-95fb-fbf833ae0d3a_831x409.png 848w, https://substackcdn.com/image/fetch/$s_!rccA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5117f452-1f6d-49ee-95fb-fbf833ae0d3a_831x409.png 1272w, https://substackcdn.com/image/fetch/$s_!rccA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5117f452-1f6d-49ee-95fb-fbf833ae0d3a_831x409.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rccA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5117f452-1f6d-49ee-95fb-fbf833ae0d3a_831x409.png" width="831" height="409" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5117f452-1f6d-49ee-95fb-fbf833ae0d3a_831x409.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:409,&quot;width&quot;:831,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:65027,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/188095219?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5117f452-1f6d-49ee-95fb-fbf833ae0d3a_831x409.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!rccA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5117f452-1f6d-49ee-95fb-fbf833ae0d3a_831x409.png 424w, https://substackcdn.com/image/fetch/$s_!rccA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5117f452-1f6d-49ee-95fb-fbf833ae0d3a_831x409.png 848w, https://substackcdn.com/image/fetch/$s_!rccA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5117f452-1f6d-49ee-95fb-fbf833ae0d3a_831x409.png 1272w, https://substackcdn.com/image/fetch/$s_!rccA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5117f452-1f6d-49ee-95fb-fbf833ae0d3a_831x409.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Error panel listing potential error-causing lines</figcaption></figure></div><p><br>You can also select individual lines to see the errors for that line. Errors are shown with checkerboards (NaNs are Red/Cyan, Infinities are Green/Magenta, Ranges are Blue/Yellow).</p><h2>Frames - GPU time graph</h2><p>A standard frame graph view that gives you CPU and GPU time, milliseconds, and frames per second modes, and stutter points. You can pause the timeline by clicking in the box or change the range in the bottom-right. Frames is for helping you find lag spikes and monitor the overall performance of your shader.</p><h2>Heatmap - Per-pixel cost</h2><p>The heatmap shows you the instruction cost for each pixel (or chunk). Most shaders have a uniform instruction count, but you can get divergence with conditional for-loops or if statements.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3zsk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7002426-1849-42ba-a752-187c5bde6597_827x518.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3zsk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7002426-1849-42ba-a752-187c5bde6597_827x518.png 424w, https://substackcdn.com/image/fetch/$s_!3zsk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7002426-1849-42ba-a752-187c5bde6597_827x518.png 848w, https://substackcdn.com/image/fetch/$s_!3zsk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7002426-1849-42ba-a752-187c5bde6597_827x518.png 1272w, https://substackcdn.com/image/fetch/$s_!3zsk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7002426-1849-42ba-a752-187c5bde6597_827x518.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3zsk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7002426-1849-42ba-a752-187c5bde6597_827x518.png" width="827" height="518" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d7002426-1849-42ba-a752-187c5bde6597_827x518.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:518,&quot;width&quot;:827,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:191543,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/188095219?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7002426-1849-42ba-a752-187c5bde6597_827x518.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!3zsk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7002426-1849-42ba-a752-187c5bde6597_827x518.png 424w, https://substackcdn.com/image/fetch/$s_!3zsk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7002426-1849-42ba-a752-187c5bde6597_827x518.png 848w, https://substackcdn.com/image/fetch/$s_!3zsk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7002426-1849-42ba-a752-187c5bde6597_827x518.png 1272w, https://substackcdn.com/image/fetch/$s_!3zsk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7002426-1849-42ba-a752-187c5bde6597_827x518.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Previewing the instruction heatmap of <a href="https://fragcoord.xyz/s/bw1shq8m">totetmatt&#8217;s Ridge Test</a></figcaption></figure></div><p>The heatmap can highlight the most problematic areas and give you a sense of what to optimize. Ideally speaking, you want to minimize instruction costs and recognize that the pixels are chunked together, so if you have a low-cost pixel next to a high-cost one, you may be paying the higher cost for both. Less variance is generally better. I plan on adding more tools here to help with this.</p><h1>Import, Export, and Formatting</h1><p>To help make FragCoord a universal tool, it automatically converts ShaderToy or twigl code to FragCoord&#8217;s format, uniforms, and structure. Ideally, you can copy and paste shaders over, and they&#8217;ll just work.</p><p>Next to the save button, there&#8217;s an export button that lets you convert to other common languages and formats (WebGL, ShaderToy, HLSL, Metal, and WGSL):</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!k1JX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bd3ea40-b104-4117-9353-10e6f480d8eb_955x818.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!k1JX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bd3ea40-b104-4117-9353-10e6f480d8eb_955x818.png 424w, https://substackcdn.com/image/fetch/$s_!k1JX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bd3ea40-b104-4117-9353-10e6f480d8eb_955x818.png 848w, https://substackcdn.com/image/fetch/$s_!k1JX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bd3ea40-b104-4117-9353-10e6f480d8eb_955x818.png 1272w, https://substackcdn.com/image/fetch/$s_!k1JX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bd3ea40-b104-4117-9353-10e6f480d8eb_955x818.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!k1JX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bd3ea40-b104-4117-9353-10e6f480d8eb_955x818.png" width="955" height="818" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4bd3ea40-b104-4117-9353-10e6f480d8eb_955x818.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:818,&quot;width&quot;:955,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:88784,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/188095219?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bd3ea40-b104-4117-9353-10e6f480d8eb_955x818.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!k1JX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bd3ea40-b104-4117-9353-10e6f480d8eb_955x818.png 424w, https://substackcdn.com/image/fetch/$s_!k1JX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bd3ea40-b104-4117-9353-10e6f480d8eb_955x818.png 848w, https://substackcdn.com/image/fetch/$s_!k1JX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bd3ea40-b104-4117-9353-10e6f480d8eb_955x818.png 1272w, https://substackcdn.com/image/fetch/$s_!k1JX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4bd3ea40-b104-4117-9353-10e6f480d8eb_955x818.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Shader Export preview</figcaption></figure></div><p>So much time can be spent manually converting code languages when it&#8217;s relatively easy to automate. I&#8217;ve also added an automatic formatting tool to organize your code. This reformats the shader to fit your chosen convention and can be helpful when using code from multiple different sources.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pEJV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd01a7f14-8da7-45d4-8f0d-31720597a0cf_526x576.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pEJV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd01a7f14-8da7-45d4-8f0d-31720597a0cf_526x576.png 424w, https://substackcdn.com/image/fetch/$s_!pEJV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd01a7f14-8da7-45d4-8f0d-31720597a0cf_526x576.png 848w, https://substackcdn.com/image/fetch/$s_!pEJV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd01a7f14-8da7-45d4-8f0d-31720597a0cf_526x576.png 1272w, https://substackcdn.com/image/fetch/$s_!pEJV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd01a7f14-8da7-45d4-8f0d-31720597a0cf_526x576.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pEJV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd01a7f14-8da7-45d4-8f0d-31720597a0cf_526x576.png" width="526" height="576" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d01a7f14-8da7-45d4-8f0d-31720597a0cf_526x576.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:576,&quot;width&quot;:526,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:45426,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/188095219?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd01a7f14-8da7-45d4-8f0d-31720597a0cf_526x576.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!pEJV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd01a7f14-8da7-45d4-8f0d-31720597a0cf_526x576.png 424w, https://substackcdn.com/image/fetch/$s_!pEJV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd01a7f14-8da7-45d4-8f0d-31720597a0cf_526x576.png 848w, https://substackcdn.com/image/fetch/$s_!pEJV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd01a7f14-8da7-45d4-8f0d-31720597a0cf_526x576.png 1272w, https://substackcdn.com/image/fetch/$s_!pEJV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd01a7f14-8da7-45d4-8f0d-31720597a0cf_526x576.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Code formatting settings</figcaption></figure></div><p>When there are errors, you can see them by hovering over the error line or the error count at the bottom of the code:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vXJq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8141030b-7c5f-4009-b625-f3cedd30adbb_435x291.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vXJq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8141030b-7c5f-4009-b625-f3cedd30adbb_435x291.png 424w, https://substackcdn.com/image/fetch/$s_!vXJq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8141030b-7c5f-4009-b625-f3cedd30adbb_435x291.png 848w, https://substackcdn.com/image/fetch/$s_!vXJq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8141030b-7c5f-4009-b625-f3cedd30adbb_435x291.png 1272w, https://substackcdn.com/image/fetch/$s_!vXJq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8141030b-7c5f-4009-b625-f3cedd30adbb_435x291.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vXJq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8141030b-7c5f-4009-b625-f3cedd30adbb_435x291.png" width="435" height="291" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8141030b-7c5f-4009-b625-f3cedd30adbb_435x291.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:291,&quot;width&quot;:435,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:27250,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/188095219?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8141030b-7c5f-4009-b625-f3cedd30adbb_435x291.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!vXJq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8141030b-7c5f-4009-b625-f3cedd30adbb_435x291.png 424w, https://substackcdn.com/image/fetch/$s_!vXJq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8141030b-7c5f-4009-b625-f3cedd30adbb_435x291.png 848w, https://substackcdn.com/image/fetch/$s_!vXJq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8141030b-7c5f-4009-b625-f3cedd30adbb_435x291.png 1272w, https://substackcdn.com/image/fetch/$s_!vXJq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8141030b-7c5f-4009-b625-f3cedd30adbb_435x291.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Errors and instruction count</figcaption></figure></div><p>You also have the auto-compile check box and manual compile button. A character counter for the minimized shader and an approximate instruction count (hover to see line-by-line estimates). This is meant to help with optimization and performance awareness. The instructions count is roughly equated to performance cost.</p><h2>Code Library</h2><p>Finally, the code library is the place where you can save and reuse your most useful functions, such as noise functions, rotations, color transformations, and many more.</p><p>FragCoord has you start with the most common ones, but you can remove or add your own as you like:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jQb6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcdfee34-0fb9-499e-b2cd-2454af15c4fc_808x510.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jQb6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcdfee34-0fb9-499e-b2cd-2454af15c4fc_808x510.png 424w, https://substackcdn.com/image/fetch/$s_!jQb6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcdfee34-0fb9-499e-b2cd-2454af15c4fc_808x510.png 848w, https://substackcdn.com/image/fetch/$s_!jQb6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcdfee34-0fb9-499e-b2cd-2454af15c4fc_808x510.png 1272w, https://substackcdn.com/image/fetch/$s_!jQb6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcdfee34-0fb9-499e-b2cd-2454af15c4fc_808x510.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jQb6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcdfee34-0fb9-499e-b2cd-2454af15c4fc_808x510.png" width="808" height="510" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fcdfee34-0fb9-499e-b2cd-2454af15c4fc_808x510.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:510,&quot;width&quot;:808,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:58527,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/188095219?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcdfee34-0fb9-499e-b2cd-2454af15c4fc_808x510.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jQb6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcdfee34-0fb9-499e-b2cd-2454af15c4fc_808x510.png 424w, https://substackcdn.com/image/fetch/$s_!jQb6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcdfee34-0fb9-499e-b2cd-2454af15c4fc_808x510.png 848w, https://substackcdn.com/image/fetch/$s_!jQb6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcdfee34-0fb9-499e-b2cd-2454af15c4fc_808x510.png 1272w, https://substackcdn.com/image/fetch/$s_!jQb6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffcdfee34-0fb9-499e-b2cd-2454af15c4fc_808x510.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Library overview</figcaption></figure></div><p>The community tab is where you can find other code snippets from other users and share your own. I want this to be the main grounds for distributing functions. The code library is meant to be the place to showcase the best shader snippets and allow for continued development of techniques, not just tech demos. Users can update their code snippets, and it will automatically update for all future references, so if you find improvements to your snippets, you can always tweak them later.</p><h1>Community</h1><p>There&#8217;s a growing community of 500 members and 650 public shaders in just a couple of weeks! I want FragCoord to become the best place for graphics professionals and hobbyists, to make it easy to learn, build, and share with tools that help bring the best tech forward.</p><p>When saving your shaders, you publish them publicly, release them unlisted (not on explore, search, or profiles), leave them personally for yourself, or invite only specific members. For collaborations, you can tag other users in your shaders, and there&#8217;s a tagged section on your profiles. Follow users to see them in your personal feed and turn on notifications for specific users if you like.</p><p>Please come join <a href="https://fragcoord.xyz">FragCoord.xyz</a> and help shape its future. I&#8217;m still constantly tweaking, fixing, and improving. Let&#8217;s make this the best place for the graphics community. If you want to suggest features or report bugs, click the changelog.</p><p>Thanks,<br>-Xor</p><p></p>]]></content:encoded></item><item><title><![CDATA[Mini: 3D Rotation]]></title><description><![CDATA[How to rotate with Euler Angles and Axis Angles]]></description><link>https://mini.gmshaders.com/p/3d-rotation</link><guid isPermaLink="false">https://mini.gmshaders.com/p/3d-rotation</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Sun, 19 Oct 2025 01:04:53 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/73076ba3-fa74-4601-899c-ce27bc898087_1920x1080.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>What&#8217;s kickin&#8217; chicken?</p><p>Previously, I wrote about 2D rotations and how rotations can be stacked for 3D Euler Angles (yaw, pitch, and roll):</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;f339e634-63a8-4dd3-a05e-d07b778e01a8&quot;,&quot;caption&quot;:&quot;We're gonna go over rotation and matrices. This tutorial should be easier to digest than code-golfing or raymarching. Let's dig in!&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Mini: Rotation&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:115837202,&quot;name&quot;:&quot;Xor&quot;,&quot;bio&quot;:&quot;Shaders and GameMaker&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b37ca2-b4f8-4d1e-a083-93ad73418f1f_512x512.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2022-09-24T02:12:28.318Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!z2rU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8401d8d-689b-4e05-a848-d9d1d8011422_1920x1080.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://mini.gmshaders.com/p/gm-shaders-mini-rotation-1364623&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:91667870,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:1,&quot;comment_count&quot;:0,&quot;publication_id&quot;:1247585,&quot;publication_name&quot;:&quot;GM Shaders&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!Y0OL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf69737-7d50-4dc8-9cce-aad9b9019215_512x512.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>In 3D, there are several other ways to rotate. First, we&#8217;ll start with Euler Angles.</p><h1>Euler Angles</h1><p>One simple way to solve 3D rotation is with a series of 2D rotations on each axis XYZ. </p><p>As previously covered, 2D rotation looks like this:</p><pre><code><code>mat2 rotate2D(float angle)
{
    return mat2(cos(angle), -sin(angle), sin(angle), cos(angle));
}</code></code></pre><p>When you want to rotate a vector around an axis line, say the z-axis, for example, it&#8217;s as simple as doing a 2D rotation on the x and y axes, leaving the z-axis unchanged. Then you can rotate again on another axis to add another degree of rotation.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BgV4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefeb97e-41a3-4d6f-acfe-07c5dbfe71fe_2560x1925.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BgV4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefeb97e-41a3-4d6f-acfe-07c5dbfe71fe_2560x1925.png 424w, https://substackcdn.com/image/fetch/$s_!BgV4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefeb97e-41a3-4d6f-acfe-07c5dbfe71fe_2560x1925.png 848w, https://substackcdn.com/image/fetch/$s_!BgV4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefeb97e-41a3-4d6f-acfe-07c5dbfe71fe_2560x1925.png 1272w, https://substackcdn.com/image/fetch/$s_!BgV4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefeb97e-41a3-4d6f-acfe-07c5dbfe71fe_2560x1925.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BgV4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefeb97e-41a3-4d6f-acfe-07c5dbfe71fe_2560x1925.png" width="1456" height="1095" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cefeb97e-41a3-4d6f-acfe-07c5dbfe71fe_2560x1925.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1095,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:290033,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/174105862?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefeb97e-41a3-4d6f-acfe-07c5dbfe71fe_2560x1925.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!BgV4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefeb97e-41a3-4d6f-acfe-07c5dbfe71fe_2560x1925.png 424w, https://substackcdn.com/image/fetch/$s_!BgV4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefeb97e-41a3-4d6f-acfe-07c5dbfe71fe_2560x1925.png 848w, https://substackcdn.com/image/fetch/$s_!BgV4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefeb97e-41a3-4d6f-acfe-07c5dbfe71fe_2560x1925.png 1272w, https://substackcdn.com/image/fetch/$s_!BgV4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcefeb97e-41a3-4d6f-acfe-07c5dbfe71fe_2560x1925.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Source: <a href="https://en.wikipedia.org/wiki/Euler_angles">en.Wikipedia.org</a>.</figcaption></figure></div><p>You could rotate the plane by applying roll, then pitch, then yaw. For example, depending on the axes, it might look something like this:</p><pre><code>//Roll (for x-axis)
vector.yz = rotate2D(ROLL) * vector.yz;

//Pitch (for y-axis)
vector.xz = rotate2D(PITCH) * vector.xz;

//Yaw (for z-axis)
vector.xy = rotate2D(YAW) * vector.xy;</code></pre><p><em><strong>Note</strong>: The order of the rotation is very important, and the following rotation can be reversed by reversing the order and swapping mat * vec with vec * mat.</em></p><p>This type of rotation is easy to understand and implement, but it has a few drawbacks in some contexts:</p><ul><li><p>Uses more operations and steps than necessary. It can be simplified further.</p></li><li><p>Rotating about an arbitrary axis is not always obvious.</p></li><li><p>Doesn&#8217;t <a href="https://thenumb.at/Exponential-Rotations/#axisangle-rotations">blend orientations</a> well.</p></li><li><p>90-degree turns can cause <a href="https://en.wikipedia.org/wiki/Gimbal_lock">gimbal lock</a>.</p></li></ul><p>If you don&#8217;t care about those concerns, then you may stop here. For the rest of us, let&#8217;s go a step further.</p><h1>Axis Angle</h1><p>When you need to rotate around an arbitrary axis, you need this formula (learned from <a href="https://shadertoyunofficial.wordpress.com/2019/01/02/programming-tricks-in-shadertoy-glsl/">Fabrice Neyret&#8217;s code golfing</a>):</p><pre><code>//Rotate vector "vec" with angle "ang" around the "axis"
vec = mix(dot(vec, axis) * axis, vec, cos(ang)) + sin(ang) * cross(vec, axis);</code></pre><p>That is a little overwhelming at first, so let&#8217;s break this down into byte-sized pieces. For this formula to work correctly, we need the axis vector to be a unit vector (<em>with a length of 1.0 units</em>), and &#8220;vec&#8221; can be whatever coordinates we want to rotate. &#8220;ang&#8221; is the rotation angle in radians.</p><p>Let&#8217;s focus on this part:</p><pre><code>dot(vec, axis) * axis,</code></pre><p>The <a href="https://mini.gmshaders.com/p/gm-shaders-mini-the-dot-product-1329407">Dot Product</a> here is finding how far our vector is along the axis and translating that many units in the axis direction. For example, let&#8217;s say we have</p><p> <code>vec = vec3(1, 2, 3)</code> and <code>axis = vec3(0, 0, 1)</code></p><p>This formula finds how far vec3(1, 2, 3) is in the z axis, which is 3 units, then we move 3 units in the z axis to get vec3(0, 0, 3). You can think of this as the center point that we are rotating around. We want the x and y to change, but not the z. This is just like our Euler Angle example, except that it generalizes to any axis direction!<br><br>Now, with that context, let&#8217;s look at the whole mix formula:</p><pre><code>mix(dot(vec, axis) * axis, vec, cos(ang))</code></pre><p>Here we are mixing between the axis center point and the vector. It oscillates between the original vector and the inverted vector across from the axis center point.<br>This is like the x-axis of our 2D rotation, but for an arbitrary axis.<br>Finally, we add the sine with a cross product:</p><pre><code> + sin(ang) * cross(vec, axis)</code></pre><p>I haven&#8217;t covered the cross product in detail yet, but for our purpose now, we can think of it as finding the perpendicular vector. For</p><p><code>cross(vec3(1,0,0), vec3(0,1,0))</code> we get <code>vec3(0,0,1)</code></p><p>It&#8217;s the perpendicular vector of the two vectors (multiplied by their magnitudes and the sine of the angle between them).</p><p>So for <code>cross(vec, axis)</code>, we get the perpendicular vector to the axis. We multiply this by sine, just like we would with the y-axis of a 2D rotation. Same idea, but for the arbitrary axis!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HJDr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f017d8-07b0-4fe5-8e95-4036b7342c72_339x291.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HJDr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f017d8-07b0-4fe5-8e95-4036b7342c72_339x291.png 424w, https://substackcdn.com/image/fetch/$s_!HJDr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f017d8-07b0-4fe5-8e95-4036b7342c72_339x291.png 848w, https://substackcdn.com/image/fetch/$s_!HJDr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f017d8-07b0-4fe5-8e95-4036b7342c72_339x291.png 1272w, https://substackcdn.com/image/fetch/$s_!HJDr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f017d8-07b0-4fe5-8e95-4036b7342c72_339x291.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HJDr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f017d8-07b0-4fe5-8e95-4036b7342c72_339x291.png" width="339" height="291" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/78f017d8-07b0-4fe5-8e95-4036b7342c72_339x291.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:291,&quot;width&quot;:339,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:27687,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/174105862?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f017d8-07b0-4fe5-8e95-4036b7342c72_339x291.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HJDr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f017d8-07b0-4fe5-8e95-4036b7342c72_339x291.png 424w, https://substackcdn.com/image/fetch/$s_!HJDr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f017d8-07b0-4fe5-8e95-4036b7342c72_339x291.png 848w, https://substackcdn.com/image/fetch/$s_!HJDr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f017d8-07b0-4fe5-8e95-4036b7342c72_339x291.png 1272w, https://substackcdn.com/image/fetch/$s_!HJDr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F78f017d8-07b0-4fe5-8e95-4036b7342c72_339x291.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Source: <a href="https://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/geometric/axisAngle/index.htm">Eucliedeanspace.com</a></figcaption></figure></div><p>It&#8217;s difficult to illustrate, but you can imagine the rotation axis as defining the place that the vector rotates on. When you rotate around, you rotate from the original point to its perpendicular and opposite points along that plane.</p><p>This solves most of the drawbacks, is more efficient, blends better, and avoids gimbal lock, but sometimes you need to interpolate between two orientations (especially in video games). The next step will be <a href="https://www.opengl-tutorial.org/intermediate-tutorials/tutorial-17-quaternions/#reading-quaternions">quaternions</a>, which deserve a separate tutorial on their own. I&#8217;ve tried to find good examples that clearly explain how they work, but I was unsuccessful, so I&#8217;ll have to write one myself.</p><h1>Conclusion</h1><p>At the end of the day, rotation happens at one plane at a time. In 2D, it&#8217;s easy, as there are only two axes to work with. In 3D (and higher dimensions), it becomes a little more complex as there any many different ways to rotate.</p><p>Euler Angles are simple to understand and work with because they&#8217;re just a series of XYZ axis rotations. It works well in video game contexts because you might only need yaw or pitch and yaw rotation. When you want to rotate in an arbitrary direction, you can use Axis Angles. They are relatively simple, clean, and easy. Beyond this, we have quaternions, which have their uses in animations (blending together better), but I&#8217;ll leave that for another day.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://mini.gmshaders.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for more!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h1>Extras</h1><p>Max Slater&#8217;s <strong><a href="https://thenumb.at/Exponential-Rotations/#the-exponential-and-logarithmic-maps">Exponentially Better Rotations</a></strong> - an overview of different rotation techniques, how they interpolate, and going beyond quaternions</p><p>That&#8217;s all for tonight. Have a great weekend!</p>]]></content:encoded></item><item><title><![CDATA[Video Interview with Outsider Supply]]></title><description><![CDATA[Talking shaders, art and games]]></description><link>https://mini.gmshaders.com/p/video-interview-with-outsider-supply</link><guid isPermaLink="false">https://mini.gmshaders.com/p/video-interview-with-outsider-supply</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Mon, 15 Sep 2025 14:06:51 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/DrX22MDCNh0" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi everyone!<br><br>Busy week, but I was able to fit an interview in last week. Last Thursday I spoke with <a href="https://bio.link/outsidersupply">Outsider Supply</a> about shaders, art, learning math and game development for an hour and forty minutes.<br><br>If that interests you, give it a watch!</p><div id="youtube2-DrX22MDCNh0" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;DrX22MDCNh0&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/DrX22MDCNh0?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>Thanks!<br>-Xor</p>]]></content:encoded></item><item><title><![CDATA[Dot Noise]]></title><description><![CDATA[A cheap alternative to 3D simplex noise]]></description><link>https://mini.gmshaders.com/p/dot-noise</link><guid isPermaLink="false">https://mini.gmshaders.com/p/dot-noise</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Fri, 05 Sep 2025 21:45:16 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/9c03c2b2-2f0c-4b2b-9920-532f0c7ab807_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Good evening!</p><p>In computer graphics, we often need ways of creating noise for clouds, water, terrain, procedural texturing, etc. I previously wrote about &#8220;<a href="https://mini.gmshaders.com/p/chaos">Efficient Chaos</a>&#8221; for cheaply scattering elements randomly and I wrote about <a href="https://mini.gmshaders.com/p/turbulence">Turbulence</a> for cheaply approximating fluids and gases. Today we are looking at new cost saving technique that I sometimes use in place of 3D <a href="https://mini.gmshaders.com/p/gm-shaders-mini-noise-1437243">Value Noise, Perlin Noise</a> and <a href="https://mini.gmshaders.com/p/noise3">Simplex Noise</a> algorithms. Those noise algorithms are designed to be fairly efficient, but if you&#8217;re sampling them many times per pixel (i.e. <a href="https://mini.gmshaders.com/p/volumetric">volumetric rendering</a>), you probably can&#8217;t afford the expensive, multi-sample functions. Here&#8217;s how it works:</p><h1>Gyroids</h1><p>The idea started from playing with <a href="https://en.wikipedia.org/wiki/Gyroid">gyroids</a>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!O78k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b4865ff-1b15-4ee7-a8c4-22ef59443ada_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!O78k!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b4865ff-1b15-4ee7-a8c4-22ef59443ada_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!O78k!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b4865ff-1b15-4ee7-a8c4-22ef59443ada_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!O78k!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b4865ff-1b15-4ee7-a8c4-22ef59443ada_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!O78k!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b4865ff-1b15-4ee7-a8c4-22ef59443ada_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!O78k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b4865ff-1b15-4ee7-a8c4-22ef59443ada_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8b4865ff-1b15-4ee7-a8c4-22ef59443ada_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!O78k!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b4865ff-1b15-4ee7-a8c4-22ef59443ada_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!O78k!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b4865ff-1b15-4ee7-a8c4-22ef59443ada_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!O78k!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b4865ff-1b15-4ee7-a8c4-22ef59443ada_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!O78k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b4865ff-1b15-4ee7-a8c4-22ef59443ada_3840x2160.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Capped Gyroid shader by <a href="https://www.shadertoy.com/view/4stfRX">FabriceNeyret2</a></figcaption></figure></div><p>Gyroids are these cool wavy infinite shapes defined with a simple trig formula:</p><pre><code>//Gyroid waves
float gyroid = dot(cos(p), sin(p.yzx));

//Gyroid waves expanded out
//float gyroid = cos(p.x)*sin(p.y) + cos(p.y)*sin(p.z) + cos(p.z)*sin(p.x);</code></pre><p>It&#8217;s neat that such a simple combination of sine, cosine and swizzling can create this interesting pattern. The problem is it&#8217;s too perfect and repeats every Tau (with a period of 6.2831) units.</p><h3>Aperiodic Gyroids</h3><p>Then I wondered if pick an irrational frequency for one of the sine waves, they should never line up. <a href="https://www.shadertoy.com/view/XcBBRz">Playing with</a> the frequencies made some interesting results:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eEmj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879b21a6-8446-44a2-b9d5-8f393dc09044_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eEmj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879b21a6-8446-44a2-b9d5-8f393dc09044_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!eEmj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879b21a6-8446-44a2-b9d5-8f393dc09044_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!eEmj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879b21a6-8446-44a2-b9d5-8f393dc09044_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!eEmj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879b21a6-8446-44a2-b9d5-8f393dc09044_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!eEmj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879b21a6-8446-44a2-b9d5-8f393dc09044_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/879b21a6-8446-44a2-b9d5-8f393dc09044_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!eEmj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879b21a6-8446-44a2-b9d5-8f393dc09044_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!eEmj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879b21a6-8446-44a2-b9d5-8f393dc09044_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!eEmj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879b21a6-8446-44a2-b9d5-8f393dc09044_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!eEmj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F879b21a6-8446-44a2-b9d5-8f393dc09044_3840x2160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Irregular gyroids</figcaption></figure></div><p>What about the golden ratio, <a href="https://mini.gmshaders.com/p/phi">Phi</a>, the most irrational number? Then the sine waves should never line up perfectly. Here&#8217;s what that looks like:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!n6vt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb489b9-e99e-4249-b988-9c23ed26681c_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!n6vt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb489b9-e99e-4249-b988-9c23ed26681c_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!n6vt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb489b9-e99e-4249-b988-9c23ed26681c_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!n6vt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb489b9-e99e-4249-b988-9c23ed26681c_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!n6vt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb489b9-e99e-4249-b988-9c23ed26681c_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!n6vt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb489b9-e99e-4249-b988-9c23ed26681c_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6eb489b9-e99e-4249-b988-9c23ed26681c_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!n6vt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb489b9-e99e-4249-b988-9c23ed26681c_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!n6vt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb489b9-e99e-4249-b988-9c23ed26681c_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!n6vt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb489b9-e99e-4249-b988-9c23ed26681c_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!n6vt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb489b9-e99e-4249-b988-9c23ed26681c_3840x2160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Irregular Gyroid using the golden ratio</figcaption></figure></div><p>That already looks pretty random and in simple cases, that may be enough, but if I zoom out, you can see that aperiodic doesn&#8217;t mean pattern-less:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!l6Yu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff90d38a6-c0f7-4395-bb54-1276327ab5be_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!l6Yu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff90d38a6-c0f7-4395-bb54-1276327ab5be_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!l6Yu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff90d38a6-c0f7-4395-bb54-1276327ab5be_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!l6Yu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff90d38a6-c0f7-4395-bb54-1276327ab5be_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!l6Yu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff90d38a6-c0f7-4395-bb54-1276327ab5be_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!l6Yu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff90d38a6-c0f7-4395-bb54-1276327ab5be_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f90d38a6-c0f7-4395-bb54-1276327ab5be_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!l6Yu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff90d38a6-c0f7-4395-bb54-1276327ab5be_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!l6Yu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff90d38a6-c0f7-4395-bb54-1276327ab5be_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!l6Yu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff90d38a6-c0f7-4395-bb54-1276327ab5be_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!l6Yu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff90d38a6-c0f7-4395-bb54-1276327ab5be_3840x2160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>There are repeating shapes that are just slightly off, but still looking clearly patterned to our brains. We can take this further with more math and the golden angle</p><h1>3D Rotation</h1><p>Part of the reason this looks so patterned, is because both waves are using the same axis lines. If you remember from Turbulence and Efficient Chaos, rotation can make an enormous difference in masking these patterns. What angle should we use and about what axis? Well how about the golden angle with phi and phi^2 for the axis:</p><pre><code>//Rotating the golden angle on the vec3(1, phi, phi*phi) axis
const mat3 GOLD = mat3(
    -0.571464913, +0.814921382, +0.096597072,
    -0.278044873, -0.303026659, +0.911518454,
    +0.772087367, +0.494042493, +0.399753815);</code></pre><p>This orientation should be the most irrational orientation, although I can&#8217;t confirm that, it works pretty well. Here&#8217;s my blobby test:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LeTK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa80fd2e1-71ae-4463-b03c-adb4ac37efcb_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LeTK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa80fd2e1-71ae-4463-b03c-adb4ac37efcb_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!LeTK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa80fd2e1-71ae-4463-b03c-adb4ac37efcb_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!LeTK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa80fd2e1-71ae-4463-b03c-adb4ac37efcb_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!LeTK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa80fd2e1-71ae-4463-b03c-adb4ac37efcb_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LeTK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa80fd2e1-71ae-4463-b03c-adb4ac37efcb_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a80fd2e1-71ae-4463-b03c-adb4ac37efcb_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!LeTK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa80fd2e1-71ae-4463-b03c-adb4ac37efcb_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!LeTK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa80fd2e1-71ae-4463-b03c-adb4ac37efcb_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!LeTK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa80fd2e1-71ae-4463-b03c-adb4ac37efcb_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!LeTK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa80fd2e1-71ae-4463-b03c-adb4ac37efcb_3840x2160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Rotated and scaled gyroid</figcaption></figure></div><p>The full function looks like this:</p><pre><code>float dot_noise(vec3 p)
{
    //The golden ratio:
    //https://mini.gmshaders.com/p/phi
    const float PHI = 1.618033988;

    //Rotating the golden angle on the vec3(1, phi, phi*phi) axis
    const mat3 GOLD = mat3(
    -0.571464913, +0.814921382, +0.096597072,
    -0.278044873, -0.303026659, +0.911518454,
    +0.772087367, +0.494042493, +0.399753815);
    
    //Gyroid with irrational orientations and scales
    return dot(cos(GOLD * p), sin(PHI * p * GOLD));
    //Ranges from [-3 to +3]
}</code></pre><h1>Uses</h1><p>The ideal usecase is low-scale noise applied on natural shapes. It can work well for clouds and fluids. <a href="https://www.shadertoy.com/view/wfsyRX">Here&#8217;s the twist demo</a> I made.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!E8bH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66629f39-2768-47b8-ad5b-0500201fc643_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!E8bH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66629f39-2768-47b8-ad5b-0500201fc643_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!E8bH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66629f39-2768-47b8-ad5b-0500201fc643_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!E8bH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66629f39-2768-47b8-ad5b-0500201fc643_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!E8bH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66629f39-2768-47b8-ad5b-0500201fc643_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!E8bH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66629f39-2768-47b8-ad5b-0500201fc643_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/66629f39-2768-47b8-ad5b-0500201fc643_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!E8bH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66629f39-2768-47b8-ad5b-0500201fc643_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!E8bH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66629f39-2768-47b8-ad5b-0500201fc643_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!E8bH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66629f39-2768-47b8-ad5b-0500201fc643_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!E8bH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66629f39-2768-47b8-ad5b-0500201fc643_3840x2160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Spiral with noise</figcaption></figure></div><p>For larger scenes, it can be layered as fractal noise at a fraction of the cost:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Xrj2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30dc4862-ae4e-4d55-ba16-d1a3cf7e15d8_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Xrj2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30dc4862-ae4e-4d55-ba16-d1a3cf7e15d8_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!Xrj2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30dc4862-ae4e-4d55-ba16-d1a3cf7e15d8_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!Xrj2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30dc4862-ae4e-4d55-ba16-d1a3cf7e15d8_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!Xrj2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30dc4862-ae4e-4d55-ba16-d1a3cf7e15d8_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Xrj2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30dc4862-ae4e-4d55-ba16-d1a3cf7e15d8_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/30dc4862-ae4e-4d55-ba16-d1a3cf7e15d8_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Xrj2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30dc4862-ae4e-4d55-ba16-d1a3cf7e15d8_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!Xrj2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30dc4862-ae4e-4d55-ba16-d1a3cf7e15d8_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!Xrj2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30dc4862-ae4e-4d55-ba16-d1a3cf7e15d8_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!Xrj2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30dc4862-ae4e-4d55-ba16-d1a3cf7e15d8_3840x2160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Fractal &#8220;Dot Noise&#8221;</figcaption></figure></div><p>The best part is that this type of noise has fewer artifacts without a rand/hash function or interpolation.</p><h3>Drawbacks</h3><p>At the end of the day, it is still composed of just two layers of periodic waves. The rotation helps and the combination is not periodic, but the underlying patterns are still visible in some conditions, especially in flat, large scale scenes:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2ZKy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F669f706b-9b8e-47d2-b0f5-81bdba8a651c_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2ZKy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F669f706b-9b8e-47d2-b0f5-81bdba8a651c_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!2ZKy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F669f706b-9b8e-47d2-b0f5-81bdba8a651c_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!2ZKy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F669f706b-9b8e-47d2-b0f5-81bdba8a651c_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!2ZKy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F669f706b-9b8e-47d2-b0f5-81bdba8a651c_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2ZKy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F669f706b-9b8e-47d2-b0f5-81bdba8a651c_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/669f706b-9b8e-47d2-b0f5-81bdba8a651c_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2ZKy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F669f706b-9b8e-47d2-b0f5-81bdba8a651c_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!2ZKy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F669f706b-9b8e-47d2-b0f5-81bdba8a651c_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!2ZKy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F669f706b-9b8e-47d2-b0f5-81bdba8a651c_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!2ZKy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F669f706b-9b8e-47d2-b0f5-81bdba8a651c_3840x2160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Planes of Dot Noise</figcaption></figure></div><p>That being said, even Simplex Noise would look quite homogenous at this scale. Everything in computer graphics is about balancing trade offs and I think the simplicity and speed of this formula is worth it in many cases. If you use a lot of 3D noise, give this a shot!</p><h1>Conclusion</h1><p>Over the years, I keep finding and developing new tricks for recreating complexity with simple formulas. This simple modification of gyroids is about as cheap as it gets while still producing aperiodic pseudo-noise! We can skip the hash function and easing functions and blending steps, going straight for unfiltered, smooth waves.</p><p>It&#8217;s not a perfect solution for every noise problem, but it is effective for the performance cost. When layered in fractal noise, it&#8217;s a powerhouse.</p><h1>Extras</h1><p><a href="https://blog.demofox.org/2022/01/01/interleaved-gradient-noise-a-different-kind-of-low-discrepancy-sequence/">Interleaved Gradient &#8220;Noise</a>&#8221; by Alan Wolfe: a 2D alternative to white noise that is cheap to compute and easy to filter.<br><br>That&#8217;s a wrap. Thanks for reading. If you found this helpful, consider subscribing!<br></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://mini.gmshaders.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">GM Shaders is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>-Xor</p>]]></content:encoded></item><item><title><![CDATA[Functions: Mix]]></title><description><![CDATA[An overview of the mix function, how it works and some unusual uses]]></description><link>https://mini.gmshaders.com/p/func-mix</link><guid isPermaLink="false">https://mini.gmshaders.com/p/func-mix</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Sat, 30 Aug 2025 02:18:59 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/c41df3cc-967c-4da2-a171-04fe06c589bd_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you have written a few shaders you have probably already used the mix function to blend some colors together. This tutorial will go deeper, understanding math behind mixing and ways to use mix.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://mini.gmshaders.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">GM Shaders is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h1><strong>Math Context</strong></h1><p>The formula behind mix(x, y, a) is just: <code>x + (y - x) * a</code></p><p>A longer formula may be easier to understand: <code>x * (1.0 - a) + y * a</code></p><p>This function &#8220;linearly interpolates&#8221; from &#8220;x&#8221; to &#8220;y&#8221; based on the amount &#8220;a&#8221;. An amount of 0.0 returns x, 1.0 returns y, 0.5 returns halfway x and y, and so on.</p><p>These variables can be floats or vectors, and the math works the same. For a color blending example:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cTrm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b47cdf2-ae2c-4d63-b82a-d52a57679189_1374x479.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cTrm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b47cdf2-ae2c-4d63-b82a-d52a57679189_1374x479.png 424w, https://substackcdn.com/image/fetch/$s_!cTrm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b47cdf2-ae2c-4d63-b82a-d52a57679189_1374x479.png 848w, https://substackcdn.com/image/fetch/$s_!cTrm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b47cdf2-ae2c-4d63-b82a-d52a57679189_1374x479.png 1272w, https://substackcdn.com/image/fetch/$s_!cTrm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b47cdf2-ae2c-4d63-b82a-d52a57679189_1374x479.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cTrm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b47cdf2-ae2c-4d63-b82a-d52a57679189_1374x479.png" width="1374" height="479" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7b47cdf2-ae2c-4d63-b82a-d52a57679189_1374x479.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:479,&quot;width&quot;:1374,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:45160,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/172172572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b47cdf2-ae2c-4d63-b82a-d52a57679189_1374x479.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!cTrm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b47cdf2-ae2c-4d63-b82a-d52a57679189_1374x479.png 424w, https://substackcdn.com/image/fetch/$s_!cTrm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b47cdf2-ae2c-4d63-b82a-d52a57679189_1374x479.png 848w, https://substackcdn.com/image/fetch/$s_!cTrm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b47cdf2-ae2c-4d63-b82a-d52a57679189_1374x479.png 1272w, https://substackcdn.com/image/fetch/$s_!cTrm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b47cdf2-ae2c-4d63-b82a-d52a57679189_1374x479.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>At the end of the day, it&#8217;s just addition, subtraction, and multiplication. The math works the same with amounts outside of the 0.0 to 1.0 range. This allows you to use mix, not just to interpolate, but also to <a href="https://en.wikipedia.org/wiki/Extrapolation">extrapolate</a>. More on that later.</p><p><em><strong>Note</strong>: You're generally still better off using built-in hardware functions, as they are very well optimized.</em></p><h1>Color Tricks</h1><p>So we already know how to blend two colors, but you can do a lot more with mix.</p><h3>Saturation</h3><p>If you want to control saturation, just interpolate between grayscale and full color:</p><pre><code>//Compute weighted luma
//https://en.wikipedia.org/wiki/Luma_(video)
float gray = dot(col.rgb, vec3(0.2126, 0.7152, 0.0722));

//Interpolate from grayscale to color
col = mix(vec3(gray), col, SATURATION);</code></pre><p>The best part is that this works with extrapolation, too. You can boost saturation by using an amount greater than 1.0 (negative values invert hue).</p><h3>Brightness and Contrast </h3><p>A simple formula for adjusting contrast and brightness with mix:</p><pre><code>col = mix(vec3(BRIGHTNESS), col, CONTRAST)</code></pre><p>The neat part about this formula is that you can adjust brightness and contrast as a vector, allowing for independent control of each RGB color channel.</p><h1>Coordinates</h1><p>You can use mix for animations, transitions, and motion. It can be used for interpolating between two positions to move something:</p><pre><code>vec2 pos = mix(POS1, POS2, time_factor);</code></pre><p>With an <a href="https://easings.net">easing function</a>, you can also control the acceleration and deceleration.</p><h3>Radial Blur</h3><p><a href="https://www.shadertoy.com/view/mdXGzs">I have</a> also used mix for radial blurs, chromatic aberration, and crepuscular rays by mixing the UV coordinates with a focus point:</p><pre><code>//Iterate 20 times from 0 to 1
for(float i = 0.; i&lt;1.; i+=.05)
{
    //Add a texture sample approaching the center (0.5, 0.5)
    //This center could moved to change how the direction of aberation
    //The mix amount determines the intensity of the radial blur
    vec2 tuv = mix(uv, vec2(0.5), i * 0.2);
    col += texture(iChannel0, tuv) * 0.05;
}</code></pre><p>Mixing helps with readability and makes it easy to control the center point.</p><h3>Texture Page Coordinates</h3><p>When working with <a href="http://mini.gmshaders.com/p/gm-shaders-mini-two-textures-1376349">texture pages</a>, you often need to convert from a normalized coordinate range to that of a specific texture on the texture page:</p><pre><code>//Blend between the top-left and bottom-right uvs
vec2 uv = mix(uvs.xy, uvs.zw, norm_uv);</code></pre><h1>Remapping Function</h1><p>One formula that I use all the time is a bit like mix 2.0. <code>mix(a, b, x)</code> takes &#8220;x&#8221; from the [0.0, 1.0] to the [a, b] range. Remap takes the &#8220;x&#8221; from [a, b] to the [c, d] range:</p><pre><code>remap(a, b, c, d, x)
{
    return (x-a) / (b-a) * (d-c) + c;
}</code></pre><p>It&#8217;s very useful, especially with texture coordinates</p><h1>Extras</h1><p>I previously wrote about the OkLab colorspace, which is optimized for human color perception. The standard RGB colorspace is not perceptually linear, meaning that blending between two RGB colors can produce unnatural results. If you need to blend colors (hues, luminances, saturations, etc) visually, you want to use OkLab mix:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;e91edb0b-6067-415d-b37a-24acca7aab57&quot;,&quot;caption&quot;:&quot;Hello there,&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Mini: OkLab&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:115837202,&quot;name&quot;:&quot;Xor&quot;,&quot;bio&quot;:&quot;Shaders and GameMaker&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b37ca2-b4f8-4d1e-a083-93ad73418f1f_512x512.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2023-12-02T01:08:02.295Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5d2bb83c-1fe1-44a1-ad11-41547dcbcfb5_1920x1080.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://mini.gmshaders.com/p/oklab&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:139161502,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:18,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;GM Shaders&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!Y0OL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf69737-7d50-4dc8-9cce-aad9b9019215_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Here&#8217;s a nice video on linear interpolation and other blending functions by SimonDev:</p><div id="youtube2-YJB1QnEmlTs" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;YJB1QnEmlTs&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/YJB1QnEmlTs?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>That&#8217;s all for tonight. Thanks for reading!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://mini.gmshaders.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">GM Shaders is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Volumetric Raymarching]]></title><description><![CDATA[Rendering clouds, fire, smoke, light rays and more with raymarching]]></description><link>https://mini.gmshaders.com/p/volumetric</link><guid isPermaLink="false">https://mini.gmshaders.com/p/volumetric</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Sat, 23 Aug 2025 02:17:41 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/390147d6-d1d7-4c07-92be-2c23e9013b1a_1920x1080.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome back,<br><br>Let&#8217;s learn how to render some <a href="https://www.shadertoy.com/view/Wf3SWn">clouds</a>, smoke, fire, and volumetric lighting.</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;f058e037-ff97-492e-9997-431dc52376b4&quot;,&quot;duration&quot;:null}"></div><h1>Density Fields</h1><p>First, for some background, we&#8217;ll start with Signed Distance Field raymarching, which I covered here:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;aef79ba5-525a-48a2-96d3-118b01407559&quot;,&quot;caption&quot;:&quot;Today we're covering \&quot;raymarching\&quot; or specifically \&quot;sphere-assisted raymarching\&quot; which is a powerful raycasting algorithm. It's frequently used by shader enthusiasts because it's relatively easy to work with and can add extra effects like soft shadows and glow as a byproduct.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Raymarching&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:115837202,&quot;name&quot;:&quot;Xor&quot;,&quot;bio&quot;:&quot;Shaders and GameMaker&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b37ca2-b4f8-4d1e-a083-93ad73418f1f_512x512.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2022-09-16T23:50:07.389Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36640a88-fc3d-49a0-9f20-7160e56e4709_1920x1080.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://mini.gmshaders.com/p/gm-shaders-mini-raymarching-1351092&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:91667871,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;GM Shaders&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf69737-7d50-4dc8-9cce-aad9b9019215_512x512.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>With regular sphere-assisted raymarching, you check the distance from the camera to the nearest surface and step that distance in the ray direction. Next you check the distance field at that sample point and step forward again, repeating this 50 to 100 times. In the end, you should have a good approximation of the first intersection point with the ray and the surface.</p><p>For volumetric rendering, I use a similar approach, but instead of distance field, I use a &#8220;density field&#8221;. With volumetrics, we don&#8217;t have to care as much about the final intersection point, but we do want to focus on taking more samples in high density areas. For example, here is a basic tunnel, but with smaller steps:</p><pre><code>//Density field (Tunnel + irregular gyroid)
//https://www.shadertoy.com/view/XcBBRz
float volume(vec3 p)
{
    return 3.5 - 0.25*length(p.xy) + 0.5*dot(sin(p), cos(p*0.618).yzx);
}</code></pre><p>The 3.5 sets the radius of the tunnel and the length(p.xy) makes a ton along the z-axis. The 0.25 scales the steps down so that raymarcher takes smaller steps along the ray and the <a href="https://www.shadertoy.com/view/wfsyRX">dot product sine wave technique</a> adds some distortion cheaply.</p><p>When rendered, it looks like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2aKG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6dc3002-ca65-4afb-9005-45cbedcbecc5_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2aKG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6dc3002-ca65-4afb-9005-45cbedcbecc5_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!2aKG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6dc3002-ca65-4afb-9005-45cbedcbecc5_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!2aKG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6dc3002-ca65-4afb-9005-45cbedcbecc5_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!2aKG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6dc3002-ca65-4afb-9005-45cbedcbecc5_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2aKG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6dc3002-ca65-4afb-9005-45cbedcbecc5_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d6dc3002-ca65-4afb-9005-45cbedcbecc5_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2aKG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6dc3002-ca65-4afb-9005-45cbedcbecc5_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!2aKG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6dc3002-ca65-4afb-9005-45cbedcbecc5_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!2aKG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6dc3002-ca65-4afb-9005-45cbedcbecc5_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!2aKG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6dc3002-ca65-4afb-9005-45cbedcbecc5_3840x2160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Volumetric rendered clouds</figcaption></figure></div><h1>Sample Accumulation</h1><p>Typically, with raymarching, you sample the color once, outside the raymarch loop, at the intersection point, but with volumetric rendering, you sample the color at every point along the ray and accumulate the samples. It is easier to accumulate light then to try to deal with scattering and opacity.</p><p>We&#8217;ll look at a simplified glowing example first:</p><pre><code>//Fog density
#define DENSITY 1.6
//Surface pass rate
#define PASSTHROUGH 0.1

//Octahedral density field
float volume(vec3 p)
{
    //Mirror the axes and average them up
    float sum = dot(abs(p), vec3(1.0/3.0));
    //Find distance to the edge with a passthrough
    return abs(sum - 2.0) / DENSITY + PASSTHROUGH;
}</code></pre><p>We won&#8217;t worry about the <a href="https://mini.gmshaders.com/p/sdf">SDF math</a> right now. The important parts are with the density, which makes the raymarch steps smaller and the passthrough which allows the raymarcher to pass through the surface. By using the absolute value of the sum, it makes the distance field hollow, so when we add a little passthrough, the step size never reaches zero and the ray continues marching. Here&#8217;s what our octahedron looks like up close (yes, that is an octahedron with a lot of perspective and translucency)</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!44YX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56822abf-a838-4b9a-bb18-79b27a9666ce_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!44YX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56822abf-a838-4b9a-bb18-79b27a9666ce_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!44YX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56822abf-a838-4b9a-bb18-79b27a9666ce_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!44YX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56822abf-a838-4b9a-bb18-79b27a9666ce_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!44YX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56822abf-a838-4b9a-bb18-79b27a9666ce_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!44YX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56822abf-a838-4b9a-bb18-79b27a9666ce_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/56822abf-a838-4b9a-bb18-79b27a9666ce_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!44YX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56822abf-a838-4b9a-bb18-79b27a9666ce_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!44YX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56822abf-a838-4b9a-bb18-79b27a9666ce_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!44YX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56822abf-a838-4b9a-bb18-79b27a9666ce_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!44YX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56822abf-a838-4b9a-bb18-79b27a9666ce_3840x2160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Translucent octahedron</figcaption></figure></div><p>My raymarch loop looks like this:</p><pre><code>//Output brightness
#define BRIGHTNESS 0.002

//Accumulative color
vec3 col = vec3(0.0);

//Glow raymarch loop
for(float i = 0.0; i&lt;STEPS; i++)
{
    //Glow density
    float vol = volume(pos);
    //Step forward
    pos += dir * vol;

    //Add the sample color
    col += vec3(3, 2, 1) / vol;
}
//Tanh tonemapping
//https://mini.gmshaders.com/p/tonemaps
col = tanh(BRIGHTNESS*col);</code></pre><p>Each sample we add a color divided by the distance/density, which creates the nice light attenuation effect. Normally, you want to avoid dividing by the distance because it could be negative or zero (breaking the color accumulation). In this case, we know the volume will always be greater than one, so this works fine!</p><p>Other <a href="https://mini.gmshaders.com/i/96397170/falloff">falloff functions</a> can be used here. For extra fun, try changing the color and brightness based on the sample position.<br>I&#8217;ve put together a more advanced demo on <a href="https://www.shadertoy.com/view/W3tSR4">ShaderToy here</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gQyA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F772f9d6c-3a7e-42f7-abe8-230cf1cf52f8_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gQyA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F772f9d6c-3a7e-42f7-abe8-230cf1cf52f8_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!gQyA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F772f9d6c-3a7e-42f7-abe8-230cf1cf52f8_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!gQyA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F772f9d6c-3a7e-42f7-abe8-230cf1cf52f8_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!gQyA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F772f9d6c-3a7e-42f7-abe8-230cf1cf52f8_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gQyA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F772f9d6c-3a7e-42f7-abe8-230cf1cf52f8_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/772f9d6c-3a7e-42f7-abe8-230cf1cf52f8_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gQyA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F772f9d6c-3a7e-42f7-abe8-230cf1cf52f8_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!gQyA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F772f9d6c-3a7e-42f7-abe8-230cf1cf52f8_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!gQyA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F772f9d6c-3a7e-42f7-abe8-230cf1cf52f8_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!gQyA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F772f9d6c-3a7e-42f7-abe8-230cf1cf52f8_3840x2160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Textured glowing ball with light rays</figcaption></figure></div><p>This showcases how this technique can be used with light rays shooting out.<br>Glowing effects are pretty easy and fun!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!c3B4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea55b340-e50b-433f-a8bf-230f0cd11f28_1920x1080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!c3B4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea55b340-e50b-433f-a8bf-230f0cd11f28_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!c3B4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea55b340-e50b-433f-a8bf-230f0cd11f28_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!c3B4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea55b340-e50b-433f-a8bf-230f0cd11f28_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!c3B4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea55b340-e50b-433f-a8bf-230f0cd11f28_1920x1080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!c3B4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea55b340-e50b-433f-a8bf-230f0cd11f28_1920x1080.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ea55b340-e50b-433f-a8bf-230f0cd11f28_1920x1080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1816939,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/165406271?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea55b340-e50b-433f-a8bf-230f0cd11f28_1920x1080.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!c3B4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea55b340-e50b-433f-a8bf-230f0cd11f28_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!c3B4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea55b340-e50b-433f-a8bf-230f0cd11f28_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!c3B4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea55b340-e50b-433f-a8bf-230f0cd11f28_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!c3B4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea55b340-e50b-433f-a8bf-230f0cd11f28_1920x1080.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Other glowing shaders I&#8217;ve written</figcaption></figure></div><p>Clouds, fog and smoke can be done with a alpha blending your samples.</p><h1>Blending</h1><p>Instead of just accumulating color, you can do regular alpha blending like so:</p><pre><code>color = mix(color, vec4(sample_rgb,1), (1.0 - color.a) * sample_alpha);</code></pre><p>This gives you a new color for the next sample, to be blended like so.<br>It&#8217;s a good idea to stop raymarching when the color is opaque, because no more blending is going to happen:</p><pre><code>//Stop when opaque (close enough to 1.0)
if (color.a &gt; 0.998) break;</code></pre><p>It never will quite reach 1.0 if you&#8217;re doing things correctly, so a threshold above 254 / 255 is good enough. <a href="https://www.shadertoy.com/view/WcdSz2">Here&#8217;s the source to my Clouds ShaderToy example</a>.</p><p>The rest of the tutorial is dedicated to my paid supporters, who help make these tutorials possible. Please consider joining!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://mini.gmshaders.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe for more</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>
      <p>
          <a href="https://mini.gmshaders.com/p/volumetric">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Decoding: Phosphor]]></title><description><![CDATA[How I created "Phosphor", with glowing, fluid particles, in just 258 chars]]></description><link>https://mini.gmshaders.com/p/decoding-phosphor</link><guid isPermaLink="false">https://mini.gmshaders.com/p/decoding-phosphor</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Sat, 26 Jul 2025 01:35:03 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/c44e0b6d-de45-4dc9-99a2-f13fea1f41bf_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Heyo!<br>Many of you have asked me to do technical breakdowns for my compact shaders. Let&#8217;s begin this series with &#8220;<a href="https://x.com/XorDev/status/1940448131671580897">Phosphor</a>&#8221;:</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;dca7dc41-7ffc-463f-bcd4-470a5430c8af&quot;,&quot;duration&quot;:null}"></div><p><a href="https://twigl.app/?ol=true&amp;ss=-OUAyaqT9B-2_LoFWD5E">Live demo</a><br>This fragment shader code is just 258 characters:</p><pre><code>for(float i,z,d;i++&lt;8e1;o+=(cos(d/.1+vec4(0,2,4,0))+1.)/d*z){vec3 p=z*normalize(FC.rgb*2.-r.xyy),a=normalize(cos(vec3(4,2,0)+t-d*8.));p.z+=5.,a=a*dot(a,p)-cross(a,p);for(d=1.;d++&lt;9.;)a+=sin(a*d+t).yzx/d;z+=d=.05*abs(length(p)-3.)+.04*abs(a.y);}o=tanh(o/1e4);</code></pre><p>That tiny little code handles the raycasting algorithm, glowing attenuation, 3D scene rotation, camera displacement, turbulent fluid dynamics, and particle distribution along a 3D ring! It&#8217;s a lot to unpack.</p><p>Before we can look at the specifics of this shader, we need to understand the shader format and variables.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://mini.gmshaders.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">GM Shaders is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h1>Twigl Format</h1><p>These tiny shaders are written for &#8220;<a href="https://twigl.app/">twigl</a>.app&#8221;, which is a minimal online shader editor like ShaderToy, designed specifically for writing shaders to share on Twitter/X in 280 chars or less. Twigl is handy because in the compact &#8220;geekest 300&#8221; mode, it compresses the variable names like <code>gl_FragColor</code> to &#8220;o&#8221;, <code>gl_FragCoord</code> to &#8220;FC&#8221;, resolution to &#8220;<code>r</code>&#8221; and time to &#8220;<code>t</code>&#8221;. It also automatic inserts your pasted code into the main function so there is no generic code required and you can focus on the parts that matter.</p><p>It also adds some handy functions, a backbuffer and which <a href="https://mini.gmshaders.com/p/gm-shaders-mini-twigl">I wrote about in more detail here</a>, but for our purposes now, this is enough!</p><h1>Adding Breathing Room</h1><p>When I am sharing code on X, every character counts, including spaces and line breaks. Usually, I don&#8217;t have enough room to spare for pretty code formatting.</p><p>With some spaces, line break,s and comments, we have this expression:</p><pre><code>//Initialize <strong>I</strong>terator, <strong>Z</strong>-depth and step <strong>D</strong>istance variables
//Raymarch 80 steps
for(float i,z,d; i++&lt;8e1;
  //Pick a color and light attentuation
  o += (cos(d/.1+vec4(0,2,4,0))+1.)/d*z)
{
  //Raymarch step sample point
  vec3 p = z*normalize(FC.rgb*2.-r.xyy),
  //Twisting rotation axis vector
  a = normalize(cos(vec3(4,2,0)+t-d*8.));
  
  //Move camera back 5 units
  p.z += 5.,
  //Rotate sample using the axis
  a = a*dot(a,p)-cross(a,p);
  
  //Apply turbulence waves at 9 frequencies
  for(d=1.;d++&lt;9.;)
  a += sin(a*d+t).yzx/d;
  
  //Approximate distance to ring and step forward
  z += d = .05*abs(length(p)-3.)+.04*abs(a.y);
}
//Tonemap with tanh
o = tanh(o/1e4);</code></pre><p>Already, this should be much more digestible, but there are several unique techniques being used here, so let&#8217;s do a closer, line-by-line analysis.</p><h1>Raymarching</h1><p>The very first line is the raymarch loop:</p><pre><code><code>for(float i,z,d; i++&lt;8e1; &#8230;</code></code></pre><p><a href="https://mini.gmshaders.com/p/gm-shaders-mini-raymarching-1351092">Raymarching</a> is a type of raycasting algorithm that marches in steps along the ray direction until it intersects with a surface or gets close enough. Each step along the way, it checks the (<em>approximate</em>) distance to the nearest surface and steps that far in the ray direction. If the distance is done exactly, you&#8217;re guaranteed not to pass through any surfaces.</p><p>This raymarch loop initializes the iterator &#8220;i&#8221;, the raymarched &#8220;z&#8221; depth, and the step distance &#8220;d&#8221;, which will be useful later. When undefined, these variables are all initialized at 0.0, and so the loop executes 80 steps.</p><p>The next line actually executes last. In GLSL, for loops are in the format:</p><pre><code>for(INIT; COND; LAST)</code></pre><p>The &#8220;INIT&#8221; part can be left blank, but it&#8217;s a free semi-colon, so I always use it! &#8220;COND&#8221; is the continue conditional boolean; if it&#8217;s true, it repeats the loop, otherwise the loop stops. The last step is usually the increment &#8220;i++&#8221; part, but not for a code-golfer. For me, it&#8217;s the last line, saving another semi-colon.</p><p>So the final step is this part, which handles the coloring. More on this later&#8230;</p><pre><code>o += (cos(d/.1+vec4(0,2,4,0))+1.)/d*z)</code></pre><p>Next, I calculate where the sample is along the ray:</p><pre><code>vec3 p = z*normalize(FC.rgb*2.-r.xyy),</code></pre><p>&#8220;z&#8221; is the raymarched depth along the ray (more on that later). The normalized bit is for calculating the ray direction for each fragment/pixel. FC is not just a 2D vector with fragment coordinates on the screen. It is a 4D vector with z component being 0.5 and w being 1.0. (<em>I use .rgb instead of .xyz to avoid the code being translated to a URL</em>). Another way to understand the vector inside normalize:</p><pre><code>//Center the xy coordinates and z is minus screen height
vec3(FC.xy*2.0 - r, 1.0 - r.y)

//Since we're normalizing this vector, the length doesn't matter
//We could divide all components by the screen height
//That gives us a more familiar ray direction formula
vec3((FC.xy*2.0 - r) / r.y, - 1.0)
//The xy coordinates are normalized and ratio-correct.
//The z axis is approximately negative -1.0 when the height is high</code></pre><p>We end up with a compact way of writing the standard camera ray direction, just with the camera facing the negative-z direction instead of positive z!</p><h1>Rotation and Trailing</h1><p>This next part deserves a full tutorial on its own (it&#8217;s on the list):</p><pre><code>a = normalize(cos(vec3(4,2,0)+t-d*8.));</code></pre><p>For a simplified explanation, this calculates a rotation axis vector with the x, y, and z out of phase from one another. Adding the time &#8220;t&#8221; means it completes the rotation cycle every Tau (6.283&#8230;) seconds. Remember, &#8220;d&#8221; is the step distance (which gets defined later). When we subtract the distance to the ring from time, it means that the rotation axis lags behind when the sample point is further from the ring. Without this bit, we would have no trailing effect!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ix7W!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a49158f-7722-47f4-896e-39be5d25cc68_1080x1080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ix7W!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a49158f-7722-47f4-896e-39be5d25cc68_1080x1080.png 424w, https://substackcdn.com/image/fetch/$s_!ix7W!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a49158f-7722-47f4-896e-39be5d25cc68_1080x1080.png 848w, https://substackcdn.com/image/fetch/$s_!ix7W!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a49158f-7722-47f4-896e-39be5d25cc68_1080x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!ix7W!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a49158f-7722-47f4-896e-39be5d25cc68_1080x1080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ix7W!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a49158f-7722-47f4-896e-39be5d25cc68_1080x1080.png" width="1080" height="1080" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7a49158f-7722-47f4-896e-39be5d25cc68_1080x1080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1080,&quot;width&quot;:1080,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1441970,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/169252168?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a49158f-7722-47f4-896e-39be5d25cc68_1080x1080.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ix7W!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a49158f-7722-47f4-896e-39be5d25cc68_1080x1080.png 424w, https://substackcdn.com/image/fetch/$s_!ix7W!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a49158f-7722-47f4-896e-39be5d25cc68_1080x1080.png 848w, https://substackcdn.com/image/fetch/$s_!ix7W!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a49158f-7722-47f4-896e-39be5d25cc68_1080x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!ix7W!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a49158f-7722-47f4-896e-39be5d25cc68_1080x1080.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Left side with trailing, right without</figcaption></figure></div><p>Before applying the rotation, we move the camera back 5 units:</p><pre><code>p.z += 5.,</code></pre><p>This makes it revolve around the sphere. <em>Remember that backwards is positive here</em>.</p><p>This simple formula applies the actual rotation to vector &#8220;p&#8221; on axis &#8220;a&#8221;</p><pre><code><code>a = a*dot(a,p)-cross(a,p);</code></code></pre><p>This is based on a <a href="https://x.com/XorDev/status/1947677527218262477">longer formula for 3D</a> rotations:</p><pre><code>mix(a*dot(p,a), p, -cos(t)) + sin(t) * cross(p,a)</code></pre><p>Which rotates about &#8220;a&#8221; with angle &#8220;t&#8221;. In this shader, I simplified by hard-coding the angle of 270 degrees (cos(t) = 0 and sin(t) = -1). This allowed me to remove the sine, cosine, and mix functions. So instead of rotating around a fixed axis, I rotate a fixed angle around a moving axis. It creates some <a href="https://x.com/XorDev/status/1947683183857688772">interesting motion</a>, too!</p><h1>Turbulence</h1><p>The next piece is the application of fluid-like dynamics:</p><pre><code>for(d=1.;d++&lt;9.;)
  a += sin(a*d+t).yzx/d;</code></pre><p>The idea is simple. Just apply some perpendicular sine waves. When repeated at frequencies from 1 to 9. This is a reduced version of my turbulence technique:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;991ec632-c3fa-4ebe-b02a-6684a8d40435&quot;,&quot;caption&quot;:&quot;What do water, fire, dust, magic, wind, fog, smoke and clouds have in common? They are all heavily influenced by turbulent dynamics. Gases and non-viscous liquids twirl and flow in a way that is quite difficult to compute.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Turbulence&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:115837202,&quot;name&quot;:&quot;Xor&quot;,&quot;bio&quot;:&quot;Shaders and GameMaker&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b37ca2-b4f8-4d1e-a083-93ad73418f1f_512x512.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2025-03-17T21:50:00.014Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/865ec5f2-382f-4deb-9803-c4b3fc01c32c_1920x1080.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://mini.gmshaders.com/p/turbulence&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:158118766,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:16,&quot;comment_count&quot;:3,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;GM Shaders&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!Y0OL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf69737-7d50-4dc8-9cce-aad9b9019215_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h1>Ring</h1><p>To approximate the distance, we start with the distance to a hollow sphere (without turbulence) plus the distance to the plane (with turbulence):</p><pre><code><code>z += d = .05*abs(length(p)-3.)+.04*abs(a.y);</code></code></pre><p>I multiplied the step distances by very small factors because the turbulence and twisting distorts the distance fields a lot, so it makes it less likely to overstep.</p><p>This distance gets fed into the color function:</p><pre><code>o += (cos(d/.1+vec4(0,2,4,0))+1.)/d*z)</code></pre><p>Every raymarch step, I pick a color using the step distance to pick the hue. The color is picked using a sine wave with RGBA phase shifting (the alpha isn&#8217;t important). Adding 1.0 puts the color in the [0.0, 2,0] range (don&#8217;t want negative colors). Dividing by the distance gives a nice light attenuation effect. Multiplying by z makes it fade when it gets close to the camera.</p><h1>Phosphor 2 and Nucleus</h1><p><a href="https://x.com/XorDev/status/1945504914253205515">Phosphor 2</a> is the same idea, but with coloring based side of the plane, the different rotation pattern, a sharper sphere falloff, and darker overall:</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;1d44bd8d-c680-47cb-bef2-5cb22e68fdbe&quot;,&quot;duration&quot;:null}"></div><p><a href="https://twigl.app/?ol=true&amp;ss=-OVJhlL5jrbIy5vPJZib">Phosphor 2 Demo</a><br><a href="https://x.com/XorDev/status/1948016291413217752">Nucleus</a> simplifies the coloring, removes the plane, and uses the turbulence sphere coordinates instead:</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;593830bc-440c-4aa1-ac38-c65363b7bbdd&quot;,&quot;duration&quot;:null}"></div><p><a href="https://twigl.app/?ol=true&amp;ss=-OVrWqy9KIaldgvEvzB_">Nucleus Demo</a><br>So that&#8217;s how it works. 3 shaders in one! Hopefully, that made sense and was interesting. Thank you for reading, and I&#8217;ll talk soon.<br></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://mini.gmshaders.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">GM Shaders is a reader-supported publication. To receive new posts and support my work, consider becoming a paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Efficient Chaos]]></title><description><![CDATA[Techniques I use for fast pseudo-randomness in pixel shaders]]></description><link>https://mini.gmshaders.com/p/chaos</link><guid isPermaLink="false">https://mini.gmshaders.com/p/chaos</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Thu, 10 Jul 2025 21:47:22 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/b7bfc0c2-cb64-4fe9-96df-ddc15700aec3_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Sup, hope you are well!<br></p><p>Nature is mostly chaotic, while computers are deterministic. Ironically, a lot of time and resources are spent trying to generate (pseudo) randomness. This tutorial will explore some techniques I use in my shaders to add some natural randomness. This technique can be used for scattering stars, rain, leaves, particles, <a href="https://store.gx.me/mods/yhsqwh/make-it-rain/">money</a>, or objects.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dLzj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed085ff9-c1e2-4932-9839-dee031113bb5_1920x1080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dLzj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed085ff9-c1e2-4932-9839-dee031113bb5_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!dLzj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed085ff9-c1e2-4932-9839-dee031113bb5_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!dLzj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed085ff9-c1e2-4932-9839-dee031113bb5_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!dLzj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed085ff9-c1e2-4932-9839-dee031113bb5_1920x1080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dLzj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed085ff9-c1e2-4932-9839-dee031113bb5_1920x1080.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ed085ff9-c1e2-4932-9839-dee031113bb5_1920x1080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1505744,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/166992315?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed085ff9-c1e2-4932-9839-dee031113bb5_1920x1080.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!dLzj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed085ff9-c1e2-4932-9839-dee031113bb5_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!dLzj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed085ff9-c1e2-4932-9839-dee031113bb5_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!dLzj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed085ff9-c1e2-4932-9839-dee031113bb5_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!dLzj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed085ff9-c1e2-4932-9839-dee031113bb5_1920x1080.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Left: Ordered grid. Right: Generated chaos</figcaption></figure></div><p>If you&#8217;ve read my guide on fluid Turbulence, you may have some ideas about how this method works:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;d9f00135-777f-4599-8dc4-77e0fb3f4cd6&quot;,&quot;caption&quot;:&quot;What do water, fire, dust, magic, wind, fog, smoke, and clouds have in common? They are all heavily influenced by turbulent dynamics.<br />Today, we are not going to be solving the Navier-Stokes equations (but I will include some resources at the end if you want to). Instead, we will fake it! Proper simulations require multiple shader passes, are slow, memory-intensive, and are limited by resolution. Most of the time, we can get away with cheaper real-time approximations and still create impressive effects. Let&#8217;s hop in!&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Turbulence&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:115837202,&quot;name&quot;:&quot;Xor&quot;,&quot;bio&quot;:&quot;Shaders and GameMaker&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b37ca2-b4f8-4d1e-a083-93ad73418f1f_512x512.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2025-03-17T21:50:00.014Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/865ec5f2-382f-4deb-9803-c4b3fc01c32c_1920x1080.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://mini.gmshaders.com/p/turbulence&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:158118766,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:16,&quot;comment_count&quot;:3,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;GM Shaders&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!Y0OL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf69737-7d50-4dc8-9cce-aad9b9019215_512x512.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>This concept is similar, but with a few additional options for greater variety. Better yet, this works in 3D without additional costs. <em>Classic <a href="https://mini.gmshaders.com/p/noise2">Worley Noise</a>, for instance, has a complexity cost of 3^N for N-dimensions (9 samples in 2D, 27 samples in 3D, 81 samples in 4D), making it less than ideal in 3D applications.</em></p><h1>Grid Cells</h1><p>We just start with a basic, tiled grid of points. Then we can add a few layers with offsets. 3 to 5 is pretty generally reasonable. Next, you can increase the spacing with each layer to break the tiling. And when combined with rotation, you eliminate most of the visible tiling:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!78-s!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bf2472b-e778-4411-bc0f-80a54c77a8ba_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!78-s!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bf2472b-e778-4411-bc0f-80a54c77a8ba_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!78-s!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bf2472b-e778-4411-bc0f-80a54c77a8ba_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!78-s!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bf2472b-e778-4411-bc0f-80a54c77a8ba_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!78-s!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bf2472b-e778-4411-bc0f-80a54c77a8ba_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!78-s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bf2472b-e778-4411-bc0f-80a54c77a8ba_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6bf2472b-e778-4411-bc0f-80a54c77a8ba_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:5101540,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/166992315?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bf2472b-e778-4411-bc0f-80a54c77a8ba_3840x2160.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!78-s!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bf2472b-e778-4411-bc0f-80a54c77a8ba_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!78-s!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bf2472b-e778-4411-bc0f-80a54c77a8ba_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!78-s!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bf2472b-e778-4411-bc0f-80a54c77a8ba_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!78-s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bf2472b-e778-4411-bc0f-80a54c77a8ba_3840x2160.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The 4 main steps to creating &#8220;Efficient Chaos&#8221;.</figcaption></figure></div><p>At a glance, the rotated image looks pretty random to me. The colored layers kind of give away the patterns, but that can be easily solved.</p><p>Here&#8217;s a grayscale output with just 5 layers:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CA0Q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72c34a6-8338-4643-a759-f98c0ffe8be8_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CA0Q!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72c34a6-8338-4643-a759-f98c0ffe8be8_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!CA0Q!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72c34a6-8338-4643-a759-f98c0ffe8be8_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!CA0Q!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72c34a6-8338-4643-a759-f98c0ffe8be8_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!CA0Q!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72c34a6-8338-4643-a759-f98c0ffe8be8_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CA0Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72c34a6-8338-4643-a759-f98c0ffe8be8_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b72c34a6-8338-4643-a759-f98c0ffe8be8_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CA0Q!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72c34a6-8338-4643-a759-f98c0ffe8be8_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!CA0Q!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72c34a6-8338-4643-a759-f98c0ffe8be8_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!CA0Q!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72c34a6-8338-4643-a759-f98c0ffe8be8_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!CA0Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb72c34a6-8338-4643-a759-f98c0ffe8be8_3840x2160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">5 scaled, shifted, and rotated layers</figcaption></figure></div><p>First, you can split the coordinates into cells using <code>fract()</code> or <code>mod()</code> and find the difference to the cell center. </p><pre><code>//Break the coordinates into cells of 2 units
vec2 subcell = mod(coord, 2.0) - 1.0;</code></pre><p>For this example, we&#8217;ll use the distance to the cell centers for our star point lights:</p><pre><code>//Distance to cell center
float len = length(subcell);
//Light attenuation with circle cutoff
float att = max(1.0-len, 0.0) / len;</code></pre><p><em><strong>Note</strong>: You can use whatever function you want here. You could have randomly rotated squares, rain particles, <a href="https://store.gx.me/mods/jaatvb/full-weather-cycle/">snowflakes</a>, or anything else that fits neatly in the cells. For extra variety, randomize the particle orientation, scale, and position within the cell (avoiding edge clipping)</em></p><p>To add layers with <strong>shifting</strong>, I have:</p><pre><code><code>//Loop through the layers
for(float i = 0.0; i&lt;LAYERS; i++)
{
    //Save the base coordinates
    vec2 p = coord;
    //Apply a layer shift for each layer
    p += LAYER_SHIFT*i;

    //...Use p for our sub-coordinates and sum lights</code></code></pre><p>Next, we can simply <strong>scale</strong> each layer like so:</p><pre><code>//Adjust layer scaling
p /= 1.0 + LAYER_SCALE*i;</code></pre><p>And finally, for <strong><a href="https://mini.gmshaders.com/p/gm-shaders-mini-rotation-1364623">rotation</a></strong>, I could just rotate the base coordinates:</p><pre><code>//Rotate layer
//Read about the Golden Angle here:
//https://mini.gmshaders.com/i/139108917/golden-angle
coord *= mat2(0.22252093, -0.97492791, 0.97492791,  0.22252093);</code></pre><p>This works well for simple cases, but since we are rotating the base coordinates, we lose the original orientation. If you want to add some parallax motion or directional lighting, you still need the original orientation.</p><p><a href="https://x.com/XorDev/status/1943406507913068700">Another method</a> is to save the orientation matrix separately so we can apply it when needed:</p><pre><code>//Starting orientation (no rotation)
mat2 orient = mat2(1.0);


//Inside the for loop:
orient *= gold;
vec2 p = coord * orient;</code></pre><p>This is the approach I will be using later for the parallax example.</p><p>Putting everything together, we&#8217;ll have the following:</p><pre><code>//Number of layers
#define LAYERS 5.0
//Overall scale
#define SCALE 0.1
//Overall brightness
#define BRIGHTNESS 0.04

//Toggle layer shift (shifting factor)
#define LAYER_SHIFT 2.618
//Toggle layer spacing (spacing factor)
#define LAYER_SCALE 0.6


//Output color for example
vec3 col = vec3(0.0);

//Starfield Coordinates
vec2 c = fragCoord / iResolution.y / SCALE;

//Loop through layers
for(float i = 0.5 / LAYERS; i&lt;1.0; i+=1.0 / LAYERS)
{
    //Rotate layer
    //Read about the Golden Angle here:
    //https://mini.gmshaders.com/i/139108917/golden-angle
    c *= mat2(0.22252093, -0.97492791, 0.97492791,  0.22252093);

    //Get rotated coordinates
    vec2 p = c;

    //Apply layer shifting
    p += LAYER_SHIFT*i;

    //Adjust layer scaling
    p /= 1.0 + LAYER_SCALE*i;

    //Distance to cell center
    float len = length(mod(p, 2.0) - 1.0);
    //Light attenuation with circle cutoff
    float att = max(1.0-len,0.0) / len;
    col += att;
}</code></pre><p>The <a href="https://x.com/XorDev/status/1770636566240674280">same concept</a> works in 3D, too, without extra steps! <a href="https://www.shadertoy.com/view/tX3GW2">Here&#8217;s my full ShaderToy demo</a> with some extra features</p><h1>Breaking Patterns</h1><p>Most of the patterns are not too obvious, but if you have an expensive shader and could only afford a couple of layers, you may have visible axis lines:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NviP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55438b0a-1329-48cd-a9e5-2e4c001727dc_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NviP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55438b0a-1329-48cd-a9e5-2e4c001727dc_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!NviP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55438b0a-1329-48cd-a9e5-2e4c001727dc_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!NviP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55438b0a-1329-48cd-a9e5-2e4c001727dc_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!NviP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55438b0a-1329-48cd-a9e5-2e4c001727dc_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NviP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55438b0a-1329-48cd-a9e5-2e4c001727dc_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/55438b0a-1329-48cd-a9e5-2e4c001727dc_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1486694,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/166992315?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55438b0a-1329-48cd-a9e5-2e4c001727dc_3840x2160.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!NviP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55438b0a-1329-48cd-a9e5-2e4c001727dc_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!NviP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55438b0a-1329-48cd-a9e5-2e4c001727dc_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!NviP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55438b0a-1329-48cd-a9e5-2e4c001727dc_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!NviP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55438b0a-1329-48cd-a9e5-2e4c001727dc_3840x2160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Grid lines are apparent with 2 layers</figcaption></figure></div><p>My quick and easy fix is to add some waves for warping</p><pre><code>//Warp the x and y axes with sine waves
p += LAYER_WAVES*sin(p.yx);</code></pre><p>Playing with the wave amplitude so you can see what it looks like on 1 and 2 layers:</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;037880df-385a-4c8a-8c97-9382a4c04ac0&quot;,&quot;duration&quot;:null}"></div><p>A wave amplitude of 1.0 is too much, but 0.2 seems to be a good balance between too much distortion and too much tiling. With just 2 layers, you really have to look for the patterns to find them!</p><p>There&#8217;s one other situational problem. If you zoom out, you&#8217;ll notice patterns, even with 5 layers. We could theoretically fix this by not zooming out or by adding more layers, but those are lazy solutions.</p><p>My preferred method is to cut random holes in the layers:</p>
      <p>
          <a href="https://mini.gmshaders.com/p/chaos">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Modeling the World in 280 Characters]]></title><description><![CDATA[An exploration of the mindset, methods, and motivations behind crafting tiny, expressive shaders that combine code, art, and constraint]]></description><link>https://mini.gmshaders.com/p/modeling-the-world</link><guid isPermaLink="false">https://mini.gmshaders.com/p/modeling-the-world</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Mon, 23 Jun 2025 14:43:16 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!-utU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a755880-28b6-46a2-9c9c-8c86921d9c8d_1200x900.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-utU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a755880-28b6-46a2-9c9c-8c86921d9c8d_1200x900.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-utU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a755880-28b6-46a2-9c9c-8c86921d9c8d_1200x900.jpeg 424w, https://substackcdn.com/image/fetch/$s_!-utU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a755880-28b6-46a2-9c9c-8c86921d9c8d_1200x900.jpeg 848w, https://substackcdn.com/image/fetch/$s_!-utU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a755880-28b6-46a2-9c9c-8c86921d9c8d_1200x900.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!-utU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a755880-28b6-46a2-9c9c-8c86921d9c8d_1200x900.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-utU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a755880-28b6-46a2-9c9c-8c86921d9c8d_1200x900.jpeg" width="1200" height="900" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1a755880-28b6-46a2-9c9c-8c86921d9c8d_1200x900.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:900,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-utU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a755880-28b6-46a2-9c9c-8c86921d9c8d_1200x900.jpeg 424w, https://substackcdn.com/image/fetch/$s_!-utU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a755880-28b6-46a2-9c9c-8c86921d9c8d_1200x900.jpeg 848w, https://substackcdn.com/image/fetch/$s_!-utU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a755880-28b6-46a2-9c9c-8c86921d9c8d_1200x900.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!-utU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a755880-28b6-46a2-9c9c-8c86921d9c8d_1200x900.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Various tiny shaders written by Xor</figcaption></figure></div><p>Hi, I&#8217;m Xor. As a graphics programmer, my job is essentially to make pixels prettier using math formulas. I work on video effects like lighting, reflections, post-processing, and more for games and animated backgrounds in software.</p><p>For fun, I like to unwind by writing compact little shader programs that fit in a &#8220;tweet&#8221; (280 characters or less). You may have seen some of these posted on X/Twitter. The process of shrinking code while maintaining its functionality is called &#8220;code golfing.&#8221;</p><p><a href="https://x.com/XorDev/status/1727908092304617661">Here</a>&#8217;s an animated galaxy I wrote in just 197 characters of GLSL code:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ammz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff578e189-be62-4135-9662-b21425f38154_891x1198.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ammz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff578e189-be62-4135-9662-b21425f38154_891x1198.png 424w, https://substackcdn.com/image/fetch/$s_!Ammz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff578e189-be62-4135-9662-b21425f38154_891x1198.png 848w, https://substackcdn.com/image/fetch/$s_!Ammz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff578e189-be62-4135-9662-b21425f38154_891x1198.png 1272w, https://substackcdn.com/image/fetch/$s_!Ammz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff578e189-be62-4135-9662-b21425f38154_891x1198.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ammz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff578e189-be62-4135-9662-b21425f38154_891x1198.png" width="728" height="978.8372615039282" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f578e189-be62-4135-9662-b21425f38154_891x1198.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:1198,&quot;width&quot;:891,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:920220,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/166599235?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff578e189-be62-4135-9662-b21425f38154_891x1198.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Ammz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff578e189-be62-4135-9662-b21425f38154_891x1198.png 424w, https://substackcdn.com/image/fetch/$s_!Ammz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff578e189-be62-4135-9662-b21425f38154_891x1198.png 848w, https://substackcdn.com/image/fetch/$s_!Ammz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff578e189-be62-4135-9662-b21425f38154_891x1198.png 1272w, https://substackcdn.com/image/fetch/$s_!Ammz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff578e189-be62-4135-9662-b21425f38154_891x1198.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This little piece of code runs in real time for every pixel on the screen and generates a unique output color using some fancy math and logic. I build these demos using a tool called <a href="https://twigl.app/">Twigl.app</a>, an online shader editor designed for sharing mini-shaders. It makes exporting videos super easy, and in its &#8220;geekiest&#8221; mode, it also takes care of the generic header code and shortens built-in variable names.</p><p>I even managed to fit a <a href="http://x.com/XorDev/status/1505308218863632384">voxel DDA raytracer</a> with edge detection into just 190 characters (<a href="https://x.com/XorDev/status/1932874688260194471">update: now 175</a>):</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3P62!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f05fa85-082e-4e5b-be69-a260ef729dfc_888x797.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3P62!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f05fa85-082e-4e5b-be69-a260ef729dfc_888x797.png 424w, https://substackcdn.com/image/fetch/$s_!3P62!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f05fa85-082e-4e5b-be69-a260ef729dfc_888x797.png 848w, https://substackcdn.com/image/fetch/$s_!3P62!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f05fa85-082e-4e5b-be69-a260ef729dfc_888x797.png 1272w, https://substackcdn.com/image/fetch/$s_!3P62!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f05fa85-082e-4e5b-be69-a260ef729dfc_888x797.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3P62!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f05fa85-082e-4e5b-be69-a260ef729dfc_888x797.png" width="888" height="797" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7f05fa85-082e-4e5b-be69-a260ef729dfc_888x797.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:797,&quot;width&quot;:888,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:864235,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/166599235?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f05fa85-082e-4e5b-be69-a260ef729dfc_888x797.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!3P62!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f05fa85-082e-4e5b-be69-a260ef729dfc_888x797.png 424w, https://substackcdn.com/image/fetch/$s_!3P62!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f05fa85-082e-4e5b-be69-a260ef729dfc_888x797.png 848w, https://substackcdn.com/image/fetch/$s_!3P62!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f05fa85-082e-4e5b-be69-a260ef729dfc_888x797.png 1272w, https://substackcdn.com/image/fetch/$s_!3P62!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f05fa85-082e-4e5b-be69-a260ef729dfc_888x797.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Today, I&#8217;d like to explain why I make these, share my creation process, and show you how you can try it yourself if you&#8217;re interested. Let&#8217;s start with the &#8220;why.&#8221;</p><h2><strong>Motivation</strong></h2><p>Why do I write these? Well, there are several factors. Since I like lists, I&#8217;ll go ahead and present them in order of relevance:</p><ul><li><p><strong>Curiosity and Passion</strong> : Sometimes I get struck by a new idea and just want to play around with it. I like Twigl because it helps lower my expectations and lets me start doodling. There&#8217;s less room for overplanning, and it&#8217;s super easy to jump in.</p></li><li><p><strong>Learning and Discovery</strong> : Working within constraints forces me to think through problems differently. By optimizing for code size, I often find ways to simplify or approximate. It doesn&#8217;t always lead to more performant code (but often it does) and I&#8217;ve learned how to squeeze the most out of every byte. Having very little code makes it easier to experiment with formulas and variations without getting overwhelmed.</p></li><li><p><strong>Challenge</strong> : Writing tiny code is both challenging and stimulating. It keeps my brain sharp, and I&#8217;m constantly developing new skills. It&#8217;s basically become a game for me. I&#8217;ve accidentally learned a ton of math while trying to solve these technical problems.</p></li><li><p><strong>Community</strong> : I&#8217;ve connected with so many interesting people through this process&#8212;artists, designers, math folks, game devs, engineers, tech enthusiasts, and more. Sharing my work has led to some exciting encounters. (More on some notable people later!)</p></li></ul><p>So, in short, it&#8217;s fun, thought-provoking, and engaging, and it&#8217;s a great way to spark interest in graphics programming. Now, what even is a shader?</p><h2><strong>Shader Introduction</strong></h2><p>In case you haven&#8217;t heard of shaders before, they are programs that run on the GPU (Graphics Processing Unit) instead of the CPU (Central Processing Unit). CPUs excel at complicated or branching operations, which are computed sequentially, one at a time (I&#8217;m simplifying here). GPUs are designed to process billions or trillions of predictable operations per second in parallel. This sounds like a lot, but a 4K screen at 60 frames per second outputs nearly 500M pixels per second. Each pixel could have 100s or 1,000s of operations, not to mention anything else the GPU might be used for.</p><p>There are several different types of shaders: vertex shaders, fragment shaders, compute shaders, and more, but these tweet shaders are specifically fragment shaders, also known as &#8220;pixel shaders,&#8221; because they run on every pixel. In essence, fragment shaders take the input fragment coordinates and output a color and opacity (or alpha). Fragment coordinates give you the position of the center of each pixel on screen, so (0.5, 0.5) is the bottom-left (or top-left). One pixel to the right is (1.5, 0.5), and so on to (width &#8211; 0.5, height &#8211; 0.5). The coordinates variable is called &#8220;FC&#8221; in Twigl. The output color, &#8220;o&#8221;, has 4 RGBA components: red, green, blue, and alpha, each ranging from 0.0 to 1.0.</p><p><code>(1.0, 1.0, 1.0, 1.0)</code> is pure white, <code>(0.0, 0.0, 0.0, 1.0)</code> is opaque black, and <code>(1.0, 0.0, 0.0, 1.0)</code> is pure red in the RGBA color format. From here, you can already make simple color gradients:</p><p><code>o = vec4(0.0, FC.y/100.0, 0.0, 1.0)</code> ;</p><p>Remember, this is run on every pixel, so each pixel will have a unique Fragment Coordinate. That formula makes a simple gradient that starts black at the bottom of the screen (FC.y = 0.0), and the green output value reaches 1.0 when FC.y reaches 100.0.</p><p>So you have an output color &#8220;o&#8221;, the input fragment coordinates &#8220;FC&#8221;, and four &#8220;uniform&#8221; inputs which are shared among all pixels: &#8220;r&#8221; is the shader screen resolution in pixels, &#8220;t&#8221; is the time in seconds, and also the less commonly used mouse position &#8220;m&#8221; and the backbuffer texture &#8220;b&#8221;. And that&#8217;s the core of it! From there, it&#8217;s a lot of math and logic to control the output colors and generate cool images.</p><p>I&#8217;m going to skip ahead a bit, but if you&#8217;re interested in learning more, try <a href="https://thebookofshaders.com/">starting here</a>!</p><h1>Codrops</h1><p>This article was written for Codrops. Please read the rest of the <a href="https://tympanus.net/codrops/2025/06/23/modeling-the-world-in-280-characters/">article over there</a>, where I describe my process, code golfing, Q&amp;A, and my story.<br>Thanks for reading!</p>]]></content:encoded></item><item><title><![CDATA[Functions: Tanh]]></title><description><![CDATA[An introduction to the hyperbolic tangent function and how to use it]]></description><link>https://mini.gmshaders.com/p/func-tanh</link><guid isPermaLink="false">https://mini.gmshaders.com/p/func-tanh</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Sat, 31 May 2025 01:15:02 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/ed1f5981-8201-448a-8429-37e97d5e6bb8_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome back!</p><p><a href="https://mini.gmshaders.com/p/design-choices">Mini tutorials</a> are back! These will cover a wide range of topics in bite-sized pieces, and I&#8217;ll start with some GLSL functions. This series will explain the math behind the functions and how they can be used.</p><p>Today is all about the Hyperbolic Tangent. &#8220;<code>tanh</code>&#8221;, which you may have seen cropping up in several of <a href="https://x.com/XorDev/status/1928504290290635042">my tweet shaders</a> lately. We&#8217;ll cover the math background, how to write your own, its uses, alternatives, and performance considerations.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!co1s!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf6a083b-1113-4d94-9aaf-8f1cdbd423f6_1600x882.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!co1s!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf6a083b-1113-4d94-9aaf-8f1cdbd423f6_1600x882.jpeg 424w, https://substackcdn.com/image/fetch/$s_!co1s!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf6a083b-1113-4d94-9aaf-8f1cdbd423f6_1600x882.jpeg 848w, https://substackcdn.com/image/fetch/$s_!co1s!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf6a083b-1113-4d94-9aaf-8f1cdbd423f6_1600x882.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!co1s!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf6a083b-1113-4d94-9aaf-8f1cdbd423f6_1600x882.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!co1s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf6a083b-1113-4d94-9aaf-8f1cdbd423f6_1600x882.jpeg" width="1456" height="803" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/af6a083b-1113-4d94-9aaf-8f1cdbd423f6_1600x882.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:803,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Image&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Image" title="Image" srcset="https://substackcdn.com/image/fetch/$s_!co1s!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf6a083b-1113-4d94-9aaf-8f1cdbd423f6_1600x882.jpeg 424w, https://substackcdn.com/image/fetch/$s_!co1s!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf6a083b-1113-4d94-9aaf-8f1cdbd423f6_1600x882.jpeg 848w, https://substackcdn.com/image/fetch/$s_!co1s!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf6a083b-1113-4d94-9aaf-8f1cdbd423f6_1600x882.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!co1s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf6a083b-1113-4d94-9aaf-8f1cdbd423f6_1600x882.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The graph of the <code>tanh</code>(x)</figcaption></figure></div><h1>Uses</h1><p>The <a href="https://mini.gmshaders.com/p/readingmath">domain</a> of <code>tanh</code>(x) is (-&#8734;, +&#8734;) and the range is (-1, +1). This means you can input any value (between negative infinity and positive infinity), and it will map to a value between -1 and +1. Large values of &#8220;x&#8221; quickly approach an output of 1.0, and negatives approach -1.0, so this function allows you to take values from an unpredictable range and smoothly map them to the normalized (-1, +1) range. Tanh can be thought of as an unbounded <a href="https://gmshaders.com/glossary/?load=smoothstep">smoothstep</a> function (I&#8217;ll have to cover that function later). It smoothly starts and stops without definite endpoints.<br>From here, you can shift (by adding to the input), scale the smoothness of the curve by multiplying the input and the spread of the output, by multiplying the function.</p><p>Here are some of the places where it is often used.</p><ul><li><p><strong>Smooth blending</strong>: If you need to blend between colors without a definite starting or ending point, you can use tanh! For example, you might use:<br><code>mix(col_a, col_b, tanh(x * SPREAD) * 0.5 + 0.5)<br></code>It could also be used for smoothly blending between two shapes or starting and stopping an animation, for example. Anything that needs to be blended without definite endpoints could use tanh!</p></li><li><p><strong>Tonemapping</strong>: I often use tanh as a way of normalizing my color values in my tweet shaders. It&#8217;s a conveniently short tonemapping function that is easy to plug in without altering the code much. It&#8217;s not the <a href="https://mini.gmshaders.com/p/tonemaps">best out there</a>, but it&#8217;s a pretty decent option and relatively cheap.</p></li><li><p><strong>Debugging</strong>: I like to use tanh to visualize variables because it works with any domain and brings it to a predictable range. If I seem to be getting values outside of the expected range, I can use tanh to see what these values are and still spot relative differences (higher values still have brighter outputs)</p></li><li><p><strong>Neural Network activations</strong>: tanh is sometimes used for bounded outputs in Generative Adversarial Networks activation functions, allowing for weights of any domain to be compressed to a usable range.</p></li></ul><p><code>tanh</code>(x) was not implemented until GLSL 1.30, so in GameMaker and WebGL 1.0, you&#8217;ll need to implement this manually. I&#8217;ll write how to implement this yourself and about more alternative functions later.</p><h1>Math Background</h1><p>Let&#8217;s look at 3 different mathematical ways to interpret the hyperbolic tangent to give a wider context for a variety of readers. Don&#8217;t worry if you&#8217;re unfamiliar with some of the terms here! It&#8217;s still quite useful to understand how this relates to other math concepts, and you can let the details fill in naturally over time later.</p><h3>Sigmoid Curve</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!whJK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F659d080d-ac32-43ef-865e-18c174a03625_1920x1280.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!whJK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F659d080d-ac32-43ef-865e-18c174a03625_1920x1280.png 424w, https://substackcdn.com/image/fetch/$s_!whJK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F659d080d-ac32-43ef-865e-18c174a03625_1920x1280.png 848w, https://substackcdn.com/image/fetch/$s_!whJK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F659d080d-ac32-43ef-865e-18c174a03625_1920x1280.png 1272w, https://substackcdn.com/image/fetch/$s_!whJK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F659d080d-ac32-43ef-865e-18c174a03625_1920x1280.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!whJK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F659d080d-ac32-43ef-865e-18c174a03625_1920x1280.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/659d080d-ac32-43ef-865e-18c174a03625_1920x1280.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:419457,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/164275069?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F659d080d-ac32-43ef-865e-18c174a03625_1920x1280.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!whJK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F659d080d-ac32-43ef-865e-18c174a03625_1920x1280.png 424w, https://substackcdn.com/image/fetch/$s_!whJK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F659d080d-ac32-43ef-865e-18c174a03625_1920x1280.png 848w, https://substackcdn.com/image/fetch/$s_!whJK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F659d080d-ac32-43ef-865e-18c174a03625_1920x1280.png 1272w, https://substackcdn.com/image/fetch/$s_!whJK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F659d080d-ac32-43ef-865e-18c174a03625_1920x1280.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">A symmetrical &#8220;S&#8221; curve</figcaption></figure></div><p>A sigmoid is a symmetrical &#8220;S&#8221; curve. <a href="https://en.wikipedia.org/wiki/Sigmoid_function#Examples">There are several</a> types of sigmoid functions with different ranges. Some range from 0 to 1, while others range from -1 to +1, but they are all symmetrical (rotating 180&#176; around the center point). <code>tanh</code>(x) is a common sigmoid that is supported in many programming languages, optimized and convenient with the [-1, +1] range.</p><h3>Trigonometry</h3><p>The hyperbolic tangent can be understood as an x / y ratio similar to the regular tangent function. With <code>tan</code>(&#920;), you are finding the x / y ratio (or slope) of a point on a unit circle. x = <code>cos</code>(&#920;) and y = <code>sin</code>(&#920;), so the tangent, ratio is cos(&#920;) / sin(&#920;).<br>The hyperbolic tangent is the same, but mapped to a <a href="https://en.wikipedia.org/wiki/Unit_hyperbola">unit hyperbola</a> instead of a circle:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HzXG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a037cac-ad2f-4c67-8d40-a25d158e70de_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HzXG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a037cac-ad2f-4c67-8d40-a25d158e70de_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!HzXG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a037cac-ad2f-4c67-8d40-a25d158e70de_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!HzXG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a037cac-ad2f-4c67-8d40-a25d158e70de_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!HzXG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a037cac-ad2f-4c67-8d40-a25d158e70de_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HzXG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a037cac-ad2f-4c67-8d40-a25d158e70de_1024x1024.png" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6a037cac-ad2f-4c67-8d40-a25d158e70de_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:176901,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/164275069?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a037cac-ad2f-4c67-8d40-a25d158e70de_1024x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HzXG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a037cac-ad2f-4c67-8d40-a25d158e70de_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!HzXG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a037cac-ad2f-4c67-8d40-a25d158e70de_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!HzXG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a037cac-ad2f-4c67-8d40-a25d158e70de_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!HzXG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a037cac-ad2f-4c67-8d40-a25d158e70de_1024x1024.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Unit Hyperbola illustration from <a href="https://en.wikipedia.org/wiki/Unit_hyperbola">Wikipedia</a></figcaption></figure></div><p>Intuitively, there are hyperbolic sin and cos functions: <code>sinh</code>, <code>cosh</code>:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;tanh(x) = \\frac{sinh(x)}{cosh(y)}&quot;,&quot;id&quot;:&quot;XUYKHTXESG&quot;}" data-component-name="LatexBlockToDOM"></div><p>You might be asking, What is a <a href="https://en.wikipedia.org/wiki/Hyperbola">hyperbola</a>? The short answer is that it is the intersection slice of a double cone. Here&#8217;s an illustration of what I mean:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MACj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7442d57b-9764-40c1-ac2a-39fcdf690eb8_800x1258.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MACj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7442d57b-9764-40c1-ac2a-39fcdf690eb8_800x1258.png 424w, https://substackcdn.com/image/fetch/$s_!MACj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7442d57b-9764-40c1-ac2a-39fcdf690eb8_800x1258.png 848w, https://substackcdn.com/image/fetch/$s_!MACj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7442d57b-9764-40c1-ac2a-39fcdf690eb8_800x1258.png 1272w, https://substackcdn.com/image/fetch/$s_!MACj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7442d57b-9764-40c1-ac2a-39fcdf690eb8_800x1258.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MACj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7442d57b-9764-40c1-ac2a-39fcdf690eb8_800x1258.png" width="800" height="1258" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7442d57b-9764-40c1-ac2a-39fcdf690eb8_800x1258.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1258,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:190665,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/164275069?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7442d57b-9764-40c1-ac2a-39fcdf690eb8_800x1258.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!MACj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7442d57b-9764-40c1-ac2a-39fcdf690eb8_800x1258.png 424w, https://substackcdn.com/image/fetch/$s_!MACj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7442d57b-9764-40c1-ac2a-39fcdf690eb8_800x1258.png 848w, https://substackcdn.com/image/fetch/$s_!MACj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7442d57b-9764-40c1-ac2a-39fcdf690eb8_800x1258.png 1272w, https://substackcdn.com/image/fetch/$s_!MACj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7442d57b-9764-40c1-ac2a-39fcdf690eb8_800x1258.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Slices from a double cone. Illustration sourced from <a href="https://en.wikipedia.org/wiki/Hyperbola">Wikipedia.com</a></figcaption></figure></div><p>The mathematical rabbit hole runs deep here, so I&#8217;ll leave the rest for you to explore if you feel compelled to do so. Let&#8217;s look at one more approach!</p><h3>Exponentials</h3><p>We can also interpret it with exponential curves! The difference of positive and negative exponentials divided by the sum of positive and negative exponentials:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;tanh(x)=\\frac{e^{x}-e^{-x}}{e^{x}+e^{-x}}&quot;,&quot;id&quot;:&quot;MJEVQUCTNC&quot;}" data-component-name="LatexBlockToDOM"></div><p>When fully reduced, it can be computed and implemented with just one exponential:</p><pre><code><code>//Hyperbolic Tangent
float tanh(float x)
{
    float exp_neg_2x = exp(-2.0 * x);
    return -1.0 + 2.0 / (1.0 + exp_neg_2x);
}</code></code></pre><p>This is how it can be implemented in older versions of GLSL or languages where the tanh function is not implemented. Next, let&#8217;s compare this to some other options.</p><h1>Alternatives</h1><p>This section and the next (on performance) will be reserved for my paid supporters!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://mini.gmshaders.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you&#8217;d like to help support my work, please    consider upgrading your subscription!      Thank you!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>
      <p>
          <a href="https://mini.gmshaders.com/p/func-tanh">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Mini: Design Choices]]></title><description><![CDATA[The guidelines I use to visually improve my shader art]]></description><link>https://mini.gmshaders.com/p/design-choices</link><guid isPermaLink="false">https://mini.gmshaders.com/p/design-choices</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Mon, 19 May 2025 21:28:30 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/f3b1fef1-d149-43fa-8712-a44913dde55d_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi again,<br><br>Before we begin, I want to give a quick newsletter update. I have been getting carried away writing larger tutorials, which take more time for research and writing. I will continue to write them, but I am reintroducing mini tutorials to fill in some of the gaps. Mini tutorials are designed to tackle smaller topics and be easily digestible.<br><br>Let&#8217;s kick it off with the design concepts I use to make <a href="https://www.shadertoy.com/user/Xor/sort=love">my shader art</a> more visually appealing. I am not the best artist, but <a href="https://www.shadertoy.com/view/Mts3D2">I have learned a ton</a> over the years, so I&#8217;ll share some pointers that may save you lots of time!</p><p>I previously wrote about &#8220;<a href="https://mini.gmshaders.com/p/visdev">Visual Development</a>&#8221;, which covered some concepts for communicating through design in video games. This one will be focusing on stand-alone shader art and suggestions with composition, lighting, coloring, and texturing. As with all art things, take this with a grain of salt. These are not rules, but merely guidelines, and there will be some exceptions to everything I cover. Use this as a tool to think through the major design elements to ensure you aren&#8217;t ignoring anything.</p><h1>1 - Composition</h1><p>In my opinion, the most important design consideration is the composition. I&#8217;m talking about where the viewer's focus is. The placement and size of these focal points. Look at these 3 examples with a <a href="https://x.com/XorDev/status/1922291638279270581">fancy sphere</a>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CZWj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5101beb-c3c1-43bd-b642-d7ac8bea1153_1155x2145.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CZWj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5101beb-c3c1-43bd-b642-d7ac8bea1153_1155x2145.png 424w, https://substackcdn.com/image/fetch/$s_!CZWj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5101beb-c3c1-43bd-b642-d7ac8bea1153_1155x2145.png 848w, https://substackcdn.com/image/fetch/$s_!CZWj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5101beb-c3c1-43bd-b642-d7ac8bea1153_1155x2145.png 1272w, https://substackcdn.com/image/fetch/$s_!CZWj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5101beb-c3c1-43bd-b642-d7ac8bea1153_1155x2145.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CZWj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5101beb-c3c1-43bd-b642-d7ac8bea1153_1155x2145.png" width="1155" height="2145" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c5101beb-c3c1-43bd-b642-d7ac8bea1153_1155x2145.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:2145,&quot;width&quot;:1155,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1554713,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/162897778?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5101beb-c3c1-43bd-b642-d7ac8bea1153_1155x2145.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CZWj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5101beb-c3c1-43bd-b642-d7ac8bea1153_1155x2145.png 424w, https://substackcdn.com/image/fetch/$s_!CZWj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5101beb-c3c1-43bd-b642-d7ac8bea1153_1155x2145.png 848w, https://substackcdn.com/image/fetch/$s_!CZWj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5101beb-c3c1-43bd-b642-d7ac8bea1153_1155x2145.png 1272w, https://substackcdn.com/image/fetch/$s_!CZWj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5101beb-c3c1-43bd-b642-d7ac8bea1153_1155x2145.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The first one is just awkward. It&#8217;s partially cropped, but also leaves a lot of empty space on the right. It feels out of balance. If there was something to look at on the other side, it might be acceptable, but for a single abstract object, on an empty scene, it&#8217;s not pretty.<br>The second one fills the screen pretty well and is horizontally centered. It&#8217;s out of balance vertically, with details below and empty above, but it feels intentional. It could work well as a slow, animated background.</p><p>Finally, the third is simple, perfectly balanced, and focused dead center. I think this works best for more complex shapes, fast animations, or with simple backgrounds. It immediately communicates where the focus should be. Our brains want to see the most flashy, vibrant, or fast things because they provide us with the most information, and we don&#8217;t like it when we cannot fully see it.</p><p>So the key points to consider are the <strong>focus points</strong> and <strong>composition balance</strong>. Aim to keep the main focus areas roughly in balance or clearly, intentionally out of balance (like the second example). Subtle, slower, and more immersive shaders can get away with showing less or having a moving focus (more on that later). Complex or animated objects should generally be fully in view. In more organic/fluid scenes, a Rule of Thirds might work better, but in any case, think about what is visible, the negative space, and what is being cropped out. Be intentional with the composition!</p><h1>2 - Lighting</h1><p>The next most important consideration, in my opinion, is the lighting, black/white level, and contrast. It&#8217;s a good idea to look at your shader under a histogram, if possible. I use GIMP and this gives a sense of the range of the value (&#8220;lightness&#8221;), in your shader:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ebWT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdac58334-bd43-4fd7-b197-a73386912cc4_1581x714.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ebWT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdac58334-bd43-4fd7-b197-a73386912cc4_1581x714.png 424w, https://substackcdn.com/image/fetch/$s_!ebWT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdac58334-bd43-4fd7-b197-a73386912cc4_1581x714.png 848w, https://substackcdn.com/image/fetch/$s_!ebWT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdac58334-bd43-4fd7-b197-a73386912cc4_1581x714.png 1272w, https://substackcdn.com/image/fetch/$s_!ebWT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdac58334-bd43-4fd7-b197-a73386912cc4_1581x714.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ebWT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdac58334-bd43-4fd7-b197-a73386912cc4_1581x714.png" width="1456" height="658" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dac58334-bd43-4fd7-b197-a73386912cc4_1581x714.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:658,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1366389,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/162897778?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdac58334-bd43-4fd7-b197-a73386912cc4_1581x714.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ebWT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdac58334-bd43-4fd7-b197-a73386912cc4_1581x714.png 424w, https://substackcdn.com/image/fetch/$s_!ebWT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdac58334-bd43-4fd7-b197-a73386912cc4_1581x714.png 848w, https://substackcdn.com/image/fetch/$s_!ebWT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdac58334-bd43-4fd7-b197-a73386912cc4_1581x714.png 1272w, https://substackcdn.com/image/fetch/$s_!ebWT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdac58334-bd43-4fd7-b197-a73386912cc4_1581x714.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Histogram of my &#8220;<a href="https://www.shadertoy.com/view/tXlXDX">Ghosts</a>&#8221; shader</figcaption></figure></div><p>You generally want to spread across the entire range of lightness from black to white, since using the full spectrum gives you the richest contrast. Typically, I aim to spread across most of the spectrum, and then if I need the image brighter or darker, I adjust the lightness distribution with <a href="https://mini.gmshaders.com/p/gamma">Gamma</a>. Squaring the color makes it darker and more vibrant. The sqrt of a color will make it whiter and softer.</p><p>If you&#8217;re dealing with anything bright, I highly recommend tonemapping, which helps prevent overexposure and washed-out colors. I wrote about that in detail here:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;0ac20b8d-8d29-41c1-b8c8-1f54cfcce7b3&quot;,&quot;caption&quot;:&quot;How to conquer washed out lights and ugly color banding&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;GM Shaders Mini: Tonemaps&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:115837202,&quot;name&quot;:&quot;Xor&quot;,&quot;bio&quot;:&quot;Shaders and GameMaker&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b37ca2-b4f8-4d1e-a083-93ad73418f1f_512x512.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:100}],&quot;post_date&quot;:&quot;2023-10-29T03:48:58.097Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7efedbd0-5dab-4627-b839-3bbbe369b816_1920x1080.jpeg&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://mini.gmshaders.com/p/tonemaps&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:138355128,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:5,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;GM Shaders&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf69737-7d50-4dc8-9cce-aad9b9019215_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h1>3 - Colors</h1><p>My third consideration is coloring: saturation, color temperatures, hues, and palettes.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mmob!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65aea85e-501c-4b04-9685-59af8d626401_1155x716.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mmob!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65aea85e-501c-4b04-9685-59af8d626401_1155x716.png 424w, https://substackcdn.com/image/fetch/$s_!mmob!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65aea85e-501c-4b04-9685-59af8d626401_1155x716.png 848w, https://substackcdn.com/image/fetch/$s_!mmob!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65aea85e-501c-4b04-9685-59af8d626401_1155x716.png 1272w, https://substackcdn.com/image/fetch/$s_!mmob!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65aea85e-501c-4b04-9685-59af8d626401_1155x716.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mmob!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65aea85e-501c-4b04-9685-59af8d626401_1155x716.png" width="1155" height="716" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/65aea85e-501c-4b04-9685-59af8d626401_1155x716.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:716,&quot;width&quot;:1155,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!mmob!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65aea85e-501c-4b04-9685-59af8d626401_1155x716.png 424w, https://substackcdn.com/image/fetch/$s_!mmob!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65aea85e-501c-4b04-9685-59af8d626401_1155x716.png 848w, https://substackcdn.com/image/fetch/$s_!mmob!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65aea85e-501c-4b04-9685-59af8d626401_1155x716.png 1272w, https://substackcdn.com/image/fetch/$s_!mmob!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F65aea85e-501c-4b04-9685-59af8d626401_1155x716.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Same shape with various color palettes</figcaption></figure></div><p>I am a big fan of color, and I tend to prefer vibrant shaders, but not every piece of art should be juicy. <a href="https://www.shadertoy.com/view/cdG3Wd">Grayscale works well for industrial styles</a>, perhaps with a touch of <a href="https://mini.gmshaders.com/p/gm-shaders-mini-chromatic-aberration">chromatic aberration</a>. Vibrant colors work well for abstract, psychedelic styles. Saturation is a good way to convey energy and use color temperature intentionally!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!91fO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e753154-b5fb-49b6-a00f-57c509343ea9_2160x720.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!91fO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e753154-b5fb-49b6-a00f-57c509343ea9_2160x720.png 424w, https://substackcdn.com/image/fetch/$s_!91fO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e753154-b5fb-49b6-a00f-57c509343ea9_2160x720.png 848w, https://substackcdn.com/image/fetch/$s_!91fO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e753154-b5fb-49b6-a00f-57c509343ea9_2160x720.png 1272w, https://substackcdn.com/image/fetch/$s_!91fO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e753154-b5fb-49b6-a00f-57c509343ea9_2160x720.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!91fO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e753154-b5fb-49b6-a00f-57c509343ea9_2160x720.png" width="1456" height="485" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0e753154-b5fb-49b6-a00f-57c509343ea9_2160x720.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:485,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2424699,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/162897778?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e753154-b5fb-49b6-a00f-57c509343ea9_2160x720.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!91fO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e753154-b5fb-49b6-a00f-57c509343ea9_2160x720.png 424w, https://substackcdn.com/image/fetch/$s_!91fO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e753154-b5fb-49b6-a00f-57c509343ea9_2160x720.png 848w, https://substackcdn.com/image/fetch/$s_!91fO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e753154-b5fb-49b6-a00f-57c509343ea9_2160x720.png 1272w, https://substackcdn.com/image/fetch/$s_!91fO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e753154-b5fb-49b6-a00f-57c509343ea9_2160x720.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><a href="https://www.shadertoy.com/view/l3cfW4">Red hot fire</a>, mixed temperature &#8220;<a href="https://www.shadertoy.com/view/WfS3Dd">Plasma</a>&#8221;, and cold &#8220;<a href="https://www.shadertoy.com/view/DlySDD">Nebula</a>&#8221;</figcaption></figure></div><p>For color palettes, I like to use sine waves with separate phases for the RGB color channels. <a href="https://www.shadertoy.com/view/ll2GD30">Iq has a nice example of the various palettes</a> you can make with this.</p><p>My personal favorites are:</p><pre><code>//Fun palletes to try
//Hue is in radians so the range is [0, 6.2831]
vec3 temperature1 = 0.5 + 0.5 * cos(hue + vec3(0.0, 0.5, 1.0));
vec3 temperature2 = 0.5 + 0.5 * cos(hue + vec3(6.0, 1.0, 2.0));
vec3 rainbow1 = 0.5 + 0.5 * cos(hue + vec3(0.0, 2.0, 4.0));
vec3 rainbow2 = 0.5 + 0.5 * cos(hue + vec3(0.0, 1.0, 3.0));</code></pre><p>Here&#8217;s what these palettes look like:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mrQo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eaf16c5-1f38-4d5d-8da1-c2a2d2939acd_1155x715.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mrQo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eaf16c5-1f38-4d5d-8da1-c2a2d2939acd_1155x715.png 424w, https://substackcdn.com/image/fetch/$s_!mrQo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eaf16c5-1f38-4d5d-8da1-c2a2d2939acd_1155x715.png 848w, https://substackcdn.com/image/fetch/$s_!mrQo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eaf16c5-1f38-4d5d-8da1-c2a2d2939acd_1155x715.png 1272w, https://substackcdn.com/image/fetch/$s_!mrQo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eaf16c5-1f38-4d5d-8da1-c2a2d2939acd_1155x715.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mrQo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eaf16c5-1f38-4d5d-8da1-c2a2d2939acd_1155x715.png" width="1155" height="715" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7eaf16c5-1f38-4d5d-8da1-c2a2d2939acd_1155x715.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:715,&quot;width&quot;:1155,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!mrQo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eaf16c5-1f38-4d5d-8da1-c2a2d2939acd_1155x715.png 424w, https://substackcdn.com/image/fetch/$s_!mrQo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eaf16c5-1f38-4d5d-8da1-c2a2d2939acd_1155x715.png 848w, https://substackcdn.com/image/fetch/$s_!mrQo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eaf16c5-1f38-4d5d-8da1-c2a2d2939acd_1155x715.png 1272w, https://substackcdn.com/image/fetch/$s_!mrQo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eaf16c5-1f38-4d5d-8da1-c2a2d2939acd_1155x715.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Sine wave color functions</figcaption></figure></div><p>One more trick: color gradients. You can make shaders significantly more interesting by adding a gradient to the image&#8217;s hue or saturation. Here&#8217;s an example with a red radial gradient overlayed on an unsaturated scene:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VKv2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151c5f0b-c4fd-4f67-87d6-9763018e226e_1920x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VKv2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151c5f0b-c4fd-4f67-87d6-9763018e226e_1920x2160.png 424w, https://substackcdn.com/image/fetch/$s_!VKv2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151c5f0b-c4fd-4f67-87d6-9763018e226e_1920x2160.png 848w, https://substackcdn.com/image/fetch/$s_!VKv2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151c5f0b-c4fd-4f67-87d6-9763018e226e_1920x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!VKv2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151c5f0b-c4fd-4f67-87d6-9763018e226e_1920x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VKv2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151c5f0b-c4fd-4f67-87d6-9763018e226e_1920x2160.png" width="1456" height="1638" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/151c5f0b-c4fd-4f67-87d6-9763018e226e_1920x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1638,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:7557404,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/162897778?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151c5f0b-c4fd-4f67-87d6-9763018e226e_1920x2160.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!VKv2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151c5f0b-c4fd-4f67-87d6-9763018e226e_1920x2160.png 424w, https://substackcdn.com/image/fetch/$s_!VKv2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151c5f0b-c4fd-4f67-87d6-9763018e226e_1920x2160.png 848w, https://substackcdn.com/image/fetch/$s_!VKv2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151c5f0b-c4fd-4f67-87d6-9763018e226e_1920x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!VKv2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151c5f0b-c4fd-4f67-87d6-9763018e226e_1920x2160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Without and with an overlay gradient</figcaption></figure></div><p>In this case, I am <a href="https://mini.gmshaders.com/p/mini-blendmodes">screen blending</a> a red on top of the scene. You could also use a depth gradient for a fog-like effect. Get creative and play around with different positions, shapes, sizes, and colors. I tend to prefer red-blue diagonal gradients, but maybe you&#8217;ll find something cooler!</p><h1>4 - Textures</h1><p>Pay attention to details at various scales:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7-LH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25dfe8e0-3139-4754-8c02-e68b1bf65c19_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7-LH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25dfe8e0-3139-4754-8c02-e68b1bf65c19_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!7-LH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25dfe8e0-3139-4754-8c02-e68b1bf65c19_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!7-LH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25dfe8e0-3139-4754-8c02-e68b1bf65c19_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!7-LH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25dfe8e0-3139-4754-8c02-e68b1bf65c19_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7-LH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25dfe8e0-3139-4754-8c02-e68b1bf65c19_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/25dfe8e0-3139-4754-8c02-e68b1bf65c19_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7-LH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25dfe8e0-3139-4754-8c02-e68b1bf65c19_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!7-LH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25dfe8e0-3139-4754-8c02-e68b1bf65c19_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!7-LH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25dfe8e0-3139-4754-8c02-e68b1bf65c19_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!7-LH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25dfe8e0-3139-4754-8c02-e68b1bf65c19_3840x2160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Homogenous detail versus fractal detailing</figcaption></figure></div><p>Our brains like fractals, perhaps because they tickle our brains at multiple levels. It&#8217;s certainly more interesting to look at an image that has both large-scale texturing and fine texturing. This can be achieved with <a href="https://mini.gmshaders.com/p/noise2">fractal noise</a>, <a href="https://mini.gmshaders.com/p/turbulence">turbulence</a>, or <a href="https://mini.gmshaders.com/p/gm-shaders-mini-fractal-texturing-1408552">fractal texturing</a>.</p><p>In any case, it&#8217;s a good idea to take a step back and look up closely. If it&#8217;s interesting in those cases, you&#8217;re probably on to something! Again, this is highly subjective and won&#8217;t be important for all types of art, but it&#8217;s worth considering!</p><h1>5 - Motion</h1><p>And finally, look at the motion: speed, rotation, shape shifting, color shifting, etc. <a href="https://www.shadertoy.com/view/wXjSRt">Here&#8217;s my sunset shader</a> at two vastly different speeds:</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;5efe26a8-3b3c-466b-b7b6-cc4e47440301&quot;,&quot;duration&quot;:null}"></div><p>I put this one last because I think it is probably the most subjective consideration, but still worth discussing. I love slower animations. Sometimes, graphics people animate too quickly (<em>in my opinion</em>) and start to show patterns in the animation. With the cloud example above, I think the extra speed reveals how artificial the motion actually is.</p><p>Try your shader 50% faster and 50% slower and see which you prefer. Background shaders should generally be slower, abstract shaders faster, and glitch/strobe shaders may need to be super fast. Just like the fractal texturing concept, you can also layer motion at multiple time scales. For example, colors could shift slowly while shapes may shift more rapidly. In 3D contexts, you may have a scrolling or twisting camera. It&#8217;s far more likely that you will make the camera go too fast than too slow, but again, play around with various speeds and see what works best.</p><h1>Conclusion</h1><p>Okay, that covers all the design decisions that I feel are most important to consider when creating animated art. Let&#8217;s do a quick recap:<br></p><ol><li><p><strong>Composition</strong> - Generally, you want to keep the designs focused in the center and roughly balanced on all sides. If you&#8217;re going for an unbalanced look, make it clearly intentional and lean into it!</p></li><li><p><strong>Lighting</strong> - Generally, use bright lights and deep shadows. Strong contrast is a great way to make the richest display possible. Use the full range and tweak the gamma as needed. There are exceptions, but most art benefits from a wider range.</p></li><li><p><strong>Colors</strong> - Play with saturation, color temperature, and palettes. Limited color palettes are often better than using all the colors. Color gradients are a great way to add large-scale character to your art!</p></li><li><p><strong>Textures</strong> - Pay attention to both the fine details and the macro scale. It should look good at a glance and up close, if possible!</p></li><li><p><strong>Motion</strong> - Test your art at multiple speeds and see what looks best. Background scenes should be more subtle, and abstract scenes can be more intense. Most people add too much speed, in my opinion! It&#8217;s good to mix elements at various speeds!</p></li></ol><p>Hopefully, this gives you some ideas to try with your next piece. I would love to see what you guys come up with. Feel free to tag me!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://mini.gmshaders.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you found this useful, you should subscribe!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Have a great night! Thanks,<br>-Xor</p>]]></content:encoded></item><item><title><![CDATA[Common Shader Mistakes]]></title><description><![CDATA[Avoiding the most common problems in graphics programming]]></description><link>https://mini.gmshaders.com/p/mistakes</link><guid isPermaLink="false">https://mini.gmshaders.com/p/mistakes</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Sat, 26 Apr 2025 21:17:51 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/fa005426-1a58-445a-a908-065cd5e3ce1a_1920x1080.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Greetings!</p><p>Everyone knows that shaders are super easy to write and to debug, so this should be a quick tutorial. In case it isn&#8217;t clear, that was sarcasm. I&#8217;ve spent thousands of hours writing shaders, and I&#8217;ve gotten quite good at avoiding most of the pitfalls, but even so, I still occasionally get stuck. Today, I&#8217;d like to share some advice that could save you dozens of hours and headaches.</p><h1>Writing For Clarity</h1><p>The first step is to write <strong>cleaner code</strong>. <a href="https://x.com/XorDev/status/1915096542014300541">It might sound hypocritical coming from me</a>, but you can save a lot of time and headaches later by writing code that others, or future you, can read. <strong>Leave comments explaining each part,</strong> and <strong>name your variables clearly</strong> and consistently. This should help you avoid naming conflicts that shorter variable names might lead to (<em>Is &#8220;c&#8221; short for coordinates or color?</em>). If you copy-paste code from a tutorial, it&#8217;s good to practice adding comments line-by-line and make sure you understand each part, if possible.</p><p>Try eliminating magic numbers, either with consts, macros, or uniforms. One of the most common issues is a <strong>mismatch in uniform names</strong>. If you accidentally mistype a uniform name inside or outside the shader, it will default to 0.0 and cause unexpected issues. If your uniforms are broken, check this first!</p><h1>Colors</h1><p>Did the screen go black? Or maybe parts of the screen? Wherever this occurs, look for floats containing undefined, &#8220;<a href="https://en.wikipedia.org/wiki/NaN">Not-a-Number</a>&#8221; values. Once you have a NaN, it&#8217;s very hard to get rid of. Any operation on a non-number will result in a non-number answer and more blank pixels.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CrVy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eefeb44-eab4-42d6-adb1-278a4d1d30b0_1280x720.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CrVy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eefeb44-eab4-42d6-adb1-278a4d1d30b0_1280x720.jpeg 424w, https://substackcdn.com/image/fetch/$s_!CrVy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eefeb44-eab4-42d6-adb1-278a4d1d30b0_1280x720.jpeg 848w, https://substackcdn.com/image/fetch/$s_!CrVy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eefeb44-eab4-42d6-adb1-278a4d1d30b0_1280x720.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!CrVy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eefeb44-eab4-42d6-adb1-278a4d1d30b0_1280x720.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CrVy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eefeb44-eab4-42d6-adb1-278a4d1d30b0_1280x720.jpeg" width="1280" height="720" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7eefeb44-eab4-42d6-adb1-278a4d1d30b0_1280x720.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:720,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;eden on X: \&quot;this happened to me once while playing minecraft and it was  horrifying (crowded vc warning) https://t.co/QSZdowWh26\&quot; / X&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="eden on X: &quot;this happened to me once while playing minecraft and it was  horrifying (crowded vc warning) https://t.co/QSZdowWh26&quot; / X" title="eden on X: &quot;this happened to me once while playing minecraft and it was  horrifying (crowded vc warning) https://t.co/QSZdowWh26&quot; / X" srcset="https://substackcdn.com/image/fetch/$s_!CrVy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eefeb44-eab4-42d6-adb1-278a4d1d30b0_1280x720.jpeg 424w, https://substackcdn.com/image/fetch/$s_!CrVy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eefeb44-eab4-42d6-adb1-278a4d1d30b0_1280x720.jpeg 848w, https://substackcdn.com/image/fetch/$s_!CrVy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eefeb44-eab4-42d6-adb1-278a4d1d30b0_1280x720.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!CrVy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7eefeb44-eab4-42d6-adb1-278a4d1d30b0_1280x720.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">NaN propagation error in a Minecraft mod from <a href="https://x.com/sadlando/status/1836410258480136540">@sadlando</a></figcaption></figure></div><p>This can happen <strong>when &#8220;x&#8221; is negative</strong> in <code>sqrt(x)</code>, <code>log(x)</code>, <code>log2(x)</code> or <code>pow(x, y)<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>.</code> In such cases, you may need to use <code>max(x, 0.0)</code> or <code>abs(x)</code> to keep x from being negative. Same with inverse functions like &#8220;<code>acos(x)</code>&#8221; or &#8220;<code>asin(x)</code>&#8221;. When <strong>x is outside the expected range</strong> [-1, 1], you&#8217;ll get NaNs. It should be clamped in such cases. </p><p>Also, look out for <strong>dividing by zero</strong>. 0.0 / 0.0 will cause a NaN, and you should consider this possibility with any division.</p><p>While not game-breaking, you should also make sure you are handling Gamma correction properly. I won&#8217;t repeat myself here, but you can read my full write-up on the topic over in this tutorial.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;6f7eb61a-9220-48a1-8374-67a5c5ad1c5f&quot;,&quot;caption&quot;:&quot;Today, we&#8217;re tackling gamma correction inside shaders. Many have written about gamma correction, but I haven&#8217;t found any that satisfy my teaching goals, so I thought I&#8217;d write my own quick overview.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;GM Shaders: Gamma&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:115837202,&quot;name&quot;:&quot;Xor&quot;,&quot;bio&quot;:&quot;Shaders and GameMaker&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b37ca2-b4f8-4d1e-a083-93ad73418f1f_512x512.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-01-24T23:56:30.186Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/44da34e3-fd03-42f3-a623-569344f4a1f5_1920x1080.jpeg&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://mini.gmshaders.com/p/gamma&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:155022729,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:12,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;GM Shaders&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf69737-7d50-4dc8-9cce-aad9b9019215_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>When doing lighting, you should choose the right lighting falloff function, and while this may depend on the style, the inverse of the square distance is physically correct.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mpCD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e210113-4b25-4c55-ad52-6eb6b18c4fff_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mpCD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e210113-4b25-4c55-ad52-6eb6b18c4fff_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!mpCD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e210113-4b25-4c55-ad52-6eb6b18c4fff_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!mpCD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e210113-4b25-4c55-ad52-6eb6b18c4fff_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!mpCD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e210113-4b25-4c55-ad52-6eb6b18c4fff_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mpCD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e210113-4b25-4c55-ad52-6eb6b18c4fff_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3e210113-4b25-4c55-ad52-6eb6b18c4fff_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!mpCD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e210113-4b25-4c55-ad52-6eb6b18c4fff_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!mpCD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e210113-4b25-4c55-ad52-6eb6b18c4fff_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!mpCD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e210113-4b25-4c55-ad52-6eb6b18c4fff_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!mpCD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e210113-4b25-4c55-ad52-6eb6b18c4fff_3840x2160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">From my <a href="http://mini.gmshaders.com/p/gm-shaders-mini-lights">Lights tutorial</a></figcaption></figure></div><p>However, regardless of the falloff function, you may see some banding artifacts, which can compound, especially with Low-Dynamic-Range additive lights. This is where <strong><a href="https://mini.gmshaders.com/p/gm-shaders-mini-dither">dithering</a> can help!</strong></p><h1>Textures</h1><p>When it comes to textures, there are a lot of things that can go wrong. Texture page issues, coordinate issues, texture sizes, mipmap artifacts, and more.</p><p>To start, make sure that you understand how sprite drawing works. In a game engine like GameMaker, multiple sprites are put together on one texture called a &#8220;texture page&#8221; and when you draw a sprite, you&#8217;re just drawing part of a texture:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WjDF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85012cf5-88b0-4022-a475-e17524c5af02_1920x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WjDF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85012cf5-88b0-4022-a475-e17524c5af02_1920x1024.png 424w, https://substackcdn.com/image/fetch/$s_!WjDF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85012cf5-88b0-4022-a475-e17524c5af02_1920x1024.png 848w, https://substackcdn.com/image/fetch/$s_!WjDF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85012cf5-88b0-4022-a475-e17524c5af02_1920x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!WjDF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85012cf5-88b0-4022-a475-e17524c5af02_1920x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WjDF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85012cf5-88b0-4022-a475-e17524c5af02_1920x1024.png" width="1456" height="777" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/85012cf5-88b0-4022-a475-e17524c5af02_1920x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:777,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WjDF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85012cf5-88b0-4022-a475-e17524c5af02_1920x1024.png 424w, https://substackcdn.com/image/fetch/$s_!WjDF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85012cf5-88b0-4022-a475-e17524c5af02_1920x1024.png 848w, https://substackcdn.com/image/fetch/$s_!WjDF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85012cf5-88b0-4022-a475-e17524c5af02_1920x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!WjDF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85012cf5-88b0-4022-a475-e17524c5af02_1920x1024.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The chair sprite is grouped with other sprites</figcaption></figure></div><p>When you draw a sprite, it is actually just drawing a quad of part of the texture page. Texture coordinates range from 0 to 1 across the entire texture page, but individual sprites will only span part of the texture page:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pGF_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8dc7f6b9-e1fd-4a43-b104-086f75a71aed_640x640.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pGF_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8dc7f6b9-e1fd-4a43-b104-086f75a71aed_640x640.png 424w, https://substackcdn.com/image/fetch/$s_!pGF_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8dc7f6b9-e1fd-4a43-b104-086f75a71aed_640x640.png 848w, https://substackcdn.com/image/fetch/$s_!pGF_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8dc7f6b9-e1fd-4a43-b104-086f75a71aed_640x640.png 1272w, https://substackcdn.com/image/fetch/$s_!pGF_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8dc7f6b9-e1fd-4a43-b104-086f75a71aed_640x640.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pGF_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8dc7f6b9-e1fd-4a43-b104-086f75a71aed_640x640.png" width="640" height="640" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8dc7f6b9-e1fd-4a43-b104-086f75a71aed_640x640.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:640,&quot;width&quot;:640,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!pGF_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8dc7f6b9-e1fd-4a43-b104-086f75a71aed_640x640.png 424w, https://substackcdn.com/image/fetch/$s_!pGF_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8dc7f6b9-e1fd-4a43-b104-086f75a71aed_640x640.png 848w, https://substackcdn.com/image/fetch/$s_!pGF_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8dc7f6b9-e1fd-4a43-b104-086f75a71aed_640x640.png 1272w, https://substackcdn.com/image/fetch/$s_!pGF_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8dc7f6b9-e1fd-4a43-b104-086f75a71aed_640x640.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Some shaders are written assuming the texture coordinates will always range from 0 to 1. This may be the case if you specifically put the texture on its own texture page and the texture is not being padded. <strong>Some devices do not support non-power-of-two sizes</strong>, though, and will add padding around the texture, which may cause unexpected issues on those devices. </p><pre><code><code>//Remap textures coordinates to [0, 1] range
vec2 texcoord_normalize(vec2 coord, vec4 uvs)
{
     return (coord - uvs.xy) / uvs.zw;
}

//Remap textures from the [0, 1] range
vec2 texcoord_unnormalize(vec2 coord, vec4 uvs)
{
     return coord * uvs.zw + uvs.xy;
}</code></code></pre><p>I wrote more about how to manage texture pages <a href="https://mini.gmshaders.com/p/gm-shaders-mini-two-textures-1376349">here</a>! If you have issues with flipped textures or clamping/repeating textures, you should read that.</p><p>When it comes to <a href="https://mini.gmshaders.com/p/gm-shaders-mini-interpolation-1430549">texture interpolation</a>, the standard texture function <a href="https://iquilezles.org/articles/hwinterpolation">interpolates at a resolution of 256</a>. Meaning, interpolated textures only blend in 256 steps, which is enough for most cases, but may not be enough if you need to scale a texture up a lot.</p><p>If you enable mipmapping, you may notice 2x2 blocks of artifacts:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1WpZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53947063-52ab-4cee-a5be-32d48a0ec438_640x360.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1WpZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53947063-52ab-4cee-a5be-32d48a0ec438_640x360.png 424w, https://substackcdn.com/image/fetch/$s_!1WpZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53947063-52ab-4cee-a5be-32d48a0ec438_640x360.png 848w, https://substackcdn.com/image/fetch/$s_!1WpZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53947063-52ab-4cee-a5be-32d48a0ec438_640x360.png 1272w, https://substackcdn.com/image/fetch/$s_!1WpZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53947063-52ab-4cee-a5be-32d48a0ec438_640x360.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1WpZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53947063-52ab-4cee-a5be-32d48a0ec438_640x360.png" width="640" height="360" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/53947063-52ab-4cee-a5be-32d48a0ec438_640x360.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:360,&quot;width&quot;:640,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1WpZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53947063-52ab-4cee-a5be-32d48a0ec438_640x360.png 424w, https://substackcdn.com/image/fetch/$s_!1WpZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53947063-52ab-4cee-a5be-32d48a0ec438_640x360.png 848w, https://substackcdn.com/image/fetch/$s_!1WpZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53947063-52ab-4cee-a5be-32d48a0ec438_640x360.png 1272w, https://substackcdn.com/image/fetch/$s_!1WpZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53947063-52ab-4cee-a5be-32d48a0ec438_640x360.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">2x2 mipmap artifacts along coordinate discontinuities</figcaption></figure></div><p>This is because <a href="https://mini.gmshaders.com/p/mipmaps">mipmaps are computed in 2x2 chunks</a>. <strong>If there are sharp changes</strong> in the texture coordinates within a 2x2 chunk, it will use a lower LOD. You can address this by either disabling mipmapping, removing the discontinuities and sharp changes in the coordinates, or calculating the mipmap level manually. In GLSL 1.30 or later, you can use <code>textureGrad()</code> and compute the derivatives manually to get around this.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://mini.gmshaders.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hey, just a heads-up! This is the last time you can subscribe for $5/mo. The prices are going up to $8/mo soon, so if you&#8217;d like to join, now is the time. Thank you for your support!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h1>Coordinates</h1><p>Handling scaling, aspect ratio, and centering at any resolution can be challenging. I wrote specifically about managing this, here:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;2a85d24f-8ef5-4edb-ad9f-6d5dc4146cdf&quot;,&quot;caption&quot;:&quot;If ever tried to center your shader without stretching, you&#8217;ve probably seen these configurations. My goal for tonight is to go over a few common scaling arrangements so that you can understand them once and for all!,&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;GM Shaders Mini: Scaling&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:115837202,&quot;name&quot;:&quot;Xor&quot;,&quot;bio&quot;:&quot;Shaders and GameMaker&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b37ca2-b4f8-4d1e-a083-93ad73418f1f_512x512.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2023-06-02T20:41:48.489Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/67a928ef-31f0-453a-b281-47484767a9e2_1920x1080.jpeg&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://mini.gmshaders.com/p/gm-shaders-mini-scaling&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:125293776,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:7,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;GM Shaders&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf69737-7d50-4dc8-9cce-aad9b9019215_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>When it comes to 3D, you often have to convert between different coordinate systems, like model-space, world-space, and screenspace. <a href="https://mini.gmshaders.com/p/gm-shaders-mini-texels-and-pixels-1308242">In 2D, we still sometimes have to convert between texture space, world-space, and screenspace</a>.</p><p>This is not something that is super easy to summarize, but if you have issues with coordinate conversions, I wrote about this here:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;8c450687-5fd1-43c6-bbd1-736db30d062d&quot;,&quot;caption&quot;:&quot;Hi friends. Maybe you&#8217;ve heard terms like &#8220;view space&#8221;, &#8220;model space&#8221; or &#8220;screen space&#8221;, but you&#8217;re not sure what it all means exactly? This is what we&#8217;ll be exploring today. For our purposes, &#8220;spaces&#8221; can be thought of as coordinate systems, which can be rotated, translated, scaled and skewed. We&#8217;ll go over the most important vector spaces, what they&#8217;r&#8230;&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;GM Shaders Mini: Vector Spaces&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:115837202,&quot;name&quot;:&quot;Xor&quot;,&quot;bio&quot;:&quot;Shaders and GameMaker&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b37ca2-b4f8-4d1e-a083-93ad73418f1f_512x512.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2023-10-07T18:16:50.060Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/68c4d07e-e9d1-4dbf-a4b7-53933a23ff89_1920x1080.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://mini.gmshaders.com/p/vector-spaces&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:135662310,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;GM Shaders&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf69737-7d50-4dc8-9cce-aad9b9019215_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h1>Precision</h1><p>Floats actually vary in quality depending on the device and hardware. <strong>Mobile devices often default to a lower precision</strong>, while desktops may use higher precision.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TXeV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcff7e6f1-bf52-4fa9-9f47-90271a6f731f_658x323.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TXeV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcff7e6f1-bf52-4fa9-9f47-90271a6f731f_658x323.png 424w, https://substackcdn.com/image/fetch/$s_!TXeV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcff7e6f1-bf52-4fa9-9f47-90271a6f731f_658x323.png 848w, https://substackcdn.com/image/fetch/$s_!TXeV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcff7e6f1-bf52-4fa9-9f47-90271a6f731f_658x323.png 1272w, https://substackcdn.com/image/fetch/$s_!TXeV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcff7e6f1-bf52-4fa9-9f47-90271a6f731f_658x323.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TXeV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcff7e6f1-bf52-4fa9-9f47-90271a6f731f_658x323.png" width="658" height="323" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cff7e6f1-bf52-4fa9-9f47-90271a6f731f_658x323.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:323,&quot;width&quot;:658,&quot;resizeWidth&quot;:658,&quot;bytes&quot;:78160,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/161093356?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcff7e6f1-bf52-4fa9-9f47-90271a6f731f_658x323.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!TXeV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcff7e6f1-bf52-4fa9-9f47-90271a6f731f_658x323.png 424w, https://substackcdn.com/image/fetch/$s_!TXeV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcff7e6f1-bf52-4fa9-9f47-90271a6f731f_658x323.png 848w, https://substackcdn.com/image/fetch/$s_!TXeV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcff7e6f1-bf52-4fa9-9f47-90271a6f731f_658x323.png 1272w, https://substackcdn.com/image/fetch/$s_!TXeV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcff7e6f1-bf52-4fa9-9f47-90271a6f731f_658x323.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">From <a href="https://www.khronos.org/files/opengles_shading_language.pdf">Khronos.org</a></figcaption></figure></div><p>Lower precisions reduce the range of values and the decimal precision. A shader might work well on your desktop, but have poor quality on your phone. You can set the precision higher, but it may run slower on those devices, so it&#8217;s best to use the lowest quality you can get away with. <code>lowp</code> works well for colors, <code>mediump</code> for texture coordinates, and <code>highp</code> for positional coordinates.</p><p>When generating noise procedurally, you&#8217;re likely to notice a big loss in quality at lower precisions, so it&#8217;s best to use high precision for that.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XbZC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75c1570e-06f0-4c74-838a-9a50821e4de5_640x360.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XbZC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75c1570e-06f0-4c74-838a-9a50821e4de5_640x360.png 424w, https://substackcdn.com/image/fetch/$s_!XbZC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75c1570e-06f0-4c74-838a-9a50821e4de5_640x360.png 848w, https://substackcdn.com/image/fetch/$s_!XbZC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75c1570e-06f0-4c74-838a-9a50821e4de5_640x360.png 1272w, https://substackcdn.com/image/fetch/$s_!XbZC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75c1570e-06f0-4c74-838a-9a50821e4de5_640x360.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XbZC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75c1570e-06f0-4c74-838a-9a50821e4de5_640x360.png" width="640" height="360" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/75c1570e-06f0-4c74-838a-9a50821e4de5_640x360.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:360,&quot;width&quot;:640,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!XbZC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75c1570e-06f0-4c74-838a-9a50821e4de5_640x360.png 424w, https://substackcdn.com/image/fetch/$s_!XbZC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75c1570e-06f0-4c74-838a-9a50821e4de5_640x360.png 848w, https://substackcdn.com/image/fetch/$s_!XbZC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75c1570e-06f0-4c74-838a-9a50821e4de5_640x360.png 1272w, https://substackcdn.com/image/fetch/$s_!XbZC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75c1570e-06f0-4c74-838a-9a50821e4de5_640x360.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>With animated shaders, you may run into precision issues with your time variable:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Vf3y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3994394f-b2a4-40c4-8c26-5408708e32f6_680x383.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Vf3y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3994394f-b2a4-40c4-8c26-5408708e32f6_680x383.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Vf3y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3994394f-b2a4-40c4-8c26-5408708e32f6_680x383.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Vf3y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3994394f-b2a4-40c4-8c26-5408708e32f6_680x383.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Vf3y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3994394f-b2a4-40c4-8c26-5408708e32f6_680x383.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Vf3y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3994394f-b2a4-40c4-8c26-5408708e32f6_680x383.jpeg" width="680" height="383" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3994394f-b2a4-40c4-8c26-5408708e32f6_680x383.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:383,&quot;width&quot;:680,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Image&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Image" title="Image" srcset="https://substackcdn.com/image/fetch/$s_!Vf3y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3994394f-b2a4-40c4-8c26-5408708e32f6_680x383.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Vf3y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3994394f-b2a4-40c4-8c26-5408708e32f6_680x383.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Vf3y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3994394f-b2a4-40c4-8c26-5408708e32f6_680x383.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Vf3y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3994394f-b2a4-40c4-8c26-5408708e32f6_680x383.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The shader is breaking down after just 400 seconds of running</figcaption></figure></div><p>Anytime you are working with a time variable, you should anticipate it becoming larger and larger. You can try multiplying the time factor by, like 1000, to see if your shader will work over time. If not, the easiest solution is just to loop the time variable every 600 or so seconds. There are several <a href="https://x.com/XorDev/status/1897684220929011950">other approaches</a>, though and I might have to do a separate tutorial on it sometime.</p><p>And finally, when it comes to sharp, pixelated edges, there&#8217;s no excuse not to implement some anti-aliasing:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;bd24818f-9d75-4b6e-95d2-519d9037bbf0&quot;,&quot;caption&quot;:&quot;Today&#8217;s tutorial is about &#8220;analytic anti-aliasing&#8221;. Anti-aliasing is all about producing soft, natural-looking edges in your shaders.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;GM Shaders: Anti-Aliasing&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:115837202,&quot;name&quot;:&quot;Xor&quot;,&quot;bio&quot;:&quot;Shaders and GameMaker&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b37ca2-b4f8-4d1e-a083-93ad73418f1f_512x512.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-01-11T23:51:55.964Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c64ed775-52a6-41e0-9585-7d7938367c38_1920x1080.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://mini.gmshaders.com/p/antialiasing&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:148862081,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:11,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;GM Shaders&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf69737-7d50-4dc8-9cce-aad9b9019215_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h1>Conclusion</h1><p>To recap, let&#8217;s summarize everything we&#8217;ve covered today:</p><p><strong>Clarity</strong>:</p><ul><li><p>Rename variables for clarity</p></li><li><p>Write comments to explain each part of the code. Future you will thank you</p></li><li><p>Double-check uniform names inside and out of shaders</p></li></ul><p><strong>Color</strong>:</p><ul><li><p>Make sure negative sqrts and logs are impossible.</p></li><li><p>Prevent zero divided by zero!</p></li><li><p>Clamp inverse trig functions as needed.</p></li><li><p>Do color operations in decoded-gamma (linear-color) and reencode to sRGB.</p></li><li><p>Optional: Dither to mask banding artifacts</p></li></ul><p><strong>Textures</strong>:</p><ul><li><p>Don't assume normalized 0 to 1 texture coordinates.</p></li><li><p>Avoid hardware texture interpolation for highly-stretched textures.</p></li><li><p>When mipmapping, avoid sudden changes (discontinuities) in texture coordinates.</p></li></ul><p><strong>Coordinates</strong>:</p><ul><li><p>Make sure your shader works as intended at a variety of resolutions and aspect ratios.</p></li><li><p>Know what coordinate system to use for the job and how to convert between them when needed.</p></li></ul><p><strong>Precision</strong>:</p><ul><li><p>Use <code>highp</code> floats when needed to avoid precision artifacts on low-end devices.</p></li><li><p>Use <code>lowp</code> or <code>mediump</code> when possible to improve performance on low-end devices.</p></li><li><p>Generally, you should generate procedural noise in <code>highp</code>.</p></li><li><p>Keep your time uniforms in a reasonable range and test your game at high speed.</p></li><li><p>Implement anti-aliasing whenever you can!</p></li></ul><p>If you take these steps, hopefully, you save yourself a lot of time and prevent several problems before they happen. Believe me, I&#8217;ve spent a lot of time wrestling with some of these issues personally and I imagine others have as well. I&#8217;d suggest bookmarking this page for later, so if you ever run into an issue, you can check this list first. If you find something I missed, don&#8217;t hesitate to reach out! Thank you!</p><h1>Extras</h1><p><a href="https://renderdoc.org">RenderDoc</a> is a well-known shader debugging tool, and for my GameMaker readers, there is a nice script for using this with GM <a href="https://github.com/odditica/renderdoc-gms2-kit">here</a>.</p><p></p><p><a href="https://shadered.org">SHADERed</a> is a neat offline tool for editing, debugging, and profiling shaders in a variety of languages.</p><p></p><p>That&#8217;s all for now, thanks for reading! Have a great day!<br>-Xor</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>You&#8217;ll also get NaN errors in <code>pow(x, y)</code> when x = 0.0 and y &lt;= 0.0!</p></div></div>]]></content:encoded></item><item><title><![CDATA[Combining Shaders]]></title><description><![CDATA[How and when to combine shaders into a single shader pass.]]></description><link>https://mini.gmshaders.com/p/combiningshaders</link><guid isPermaLink="false">https://mini.gmshaders.com/p/combiningshaders</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Sat, 19 Apr 2025 00:48:25 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/3ade70fb-26d6-4096-9aa7-d58d6f716500_1920x1080.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Howdy folks,<br>Hope you are doing well! Previously, I wrote about setting up &#8220;multi-pass&#8221; shaders as a way of layering shaders together (or repeating a shader multiple times):</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;c15a9480-6f75-4b0c-88db-2dd82107a9a4&quot;,&quot;caption&quot;:&quot;Today's mini tutorial is about recursive shader effects as a way of layering multiple passes of shaders together.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;GM Shaders Mini: Recursive shaders&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:115837202,&quot;name&quot;:&quot;Xor&quot;,&quot;bio&quot;:&quot;Shaders and GameMaker&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b37ca2-b4f8-4d1e-a083-93ad73418f1f_512x512.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2022-08-19T17:19:26.583Z&quot;,&quot;cover_image&quot;:&quot;https://s3.amazonaws.com/revue/items/images/017/523/782/original/Samples.png?1660926317&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://mini.gmshaders.com/p/gm-shaders-mini-recursive-shaders-1308459&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:91667877,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:3,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;GM Shaders&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf69737-7d50-4dc8-9cce-aad9b9019215_512x512.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>This is commonly used for blurring because it can be more efficient to apply a large blur in separate passes (<em>for example, instead of a 9x9 Gaussian blur, you can do a 9x1 blur and 1x9 blur, 2*N samples instead of N^2</em>). However, multipass effects have downsides. They are more difficult to implement, requiring surfaces/canvases, additional draw calls, and more video memory usage. They will complicate your draw pipeline process, and in some cases, are unfeasible. The alternative is to combine two shaders together. So, when is it worth it to combine shaders?</p><h1>When To Combine Shaders</h1><p>Here&#8217;s a quick checklist to go over when considering shader compatibility, roughly in order of most important to least important:</p><ul><li><p><strong>Performance</strong>: Are the effects expensive? Chances are, if both shaders being combined are expensive, the combined shader will be very expensive. Unless you can think of some clever optimizations that simplify it, it&#8217;s probably not going to work!</p></li><li><p><strong>Sample Count</strong>: Do these shaders require lots of texture samples? If you have an outline shader that uses 8 texture samples and a blur shader that uses 32 samples, you&#8217;ll likely have to do 8 outline samples for every blur sample, meaning you&#8217;ll be using <code>8*32</code> or <code>256</code> samples! That is a lot of texture samples for moderately-sized textures! Generally, it&#8217;s safe to combine a multi-sample shader with a single sample shader, but be careful if both shaders use a lot of samples. The shader order does matter, though. If you have an expensive 1-sample shader before a cheap 8-sample blur, you have to do the expensive code 8 times! If the blurring occurs first, you&#8217;d only have to do the expensive part once.</p></li><li><p><strong>Coordinates</strong>: Check if the shaders are operating in the same coordinates. You might have one shader that is a screenspace effect applied to pixel coordinates on the screen, another might be <a href="https://mini.gmshaders.com/p/vector-spaces">world-space coordinates</a>, or <a href="https://mini.gmshaders.com/p/gm-shaders-mini-texels-and-pixels-1308242">texel coordinates</a>. That doesn&#8217;t necessarily mean they are incompatible, but it will take some unit conversion and may add some complications.</p></li><li><p><strong>Textures</strong>: Do your shaders require specific texture sizes? What about texture filtering (<em>e.g., linear interpolation vs nearest neighbor</em>)? How about texture boundaries? Distortion effects, outlines, or blurs may sample outside of the texture boundary, so you&#8217;ll have to consider how you handle these cases. Sometimes it may be texture repeat, texture clamping, or discarding outside fragments. Make sure they can be adapted to work together! You&#8217;ll also want to consider the GPU settings: mainly blend modes and alpha testing.</p></li><li><p><strong>Uniforms and Inputs</strong>: When combining shaders, you&#8217;ll need all the uniforms from both of the shaders. While the limits are pretty high, <a href="https://mini.gmshaders.com/p/limits">shaders do have limits</a>, especially when it comes to having lots of large textures or uniform arrays. You also want to make sure the shader inputs and outputs match (attributes and varyings in old-school GLSL). The good news is that sometimes you can reduce shader complexity by reusing uniforms for both effects (like resolution or time).</p></li></ul><p>If you&#8217;ve made it past that, then you&#8217;re probably ready to start combining!</p><h1>Merging Shader Code</h1><p>That checklist also hints a bit about how shaders are combined. The first step is to turn your shaders into functions. Let&#8217;s start with a sample grayscale shader like this:</p><pre><code>//Saturation macro (0.0 = gray, 1.0 = full color)
#define SATURATION 0.0

varying vec2 v_vTexcoord;

void main()
{
    vec2 uv = v_vTexcoord;

    //Sample base texture.
    vec4 tex = texture2D(gm_BaseTexture, uv);
    
    //Output color and alpha
    vec4 col = tex;
    //Grayscale luma
    //https://GMshaders.com/tutorials/tips_and_tricks/#useful
    float gray = dot(tex, vec4(0.299, 0.587, 0.114, 0.0));
    //Mix between gray and color with saturation
    col.rgb = gray + (col.rgb - gray) * SATURATION;
}</code></pre><p>We can rewrite this as a function, with fragment shader inputs as the arguments. In this case, we just have the UV coordinates:</p><pre><code>//Simple saturation shader:
vec4 saturation(vec2 uv)
{
    //Sample base texture.
    vec4 tex = texture2D(gm_BaseTexture, uv);
    
    //Output color and alpha
    vec4 col = tex;
    //Grayscale luma
    //https://GMshaders.com/tutorials/tips_and_tricks/#useful
    float gray = dot(tex, vec4(0.299, 0.587, 0.114, 0.0));
    //Mix between gray and color with saturation
    col.rgb = gray + (col.rgb - gray) * SATURATION;
    
    return col;
}</code></pre><p>Uniforms and macros can be directly referenced in the function, so they do not need to be added as arguments. This is a fairly light-weight function. It doesn&#8217;t require a bunch of texture samples, for-loops, branching, or lots of math. Now let&#8217;s say you want to combine it with this chromatic aberration shader:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;6def7053-d9a8-4a8e-b782-8ab88227ffef&quot;,&quot;caption&quot;:&quot;An introduction to quality color shift effects&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;GM Shaders Mini: Chromatic Aberration&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:115837202,&quot;name&quot;:&quot;Xor&quot;,&quot;bio&quot;:&quot;Shaders and GameMaker&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b37ca2-b4f8-4d1e-a083-93ad73418f1f_512x512.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2023-06-11T21:53:14.040Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/16904a5d-6c24-4688-a113-4e42f88713d7_1920x1080.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://mini.gmshaders.com/p/gm-shaders-mini-chromatic-aberration&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:126882754,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:4,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;GM Shaders&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf69737-7d50-4dc8-9cce-aad9b9019215_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Following a similar process, we end up with this function for radial CA:</p><pre><code>//From GM Shaders Chromatic Aberration:
//shadertoy.com/view/DtGSRt

//Chromatic blur intensity
#define BLUR 0.05
//Chromatic constrast
#define CONTRAST 2.0
//Must be an even number
#define SAMPLES 20.0

vec4 chromatic(vec2 uv)
{
    //Color output starts at 0.
&#9;vec4 color_sum = vec4(0);
    vec4 weight_sum = vec4(0);
    
    //Iterate 20 times from 0 to 1
&#9;for(float i = 0.0; i&lt;=1.0; i+=1.0/SAMPLES)
    {
        //Add a texture sample approaching the center (0.5, 0.5)
        //This center could moved to change how the direction of aberation
        //The mix amount determines the intensity of the aberration smearing
        vec2 coord = mix(uv, vec2(0.5), BLUR*(i-0.5));
        
        vec4 weight = vec4(i,1.0 - abs(i+i-1.0), 1.0-i, 0.5);
        weight = mix(vec4(0.5), weight,  CONTRAST);
        
        //Here we use the saturation effect as input
        vec4 color = texture2D(gm_BaseTexture, coord);
&#9;color_sum += color * color * weight;
        //Shift sample tint color from red to green to blue
        //The total should be multiplied by the 2/number of samples, (e.g. 0.1)
        weight_sum += weight;
     }
        
    //Output the resulting color
    return sqrt(clamp(color_sum / weight_sum, 0.0, 1.0));
}</code></pre><p>Now that we have both shaders as functions, we can put them together in one fragment shader. While we&#8217;re doing that, we should combine the uniforms, macros, functions, and shader inputs. This is a good time to remove any redundancies you find along the way and share functions and uniforms if possible.</p><h3>Order of Effects</h3><p>Now is the point where you can actually combine the two functions as needed. In this case, I want to apply the desaturation first and then apply the chromatic aberration. To do this, I need to replace the texture2D in the &#8220;<code>chromatic</code>&#8221; function with my &#8220;<code>saturation</code>&#8221; function. If I wanted to apply the chromatic aberration first and desaturate after, I would put the <code>chromatic</code> function inside the <code>saturation</code> function. This way would be faster because it wouldn&#8217;t require executing the saturation function within a for-loop, but we don&#8217;t want to desaturate after applying a color effect.</p><p><a href="https://www.shadertoy.com/view/WXlSz7">I&#8217;ve put together a ShaderToy example for this</a>.</p><h1>Newsletter Update</h1><p>Just a heads-up, the cost for paid subscriptions is going up to $8/mo or $80/yr soon. This will help me keep a consistent schedule and take fewer contracts. If you&#8217;ve ever wanted to join, now is the last chance to get it for $50/yr!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://mini.gmshaders.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe for more tutorials</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Thank you for the support!</p><h1>Conclusion</h1><p>Thankfully, the process for combining shaders is intuitive most of the time. It can be summed up be turning your shaders into functions and feeding one function into another as needed. The inner function is applied first. It&#8217;s preferable to do the most samples first because shader costs can compound quickly.</p><p>To recap, before you consider combining, you should look at: </p><p><strong>Performance</strong> - Expensive shader + expensive shader &#8776; very expensive!</p><p><strong>Sample Count</strong> - Avoid combining shaders that use a lot of texture samples.</p><p><strong>Coordinates</strong> - Make sure the type of coordinates matches up correctly.</p><p><strong>Textures</strong> - They should share texture settings and formats.</p><p><strong>Inputs</strong> - Vertex formats, uniforms, and varying will have to match up.</p><p>If you pass that test, then it may be worth trying.</p><h1>Extras</h1><p>Here are my 3 examples of one-pass blur shaders</p><p><a href="https://github.com/XorDev/1PassBlur/wiki">1 Pass Blur</a> - A simple, cheap disk blur based on the <a href="https://mini.gmshaders.com/p/phi">golden angle</a>.</p><p><a href="https://github.com/XorDev/GM_MipBlur">Mip Blur</a> - An efficient blur that makes use of mipmapping.</p><p><a href="https://github.com/XorDev/Bokeh/wiki">Bokeh</a> - A rich Depth of Field effect that is good for backgrounds.</p><p></p><p>That&#8217;s all for today. Thank you for reading and have a great night!</p><p>-Xor</p>]]></content:encoded></item><item><title><![CDATA[Reading Math Papers]]></title><description><![CDATA[An overview of mathematics for programmers]]></description><link>https://mini.gmshaders.com/p/readingmath</link><guid isPermaLink="false">https://mini.gmshaders.com/p/readingmath</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Thu, 10 Apr 2025 01:20:26 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/d92444ce-014d-4b4d-a3a1-072d261723c0_1920x1080.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello friends!<br><br>Mathematical papers or math-related wiki pages can be challenging for anyone to read. Shaders often do use a lot of math, but concepts are written quite differently. You don&#8217;t have to be good at mathematics before becoming a good programmer, but mathematical concepts can certainly help you take things further. There&#8217;s often a lot of overlap between physics and computer graphics.<br>Take, for example, the <a href="https://en.wikipedia.org/wiki/Rendering_equation">Rendering Equation</a>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Pkq7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea65928-2e09-497b-869b-0fd2b3b6346e_2071x1138.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Pkq7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea65928-2e09-497b-869b-0fd2b3b6346e_2071x1138.png 424w, https://substackcdn.com/image/fetch/$s_!Pkq7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea65928-2e09-497b-869b-0fd2b3b6346e_2071x1138.png 848w, https://substackcdn.com/image/fetch/$s_!Pkq7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea65928-2e09-497b-869b-0fd2b3b6346e_2071x1138.png 1272w, https://substackcdn.com/image/fetch/$s_!Pkq7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea65928-2e09-497b-869b-0fd2b3b6346e_2071x1138.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Pkq7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea65928-2e09-497b-869b-0fd2b3b6346e_2071x1138.png" width="1456" height="800" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9ea65928-2e09-497b-869b-0fd2b3b6346e_2071x1138.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:800,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:329793,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/159574255?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea65928-2e09-497b-869b-0fd2b3b6346e_2071x1138.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Pkq7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea65928-2e09-497b-869b-0fd2b3b6346e_2071x1138.png 424w, https://substackcdn.com/image/fetch/$s_!Pkq7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea65928-2e09-497b-869b-0fd2b3b6346e_2071x1138.png 848w, https://substackcdn.com/image/fetch/$s_!Pkq7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea65928-2e09-497b-869b-0fd2b3b6346e_2071x1138.png 1272w, https://substackcdn.com/image/fetch/$s_!Pkq7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ea65928-2e09-497b-869b-0fd2b3b6346e_2071x1138.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Source: <a href="https://www.cg.tuwien.ac.at/courses/Rendering/VU/2021S">cg.tuwien.ac.at</a></figcaption></figure></div><p>Maybe it&#8217;s been a few years since you&#8217;ve taken a math class, or you&#8217;ve never had a strong educational mathematical foundation to begin with. This article is aimed specifically to serve as a reference point to help get you caught up to speed. I was homeschooled and taught myself after Geometry, so I know firsthand how challenging it can be to approach on your own! I have even learned a bit while writing this. If you get stuck somewhere along the way, don&#8217;t worry; that&#8217;s perfectly normal! Skip that part, or take a break and revisit it later. Hopefully, this will save you lots of time on your journey and give you new mathematical tools to use!</p><h1>Basic Notation</h1><p>At this point, you&#8217;re familiar with the concepts of scalars (floats), vectors, and matrices, but they are written differently in mathematics papers, so it&#8217;s important to know what to look for. Because countless people developed the field of mathematics over the course of thousands of years, there are many different ways things can be written, making it a little more difficult to interpret. This article will serve as a general guideline, but there are exceptions to watch out for.</p><p><strong>Scalars</strong> are one-letter variables like &#8220;a&#8221; or &#8220;b&#8221;.</p><p><strong>Vectors</strong> are usually written as a boldface &#8220;<strong>v</strong>&#8221; or with a right arrow over top, like so <strong>v&#8407;</strong>.</p><p>Vector components are written in subscript, starting from 1, 2, 3&#8230; (<em>not 0</em>), or often just x, y, z, like in GLSL:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!T0JW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe8a82e1-7f0b-416a-8222-b6d5547f1c6a_1540x957.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!T0JW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe8a82e1-7f0b-416a-8222-b6d5547f1c6a_1540x957.png 424w, https://substackcdn.com/image/fetch/$s_!T0JW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe8a82e1-7f0b-416a-8222-b6d5547f1c6a_1540x957.png 848w, https://substackcdn.com/image/fetch/$s_!T0JW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe8a82e1-7f0b-416a-8222-b6d5547f1c6a_1540x957.png 1272w, https://substackcdn.com/image/fetch/$s_!T0JW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe8a82e1-7f0b-416a-8222-b6d5547f1c6a_1540x957.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!T0JW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe8a82e1-7f0b-416a-8222-b6d5547f1c6a_1540x957.png" width="1456" height="905" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/be8a82e1-7f0b-416a-8222-b6d5547f1c6a_1540x957.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:905,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:95340,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/159574255?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe8a82e1-7f0b-416a-8222-b6d5547f1c6a_1540x957.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!T0JW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe8a82e1-7f0b-416a-8222-b6d5547f1c6a_1540x957.png 424w, https://substackcdn.com/image/fetch/$s_!T0JW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe8a82e1-7f0b-416a-8222-b6d5547f1c6a_1540x957.png 848w, https://substackcdn.com/image/fetch/$s_!T0JW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe8a82e1-7f0b-416a-8222-b6d5547f1c6a_1540x957.png 1272w, https://substackcdn.com/image/fetch/$s_!T0JW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe8a82e1-7f0b-416a-8222-b6d5547f1c6a_1540x957.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Source: <a href="https://en.wikipedia.org/wiki/Euclidean_vector">en.wikipedia.com</a></figcaption></figure></div><p>Hats are used for unit vectors (<em>normalized vectors with a length of 1.0</em>): <strong>&#226;</strong>. Cute right?</p><p><strong>Matrices</strong> are typically written with capital letters like &#8220;<strong>A</strong>&#8221;, and the values are arranged in big square brackets, row by row, with spaces to separate components. Sometimes matrices with one row or one column are used as vectors. <a href="https://mini.gmshaders.com/p/matrix">I wrote an article about matrices</a> if you&#8217;d like to learn more!</p><p><strong>Tensors</strong> are the extension of the idea of scalars (0th order tensors), vectors (1st order tensors), and matrices (2nd order tensors). A first-order tensor can be thought of as a 1D array or vector, and as you can imagine, a third-order tensor is the same as a 3D array. So you&#8217;ve already been using Tensors, whether you knew it or not.</p><h1>Operations</h1><p><strong>Multiplication</strong> is generally just two or more variables concatenated. &#8220;ab&#8221; is the same as &#8220;a*b&#8221;. In arithmetic, scalar multiplication may use &#8220;a&#215;b&#8221; or &#8220;a&#183;b&#8221;, but it is not to be confused with vector cross products or dot products. We&#8217;ll cover that later!</p><p><strong>Summations</strong> (&#931;) and <strong>Products</strong> (&#928;) can be recreated using for loops (within reasonable limits). <a href="https://x.com/FreyaHolmer/status/1663263178099228674">Here&#8217;s a handy illustration from Freya Holmer</a>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pQbC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00bd81f-498c-4f47-8746-eaf132b9fbcc_900x507.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pQbC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00bd81f-498c-4f47-8746-eaf132b9fbcc_900x507.png 424w, https://substackcdn.com/image/fetch/$s_!pQbC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00bd81f-498c-4f47-8746-eaf132b9fbcc_900x507.png 848w, https://substackcdn.com/image/fetch/$s_!pQbC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00bd81f-498c-4f47-8746-eaf132b9fbcc_900x507.png 1272w, https://substackcdn.com/image/fetch/$s_!pQbC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00bd81f-498c-4f47-8746-eaf132b9fbcc_900x507.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pQbC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00bd81f-498c-4f47-8746-eaf132b9fbcc_900x507.png" width="900" height="507" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a00bd81f-498c-4f47-8746-eaf132b9fbcc_900x507.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:507,&quot;width&quot;:900,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Image&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Image" title="Image" srcset="https://substackcdn.com/image/fetch/$s_!pQbC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00bd81f-498c-4f47-8746-eaf132b9fbcc_900x507.png 424w, https://substackcdn.com/image/fetch/$s_!pQbC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00bd81f-498c-4f47-8746-eaf132b9fbcc_900x507.png 848w, https://substackcdn.com/image/fetch/$s_!pQbC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00bd81f-498c-4f47-8746-eaf132b9fbcc_900x507.png 1272w, https://substackcdn.com/image/fetch/$s_!pQbC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa00bd81f-498c-4f47-8746-eaf132b9fbcc_900x507.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Summations add an expression with multiple iterations, and Products multiply with multiple iterations.</figcaption></figure></div><p><br><strong>Exponentiation</strong> is written with superscript exponents: 3<sup>2</sup> = <code>pow(3.0, 2.0).</code></p><p>In <strong>Boolean Algebra,</strong> you have the logical operations: &#8220;a&#8743;b&#8221; (<em>AND</em>), &#8220;a&#8744;b&#8221; (<em>OR</em>), &#8220;&#172;a&#8221; (<em>NOT</em>), and &#8220;a&#8853;b&#8221; (<em>XOR, yes&#8230; that&#8217;s where my username comes from</em>). Boolean equivalence can be written with &#8220;a&#8801;b&#8221; which would be written as &#8220;a==b&#8221; in most programming languages. Not-equal is written with &#8220;a&#8800;b&#8221;</p><p>The <strong>Absolute Value</strong> operation is written with vertical bars like &#8220;|x|&#8221; instead of the &#8220;<code>abs(x)</code>&#8221; function.</p><p><strong>Vector Length</strong> is written with double vertical bars like &#8220;||v||&#8221;. So, for example, you could write a normalized vector &#8220;a&#8221; like so:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\hat{a} = \\frac{ \\vec a}{\\|\\vec a\\|}&quot;,&quot;id&quot;:&quot;UHYSLONTZM&quot;}" data-component-name="LatexBlockToDOM"></div><p><strong><a href="https://mini.gmshaders.com/p/gm-shaders-mini-the-dot-product-1329407">Dot Products</a></strong> and <strong>Cross Products</strong> are written with &#8220;&#8901;&#8221; and &#8220;&#215;&#8221; respectively. Not to be confused with arithmetic scalar multiplication. The dot product may also be written as an Einstein Summation, but it does the same thing:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\vec a \\cdot \\vec b = \\sum_i a_i b_i&quot;,&quot;id&quot;:&quot;RMLTJKISFM&quot;}" data-component-name="LatexBlockToDOM"></div><h1>Functions</h1><p>Functions are typically written like &#8220;f(x, y) = x+y&#8221; with x and y being the function arguments and &#8220;x+y&#8221; being the returned value. Sometimes, unambiguous functions are written without parentheses, like &#8220;sin x&#8221;.</p><p><strong><a href="https://en.wikipedia.org/wiki/Inverse_function">Inverse functions</a></strong> undo the operation of another function. They are typically written like &#8220;f<sup>-1</sup>(y) = x&#8221;. For example, if you have &#8220;f(x) = x+2&#8221;, then the inverse function would be &#8220;f<sup>-1</sup>(x) = x-2&#8221;. Not all functions are invertible. For example, with &#8216;f(x) = x&#178;&#8217;, &#8216;f&#8315;&#185;(25)&#8217; could be 5 or -5 because both 5&#178; and (-5)&#178; equal 25. A function needs a unique output for every input (one-to-one) to have a proper inverse.&#8221;</p><p><strong>Arrow Notation</strong> may also be used for &#8220;inline&#8221; functions. For example, an incrementing function could be written like &#8220;x &#8614; x+1&#8221;.  So, if x = 5, the output is 5+1 or 6. Barred arrows &#8220;&#8614;&#8221; are used for remapping elements, while unbarred arrows &#8220;&#8594;&#8221; are used to describe &#8220;sets&#8221;, which we will cover next.</p><p><strong>Set Theory</strong> intuitively has to do with sets of numbers. You have Natural Numbers: 1, 2, 3&#8230; and these are just positive whole numbers (so no fractions or decimals). Integers include negative numbers: &#8230;-2, -1, 0, 1, 2&#8230; and are written as &#8220;int&#8221; in GLSL. Here&#8217;s a quick list of the main relevant sets:</p><ul><li><p>&#8469; - <strong>Natural numbers</strong>, positive whole numbers: 1, 2, 3&#8230;  In some math contexts, 0 is included (written as &#8469;&#8320;), but we&#8217;ll stick to excluding 0 (&#8469;&#8321;) unless noted, aligning with positive integers. Compare this to uint in GLSL, which starts at 0..</p></li><li><p>&#8484; - <strong>Integers</strong>, positive, negative or zero, whole numbers: &#8230; -1, 0, 1&#8230; These are &#8220;ints&#8221; in GLSL.</p></li><li><p>&#8474; - <strong>Rational numbers</strong>, include integers, fractions, and decimals like 1/2 or -0.8. They include any number that can be written as a ratio of two integers: x/y. &#8220;floats&#8221; are generally rational numbers that can be written as a ratio. The exceptions are positive infinity, negative infinity, and the undefined &#8220;Not a Number&#8221;.</p></li><li><p>&#8477; - <strong>Real numbers</strong>, include rational numbers and irrational numbers like <code>sqrt(2.0)</code>, <code>log(7.0)</code>, or &#960;. These numbers cannot be represented with just fractions (decimals), and they have to be rationally approximated with floats. The positive-only real number set is written with a plus: &#8477;<strong><sup>+</sup></strong>.</p></li><li><p>&#8450; - <strong>Complex Numbers</strong>, include <a href="https://en.wikipedia.org/wiki/Imaginary_number">imaginary numbers</a> like &#8220;<em>i</em>&#8221; and numbers with both real and imaginary components like &#8220;2+i&#8221;. Most programming languages do not support Complex Number calculations directly, but they can be thought of as 2D vectors with both a real component and an imaginary &#8220;<em>i</em>&#8221; component. I won&#8217;t get into imaginary numbers here, but if you do come across them (e.g., with sqrt or the log of a negative), you will have to calculate the imaginary components manually to avoid errors. All GLSL functions return &#8220;undefined&#8221; when an imaginary number would occur, so you&#8217;ll have to handle these edge cases yourself. A common example might be the Mandelbrot Set or quaternions.</p></li><li><p>&#8709; - <strong>Empty Sets</strong> contain nothing.</p></li><li><p>{a, b, c} - <strong>Finite</strong> <strong>Sets</strong> can contain any number of elements. For example, <code>sign(x)</code> returns values in the set {-1, 0, +1}.</p></li><li><p>[a, b] - <strong>Closed</strong> <strong>Intervals</strong> include &#8220;a&#8221;, &#8220;b&#8221;, and all real numbers between. Parentheses can be used instead of square brackets for &#8220;Open Intervals&#8221;, which exclude the endpoint numbers. For instance, <code>fract(x)</code> returns a value between 0.0 and 1.0. This is an interval of [0 1). Why? Well, with an input of x=0.0, you get an output of 0.0,  but with an input of 1.0, you also get an output of 0.0. You can get close to 1.0, like 0.999, but the interval does not include 1.0. This concept is quite useful to know!</p></li><li><p>&#8712; - <strong>Element of Set, </strong>used to show when a number or variable is an element of a set like: &#8220;x &#8712; &#8477;&#8221;. This means that &#8220;x&#8221; is a real number. &#8220;&#8713;&#8221; is used when an element is not a member of a set.</p></li></ul><p>Hopefully, that wasn&#8217;t too overwhelming. The most important are real numbers, sets, and intervals because they show up the most. Now, let&#8217;s put Set Theory together with Arrow Notation and look at an easy example of the sign function:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Tucp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb5fe491-71eb-428a-b98e-57e9d1345856_1765x979.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Tucp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb5fe491-71eb-428a-b98e-57e9d1345856_1765x979.png 424w, https://substackcdn.com/image/fetch/$s_!Tucp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb5fe491-71eb-428a-b98e-57e9d1345856_1765x979.png 848w, https://substackcdn.com/image/fetch/$s_!Tucp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb5fe491-71eb-428a-b98e-57e9d1345856_1765x979.png 1272w, https://substackcdn.com/image/fetch/$s_!Tucp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb5fe491-71eb-428a-b98e-57e9d1345856_1765x979.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Tucp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb5fe491-71eb-428a-b98e-57e9d1345856_1765x979.png" width="1456" height="808" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bb5fe491-71eb-428a-b98e-57e9d1345856_1765x979.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:808,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:128038,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://mini.gmshaders.com/i/159574255?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb5fe491-71eb-428a-b98e-57e9d1345856_1765x979.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Tucp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb5fe491-71eb-428a-b98e-57e9d1345856_1765x979.png 424w, https://substackcdn.com/image/fetch/$s_!Tucp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb5fe491-71eb-428a-b98e-57e9d1345856_1765x979.png 848w, https://substackcdn.com/image/fetch/$s_!Tucp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb5fe491-71eb-428a-b98e-57e9d1345856_1765x979.png 1272w, https://substackcdn.com/image/fetch/$s_!Tucp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbb5fe491-71eb-428a-b98e-57e9d1345856_1765x979.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><a href="https://en.wikipedia.org/wiki/Sign_(mathematics)#Sign_functions">Sign function from Wikipedia</a>.</figcaption></figure></div><p>This tells us that &#8220;sgn&#8221; takes a real number input (between -&#8734; and +&#8734;) and remaps it to the set -1, 0, or 1. Thankfully, the if-statement logic is pretty intuitive to us programmers:</p><pre><code>//Reconstructed sign function
//"x" is any number between -&#8734; and +&#8734;
float sgn(float x)
{
    if (x &gt;0.0) return +1.0; //Positive numbers
    if (x==0.0) return  0.0; //Zero
    if (x &lt;0.0) return -1.0; //Negative numbers
}</code></pre><p>Now on to function domains and ranges&#8230;</p>
      <p>
          <a href="https://mini.gmshaders.com/p/readingmath">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Turbulence]]></title><description><![CDATA[Approximating fluid dynamics and flames efficiently with shaders]]></description><link>https://mini.gmshaders.com/p/turbulence</link><guid isPermaLink="false">https://mini.gmshaders.com/p/turbulence</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Mon, 17 Mar 2025 21:50:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/865ec5f2-382f-4deb-9803-c4b3fc01c32c_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Bonjour! Hope you are well!</p><p>What do water, fire, dust, magic, wind, fog, smoke and clouds have in common? They are all heavily influenced by turbulent dynamics. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!57mm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38264ad3-995d-4fc0-986b-d7ac64ba295e_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!57mm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38264ad3-995d-4fc0-986b-d7ac64ba295e_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!57mm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38264ad3-995d-4fc0-986b-d7ac64ba295e_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!57mm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38264ad3-995d-4fc0-986b-d7ac64ba295e_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!57mm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38264ad3-995d-4fc0-986b-d7ac64ba295e_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!57mm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38264ad3-995d-4fc0-986b-d7ac64ba295e_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/38264ad3-995d-4fc0-986b-d7ac64ba295e_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!57mm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38264ad3-995d-4fc0-986b-d7ac64ba295e_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!57mm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38264ad3-995d-4fc0-986b-d7ac64ba295e_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!57mm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38264ad3-995d-4fc0-986b-d7ac64ba295e_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!57mm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38264ad3-995d-4fc0-986b-d7ac64ba295e_3840x2160.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Gases and non-viscous liquids twirl and flow in a way that is quite difficult to compute. There is a lot of mathematical research into problems of fluid and<a href="https://en.wikipedia.org/wiki/Millennium_Prize_Problems#Navier%E2%80%93Stokes_existence_and_smoothness"> there is even a $1,000,000 prize</a> if you solve one of them. I don&#8217;t say this to discourage you, but I felt this context is important.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!B8hc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b71e62-6ec8-4618-b9ed-49b22cd42814_1800x1200.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!B8hc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b71e62-6ec8-4618-b9ed-49b22cd42814_1800x1200.jpeg 424w, https://substackcdn.com/image/fetch/$s_!B8hc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b71e62-6ec8-4618-b9ed-49b22cd42814_1800x1200.jpeg 848w, https://substackcdn.com/image/fetch/$s_!B8hc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b71e62-6ec8-4618-b9ed-49b22cd42814_1800x1200.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!B8hc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b71e62-6ec8-4618-b9ed-49b22cd42814_1800x1200.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!B8hc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b71e62-6ec8-4618-b9ed-49b22cd42814_1800x1200.jpeg" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/95b71e62-6ec8-4618-b9ed-49b22cd42814_1800x1200.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Physics In History on X: \&quot;The Navier-Stokes equation is a mathematical  model that describes the motion of fluids, such as liquids and gases. It is  a partial differential equation that relates the&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Physics In History on X: &quot;The Navier-Stokes equation is a mathematical  model that describes the motion of fluids, such as liquids and gases. It is  a partial differential equation that relates the" title="Physics In History on X: &quot;The Navier-Stokes equation is a mathematical  model that describes the motion of fluids, such as liquids and gases. It is  a partial differential equation that relates the" srcset="https://substackcdn.com/image/fetch/$s_!B8hc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b71e62-6ec8-4618-b9ed-49b22cd42814_1800x1200.jpeg 424w, https://substackcdn.com/image/fetch/$s_!B8hc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b71e62-6ec8-4618-b9ed-49b22cd42814_1800x1200.jpeg 848w, https://substackcdn.com/image/fetch/$s_!B8hc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b71e62-6ec8-4618-b9ed-49b22cd42814_1800x1200.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!B8hc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95b71e62-6ec8-4618-b9ed-49b22cd42814_1800x1200.jpeg 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Navier-Stokes equations from <a href="https://x.com/PhysInHistory/status/1737694769709457717">Physics In History</a></figcaption></figure></div><p>Today, we are not going to be solving the Navier-Stokes equations (but I will include some resources at the end if you want to). Instead, we will fake it! Proper simulations require multiple shader passes, are slow, memory intensive, and are limited by resolution. Most of the time, we can get away with cheaper real-time approximations and still create impressive effects. Let&#8217;s hop in!</p><h1>Waves</h1><p>In essence, all we need to do is add some sine wave displacement. We&#8217;ll need to rotate each wave separately to create some curves and break up the visible alignment  patterns. The rotation angle isn&#8217;t super important as long as the results look natural (not 90 degrees or 45 degrees). For better results, we can also increase the frequency and decrease the amplitude proportionally.</p><p>Making a simple horizontal wave might look like this:</p><pre><code>//Shift the x-coordinates with a sine wave
pos.x += WAVE_AMP * sin(pos.y * freq) / freq;</code></pre><p>WAVE_AMP can be a constant or macro between 0.0 and 1.0 which sets the overall waviness and the frequency can increase with each pass.<br></p><p>We can rotate with a mat2x2, &#8220;rot&#8221; and animate with the waves with a time uniform:</p><pre><code>//Scroll along the rotated y coordinate
float phase = freq * (pos * rot).y + WAVE_SPEED * u_time;

//Add a perpendicular sine wave offset
pos += WAVE_AMP * rot[0] * sin(phase) / freq;</code></pre><p>Now let&#8217;s bring it all together with a loop like so:</p><pre><code>//Number of turbulence waves
#define TURB_NUM 10.0
//Turbulence wave amplitude
#define TURB_AMP 0.7
//Turbulence wave speed
#define TURB_SPEED 0.3
//Turbulence frequency
#define TURB_FREQ 2.0
//Turbulence frequency multiplier
#define TURB_EXP 1.4


//Turbulence starting scale
float freq = TURB_FREQ;

//Turbulence rotation matrix
mat2 rot = mat2(0.6, -0.8, 0.8, 0.6);

//Loop through turbulence octaves
for(float i=0.0; i&lt;TURB_NUM; i++)
{
    //Scroll along the rotated y coordinate
    float phase = freq * (pos * rot).y + TURB_SPEED*iTime + i;
    //Add a perpendicular sine wave offset
    pos += TURB_AMP * rot[0] * sin(phase) / freq;

    //Rotate for the next octave
    rot *= mat2(0.6, -0.8, 0.8, 0.6);
    //Scale down for the next octave
    freq *= TURB_EXP;
}</code></pre><p>Here&#8217;s an illustration of the compounding waves:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gxUv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19addc9-41e9-45ba-95ab-daea344211d5_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gxUv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19addc9-41e9-45ba-95ab-daea344211d5_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!gxUv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19addc9-41e9-45ba-95ab-daea344211d5_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!gxUv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19addc9-41e9-45ba-95ab-daea344211d5_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!gxUv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19addc9-41e9-45ba-95ab-daea344211d5_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gxUv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19addc9-41e9-45ba-95ab-daea344211d5_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e19addc9-41e9-45ba-95ab-daea344211d5_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gxUv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19addc9-41e9-45ba-95ab-daea344211d5_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!gxUv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19addc9-41e9-45ba-95ab-daea344211d5_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!gxUv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19addc9-41e9-45ba-95ab-daea344211d5_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!gxUv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe19addc9-41e9-45ba-95ab-daea344211d5_3840x2160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Grid distorted by waves represented in blue</figcaption></figure></div><p>Here we are starting with a red and green grid to visualize wave distortions. On the left side, we add the first wave (highlighted in blue). The blue arrow represents the direction and length of the waves. As we move right, we add additional waves, and you can see the compounding distortion as more waves are added. This simple effect is enough to create a sense of turbulence in the coordinates.</p><p>We can use a texture or noise instead of a grid. Even just 8 iterations can produce convincing results!</p><p><a href="https://www.shadertoy.com/view/WclSWn">Try my full ShaderToy demo here</a>. By adjusting the parameters you can get smooth or swirly turbulence. Here&#8217;s what various frequency multiplier values look like&#8221;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GoZ2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bcdd2ce-1803-4983-ab91-ff9919776d82_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GoZ2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bcdd2ce-1803-4983-ab91-ff9919776d82_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!GoZ2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bcdd2ce-1803-4983-ab91-ff9919776d82_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!GoZ2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bcdd2ce-1803-4983-ab91-ff9919776d82_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!GoZ2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bcdd2ce-1803-4983-ab91-ff9919776d82_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GoZ2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bcdd2ce-1803-4983-ab91-ff9919776d82_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3bcdd2ce-1803-4983-ab91-ff9919776d82_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!GoZ2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bcdd2ce-1803-4983-ab91-ff9919776d82_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!GoZ2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bcdd2ce-1803-4983-ab91-ff9919776d82_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!GoZ2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bcdd2ce-1803-4983-ab91-ff9919776d82_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!GoZ2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bcdd2ce-1803-4983-ab91-ff9919776d82_3840x2160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Increasing the frequency multiplier</figcaption></figure></div><p>Increasing the multiplier makes finer, more detailed whirls.</p><h1>Fire</h1><p>This technique works great for fire as well!</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;4379f225-c335-49a6-aa10-44707c603195&quot;,&quot;duration&quot;:null}"></div><p>It follows the same basic principle with the layering waves, but also with scrolling upward and stretching the coordinates. It starts with the waves tightly compressed horizontally and stretched vertically to simulate fast expansion upwards. As it rises, the space stretches out horizontally and squishes a little vertically for horizontal expansion. It&#8217;s pretty subtle, but these fine tweaks can make a big difference.<br></p><p>I won&#8217;t put the code here, but you can check out the <a href="https://www.shadertoy.com/view/wffXDr">full source code over on ShaderToy as well</a>!</p><h1>Conclusion</h1><p>The technique for fluid-like motion is surprisingly simple, yet it produces some pretty decent results! In real-time applications, full fluid simulations are often too expensive, complicated, or too limited to be practical. This method is very practical in a wide range of applications like fluids, flames, steam, smoke, or magic effects!</p><p>To recap, it all starts with a sine wave. Just shift the coordinates with a sine wave, rotate, scale, and repeat. After just a few iterations, you&#8217;ll get some nice swirls and fluid motion!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://mini.gmshaders.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe for many more free tutorials or support my work here</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Now subscribers can help pick the next tutorial</p><div class="poll-embed" data-attrs="{&quot;id&quot;:289169}" data-component-name="PollToDOM"></div><h1>Extras</h1><p>Here are some great resources I found while putting this tutorial together:<br></p><p><strong><a href="https://www.shadertoy.com/view/tt3yzn">Navier Stokes by Gijs</a>:</strong> This is an excellent example of how simple a Navier Stokes simulation can be implemented in 2D with minimal code!<br></p><p><strong><a href="https://www.shadertoy.com/view/4tGfDW">Chimera's Breath by Nimitz</a>: </strong>Taking the simulations a bit further with &#8220;vorticity confinement&#8221;, this shows off some impressive 2D fire and smoke effects.</p><p></p><p><strong><a href="https://www.shadertoy.com/view/XcXBzN">Cloud Simulation by wyatt</a>: </strong>Here is a good example of a 3D steam cloud simulation.</p><p></p><p><strong><a href="https://developer.nvidia.com/gpugems/gpugems/part-vi-beyond-triangles/chapter-38-fast-fluid-dynamics-simulation-gpu">Fast Fluid Dynamics Simulation on the GPU by Mark J. Harris</a>: </strong>For a thorough tutorial on fluid simulation, start here! It covers some of the background context, math, and the implementation.</p><p><br>Okay, that&#8217;s it for today. Au revoir!</p>]]></content:encoded></item><item><title><![CDATA[Signed Distance Fields]]></title><description><![CDATA[How SDFs work in shaders and how to write your own]]></description><link>https://mini.gmshaders.com/p/sdf</link><guid isPermaLink="false">https://mini.gmshaders.com/p/sdf</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Wed, 19 Feb 2025 03:40:14 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/af97e04d-f47e-4ea6-bd3c-e5a7969b2fcc_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hey guys,<br>I ran a poll on X, and about half of my followers know what Distance Fields are and half don&#8217;t. Today&#8217;s tutorial is intended to be a quick overview of the topic for complete beginners and intermediate readers. This should give you a more comprehensive understanding, even if you already know a bit about SDFs (Signed Distance Fields).</p><p>I will be referring to <a href="https://iquilezles.org">Inigo Quilez&#8217;s work</a> a lot today because he&#8217;s published many great articles on the topic, however, I&#8217;ll try to summarize as much as I can here.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hhnU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f18748a-f13d-4f94-bc37-4c4efcee2ed9_1920x1080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hhnU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f18748a-f13d-4f94-bc37-4c4efcee2ed9_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!hhnU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f18748a-f13d-4f94-bc37-4c4efcee2ed9_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!hhnU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f18748a-f13d-4f94-bc37-4c4efcee2ed9_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!hhnU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f18748a-f13d-4f94-bc37-4c4efcee2ed9_1920x1080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hhnU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f18748a-f13d-4f94-bc37-4c4efcee2ed9_1920x1080.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9f18748a-f13d-4f94-bc37-4c4efcee2ed9_1920x1080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2159926,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hhnU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f18748a-f13d-4f94-bc37-4c4efcee2ed9_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!hhnU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f18748a-f13d-4f94-bc37-4c4efcee2ed9_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!hhnU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f18748a-f13d-4f94-bc37-4c4efcee2ed9_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!hhnU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f18748a-f13d-4f94-bc37-4c4efcee2ed9_1920x1080.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Source: <a href="https://www.shadertoy.com/view/4XlyW8">iq&#8217;s Signed Jump Flooding</a></figcaption></figure></div><p>In short, SDFs are functions that describe shapes. More specifically, the distance to a shape&#8217;s surface at any given point. The &#8220;Signed&#8221; part means it will return a negative distance value if the sample point is inside the shape and a positive distance if outside.</p><p>That&#8217;s it. Now you know what a Signed Distance Field is! These functions are far more useful than you might think.</p><h1>Why you should learn this</h1><p>What can you do with SDFs? Let&#8217;s look at a few examples</p><h3>Shapes</h3><p>Firstly, it&#8217;s a nice way to draw vector shapes with pure code. <a href="https://danielchasehooper.com/posts/code-animated-rick/">Some have even recreated Rick from Rick and Morty</a> using just SDFs! There are many ways to draw shapes in shaders, but SDFs can make the process of building and editing these shapes more intuitive. There are lots of useful operations that can be applied to them.</p><p><a href="https://mini.gmshaders.com/p/antialiasing">I&#8217;ve recently written about the process of anti-aliasing</a>, and it can be challenging to implement, but not with SDFs. With SDFs, it&#8217;s super easy to implement!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8BqN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5adf8eb1-7e9b-4a1e-8275-9a6dcc38b987_2560x1440.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8BqN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5adf8eb1-7e9b-4a1e-8275-9a6dcc38b987_2560x1440.png 424w, https://substackcdn.com/image/fetch/$s_!8BqN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5adf8eb1-7e9b-4a1e-8275-9a6dcc38b987_2560x1440.png 848w, https://substackcdn.com/image/fetch/$s_!8BqN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5adf8eb1-7e9b-4a1e-8275-9a6dcc38b987_2560x1440.png 1272w, https://substackcdn.com/image/fetch/$s_!8BqN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5adf8eb1-7e9b-4a1e-8275-9a6dcc38b987_2560x1440.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8BqN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5adf8eb1-7e9b-4a1e-8275-9a6dcc38b987_2560x1440.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5adf8eb1-7e9b-4a1e-8275-9a6dcc38b987_2560x1440.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Image&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Image" title="Image" srcset="https://substackcdn.com/image/fetch/$s_!8BqN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5adf8eb1-7e9b-4a1e-8275-9a6dcc38b987_2560x1440.png 424w, https://substackcdn.com/image/fetch/$s_!8BqN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5adf8eb1-7e9b-4a1e-8275-9a6dcc38b987_2560x1440.png 848w, https://substackcdn.com/image/fetch/$s_!8BqN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5adf8eb1-7e9b-4a1e-8275-9a6dcc38b987_2560x1440.png 1272w, https://substackcdn.com/image/fetch/$s_!8BqN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5adf8eb1-7e9b-4a1e-8275-9a6dcc38b987_2560x1440.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Left: No anti-aliasing. Right: analytic anti-aliasing</figcaption></figure></div><h3>Outlines, lights and drop shadows</h3><p>Better yet, we can easily add outlines of any thickness when we know the distance from each pixel to the shapes:</p><pre><code><code>float outline_dist = dist - outline_thickness;</code></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ryAj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99f9d188-809d-4fa5-bc56-2f99f7eef491_1280x720.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ryAj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99f9d188-809d-4fa5-bc56-2f99f7eef491_1280x720.png 424w, https://substackcdn.com/image/fetch/$s_!ryAj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99f9d188-809d-4fa5-bc56-2f99f7eef491_1280x720.png 848w, https://substackcdn.com/image/fetch/$s_!ryAj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99f9d188-809d-4fa5-bc56-2f99f7eef491_1280x720.png 1272w, https://substackcdn.com/image/fetch/$s_!ryAj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99f9d188-809d-4fa5-bc56-2f99f7eef491_1280x720.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ryAj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99f9d188-809d-4fa5-bc56-2f99f7eef491_1280x720.png" width="1280" height="720" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/99f9d188-809d-4fa5-bc56-2f99f7eef491_1280x720.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:720,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ryAj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99f9d188-809d-4fa5-bc56-2f99f7eef491_1280x720.png 424w, https://substackcdn.com/image/fetch/$s_!ryAj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99f9d188-809d-4fa5-bc56-2f99f7eef491_1280x720.png 848w, https://substackcdn.com/image/fetch/$s_!ryAj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99f9d188-809d-4fa5-bc56-2f99f7eef491_1280x720.png 1272w, https://substackcdn.com/image/fetch/$s_!ryAj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99f9d188-809d-4fa5-bc56-2f99f7eef491_1280x720.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>You can add glow effects or soft drop shadows when using the distance field as well.</p><p>I wrote in more detail about these effects here:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;5e20b7a4-db69-4d18-bf4c-07806c3356b9&quot;,&quot;caption&quot;:&quot;Many ways to use SDFs&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;GM Shaders Mini: SDF Tricks&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:115837202,&quot;name&quot;:&quot;Xor&quot;,&quot;bio&quot;:&quot;Shaders and GameMaker&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b37ca2-b4f8-4d1e-a083-93ad73418f1f_512x512.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2023-05-05T23:17:17.813Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/48d28782-f181-4a9f-9bf1-d05a6a93ba99_1920x1080.jpeg&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://mini.gmshaders.com/p/gm-shaders-mini-sdf-tricks&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:119269127,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:1,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;GM Shaders&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf69737-7d50-4dc8-9cce-aad9b9019215_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h3>Raymarching</h3><p>You can even do raycasting or physics simulations with SDFs. Raymarching with distance fields is more intuitive than raytracing, and it&#8217;s much easier to build and modify the shapes with raymarching. You can use raymarching in 2D for cheap soft shadows, but the formulas also work well for 3D rendering.</p><p>I wrote in more detail about raymarching here (the burgers shown below are fully modeled in code using SDFs too!):</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;92bea169-2a50-40cd-8a7a-e3f954744ae5&quot;,&quot;caption&quot;:&quot;Raycasting with SDFs&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;GM Shaders Mini: Raymarching&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:115837202,&quot;name&quot;:&quot;Xor&quot;,&quot;bio&quot;:&quot;Shaders and GameMaker&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b37ca2-b4f8-4d1e-a083-93ad73418f1f_512x512.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2022-09-16T23:50:07.389Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F36640a88-fc3d-49a0-9f20-7160e56e4709_1920x1080.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://mini.gmshaders.com/p/gm-shaders-mini-raymarching-1351092&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:91667871,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:1,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;GM Shaders&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf69737-7d50-4dc8-9cce-aad9b9019215_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>I could go on with some cool technical goodies like: Ambient Occlusion, Depth of Field, collision detection, calculating normals, and more, but hopefully, this is enough to pique your interest! So now that you know a little about how they can be used, how do you actually find the formulas?</p><h1>How to find them</h1><p>As much as I like to reinvent the wheel, I can&#8217;t recommend writing your own SDFs just yet. <a href="https://iquilezles.org/articles/distfunctions2d">Inigo Quilez has done a ton of work to find efficient and exact formulas for many different shapes</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Rpxl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec1531f-1dda-412a-ba39-fdf4644b8040_1868x1017.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Rpxl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec1531f-1dda-412a-ba39-fdf4644b8040_1868x1017.png 424w, https://substackcdn.com/image/fetch/$s_!Rpxl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec1531f-1dda-412a-ba39-fdf4644b8040_1868x1017.png 848w, https://substackcdn.com/image/fetch/$s_!Rpxl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec1531f-1dda-412a-ba39-fdf4644b8040_1868x1017.png 1272w, https://substackcdn.com/image/fetch/$s_!Rpxl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec1531f-1dda-412a-ba39-fdf4644b8040_1868x1017.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Rpxl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec1531f-1dda-412a-ba39-fdf4644b8040_1868x1017.png" width="1456" height="793" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8ec1531f-1dda-412a-ba39-fdf4644b8040_1868x1017.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:793,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1914143,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Rpxl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec1531f-1dda-412a-ba39-fdf4644b8040_1868x1017.png 424w, https://substackcdn.com/image/fetch/$s_!Rpxl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec1531f-1dda-412a-ba39-fdf4644b8040_1868x1017.png 848w, https://substackcdn.com/image/fetch/$s_!Rpxl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec1531f-1dda-412a-ba39-fdf4644b8040_1868x1017.png 1272w, https://substackcdn.com/image/fetch/$s_!Rpxl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ec1531f-1dda-412a-ba39-fdf4644b8040_1868x1017.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">A list of 2D SDF primitives</figcaption></figure></div><p>And not just in 2D, <a href="https://iquilezles.org/articles/distfunctions">but in 3D as well</a>! If you&#8217;re just starting out, this is the best way to get a feel for SDFs. Once you get more comfortable, you can experiment with writing your own.</p><h1>Modifications</h1><p>Let&#8217;s look at some ways you can adapt your distance field for extra variations.</p><p>The first class of modifications is distance modifications:</p><h3>Distance Mods</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NTqb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0eedac-d4f4-4fa1-8cca-ca083627cfa9_1350x1350.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NTqb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0eedac-d4f4-4fa1-8cca-ca083627cfa9_1350x1350.png 424w, https://substackcdn.com/image/fetch/$s_!NTqb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0eedac-d4f4-4fa1-8cca-ca083627cfa9_1350x1350.png 848w, https://substackcdn.com/image/fetch/$s_!NTqb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0eedac-d4f4-4fa1-8cca-ca083627cfa9_1350x1350.png 1272w, https://substackcdn.com/image/fetch/$s_!NTqb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0eedac-d4f4-4fa1-8cca-ca083627cfa9_1350x1350.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NTqb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0eedac-d4f4-4fa1-8cca-ca083627cfa9_1350x1350.png" width="1350" height="1350" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1d0eedac-d4f4-4fa1-8cca-ca083627cfa9_1350x1350.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1350,&quot;width&quot;:1350,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2348953,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!NTqb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0eedac-d4f4-4fa1-8cca-ca083627cfa9_1350x1350.png 424w, https://substackcdn.com/image/fetch/$s_!NTqb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0eedac-d4f4-4fa1-8cca-ca083627cfa9_1350x1350.png 848w, https://substackcdn.com/image/fetch/$s_!NTqb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0eedac-d4f4-4fa1-8cca-ca083627cfa9_1350x1350.png 1272w, https://substackcdn.com/image/fetch/$s_!NTqb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0eedac-d4f4-4fa1-8cca-ca083627cfa9_1350x1350.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Top, left to right, star, rounded star. Bottom: hollow star and layered star.</figcaption></figure></div><p>We can make any shape rounded by subtracting by the desired thickness:</p><pre><code>//Set outline thickness around the shape
float round_dist = shape_dist - thickness;</code></pre><p><em><strong>Note</strong>: This will make the shape larger, so you will have to reduce the size by the thickness first, which will depend on the shape.</em></p><p>You can also create hollow shapes from any SDF like so:</p><pre><code>//Make hollow and set thickness
float hollow_dist = abs(shape_dist) - thickness;</code></pre><p>Just for fun, onion-layered distance fields can be made like this:</p><pre><code><code>//Repeat in spaced-out layers
float layered_edge = mod(shape_dist + spacing/2.0, spacing) - spacing/2.0;
//Set layer thickness
float layered_dist = abs(layered_edge) - thickness;</code></code></pre><p>Maybe it&#8217;s not the most practical, but it&#8217;s a neat trick.</p><h3>Set Operations</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2nY_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc15a3b-6017-4552-9a11-80ca7657610c_1350x1350.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2nY_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc15a3b-6017-4552-9a11-80ca7657610c_1350x1350.png 424w, https://substackcdn.com/image/fetch/$s_!2nY_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc15a3b-6017-4552-9a11-80ca7657610c_1350x1350.png 848w, https://substackcdn.com/image/fetch/$s_!2nY_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc15a3b-6017-4552-9a11-80ca7657610c_1350x1350.png 1272w, https://substackcdn.com/image/fetch/$s_!2nY_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc15a3b-6017-4552-9a11-80ca7657610c_1350x1350.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2nY_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc15a3b-6017-4552-9a11-80ca7657610c_1350x1350.png" width="1350" height="1350" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0fc15a3b-6017-4552-9a11-80ca7657610c_1350x1350.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1350,&quot;width&quot;:1350,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2326527,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2nY_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc15a3b-6017-4552-9a11-80ca7657610c_1350x1350.png 424w, https://substackcdn.com/image/fetch/$s_!2nY_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc15a3b-6017-4552-9a11-80ca7657610c_1350x1350.png 848w, https://substackcdn.com/image/fetch/$s_!2nY_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc15a3b-6017-4552-9a11-80ca7657610c_1350x1350.png 1272w, https://substackcdn.com/image/fetch/$s_!2nY_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc15a3b-6017-4552-9a11-80ca7657610c_1350x1350.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Top, left to right: Union and intersection. Bottom: subtracting circle and star</figcaption></figure></div><p>You can combine (union) two distance fields together by taking the minimum of both:</p><pre><code>//The union of two distances fields
float union_dist = min(shape_dist1, shape_dist2);</code></pre><p>When you think about it, it makes sense. The distance to the closest shape exterior edge will be the lesser of the two distances. And of course, to combine 3 or more shapes, you can just repeat it as needed.</p><p><em><strong>Note</strong>: it does break sometimes the <a href="https://iquilezles.org/articles/interiordistance">interior distance</a>, returning a greater distance than it should. These breaks aren&#8217;t usually a big deal, but they can break outline thickness or require more steps to raymarch!</em></p><p>To subtract one shape from another:</p><pre><code><code>//Subtract shape 2 from shape 1
float sub_dist1 = max(shape_dist1, -shape_dist2);

//Subtract shape 1 from shape 2
float sub_dist2 = max(shape_dist2, -shape_dist1);</code></code></pre><p>Or to find the overlapping/intersection of two shapes:</p><pre><code>//The intersection of two distances fields
float intersect_dist = max(shape_dist1, shape_dist2);</code></pre><p><em><strong>Note</strong>: Union can break the interior distance field, while intersection and subtraction break the exterior distance field. To my knowledge, this is still an unsolved problem, requiring case-by-case solutions or <a href="https://mini.gmshaders.com/p/gm-shaders-mini-jfa">JFA</a>. Read iq&#8217;s interior distance article for more insights into this problem.</em></p><h3>Spatial Mods</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Cpo8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2864627-34aa-4335-9ccb-1dc2c046293a_1350x675.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Cpo8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2864627-34aa-4335-9ccb-1dc2c046293a_1350x675.png 424w, https://substackcdn.com/image/fetch/$s_!Cpo8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2864627-34aa-4335-9ccb-1dc2c046293a_1350x675.png 848w, https://substackcdn.com/image/fetch/$s_!Cpo8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2864627-34aa-4335-9ccb-1dc2c046293a_1350x675.png 1272w, https://substackcdn.com/image/fetch/$s_!Cpo8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2864627-34aa-4335-9ccb-1dc2c046293a_1350x675.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Cpo8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2864627-34aa-4335-9ccb-1dc2c046293a_1350x675.png" width="1350" height="675" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e2864627-34aa-4335-9ccb-1dc2c046293a_1350x675.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:675,&quot;width&quot;:1350,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:938031,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Cpo8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2864627-34aa-4335-9ccb-1dc2c046293a_1350x675.png 424w, https://substackcdn.com/image/fetch/$s_!Cpo8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2864627-34aa-4335-9ccb-1dc2c046293a_1350x675.png 848w, https://substackcdn.com/image/fetch/$s_!Cpo8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2864627-34aa-4335-9ccb-1dc2c046293a_1350x675.png 1272w, https://substackcdn.com/image/fetch/$s_!Cpo8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2864627-34aa-4335-9ccb-1dc2c046293a_1350x675.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Left to right: mirroring the y-axis and tiling the space</figcaption></figure></div><p>What about modifying the coordinate space itself?</p><p>You can mirror the y-axis by using the absolute of y (flipping the negative back to positive):</p><pre><code>//Mirror the y-axis
pos.y = abs(pos.y);
//Use these mirrored coordinates with your SDF
float shape_dist = sdf(pos);</code></pre><p>You can also flip across an arbitrary (normalized) direction like so:</p><pre><code>//Flip in the direction "dir":
vec2 mirror_pos = pos - 2.0 * dir * max(dot(pos, dir), 0.0);</code></pre><p><em><strong>Note</strong>: This works best if you&#8217;re mirroring perpendicular to the shape&#8217;s edge. If you look closely at the left star example above, the mirrored shape creates concave edges, breaking the interior distance field. The new distance field doesn&#8217;t know there&#8217;s a corner there. Mirroring the bottom half will create a new concave edge and break the exterior.<br></em></p><p>You can also repeat shapes infinitely by tiling. The simplest way is with mod:</p><pre><code>//Repeat the coordinates
vec2 repeat_pos = mod(pos + spacing/2.0, spacing) - spacing/2.0;</code></pre><p>This method works for symmetrical shapes like circles and squares however for irregular shapes, it may take a little more work. For instance, these stars are symmetrical horizontally so the left and right sides tile seamlessly, but not vertically:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!u96U!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b682e0-085a-4478-95fa-9d2ac4944926_1350x675.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!u96U!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b682e0-085a-4478-95fa-9d2ac4944926_1350x675.png 424w, https://substackcdn.com/image/fetch/$s_!u96U!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b682e0-085a-4478-95fa-9d2ac4944926_1350x675.png 848w, https://substackcdn.com/image/fetch/$s_!u96U!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b682e0-085a-4478-95fa-9d2ac4944926_1350x675.png 1272w, https://substackcdn.com/image/fetch/$s_!u96U!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b682e0-085a-4478-95fa-9d2ac4944926_1350x675.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!u96U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b682e0-085a-4478-95fa-9d2ac4944926_1350x675.png" width="1350" height="675" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/27b682e0-085a-4478-95fa-9d2ac4944926_1350x675.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:675,&quot;width&quot;:1350,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:454841,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!u96U!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b682e0-085a-4478-95fa-9d2ac4944926_1350x675.png 424w, https://substackcdn.com/image/fetch/$s_!u96U!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b682e0-085a-4478-95fa-9d2ac4944926_1350x675.png 848w, https://substackcdn.com/image/fetch/$s_!u96U!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b682e0-085a-4478-95fa-9d2ac4944926_1350x675.png 1272w, https://substackcdn.com/image/fetch/$s_!u96U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27b682e0-085a-4478-95fa-9d2ac4944926_1350x675.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Simple mod tiling vs a vertical loop</figcaption></figure></div><p>In this case, it can be solved by sampling the distance field one unit above and below to find the minimum. <a href="https://iquilezles.org/articles/sdfrepetition">iq has a written great article on this and much more</a>!</p><h3>Distortion Mods</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ibku!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1f2c435-092e-4cc4-9f48-9d473d3c07aa_1350x1350.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ibku!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1f2c435-092e-4cc4-9f48-9d473d3c07aa_1350x1350.png 424w, https://substackcdn.com/image/fetch/$s_!ibku!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1f2c435-092e-4cc4-9f48-9d473d3c07aa_1350x1350.png 848w, https://substackcdn.com/image/fetch/$s_!ibku!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1f2c435-092e-4cc4-9f48-9d473d3c07aa_1350x1350.png 1272w, https://substackcdn.com/image/fetch/$s_!ibku!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1f2c435-092e-4cc4-9f48-9d473d3c07aa_1350x1350.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ibku!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1f2c435-092e-4cc4-9f48-9d473d3c07aa_1350x1350.png" width="1350" height="1350" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d1f2c435-092e-4cc4-9f48-9d473d3c07aa_1350x1350.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1350,&quot;width&quot;:1350,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2403471,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ibku!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1f2c435-092e-4cc4-9f48-9d473d3c07aa_1350x1350.png 424w, https://substackcdn.com/image/fetch/$s_!ibku!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1f2c435-092e-4cc4-9f48-9d473d3c07aa_1350x1350.png 848w, https://substackcdn.com/image/fetch/$s_!ibku!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1f2c435-092e-4cc4-9f48-9d473d3c07aa_1350x1350.png 1272w, https://substackcdn.com/image/fetch/$s_!ibku!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1f2c435-092e-4cc4-9f48-9d473d3c07aa_1350x1350.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Top, left to right: bending and smooth minimum. Bottom: waves and twisting.</figcaption></figure></div><p>If we don&#8217;t care about exact accuracy, there are many distortions that can be done.</p><p>Minor displacements can be added directly to the distance field for distortions.</p><p>Twisting and rotating can be applied directly to the coordinate spaces.</p><p>Smooth union, subtraction, and intersections are possible with approximations:</p><pre><code>//Exponential smooth minimum
float smin( float a, float b, float k )
{
    float r = exp2(-a/k) + exp2(-b/k);
    return -k*log2(r);
}</code></pre><p>Rather than reinvent the wheel, I&#8217;ll refer to <a href="https://iquilezles.org/articles/smin/">iq&#8217;s work with smooth blending functions here</a>.</p><p>Again, these are no longer exact distance fields, but distorted approximations, and the more distorted they become, the more difficult they may be to work with. Now I think we&#8217;re ready to talk about constructing our own distance functions!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://mini.gmshaders.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe for more tutorials like these. My work is supported by readers like you.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>
      <p>
          <a href="https://mini.gmshaders.com/p/sdf">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Gamma]]></title><description><![CDATA[How computers handle lighting]]></description><link>https://mini.gmshaders.com/p/gamma</link><guid isPermaLink="false">https://mini.gmshaders.com/p/gamma</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Fri, 24 Jan 2025 23:56:30 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/44da34e3-fd03-42f3-a623-569344f4a1f5_1920x1080.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi there!<br><br>Today, we&#8217;re tackling gamma correction inside shaders. Many have written about gamma correction, but I haven&#8217;t found any that satisfy my teaching goals, so I thought I&#8217;d write my own quick overview. Your time is precious, so I&#8217;ll save you trouble and start with why you should care about gamma.</p><p>You may know of Gamma as a brightness slider in games, but there&#8217;s much more to it than that. Color, specifically luminance, is often stored and displayed differently to how light behaves in real life. There are two main formats: linear color (as in real life) and Standard RGB or &#8220;sRGB&#8221; (encoded light). Images are stored in sRGB because it&#8217;s a more efficient use of bits, but it&#8217;s incorrect to do our lighting operations in sRGB, so we need to know how to convert back and forth.</p><p><a href="https://iquilezles.org/articles/gamma">Even common shader effects like blurs are often done incorrectly</a>, by not accounting for gamma correction. Here&#8217;s how gamma can affect blurring:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MvIT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae06d3c1-b278-46b6-9870-95128460acd4_3840x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MvIT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae06d3c1-b278-46b6-9870-95128460acd4_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!MvIT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae06d3c1-b278-46b6-9870-95128460acd4_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!MvIT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae06d3c1-b278-46b6-9870-95128460acd4_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!MvIT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae06d3c1-b278-46b6-9870-95128460acd4_3840x2160.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MvIT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae06d3c1-b278-46b6-9870-95128460acd4_3840x2160.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ae06d3c1-b278-46b6-9870-95128460acd4_3840x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:10538366,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!MvIT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae06d3c1-b278-46b6-9870-95128460acd4_3840x2160.png 424w, https://substackcdn.com/image/fetch/$s_!MvIT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae06d3c1-b278-46b6-9870-95128460acd4_3840x2160.png 848w, https://substackcdn.com/image/fetch/$s_!MvIT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae06d3c1-b278-46b6-9870-95128460acd4_3840x2160.png 1272w, https://substackcdn.com/image/fetch/$s_!MvIT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae06d3c1-b278-46b6-9870-95128460acd4_3840x2160.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Blurring an image with different gamma levels</figcaption></figure></div><p>So, gamma is not about the overall brightness of an image. Black is still black, and white is still white, but it does affect the distribution of brightness. Doing it correctly can make a big difference in the look and feel of your game!</p><h1>Encoding and Decoding</h1><p>You&#8217;ve probably heard of Gamma 2.2 before. It&#8217;s been the standard in most displays for a while now, but what does the &#8220;2.2&#8221; mean? Well, the gamma number is the exponent used to map the RGB back to the linear color. Some rare monitors might have a gamma of 2.0 or 2.4, but 2.2 is the general standard to assume.<br>Here&#8217;s an illustration of what that looks like:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jCw3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9ec757a-30ae-4427-87be-b07c3febd609_301x292.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jCw3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9ec757a-30ae-4427-87be-b07c3febd609_301x292.jpeg 424w, https://substackcdn.com/image/fetch/$s_!jCw3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9ec757a-30ae-4427-87be-b07c3febd609_301x292.jpeg 848w, https://substackcdn.com/image/fetch/$s_!jCw3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9ec757a-30ae-4427-87be-b07c3febd609_301x292.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!jCw3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9ec757a-30ae-4427-87be-b07c3febd609_301x292.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jCw3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9ec757a-30ae-4427-87be-b07c3febd609_301x292.jpeg" width="301" height="292" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e9ec757a-30ae-4427-87be-b07c3febd609_301x292.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:292,&quot;width&quot;:301,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;gamma curve&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="gamma curve" title="gamma curve" srcset="https://substackcdn.com/image/fetch/$s_!jCw3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9ec757a-30ae-4427-87be-b07c3febd609_301x292.jpeg 424w, https://substackcdn.com/image/fetch/$s_!jCw3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9ec757a-30ae-4427-87be-b07c3febd609_301x292.jpeg 848w, https://substackcdn.com/image/fetch/$s_!jCw3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9ec757a-30ae-4427-87be-b07c3febd609_301x292.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!jCw3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe9ec757a-30ae-4427-87be-b07c3febd609_301x292.jpeg 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Source: <a href="https://handlespixels.wordpress.com/2018/02/06/understanding-gamma-correction/">HandlesPixels.wordpress.com</a></figcaption></figure></div><p>The red line represents how colors are stored (black being on the left end of the spectrum and white being on the right). You can see that the gray values in the middle are shifted downward towards darker shades. If you want to reverse this curve, you need to apply the green decoding curve, which gets us back to linear color.<br><br>With linear color, you can do any color transformations you want (like additive blending), but then, when you&#8217;re done, you need to convert the output back to sRGB.<br>Here are my little approximate functions to do these conversions:</p><pre><code>//Standard gamma exponent
#define GAMMA 2.2

//Decode sRGB to linear color //Note: this is a standard approximation vec3 gamma_decode(vec3 srgb)
{
     return pow(srgb, vec3(GAMMA));
}

//Encode linear color to sRGB
vec3 gamma_encode(vec3 lrgb)
{
     return pow(lrgb, vec3(1.0/GAMMA));
}</code></pre><p><a href="https://www.shadertoy.com/view/XXyBRR">I&#8217;ve put together a simple example</a> using two point lights:</p><pre><code>//Screen coordinates from -1 to +1 (aspect ratio corrected)
vec2 suv = (gl_FragCoord*2.0 - u_resolution.xy) / u_resolution.y;
//2.2 gamma on the right
//Light source positions
vec2 pos = 0.4 * sin(u_time+vec2(0,11)) * cos(u_time*0.618);

//Red light
vec3 light1 = 1.0 / (1.0 + length(suv+pos) * vec3(1,5,9));
//Blue Light
vec3 light2 = 1.0 / (1.0 + length(suv-pos) * vec3(9,5,1));

//Linear color values
vec3 lin1 = gamma_decode(light1);
vec3 lin2 = gamma_decode(light2);

//Add light sources together
vec3 col = lin1+lin2;
//Convert back to sRGB
gl_FragColor = vec4(gamma_encode(col), 1.0);</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hWpX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9de46137-cabb-46a2-a878-36119190fc10_2560x1440.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hWpX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9de46137-cabb-46a2-a878-36119190fc10_2560x1440.png 424w, https://substackcdn.com/image/fetch/$s_!hWpX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9de46137-cabb-46a2-a878-36119190fc10_2560x1440.png 848w, https://substackcdn.com/image/fetch/$s_!hWpX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9de46137-cabb-46a2-a878-36119190fc10_2560x1440.png 1272w, https://substackcdn.com/image/fetch/$s_!hWpX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9de46137-cabb-46a2-a878-36119190fc10_2560x1440.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hWpX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9de46137-cabb-46a2-a878-36119190fc10_2560x1440.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9de46137-cabb-46a2-a878-36119190fc10_2560x1440.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hWpX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9de46137-cabb-46a2-a878-36119190fc10_2560x1440.png 424w, https://substackcdn.com/image/fetch/$s_!hWpX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9de46137-cabb-46a2-a878-36119190fc10_2560x1440.png 848w, https://substackcdn.com/image/fetch/$s_!hWpX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9de46137-cabb-46a2-a878-36119190fc10_2560x1440.png 1272w, https://substackcdn.com/image/fetch/$s_!hWpX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9de46137-cabb-46a2-a878-36119190fc10_2560x1440.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">On the left, no gamma correction, 2.2 gamma on the right</figcaption></figure></div><p>You can see the image is overexposed on the right and does not blend as cleanly. Anything that has to do with color blending or light should be done in linear color space! If you sample a texture, it will be in sRGB, so you&#8217;ll need to convert that to linear color, do your operations, and then finally convert the results back to sRGB.</p><h1>Exact sRGB</h1><p>Okay, I must admit I simplified a little bit. sRGB is a bit more complicated than gamma 2.2, but gamma 2.2 is a good approximation.</p><p>Here&#8217;s the real formula with a lot more magic numbers:</p><pre><code>//Decode sRGB to linear
vec3 SRGB_decode(vec3 srgb)
{
    return mix(
        srgb / 12.92,
        pow((srgb + 0.055) / 1.055, vec3(2.4)),
        step(0.04045, srgb)
    );
}

//Encode linear to sRGB
vec3 SRGB_encode(vec3 lrgb)
{
    return mix(
        12.92 * lrgb,
        1.055 * pow(lrgb, vec3(1.0 / 2.4)) - 0.055,
        step(0.0031308, lrgb)
    );
}</code></pre><p><a href="https://www.shadertoy.com/view/M3KBR3">The difference is quite subtle</a>, but it does make a difference with darker shades and the upper middle:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RXdA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa358481-ea41-4193-a805-4570518cd4e3_800x450.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RXdA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa358481-ea41-4193-a805-4570518cd4e3_800x450.png 424w, https://substackcdn.com/image/fetch/$s_!RXdA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa358481-ea41-4193-a805-4570518cd4e3_800x450.png 848w, https://substackcdn.com/image/fetch/$s_!RXdA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa358481-ea41-4193-a805-4570518cd4e3_800x450.png 1272w, https://substackcdn.com/image/fetch/$s_!RXdA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa358481-ea41-4193-a805-4570518cd4e3_800x450.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RXdA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa358481-ea41-4193-a805-4570518cd4e3_800x450.png" width="800" height="450" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aa358481-ea41-4193-a805-4570518cd4e3_800x450.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:450,&quot;width&quot;:800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!RXdA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa358481-ea41-4193-a805-4570518cd4e3_800x450.png 424w, https://substackcdn.com/image/fetch/$s_!RXdA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa358481-ea41-4193-a805-4570518cd4e3_800x450.png 848w, https://substackcdn.com/image/fetch/$s_!RXdA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa358481-ea41-4193-a805-4570518cd4e3_800x450.png 1272w, https://substackcdn.com/image/fetch/$s_!RXdA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa358481-ea41-4193-a805-4570518cd4e3_800x450.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Red: sRGB, green: gamma 2.2, blue: linear</figcaption></figure></div><p>In professional contexts where precision is essential, you&#8217;ll want to use the exact sRGB formula, but in most contexts, gamma 2.2 is a good, cheaper approximation. It&#8217;s way better than nothing!</p><h1>Gamma Origins</h1><p>Now that we covered the basics, here&#8217;s a little background context.<br>The sRGB format originated because the old Cathode-Ray Tube displays had a non-linear relationship between the input voltage and the output pixel brightness (usually a gamma of 2.2 to 2.5). This was convenient because our eyes do not perceive lightness linearly either, and we are more sensitive to changes in darker shades than lighter ones, so if we have a limited number of bits to store colors, we should store more darker shades. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6uJB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a7e36b4-a6b7-4931-9266-173bfc3bcf9a_964x497.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6uJB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a7e36b4-a6b7-4931-9266-173bfc3bcf9a_964x497.png 424w, https://substackcdn.com/image/fetch/$s_!6uJB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a7e36b4-a6b7-4931-9266-173bfc3bcf9a_964x497.png 848w, https://substackcdn.com/image/fetch/$s_!6uJB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a7e36b4-a6b7-4931-9266-173bfc3bcf9a_964x497.png 1272w, https://substackcdn.com/image/fetch/$s_!6uJB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a7e36b4-a6b7-4931-9266-173bfc3bcf9a_964x497.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6uJB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a7e36b4-a6b7-4931-9266-173bfc3bcf9a_964x497.png" width="964" height="497" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1a7e36b4-a6b7-4931-9266-173bfc3bcf9a_964x497.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:497,&quot;width&quot;:964,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;How to Take Control of Color Space | Babylon.js Documentation&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="How to Take Control of Color Space | Babylon.js Documentation" title="How to Take Control of Color Space | Babylon.js Documentation" srcset="https://substackcdn.com/image/fetch/$s_!6uJB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a7e36b4-a6b7-4931-9266-173bfc3bcf9a_964x497.png 424w, https://substackcdn.com/image/fetch/$s_!6uJB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a7e36b4-a6b7-4931-9266-173bfc3bcf9a_964x497.png 848w, https://substackcdn.com/image/fetch/$s_!6uJB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a7e36b4-a6b7-4931-9266-173bfc3bcf9a_964x497.png 1272w, https://substackcdn.com/image/fetch/$s_!6uJB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a7e36b4-a6b7-4931-9266-173bfc3bcf9a_964x497.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Source: <a href="https://doc.babylonjs.com/preparingArtForBabylon/controllingColorSpace/#luminance-and-how-we-perceive-color-value">babylonjs.com</a></figcaption></figure></div><p>Camera sensors capture light in linear color but store their images with gamma encoding for image storage efficiency. This is why it&#8217;s crucial to convert images back to linear color before you do anything with them!</p><h1>Conclusion</h1><p>As with many topics, gamma correction can be quite complicated, and this tutorial only scratches the surface. I hope that this will make the concepts more approachable so that you can do further experimenting and learning as you go, just with a bit more context about how computers handle colors.<br><br>If I could reduce this tutorial down to one point, it&#8217;d be that computers store colors at the power of 1/2.2 (sRGB), and if you want to do any math with your colors, you should convert to linear first and then finally convert the end results back to sRGB. This one lesson is actually super important, and not enough shader people do this, so it&#8217;s best to start now! As always, I&#8217;m including some additional resources below</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://mini.gmshaders.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">That&#8217;s all for this week! If these sorts of tutorials interest you, please consider subscribing! Many more tutorials are on the way!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h1>Extras</h1><p>John Novak has an <a href="https://blog.johnnovak.net/2016/09/21/what-every-coder-should-know-about-gamma/">excellent and thorough guide on everything about gamma</a>! If you need a little more clarity on anything, I&#8217;d check here first!<br><br>Kostas Anagnostou wrote about the <a href="https://interplayoflight.wordpress.com/2025/01/19/the-hidden-cost-of-shader-instructions/">hidden cost of shader instruction,s</a> which explains why some functions are much slower than others. Avoid the arc trigonometry functions like atan and aco,s particularly!<br><br>Thanks for reading! Have a great night!<br>-Xor</p>]]></content:encoded></item><item><title><![CDATA[Anti-Aliasing]]></title><description><![CDATA[How to filter your shaders]]></description><link>https://mini.gmshaders.com/p/antialiasing</link><guid isPermaLink="false">https://mini.gmshaders.com/p/antialiasing</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Sat, 11 Jan 2025 23:51:55 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/c64ed775-52a6-41e0-9585-7d7938367c38_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi! Happy 2025. It&#8217;s been a long time (insert Dad joke about the new year here), but I&#8217;m back and ready to get some new tutorials done!<br><br>Today&#8217;s tutorial is about &#8220;analytic anti-aliasing&#8221;. Anti-aliasing is all about producing soft, natural-looking edges in your shaders. Here&#8217;s an image to illustrate the difference between an unfiltered and filtered checkerboard pattern:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BfTH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03194c4f-981c-4997-b1bf-c08523de4426_2250x1265.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BfTH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03194c4f-981c-4997-b1bf-c08523de4426_2250x1265.png 424w, https://substackcdn.com/image/fetch/$s_!BfTH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03194c4f-981c-4997-b1bf-c08523de4426_2250x1265.png 848w, https://substackcdn.com/image/fetch/$s_!BfTH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03194c4f-981c-4997-b1bf-c08523de4426_2250x1265.png 1272w, https://substackcdn.com/image/fetch/$s_!BfTH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03194c4f-981c-4997-b1bf-c08523de4426_2250x1265.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BfTH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03194c4f-981c-4997-b1bf-c08523de4426_2250x1265.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/03194c4f-981c-4997-b1bf-c08523de4426_2250x1265.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:388850,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!BfTH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03194c4f-981c-4997-b1bf-c08523de4426_2250x1265.png 424w, https://substackcdn.com/image/fetch/$s_!BfTH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03194c4f-981c-4997-b1bf-c08523de4426_2250x1265.png 848w, https://substackcdn.com/image/fetch/$s_!BfTH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03194c4f-981c-4997-b1bf-c08523de4426_2250x1265.png 1272w, https://substackcdn.com/image/fetch/$s_!BfTH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03194c4f-981c-4997-b1bf-c08523de4426_2250x1265.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Left: unfiltered, Right: analytic anti-aliasing. <a href="https://www.shadertoy.com/view/XlcSz2">This is from iq&#8217;s ShaderToy demo</a></figcaption></figure></div><p><a href="https://en.wikipedia.org/wiki/Aliasing">Aliasing</a> occurs when we have more color data than we can fit in the pixels. There are many ways to approach this. We could <a href="https://en.wikipedia.org/wiki/Supersampling">sample multiple points per pixel</a> and average the results, but this can get expensive at higher resolutions.<br>In real life, cameras don&#8217;t have this issue because the pixel sensors receive zillions of photons to construct an image. They aren&#8217;t limited to one sample per pixel. We don&#8217;t have the luxury of simulating every photon, so we&#8217;ll have to find a way to approximate them.</p><p>Previously, we looked at a post-processing solution:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;6c6b9644-49d1-4ace-a4bd-70735c068893&quot;,&quot;caption&quot;:&quot;Hi again! Today, we&#8217;re gonna take a look at the famous Fast Approximate Anti-Aliasing algorithm.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;GM Shaders Mini: FXAA&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:115837202,&quot;name&quot;:&quot;Xor&quot;,&quot;bio&quot;:&quot;Shaders and GameMaker&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b37ca2-b4f8-4d1e-a083-93ad73418f1f_512x512.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2023-04-22T01:07:03.781Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/39c26127-5173-48bc-b658-493a8348a5d9_1920x1080.jpeg&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://mini.gmshaders.com/p/gm-shaders-mini-fxaa&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:112910625,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:5,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;GM Shaders&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf69737-7d50-4dc8-9cce-aad9b9019215_512x512.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>FXAA is a decent generalized solution, but it sometimes can cause unintended softening or other artifacts. There&#8217;s only so much that can be done with post-processing on the pixel colors. Let&#8217;s take a deeper look at the anti-aliasing process to expand our toolset!</p><h1>Circle Example</h1><p>The simplest example I could think of is drawing a circle. You could draw a circle by checking if a pixel is within the radius of the circle:</p><p><code>gl_FragColor = vec4(dist &lt; rad);</code></p><p>This method introduces hard pixel edges. Alternatively, if you know (or can approximate) how close a pixel is to the edge, you can do a little sub-pixel gradient along that edge. This is easy to do with a circle because we already have the distance.</p><p>Here&#8217;s a close-up illustration of a circle with and without anti-aliasing:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!b-IA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc59654f-e3f8-48d2-8601-36654905f268_1920x1080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!b-IA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc59654f-e3f8-48d2-8601-36654905f268_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!b-IA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc59654f-e3f8-48d2-8601-36654905f268_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!b-IA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc59654f-e3f8-48d2-8601-36654905f268_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!b-IA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc59654f-e3f8-48d2-8601-36654905f268_1920x1080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!b-IA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc59654f-e3f8-48d2-8601-36654905f268_1920x1080.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fc59654f-e3f8-48d2-8601-36654905f268_1920x1080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:90621,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!b-IA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc59654f-e3f8-48d2-8601-36654905f268_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!b-IA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc59654f-e3f8-48d2-8601-36654905f268_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!b-IA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc59654f-e3f8-48d2-8601-36654905f268_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!b-IA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc59654f-e3f8-48d2-8601-36654905f268_1920x1080.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Hard edges on the left, gradient for anti-aliasing on the right</figcaption></figure></div><p>The green dots represent the centers of each pixel. On the left side of the blue line, we simply check if the distance from the pixel to the center of the circle is within the radius. On the right side, we estimate how much of the pixel is within the radius. This could be calculated with complicated integrals of our function, but that is overkill!</p><p>Instead, a simple and effective approximation is to use the same distance value as earlier, but we blend from 0 to 1. So if the center of the pixel is on the radius line, we should expect 50% opacity. 50% roughly of the pixel is within the radius and 50% is out (it depends on the radius, but the effects are negligible). We should expect 100% opacity when the circle_radius - pixel_distance is greater than 0.5 pixels because it means that the radius is at the outside edge of the pixel. And we can expect 0% opacity when less than -0.5 pixels because that is the inside edge of the pixel.</p><p></p><p>So now all we need to do is make sure the distance is in pixel units and clamp the gradient to the [0, 1] range. We can divide by our &#8220;texel&#8221; scale to correct for</p><p><code>float gradient = clamp((radius - dist) / texel + 0.5, 0.0, 1.0);</code></p><p>Now the opacity can be set based on how close the pixel is to the edge, instead of just doing a distance check. <a href="https://www.shadertoy.com/view/XcKcWh">I&#8217;ve put together a ShaderToy demo for more details here</a>.</p><p>The best part is this method works with all <a href="https://iquilezles.org/articles/distfunctions2d/">Signed Distance Fields</a>! All you need is to correct the scale of the distance field so that the 0 to 1 range matches the pixel scale. This method is perfect when you know the distance to the edges and the scale, but what if it&#8217;s not a perfect distance field? What if the gradient is inconsistent? A common example is with noise functions where some areas may have steep gradients while other spots can be flat. If this is your case, we&#8217;ll need to go further!</p><h1>Derivative Methods</h1><p>Let&#8217;s look at an example with a noise function. We can still get a gradient edge by doing: <code>some_threshold - noise_value</code>, which should generally return higher values as we get further from the edge, but not reliably. Noise is supposed to be unpredictable so it makes this more difficult. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lUAI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3345fd3-a942-4377-ac0b-b9a32e5c7d9b_1420x800.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lUAI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3345fd3-a942-4377-ac0b-b9a32e5c7d9b_1420x800.png 424w, https://substackcdn.com/image/fetch/$s_!lUAI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3345fd3-a942-4377-ac0b-b9a32e5c7d9b_1420x800.png 848w, https://substackcdn.com/image/fetch/$s_!lUAI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3345fd3-a942-4377-ac0b-b9a32e5c7d9b_1420x800.png 1272w, https://substackcdn.com/image/fetch/$s_!lUAI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3345fd3-a942-4377-ac0b-b9a32e5c7d9b_1420x800.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lUAI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3345fd3-a942-4377-ac0b-b9a32e5c7d9b_1420x800.png" width="1420" height="800" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e3345fd3-a942-4377-ac0b-b9a32e5c7d9b_1420x800.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:800,&quot;width&quot;:1420,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:36817,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lUAI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3345fd3-a942-4377-ac0b-b9a32e5c7d9b_1420x800.png 424w, https://substackcdn.com/image/fetch/$s_!lUAI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3345fd3-a942-4377-ac0b-b9a32e5c7d9b_1420x800.png 848w, https://substackcdn.com/image/fetch/$s_!lUAI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3345fd3-a942-4377-ac0b-b9a32e5c7d9b_1420x800.png 1272w, https://substackcdn.com/image/fetch/$s_!lUAI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3345fd3-a942-4377-ac0b-b9a32e5c7d9b_1420x800.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Anti-aliasing with constant scale versus dynamic scale</figcaption></figure></div><p>If we try to use a constant scaling factor for the edges as on the left, some edges may be too soft while others could be too sharp. On the right, we are using the same method as before, but we&#8217;re calculating the gradient scale for every pixel, which leads to more consistent edges.</p><p>This is still an approximation, but it&#8217;s a step in the right direction and a big improvement on our previous method!<br>We can approximate the rate of change in the gradient by using derivatives. Definitely read my article on the topic if you haven&#8217;t yet because it&#8217;s important here!</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;768c7bed-8d6f-4b5f-88e7-3d9a8b4d2cc1&quot;,&quot;caption&quot;:&quot;Today, let's unpack &#8220;derivatives&#8221;. What are they for and how do they work?&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;GM Shaders Mini: Derivatives&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:115837202,&quot;name&quot;:&quot;Xor&quot;,&quot;bio&quot;:&quot;Shaders and GameMaker&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b37ca2-b4f8-4d1e-a083-93ad73418f1f_512x512.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2023-03-31T23:24:24.364Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fbd1fe1f-7990-4747-b00a-58756c11d818_1280x720.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://mini.gmshaders.com/p/gm-shaders-mini-derivatives&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:108188146,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:19,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;GM Shaders&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf69737-7d50-4dc8-9cce-aad9b9019215_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>The super simple solution here is to use <code>fwidth()</code> to calculate our scaling factor for us. The best part is that this works with most things out of the box:</p><pre><code><code>//Approximate the smooth edges from any continuous function
float antialias_l1(float d)
{
    //Divide d by it's derivative width
    return clamp(0.5 +  d / fwidth(d), 0.0, 1.0);
}</code></code></pre><p><strong>Note</strong>: <em>We should be careful about potentially dividing by 0 here! You can do a quick check like so: </em><code>float scale = width&gt;0.0? 1.0/width : 1e7;</code></p><p>I like to take things a little further and calculate my fwidth manually with length. <a href="https://www.shadertoy.com/view/7dfGR4">This produces better results</a> at a slightly higher cost:</p><pre><code>//Approximate the smooth edges from any continuous function
float antialias_l2(float d)
{
    //x and y derivatives
    vec2 dxy = vec2(dFdx(d), dFdy(d));    
    //Get gradient width
    float width = length(dxy);
    //Calculate reciprocal scale (avoid division by 0!)
    float scale = width&gt;0.0? 1.0/width : 1e7;
    //Normalize the gradient d with its scale
    return clamp(0.5 + 0.7 * scale * d, 0.0, 1.0);
}</code></pre><p>This magic formula can blend the edges of just about any continuous function. It can handle a heavily distorted distance field, noise, and gradients of any sort. It&#8217;s only weakness is discontinuous functions since they break the derivatives. A distance expression like <code>dist = floor(x/10.0)</code> will not work because it jumps in steps and there is no gradient width to even approximate the scale with. If this is an issue for your shader needs, continue to the final step!</p><h1>Edge Cases</h1><p>There may be some instances where the partial derivatives computed with dFdx and dFdy are not good enough. Even something simple like a checkerboard can seem challenging to do, but there are two ways to solve it. You can either try to get rid of the discontinuities or you can calculate the derivative manually. A checkerboard can also be achieved with a formula like <code>sin(x*PI)*sin(y*PI)</code>. This gives a continuous function that ranges from -1 to 1.</p><p>Also, sometimes  2x2 derivative chunks are not suitable for your shader or not precise enough. In these cases, you may have to calculate the derivatives manually. </p><p>Here&#8217;s an example of artifacts produced from coarse derivatives:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WFlj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440db560-4f82-4f03-9476-7aa675d52522_2560x1440.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WFlj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440db560-4f82-4f03-9476-7aa675d52522_2560x1440.png 424w, https://substackcdn.com/image/fetch/$s_!WFlj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440db560-4f82-4f03-9476-7aa675d52522_2560x1440.png 848w, https://substackcdn.com/image/fetch/$s_!WFlj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440db560-4f82-4f03-9476-7aa675d52522_2560x1440.png 1272w, https://substackcdn.com/image/fetch/$s_!WFlj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440db560-4f82-4f03-9476-7aa675d52522_2560x1440.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WFlj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440db560-4f82-4f03-9476-7aa675d52522_2560x1440.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/440db560-4f82-4f03-9476-7aa675d52522_2560x1440.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WFlj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440db560-4f82-4f03-9476-7aa675d52522_2560x1440.png 424w, https://substackcdn.com/image/fetch/$s_!WFlj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440db560-4f82-4f03-9476-7aa675d52522_2560x1440.png 848w, https://substackcdn.com/image/fetch/$s_!WFlj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440db560-4f82-4f03-9476-7aa675d52522_2560x1440.png 1272w, https://substackcdn.com/image/fetch/$s_!WFlj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F440db560-4f82-4f03-9476-7aa675d52522_2560x1440.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Checkboard with coarse derivatives on left versus manual derivatives on right</figcaption></figure></div><p>For these scenarios, we&#8217;ll use this function: </p><pre><code>//For when the derivatives must be manually calculated
float antialias_l2_dxy(float d, vec2 dxy)
{
    //Get gradient width
    float width = length(dxy);
    //Calculate reciprocal scale (avoid division by 0!)
    float scale = width&gt;0.0? 1.0/width : 1e7;
    //Normalize the gradient d with its scale
    return clamp(0.5 + 0.7 * scale * d, 0.0, 1.0);
}</code></pre><p>Then we can generate our own partial derivatives like so:</p><pre><code>//Get gradient (neighbors for manual derivatives) 
float grad00 = grad(pos);
float grad10 = grad(pos+vec2(1,0));
float grad01 = grad(pos+vec2(0,1));
//Compute the xy derivatives
vec2 dxy = vec2(grad10, grad01) - grad00;</code></pre><p> <strong>Note</strong>: <em>This is more expensive than the native derivative functions, which compute derivatives for nearly free! This is sampling our gradient function 3 times, so it should be used cautiously with larger or more complicated functions.</em></p><p>This manual derivative method can be used for anti-aliasing on multiple layers. A common example is with stripy patterns that have many edges:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!E-AB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47429fc2-7287-41f7-b70e-2ed18be90717_1200x675.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!E-AB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47429fc2-7287-41f7-b70e-2ed18be90717_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!E-AB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47429fc2-7287-41f7-b70e-2ed18be90717_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!E-AB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47429fc2-7287-41f7-b70e-2ed18be90717_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!E-AB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47429fc2-7287-41f7-b70e-2ed18be90717_1200x675.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!E-AB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47429fc2-7287-41f7-b70e-2ed18be90717_1200x675.png" width="1200" height="675" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/47429fc2-7287-41f7-b70e-2ed18be90717_1200x675.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:675,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!E-AB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47429fc2-7287-41f7-b70e-2ed18be90717_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!E-AB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47429fc2-7287-41f7-b70e-2ed18be90717_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!E-AB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47429fc2-7287-41f7-b70e-2ed18be90717_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!E-AB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47429fc2-7287-41f7-b70e-2ed18be90717_1200x675.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Circular stripy pattern</figcaption></figure></div><p>Let&#8217;s say we have a stripe gradient &#8220;grad&#8221; that we want to range from 0 to 5 for 5 stripes. The edges separating the stripes, can be found with <code>fract(grad) - 0.5</code>, but fract is discontinuous and will break our derivatives. So for this, we want to calculate our derivatives on the original &#8220;grad&#8221; variable. <a href="https://www.shadertoy.com/view/l3VcWG">I&#8217;ve put together a shader example here</a>.</p><p>This same method can even be applied to pixel art anti-aliasing, but you do it on the x and y axes simultaneously using texture interpolation. I&#8217;m not going to go over the details here, but there are <a href="https://youtu.be/d6tp43wZqps">excellent resources out there on this topic if you&#8217;re interested</a>!</p><h1>Conclusion</h1><p>To summarize, anti-aliasing is all about smoothly filtered edges without requiring rendering a higher resolution and downscaling. Today we covered the main 3 levels of filtering which can be used depending on the situation.</p><p>For simple shapes like circles, lines, squares, or Signed Distance Fields, it&#8217;s pretty trivial to generate smooth edges cheaply and there are very few reasons not to! It&#8217;s just a matter of matching the gradients to the pixel scale for consistent edge thickness.</p><p><br>Most other shaders will have more complicated shapes where we won&#8217;t have the luxury of knowing the exact distance to the nearest edge, but that&#8217;s okay. As long as we have a gradient that generally points towards the nearest edge, we can approximate the distance with derivatives (measuring the rates of change). The math can sound scary, but it&#8217;s usually not much more than <code>fwidth()</code>. Using the <code>antialias_l2() </code>function above should handle most situations without much work!</p><p>Finally, there are scenarios where you don&#8217;t even have a gradient or where gradient is discontinuous (has breaks in it). These cases require a little more work on a case-by-case basis, but many times you can rework it to make the gradient continuous or calculate the derivatives using a continuous, break-less version of the gradient. The derivatives only care about the rate of change and not the absolute value, so they can use independent formulas!<br><br>Hopefully, this guide gives you a good idea of how anti-aliasing actually works and how you can start to implement it into your own shaders! This is something every shader programmer should know because it&#8217;s useful all of the time. It&#8217;s better to produce smooth images from the start than to produce something rough and try to filter it after the fact.</p><h1>Extras</h1><p>&#8220;<a href="https://lisyarus.github.io/blog/posts/exploring-ways-to-mipmap-alpha-tested-textures.html">Exploring ways to mipmap alpha-tested textures</a>&#8221; by &#8234;Nikita Lisitsa&#8236; (@lisyarus)</p><p>This is a nice overview on handling grass or vegetation textures in large-scale games. Grass textures tend to suffer when viewed from a distance because they are either too sharp (aliasing) or too blobby, but SDFs can be used to address that.</p><p></p><p><a href="https://x.com/KostasAAA/status/1877760801844384126">Here&#8217;s a reminder</a> of why you should avoid using a lot of trig functions, especially atan2. Generally speaking, if you are converting a vector to an angle (with atan) and then back to a vector (with cos/sin) you&#8217;re probably overcomplicating something. Many operations can be applied directly to vectors and that&#8217;s how shaders are designed to operate! One atan2 here and there isn&#8217;t the end of the world, but it&#8217;s something to consider if you&#8217;re doing it a lot.<br><br>Alright, that&#8217;s it for now.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://mini.gmshaders.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">GM Shaders is supported by people like you! Thanks for reading and I&#8217;ll be writing more soon</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Vis Dev]]></title><description><![CDATA[Doctoring programmer art up with visual design techniques]]></description><link>https://mini.gmshaders.com/p/visdev</link><guid isPermaLink="false">https://mini.gmshaders.com/p/visdev</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Sun, 01 Sep 2024 00:05:08 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/5667ab6c-2328-42c2-a837-c6b98a14dbaf_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello there,<br><br>I don&#8217;t know about you, but I&#8217;m more of a programmer than an artist. I find writing fancy shaders much easier than hand drawing sprites (pixel art is okay). Over the years, I&#8217;ve learned some tricks that helped me work with my own programmer art</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HNVT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3beb9a7b-f82c-4942-9961-a8c306b64ca8_640x461.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HNVT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3beb9a7b-f82c-4942-9961-a8c306b64ca8_640x461.jpeg 424w, https://substackcdn.com/image/fetch/$s_!HNVT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3beb9a7b-f82c-4942-9961-a8c306b64ca8_640x461.jpeg 848w, https://substackcdn.com/image/fetch/$s_!HNVT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3beb9a7b-f82c-4942-9961-a8c306b64ca8_640x461.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!HNVT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3beb9a7b-f82c-4942-9961-a8c306b64ca8_640x461.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HNVT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3beb9a7b-f82c-4942-9961-a8c306b64ca8_640x461.jpeg" width="640" height="461" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3beb9a7b-f82c-4942-9961-a8c306b64ca8_640x461.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:461,&quot;width&quot;:640,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Does Graphic Design Need a Rebrand?&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Does Graphic Design Need a Rebrand?" title="Does Graphic Design Need a Rebrand?" srcset="https://substackcdn.com/image/fetch/$s_!HNVT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3beb9a7b-f82c-4942-9961-a8c306b64ca8_640x461.jpeg 424w, https://substackcdn.com/image/fetch/$s_!HNVT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3beb9a7b-f82c-4942-9961-a8c306b64ca8_640x461.jpeg 848w, https://substackcdn.com/image/fetch/$s_!HNVT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3beb9a7b-f82c-4942-9961-a8c306b64ca8_640x461.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!HNVT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3beb9a7b-f82c-4942-9961-a8c306b64ca8_640x461.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Games like SUPERHOT, <a href="https://store.steampowered.com/app/385710/INK/">INK</a>, <a href="https://gamemaker.io/en/showcase/forager">Forager</a>, <a href="https://gamemaker.io/en/showcase/buzz">BUZZ</a>, <a href="https://gamemaker.io/en/showcase/otxo">OTXO</a>, Thomas was alone, Minecraft and Inside are all great examples of well-polished games with relatively simple graphics.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://mini.gmshaders.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">GM Shaders is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pfwE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf5041e6-e9e2-49de-ac5f-5047439a02f6_1920x1080.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pfwE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf5041e6-e9e2-49de-ac5f-5047439a02f6_1920x1080.jpeg 424w, https://substackcdn.com/image/fetch/$s_!pfwE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf5041e6-e9e2-49de-ac5f-5047439a02f6_1920x1080.jpeg 848w, https://substackcdn.com/image/fetch/$s_!pfwE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf5041e6-e9e2-49de-ac5f-5047439a02f6_1920x1080.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!pfwE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf5041e6-e9e2-49de-ac5f-5047439a02f6_1920x1080.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pfwE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf5041e6-e9e2-49de-ac5f-5047439a02f6_1920x1080.jpeg" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/af5041e6-e9e2-49de-ac5f-5047439a02f6_1920x1080.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;SUPERHOT VR on Steam&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="SUPERHOT VR on Steam" title="SUPERHOT VR on Steam" srcset="https://substackcdn.com/image/fetch/$s_!pfwE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf5041e6-e9e2-49de-ac5f-5047439a02f6_1920x1080.jpeg 424w, https://substackcdn.com/image/fetch/$s_!pfwE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf5041e6-e9e2-49de-ac5f-5047439a02f6_1920x1080.jpeg 848w, https://substackcdn.com/image/fetch/$s_!pfwE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf5041e6-e9e2-49de-ac5f-5047439a02f6_1920x1080.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!pfwE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf5041e6-e9e2-49de-ac5f-5047439a02f6_1920x1080.jpeg 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Screenshot from <a href="https://store.steampowered.com/app/322500/SUPERHOT/">SUPERHOT</a></figcaption></figure></div><p>Today&#8217;s tutorial is all about how visual design concepts and post-processing can improve the visuals of just about any game. Visual appeal is a somewhat subjective topic with a wide range of perspectives and opinions. I&#8217;m not here to tell you what style to use, but I would like to share concepts to consider when designing your games.</p><h1>Color Palette</h1><p>The first thing I&#8217;d consider is color palette. Coloring can be tricky, and it heavily depends on your vision for your game. Generally, it seems that programmers tend to use too many vibrant colors. It&#8217;s an easy mistake to make. If you&#8217;re not certain about coloring, you should probably use fewer colors. SUPERHOT and OXTO are great examples of games that use limited palettes of just 2 or 3 colors, producing a nice and consistent aesthetic. Dithering is also a great tool for stylizing some games. </p><p>Here&#8217;s an example from <a href="https://x.com/Housefiregames">Eclipsium</a>:</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;2bc6da41-cde1-4a93-9135-cb1a29cda040&quot;,&quot;duration&quot;:null}"></div><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;6964233f-53db-4837-9848-730fe786dcd6&quot;,&quot;caption&quot;:&quot;Dithering can be used for cartoon or retro stylization, or it can be used to make more expensive effects look smooth with less of a performance cost&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;md&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;GM Shaders Mini: Dither&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:115837202,&quot;name&quot;:&quot;Xor&quot;,&quot;bio&quot;:&quot;Shaders and GameMaker&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b37ca2-b4f8-4d1e-a083-93ad73418f1f_512x512.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2023-07-25T21:35:02.685Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1bf1e8a0-de30-4abf-82d4-eb2ae8909f62_1920x1080.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://mini.gmshaders.com/p/gm-shaders-mini-dither&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:135338899,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:10,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;GM Shaders&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf69737-7d50-4dc8-9cce-aad9b9019215_512x512.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p><br>Here, I&#8217;ve applied a range of color filters and dithering directly to my textures:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PyR3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F705f7706-d94e-49d3-8c35-507ba52af478_1920x1080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PyR3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F705f7706-d94e-49d3-8c35-507ba52af478_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!PyR3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F705f7706-d94e-49d3-8c35-507ba52af478_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!PyR3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F705f7706-d94e-49d3-8c35-507ba52af478_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!PyR3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F705f7706-d94e-49d3-8c35-507ba52af478_1920x1080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PyR3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F705f7706-d94e-49d3-8c35-507ba52af478_1920x1080.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/705f7706-d94e-49d3-8c35-507ba52af478_1920x1080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3462634,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!PyR3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F705f7706-d94e-49d3-8c35-507ba52af478_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!PyR3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F705f7706-d94e-49d3-8c35-507ba52af478_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!PyR3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F705f7706-d94e-49d3-8c35-507ba52af478_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!PyR3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F705f7706-d94e-49d3-8c35-507ba52af478_1920x1080.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">4 variants of my voxel game with different styles</figcaption></figure></div><p>In this particular case, I went with the left most coloring by <a href="https://x.com/RowanFuture">@RowanFuture</a>. It feels quite natural and while still being vibrant and warm, but you can probably imagine other types of games where the other aesthetics may be more appropriate.<br></p><p>It&#8217;s also a great idea to look at your screenshots in an image editor like GIMP</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!T8ju!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27dd8506-eb10-4743-abcc-7067047db638_1279x720.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!T8ju!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27dd8506-eb10-4743-abcc-7067047db638_1279x720.png 424w, https://substackcdn.com/image/fetch/$s_!T8ju!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27dd8506-eb10-4743-abcc-7067047db638_1279x720.png 848w, https://substackcdn.com/image/fetch/$s_!T8ju!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27dd8506-eb10-4743-abcc-7067047db638_1279x720.png 1272w, https://substackcdn.com/image/fetch/$s_!T8ju!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27dd8506-eb10-4743-abcc-7067047db638_1279x720.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!T8ju!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27dd8506-eb10-4743-abcc-7067047db638_1279x720.png" width="1279" height="720" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/27dd8506-eb10-4743-abcc-7067047db638_1279x720.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:720,&quot;width&quot;:1279,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1116128,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!T8ju!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27dd8506-eb10-4743-abcc-7067047db638_1279x720.png 424w, https://substackcdn.com/image/fetch/$s_!T8ju!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27dd8506-eb10-4743-abcc-7067047db638_1279x720.png 848w, https://substackcdn.com/image/fetch/$s_!T8ju!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27dd8506-eb10-4743-abcc-7067047db638_1279x720.png 1272w, https://substackcdn.com/image/fetch/$s_!T8ju!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27dd8506-eb10-4743-abcc-7067047db638_1279x720.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Adjusting brightness and contrast with GIMP</figcaption></figure></div><p>GIMP shows you a &#8220;<a href="https://en.wikipedia.org/wiki/Image_histogram">histogram</a>&#8221; which tells you the distribution and range of pixel brightnesses. Here I&#8217;ve found that the brightest pixels seem to be capped at 195 out of 255 (about 76%). This means I could boost the brightness about 30%, and it looks much better (shown on the left). Not all games need to use the full range of values, but most look better when they do!<br>The same adjustments can be done with the black level, but in this case, it looks good as is.</p><h1>Resolution and Scale</h1><p>For pixel art games, it&#8217;s especially important to consider texture resolution. You don&#8217;t want to mix different pixel art scales together without a good reason. Lower resolution portrays a smaller scale and can give a fun/silly tone. If it&#8217;s too small it can be difficult to convey details, so it&#8217;s good to get an early idea of what kind of sprites you&#8217;ll need and work around that. Higher resolutions can convey a more serious or realistic tone, but may become more time-consuming to work with. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SmLq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a8bd3a3-24e9-48cf-954a-45de7d5943d1_1920x1080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SmLq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a8bd3a3-24e9-48cf-954a-45de7d5943d1_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!SmLq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a8bd3a3-24e9-48cf-954a-45de7d5943d1_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!SmLq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a8bd3a3-24e9-48cf-954a-45de7d5943d1_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!SmLq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a8bd3a3-24e9-48cf-954a-45de7d5943d1_1920x1080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SmLq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a8bd3a3-24e9-48cf-954a-45de7d5943d1_1920x1080.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3a8bd3a3-24e9-48cf-954a-45de7d5943d1_1920x1080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2351505,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!SmLq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a8bd3a3-24e9-48cf-954a-45de7d5943d1_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!SmLq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a8bd3a3-24e9-48cf-954a-45de7d5943d1_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!SmLq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a8bd3a3-24e9-48cf-954a-45de7d5943d1_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!SmLq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a8bd3a3-24e9-48cf-954a-45de7d5943d1_1920x1080.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Block resolutions of <a href="https://kenney.nl/assets/voxel-pack">Kenney&#8217;s voxel pack</a></figcaption></figure></div><p>The lower resolution textures could look better with some hand adjustments, but I&#8217;m the programmer artist, so you get the picture. Try to find a texture and screen resolution that you&#8217;re comfortable working with and stick with it consistently.<br>Consistent scaling can make an enormous difference in your game&#8217;s perception!</p><h1>Detail Distribution</h1><p>Another factor I like to think about is the distribution of details in a scene. We programmers tend to focus too much on the function of the scene/objects and can miss the bigger picture. I&#8217;ve found it helpful to pay special attention to what the distribution of details. If some objects or elements of the lots of detail, we may need to add more details to the rest of the scene. A minimalistic player sprite can look out of place in a detailed world, or vice versa.<br>Here&#8217;s an example scene with a somewhat detailed <a href="https://www.youtube.com/watch?v=a1NDo_73Fxw">lock and key</a> on a plain background:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!URGF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F445e932f-3b86-4601-867e-2991b143737b_1239x722.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!URGF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F445e932f-3b86-4601-867e-2991b143737b_1239x722.png 424w, https://substackcdn.com/image/fetch/$s_!URGF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F445e932f-3b86-4601-867e-2991b143737b_1239x722.png 848w, https://substackcdn.com/image/fetch/$s_!URGF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F445e932f-3b86-4601-867e-2991b143737b_1239x722.png 1272w, https://substackcdn.com/image/fetch/$s_!URGF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F445e932f-3b86-4601-867e-2991b143737b_1239x722.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!URGF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F445e932f-3b86-4601-867e-2991b143737b_1239x722.png" width="1239" height="722" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/445e932f-3b86-4601-867e-2991b143737b_1239x722.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:722,&quot;width&quot;:1239,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:588221,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!URGF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F445e932f-3b86-4601-867e-2991b143737b_1239x722.png 424w, https://substackcdn.com/image/fetch/$s_!URGF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F445e932f-3b86-4601-867e-2991b143737b_1239x722.png 848w, https://substackcdn.com/image/fetch/$s_!URGF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F445e932f-3b86-4601-867e-2991b143737b_1239x722.png 1272w, https://substackcdn.com/image/fetch/$s_!URGF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F445e932f-3b86-4601-867e-2991b143737b_1239x722.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Adding a little texture and shading to improve the scene</figcaption></figure></div><p>As you can see on the right, we added a soft wave pattern and some drop shadows. This helps the objects match the scene without requiring a lot of artwork. The background here is still much softer than the objects, but that&#8217;s okay. Here, we still want to emphasize the separation of the background and interactive objects, but just with a little more to look at. You could also imagine using patches of grass or other background sprites if you have those. In some contexts, you may want a Depth Of Field effect with a focus on the foreground and a blurry background.</p><p>To summarize, develop a design language for emphasizing what should be important to the player and sprinkle in some fun flavoring where it might be missing</p><h1>Lighting and Shading</h1><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Wa0T!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86b7def8-2e26-4519-a09a-3fe699adf7d8_1920x1080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Wa0T!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86b7def8-2e26-4519-a09a-3fe699adf7d8_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!Wa0T!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86b7def8-2e26-4519-a09a-3fe699adf7d8_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!Wa0T!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86b7def8-2e26-4519-a09a-3fe699adf7d8_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!Wa0T!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86b7def8-2e26-4519-a09a-3fe699adf7d8_1920x1080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Wa0T!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86b7def8-2e26-4519-a09a-3fe699adf7d8_1920x1080.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/86b7def8-2e26-4519-a09a-3fe699adf7d8_1920x1080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2497803,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Wa0T!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86b7def8-2e26-4519-a09a-3fe699adf7d8_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!Wa0T!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86b7def8-2e26-4519-a09a-3fe699adf7d8_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!Wa0T!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86b7def8-2e26-4519-a09a-3fe699adf7d8_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!Wa0T!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86b7def8-2e26-4519-a09a-3fe699adf7d8_1920x1080.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Notice the drop shadows, glows and vignette/fog</figcaption></figure></div><p>Lighting is super important, not just for the overall aesthetic, but also for its usefulness in directing attention where you want it. Lighting is a tool for communicating mood and atmosphere. Make sure you&#8217;re considering the many possibilities. You could use warm orange lights or cold blue/white lights. You could have soft-blue shadows, hard-dark shadows, or no shadows at all. Whatever sort of lighting you choose, make it intentional.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rQxR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29fc7c28-9d4a-4154-b343-953ed83adeb9_542x401.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rQxR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29fc7c28-9d4a-4154-b343-953ed83adeb9_542x401.png 424w, https://substackcdn.com/image/fetch/$s_!rQxR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29fc7c28-9d4a-4154-b343-953ed83adeb9_542x401.png 848w, https://substackcdn.com/image/fetch/$s_!rQxR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29fc7c28-9d4a-4154-b343-953ed83adeb9_542x401.png 1272w, https://substackcdn.com/image/fetch/$s_!rQxR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29fc7c28-9d4a-4154-b343-953ed83adeb9_542x401.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rQxR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29fc7c28-9d4a-4154-b343-953ed83adeb9_542x401.png" width="542" height="401" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/29fc7c28-9d4a-4154-b343-953ed83adeb9_542x401.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:401,&quot;width&quot;:542,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Image&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Image" title="Image" srcset="https://substackcdn.com/image/fetch/$s_!rQxR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29fc7c28-9d4a-4154-b343-953ed83adeb9_542x401.png 424w, https://substackcdn.com/image/fetch/$s_!rQxR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29fc7c28-9d4a-4154-b343-953ed83adeb9_542x401.png 848w, https://substackcdn.com/image/fetch/$s_!rQxR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29fc7c28-9d4a-4154-b343-953ed83adeb9_542x401.png 1272w, https://substackcdn.com/image/fetch/$s_!rQxR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29fc7c28-9d4a-4154-b343-953ed83adeb9_542x401.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Left: without shading. Right: with soft shading</figcaption></figure></div><p>I personally love soft drop shadows because they can convey depth and separate the background from the foreground. In the image, I actually put the soft shadows on top, giving it a 3D shaded look without much effort. <a href="https://x.com/XorDev/status/1786467443885351147">I wrote more about that here</a>.</p><p>Blooms and glows are a good way to draw eyes to treasure and rewards in games. Outlines are often used to highlight interactive objects when you are near. You can also use vignette or fog for an added sense of depth or to direct the player&#8217;s focus. <a href="https://gamemaker.io/en/showcase/dap">Dap</a> is a great example of lighting being put to good work.</p><h1>Conclusion</h1><p>If nothing else, I want this tutorial to demonstrate how non-artists can still make pretty things by leaning into styles they are comfortable with and working with what they have available. You don&#8217;t need the most detailed textures or the fanciest logo, you only need to be intentional and consistent. Let&#8217;s recap what we&#8217;ve covered today.<br>First and most importantly, be intentional with your coloring. Pick a good palette that fits your game and try to stick with it. Adjust the brightness and contrast to match your vision for the game.</p><p>Second, pick an appropriate scale and don&#8217;t causally mix different texture resolutions.</p><p>Third, pay conscious attention with distribution of texture and geometry details. Make sure there are no areas that are unintentionally lacking details. If you are consistent with your detailing, you can use it as a tool to direct your players&#8217; focus!</p><p>Finally, using good lighting and shading! Soft shadows, glows, blooms, lights, fog and vignette effects are all great tools at your disposal. They can cover for where art might otherwise be lacking.<br><br><a href="https://x.com/JanOrszulik">Jan Orszulik</a> is an extremely talented artist and programmer who demonstrates all of these concepts put together in his game, Precursor:</p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;8058fd99-2cfc-416f-9ec7-87feecd9a9f9&quot;,&quot;duration&quot;:null}"></div><p>You should definitely give him a follow if you haven&#8217;t already. His work is rich with post-processing and game polish.</p><h1>Extras</h1><p>Foxy Of Jungle is currently developing a powerful 2D light and shadow solution for GameMaker users: <a href="https://forum.gamemaker.io/index.php?threads/crystal-2d-lighting-shadows-pbr-normal-maps-emissive-etc-a-lot-more-work-in-progress.114022/">forum.gamemaker.io</a><br><br><a href="https://x.com/XorDev/status/1824564845821432150">Here</a>&#8217;s a sneak peek for my voxel Ambient Occlusion technique.<br><br>Alright, that&#8217;s all for now. I&#8217;m away next weekend, but I intend to be back the week after. Take care!<br>-Xor</p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://mini.gmshaders.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">GM Shaders is supported by readers. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Voxels 2]]></title><description><![CDATA[Creating editable voxel maps with 2D textures]]></description><link>https://mini.gmshaders.com/p/voxels2</link><guid isPermaLink="false">https://mini.gmshaders.com/p/voxels2</guid><dc:creator><![CDATA[Xor]]></dc:creator><pubDate>Sun, 25 Aug 2024 02:09:16 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/b23ebbad-8ba6-437b-af15-0f3ee8c5a040_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi everyone!<br><br>Previously, I wrote about voxel raytracing using the &#8220;DDA&#8221; algorithm:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;1b532ceb-3b94-431c-956d-45463a13db13&quot;,&quot;caption&quot;:&quot;Hey all! It&#8217;s been a while. Sorry about the delay. I hit a few roadblocks along the way, but it&#8217;s finally here! Thanks for your patience&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;md&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;GM Shaders: Voxels&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:115837202,&quot;name&quot;:&quot;Xor&quot;,&quot;bio&quot;:&quot;Shaders and GameMaker&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b37ca2-b4f8-4d1e-a083-93ad73418f1f_512x512.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2024-06-01T06:11:26.384Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54957315-bb35-4a9f-bd23-c984365694e9_1920x1080.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://mini.gmshaders.com/p/voxels&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:143793598,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:1,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;GM Shaders&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedf69737-7d50-4dc8-9cce-aad9b9019215_512x512.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Today, we&#8217;re going to continue from there and get into actually implementing this in a game engine. So far, we can render blocks however we need, but we have no way of interacting with them and our map is generated on the fly. If we wanted a more complex scene, the formula could get big and expensive. We need a way of pre-generating our map so that we don&#8217;t have to every frame, and so we can edit it during runtime. Let&#8217;s start by generating a map.<br>This tutorial comes with a <a href="https://github.com/XorDev/GM_Voxels">full GameMaker demo</a>.</p><h1>Map Storage</h1><p>We need an efficient way to pass lots of voxel map data to the GPU. Since I&#8217;m using GameMaker for this demo, so we don&#8217;t yet have access to Shader Storage Buffer Objects or 3D textures, but we have a backup plan: Every engine has 2D textures!<br>If you&#8217;re implementing this in an engine that supports 3D textures, you can skip this step, but for the rest of us, let&#8217;s look at 3D Look-Up-Tables:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VXdi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7755395e-d789-41b2-bfdc-ada910a040fa_512x512.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VXdi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7755395e-d789-41b2-bfdc-ada910a040fa_512x512.png 424w, https://substackcdn.com/image/fetch/$s_!VXdi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7755395e-d789-41b2-bfdc-ada910a040fa_512x512.png 848w, https://substackcdn.com/image/fetch/$s_!VXdi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7755395e-d789-41b2-bfdc-ada910a040fa_512x512.png 1272w, https://substackcdn.com/image/fetch/$s_!VXdi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7755395e-d789-41b2-bfdc-ada910a040fa_512x512.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VXdi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7755395e-d789-41b2-bfdc-ada910a040fa_512x512.png" width="512" height="512" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7755395e-d789-41b2-bfdc-ada910a040fa_512x512.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:512,&quot;width&quot;:512,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!VXdi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7755395e-d789-41b2-bfdc-ada910a040fa_512x512.png 424w, https://substackcdn.com/image/fetch/$s_!VXdi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7755395e-d789-41b2-bfdc-ada910a040fa_512x512.png 848w, https://substackcdn.com/image/fetch/$s_!VXdi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7755395e-d789-41b2-bfdc-ada910a040fa_512x512.png 1272w, https://substackcdn.com/image/fetch/$s_!VXdi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7755395e-d789-41b2-bfdc-ada910a040fa_512x512.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Here&#8217;s a 512x512 for 64x64x64 RGB LUT for color grading</figcaption></figure></div><p>For our purposes, LUTs are just textures used to store data in a structured way. In a regular 2D texture, you read from a specific x and y coordinate, and you get the RGBA values back. This could work for a simple height map where at any give x and y coordinate, you get the terrain height and material data (using different color channels). For some purposes, this would be fine, but here we want to be able to have caves and to be able to stack different materials on top of each other and for that we need to add &#8220;z&#8221; layers to our texture.<br>The easiest way is to put all the layers on one texture, with each layer side by side. So if we have a 64x64x64 world, we could use a texture that is 64*64x64 (or 4096x64).<br>Then, when we need to read the texture, we add the z-layer coordinate times 64 to the x coordinate. The biggest texture we can do is 16k, which means we would only be able to go as large as 256 pixel wide cells with 64 layers unless we stack layers in the y-axis too.<br>You can see in the LUT above, we have 8x8 cells of 64x64 pixels. To read the z layers, you start from the top-left and go right, row by row, like reading a book. Notice the blue channel becomes brighter as you go down. We can do the same for our 3D world, and this allows us to support much larger map/chunk sizes (theoretically up to 1024x1024x256 by using splitting 16 layers vertically and horizontally). This can actually be thought of as a 4D data structure because it requires 4 resolution values (cell width, cell height, horizontal cell count, vertical cell count).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3ZHG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6daf2d1e-fcb7-4489-b73c-3cd0081990af_1920x1080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3ZHG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6daf2d1e-fcb7-4489-b73c-3cd0081990af_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!3ZHG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6daf2d1e-fcb7-4489-b73c-3cd0081990af_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!3ZHG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6daf2d1e-fcb7-4489-b73c-3cd0081990af_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!3ZHG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6daf2d1e-fcb7-4489-b73c-3cd0081990af_1920x1080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3ZHG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6daf2d1e-fcb7-4489-b73c-3cd0081990af_1920x1080.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6daf2d1e-fcb7-4489-b73c-3cd0081990af_1920x1080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3432494,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!3ZHG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6daf2d1e-fcb7-4489-b73c-3cd0081990af_1920x1080.png 424w, https://substackcdn.com/image/fetch/$s_!3ZHG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6daf2d1e-fcb7-4489-b73c-3cd0081990af_1920x1080.png 848w, https://substackcdn.com/image/fetch/$s_!3ZHG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6daf2d1e-fcb7-4489-b73c-3cd0081990af_1920x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!3ZHG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6daf2d1e-fcb7-4489-b73c-3cd0081990af_1920x1080.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Visualizing the map on the top-left texture. The red channel is for block type</figcaption></figure></div><p>Here are the functions I&#8217;ve written to go from UV coordinates (0 to 1) to 3D voxel coordinates and back :</p><pre><code>vec3 uv_to_block(vec2 uv)
{
    //Convert uv coordinates to pixel coordinates
    vec2 p = floor(uv * RES.xy * RES.zw);
    //Get the subcell x and y coordinates
    vec2 xy = mod(p, RES.xy);
    //Compute cell coordinates
    vec2 zw = mod((p-xy) / RES.xy, RES.zw);
    //Calculate the z value from xy cell position
    float z = dot(zw, vec2(1, RES.z));
    return vec3(xy,z);&#9;
}

vec2 block_to_uv(vec3 b)
{
    //Clamp the z to the map height range
    b.z = clamp(b.z, 0.0, RES.z * RES.w-1.0);
    //Compute subcell coordinates
    vec2 sub_cell = fract(b.xy / RES.xy) / RES.zw;
    //Compute cell coordinates
    vec2 cell = fract(floor(b.z / vec2(1,RES.z)) / RES.zw);
    return sub_cell + cell;&#9;
}</code></pre>
      <p>
          <a href="https://mini.gmshaders.com/p/voxels2">
              Read more
          </a>
      </p>
   ]]></content:encoded></item></channel></rss>