<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="http://simonekstrom.se/feed.xml" rel="self" type="application/atom+xml" /><link href="http://simonekstrom.se/" rel="alternate" type="text/html" /><updated>2026-06-01T19:40:33+00:00</updated><id>http://simonekstrom.se/feed.xml</id><title type="html">Simon</title><author><name>Simon Ekström</name></author><entry><title type="html">May 2026</title><link href="http://simonekstrom.se/2026/06/01/may-2026.html" rel="alternate" type="text/html" title="May 2026" /><published>2026-06-01T00:00:00+00:00</published><updated>2026-06-01T00:00:00+00:00</updated><id>http://simonekstrom.se/2026/06/01/may-2026</id><content type="html" xml:base="http://simonekstrom.se/2026/06/01/may-2026.html"><![CDATA[<p><img src="/images/may26/pixelized.png" alt="Pixelized" /></p>

<p>Slow month in the programming department in general. I’ve had books to read, music to listen to, and clouds to watch. Right now I’m mid-pivot so not that much to show but I’m excited!</p>

<!--more-->

<h2 id="pivot">Pivot</h2>

<p><img src="/images/may26/ortho.png" alt="Ortho" /></p>

<p>For a moment I had a hard time figuring out what I was even building. I went too deep on the tech side of things. Of course I need networking and physics, those things are cool! But they happen to be a pain in the ass as well. I’m not one to say no to a challenge but am I doing this because it’s a challenge or because it actually takes me to a place I want to be?</p>

<p>So I did what I typically do when I feel frustrated, I started a new repository. Tinkering with the basics always gets me in a good mood. Just typing out simple and <strong>handwritten</strong> glue code, mm, pure bliss. Now I’m back to where I was a month ago but with a new codebase and a new idea in my head.</p>

<h2 id="game">Game</h2>

<iframe width="560" height="315" src="https://www.youtube.com/embed/Fa9A7CcFBQc?si=Y5CHKX6-paFwQ_h4" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>Not going into too much detail since it’s still just an idea in my head. But I wanted to constrain myself a bit and have been thinking of a more top-down style. I’ve played around a bit with using an <a href="https://en.wikipedia.org/wiki/Orthographic_projection">orthographic projection</a> to give it more of a pixel game style.</p>

<p>I’ve noted another benefit of this approach as well. Not only does it limit the scope of what you can do in terms of gameplay compared to a FPS, but it also simplifies a lot of the technical challenges I’ve encountered with my rendering.</p>

<h2 id="style">Style</h2>

<p>Now I’m playing around with the style. I feel it would be neat to target something that looks like pixel art, but that is in 3D. As an example I tried pixelizing the lighting:</p>

<p><img src="/images/may26/pixelized.png" alt="Pixelized" /></p>

<p>But as you see if you rotate, it’s actually 3D.</p>

<p><img src="/images/may26/pixelized3d.png" alt="Pixelized 3D" /></p>

<p>As you can see, the regular light gives a bit of another feel.</p>

<p><img src="/images/may26/regularlight.png" alt="Regular light" /></p>

<p>So I have some experimentation to look forward to in the coming month. I think first order of business is probably to fix my foliage, which for now is full of artifacts.</p>

<h2 id="other">Other</h2>

<ul>
  <li><a href="https://www.goodreads.com/book/show/217388097-monk-and-robot">Monk and Robot</a>: I recently got introduced to solarpunk by a colleague and I was scrolling through some books. This was one of the few that didn’t seem to want to force a lesson on me so I bought it. It was actually an amazing read, very simple and like therapy for my overthinking brain.</li>
  <li><a href="https://www.youtube.com/watch?v=f3_UVYFyMno">How rendering changed | Sebastian Aaltonen</a>: Interesting interview with Sebastian Aaltonen.</li>
  <li>Goings-on in Ziglang with <a href="https://ziglang.org/devlog/2026/#2026-05-26">Build System Reworked</a> and <a href="https://ziglang.org/devlog/2026/#2026-05-30">ELF Linker Improvements</a>.</li>
  <li>Boards of Canada - Inferno: First album in 13 years and it was amazing!</li>
</ul>]]></content><author><name>Simon Ekström</name></author><summary type="html"><![CDATA[Slow month in the programming department in general. I’ve had books to read, music to listen to, and clouds to watch. Right now I’m mid-pivot so not that much to show but I’m excited!]]></summary></entry><entry><title type="html">April 2026</title><link href="http://simonekstrom.se/2026/04/25/apr-2026.html" rel="alternate" type="text/html" title="April 2026" /><published>2026-04-25T00:00:00+00:00</published><updated>2026-04-25T00:00:00+00:00</updated><id>http://simonekstrom.se/2026/04/25/apr-2026</id><content type="html" xml:base="http://simonekstrom.se/2026/04/25/apr-2026.html"><![CDATA[<p><img src="/images/apr26/colors.png" alt="Balls" /></p>

<p>Sadly not much to show in terms of screenshots but I’m slowly getting back to it. Spent some time on refactoring, multiplayer (dealing with third party libraries) and migrating to Zig 0.16. Look ma, <code class="language-plaintext highlighter-rouge">std.log</code> <a href="https://github.com/ziglang/zig/pull/25726">received some colors</a>!</p>

<!--more-->

<h2 id="refactoring">Refactoring</h2>

<p>Old codebase was a mess of various types of update functions, we once per frame updates for things like UI, fixed timesteps for physics, etc. To make both physics and multiplayer a bit easier to implement I set out to clean that up. With some nice references in hand, like <a href="https://gafferongames.com/post/fix_your_timestep/">Fix Your Timestep!</a> and <a href="https://jakubtomsu.github.io/posts/fixed_timestep_without_interpolation/">Fixed timestep without interpolation</a> I set out to perform this at first seemingly simple task. As usual it went a bit out of hand, but here I am with a new shiny foundation!</p>

<h2 id="multiplayer">Multiplayer</h2>

<p>Not much fun to show in this department, but it’s getting there. Not wanting to reinvent the wheel I’ve spent some time looking at third-party libraries for networking. The two contenders are <a href="https://github.com/valvesoftware/gamenetworkingsockets">Valve’s GameNetworkingSockets</a> and Glenn Fiedler’s <a href="https://github.com/mas-bandwidth/netcode">netcode</a> + <a href="https://github.com/mas-bandwidth/reliable">reliable</a>. Though I appreciate Glenn Fiedler’s philosophy since GNS is somewhat of a mess, I’m still leaning towards using GNS. It’s tested and if I would ever release a game it would likely end up on Steam.</p>

<p>They both were a real pain in the ass to get building within the Zig build system though. GNS is a messy C++ project with a bunch of dependencies and doesn’t really provide a clean C API I can just hook up. Netcode on the other hand just straight up produced miscompiled code (Zig toolchain bug that seems to have been resolved now), and sodium vendored with the project is dated so I had to deal with that as well.</p>

<p>Now they both <del>build</del> (need some fixes for Zig 0.16) and next step is to actually write some code.</p>

<h2 id="zig-016">Zig 0.16</h2>

<p>Zig 0.16 was <a href="https://ziglang.org/download/0.16.0/release-notes.html">released</a> introducing a lot of goodies, one of them being the breaking <a href="https://ziglang.org/download/0.16.0/release-notes.html#IO-as-an-Interface">I/O as an interface</a> change. And thus, one of the goals for this month has been migrating existing code. This includes both mine and third-party libraries.</p>

<p>The migration wasn’t that bad, but there are spots that got a bit awkward and that I might want to revisit when I understand the new features better. This release also came with some fixes for bugs affecting me so I’m stoked that I’m finally able to remove some workarounds.</p>

<p><a href="https://github.com/Snektron/vulkan-zig">vulkan-zig</a> also had some breaking changes so put had to do some maintenance in my RHI as well.</p>

<h2 id="cool-stuff">Cool stuff</h2>

<ul>
  <li>Bunch of talks from <a href="https://graphicsprogrammingconference.com/">Graphics Programming Conference</a>, most notably the one about <a href="https://www.youtube.com/watch?v=IM1Dr98f3xU">teardown and beyond</a></li>
  <li><a href="https://gpfault.net/posts/sph.html">Introduction to Spherical Harmonics for Graphics Programmers</a></li>
  <li><a href="https://www.youtube.com/watch?v=2AnbYNudAyM">Razor1911</a></li>
  <li><a href="https://blog.runevision.com/2026/03/fast-and-gorgeous-erosion-filter.html">Fast and Gorgeous Erosion Filter</a></li>
  <li><a href="https://krupitskas.com/posts/modern_culling_techniques/">Modern culling techniques</a> with visualizations.</li>
  <li><a href="https://www.youtube.com/watch?v=0IrzX4LDIx8">The Future of Path Tracing | Best Practices, Optimizations &amp; Future Standards</a></li>
  <li><a href="https://www.youtube.com/watch?v=dfRownfn6NY">Slang Shading Language Demo &amp; Single-Source Graphics Pipeline Explained</a></li>
</ul>

<h2 id="robots-and-the-goblin">Robots and the goblin</h2>

<p>I don’t know if it was my ability to write reasonable prompts or if Claude just got worse, but one thing that is certain is that I was slowly losing my mind. Claude is great at quickly spewing out features. In fact, it’s so good that you wake that nasty goblin in your head that just wants more. Initially it wasn’t that much of an issue. I carefully reviewed everything and was always there to push the LLM in the right direction. However, as the codebase grew, Claude clearly got dumber. As I started questioning my decisions in life, the goblin took over. Now I have a bunch of experimental codebases that I despise and an LLM that probably dislikes me just as much as I dislike it.</p>

<p>I hereby declare, next month is no-LLM month. I’ve already started and who knew, programming is kinda fun! Sure, I won’t have the great power of AI at my disposal but I don’t see it as that big of a loss. The fact that I don’t have to manage the utter chaos spread by a bunch of mansplaining robots and a lazy goblin should give me more time to focus on something useful.</p>]]></content><author><name>Simon Ekström</name></author><summary type="html"><![CDATA[Sadly not much to show in terms of screenshots but I’m slowly getting back to it. Spent some time on refactoring, multiplayer (dealing with third party libraries) and migrating to Zig 0.16. Look ma, std.log received some colors!]]></summary></entry><entry><title type="html">March 2026</title><link href="http://simonekstrom.se/2026/03/25/mar-2026.html" rel="alternate" type="text/html" title="March 2026" /><published>2026-03-25T00:00:00+00:00</published><updated>2026-03-25T00:00:00+00:00</updated><id>http://simonekstrom.se/2026/03/25/mar-2026</id><content type="html" xml:base="http://simonekstrom.se/2026/03/25/mar-2026.html"><![CDATA[<p>I’ve done jack-shit this month, but such is life and that’s OK.</p>

<p>Things were not clicking and that fantastic high from doing something that is genuinely fun was slowly dissipating. I accepted my fate and didn’t force it. Instead I’ve watched countless reels, YouTube videos, explored new music, and even found myself staring at clouds through my window. I’m not gloomy, rather the opposite.</p>

<p>Since I’ve been, and probably still am, procrastinating; I don’t have much fun to show. So let’s take this opportunity to appreciate what other people are up to instead.</p>

<!--more-->

<h2 id="youtube">Youtube</h2>

<p>Even if I might not dare to admit it, my most visited site is probably YouTube. It’s the first ocean of content I find myself in when my mind is drifting. There is however so much useful content on there nowadays that it might not even be fair to call it procrastination. Here are some of the things that I have watched recently.</p>

<p><img src="/images/mar26/pewdiepie.jpg" alt="Pewdiepie" /></p>

<p><a href="https://www.youtube.com/watch?v=aV4j5pXLP-I">PewDiePie’s training his own AI</a>: The PewDiePie tech arc has been incredible to follow. A year ago he built his first PC and now he has his monster of an AI computer almost burning his house down. It’s a very inspiring story on perseverance and embracing failure. I definitely recognize his obsession as it’s the same thing I went through when first starting. You had no clue what you were doing but you get nowhere if you give up.</p>

<p><img src="/images/mar26/jonblow.jpg" alt="Jonathan Blow" /></p>

<p><a href="https://www.youtube.com/watch?v=1blhmslxkWg">Jonathan Blow on Wookash Podcast</a>: Wookash Podcast has a lot of great guests and this one I liked a lot. They discuss technical details of Jonathan Blow’s <a href="https://www.orderofthesinkingstar.com/en/">new game</a> and the Jai programming language. There are plenty of Jonathan Blow’s rants out there but this provides some of these directly linked to the game he is currently developing.</p>

<p><img src="/images/mar26/gorillaz.webp" alt="Gorillaz" /></p>

<p><a href="https://www.youtube.com/watch?v=ucRulNQsuYQ">Gorillaz - The Mountain, The Moon Cave and the Sad God</a>: Gorillaz recently released a new album together with an amazing animated short film! It apparently took 18 months to complete and it’s very much a love letter to the craftsmanship of older animated films. Very refreshing to see in the era of AI.</p>

<p><img src="/images/mar26/kamikaze.jpg" alt="Kamikaze_Shortbus" /></p>

<p><a href="https://www.youtube.com/watch?v=qvLEvwwX82s">Kamikaze_Shortbus</a>: More on the art side but insane humor in combination with cool insights. I re-watched this video which provides another perspective on what I have dealt with the last couple of months, color and lighting. She also hosted an <a href="https://www.youtube.com/watch?v=xbialT8ZNtU">art contest</a> recently where she and friends went through and gave constructive criticism to all contributors and now I feel a sudden urge to start drawing.</p>

<h2 id="gamedev">Gamedev</h2>

<p>The algorithm knows me too well so of course my feeds are full of gamedev news. If you can’t progress yourself, be amazed at the progress of others.</p>

<p><img src="/images/mar26/teardown.jpg" alt="Teardown multiplayer" /></p>

<p><a href="https://blog.voxagon.se/2026/03/13/teardown-multiplayer.html">Teardown multiplayer</a>: Teardown is what inspired me to dig this deep into voxels and I’ve done my best to gather all knowledge about its inner workings. This makes me appreciate how complicated something like multiplayer can be to implement, but they did it! This write-up by Dennis Gustafsson was an amazing read.</p>

<p><img src="/images/mar26/texelsplatting.jpg" alt="Texel splatting" /></p>

<p><a href="https://dylanebert.com/texel-splatting/">Texel Splatting</a>: This work has started showing up in my feeds and it’s really cool. Stable 3D pixel art! I’ve never thought about this problem before but this was a thought-through approach for another interesting way of rendering.</p>

<p><img src="/images/mar26/tangytd.jpg" alt="tangytd" /></p>

<p><a href="https://store.steampowered.com/app/2245620/Tangy_TD/">Tangy TD</a>: Twitch streamer <a href="https://www.twitch.tv/cakez77">cakez77</a> released his game after working on it on stream for years. I have followed him on-and-off and it’s very cool to see him finally release the game.</p>

<h2 id="reading">Reading</h2>

<p><img src="/images/mar26/exhalation.jpg" alt="Exhalation" width="20%" height="20%" /></p>

<p>I’m not great at reading books but I finally started reading Ted Chiang’s Exhalation (with included stories). I picked it up with the intent of reading it during Christmas, but then I got stuck with FRA puzzles or something. I read Stories of your life and others a long time ago and really enjoyed it. He presents cool ideas with a human touch and the fact that it holds short stories makes it easier for a scatterbrain like me.</p>

<p>And finally some other note-worthy reads:</p>

<ul>
  <li><a href="https://www.libraryofshortstories.com/">Library of Short Stories</a>: Found this on hackernews and it’s like fate is mocking me. Now I just need to find time reading.</li>
  <li><a href="https://box2d.org/posts/2024/08/determinism/">Determinism</a>: Been doing some reading on game physics and this is a very interesting topic. You would expect same code and same data to yield the same result but there is more to it. I fear a major screw-up in my future if I attempt this myself, but that hasn’t stopped me before.</li>
  <li><a href="https://terriblesoftware.org/2026/03/03/nobody-gets-promoted-for-simplicity/">Nobody gets promoted for simplicity</a>: It’s sad because it’s true. This is something I fight daily at work and there were some good lessons in there. <a href="https://grugbrain.dev/">Complexity bad</a>.</li>
  <li><a href="https://cemrehancavdar.com/2026/03/10/optimization-ladder/">Optimization ladder</a>: Another topic that frequently comes up at work. This talks about how to make Python go wroom.</li>
</ul>

<h2 id="what-now">What now?</h2>

<p>The month might have felt a bit unproductive but I haven’t been bored. I attribute the cloud watching to the slow and steady return of daylight. Can’t say the same about the reels though, but I can tell you that I now know a lot about beavers.</p>

<p>The benefit of these hobby projects of mine is that the only value I see in them is having fun (and maybe learn something). Taking a break is basically free and the alternative is burning out on something I don’t even value. With that said, back to it!</p>]]></content><author><name>Simon Ekström</name></author><summary type="html"><![CDATA[I’ve done jack-shit this month, but such is life and that’s OK. Things were not clicking and that fantastic high from doing something that is genuinely fun was slowly dissipating. I accepted my fate and didn’t force it. Instead I’ve watched countless reels, YouTube videos, explored new music, and even found myself staring at clouds through my window. I’m not gloomy, rather the opposite. Since I’ve been, and probably still am, procrastinating; I don’t have much fun to show. So let’s take this opportunity to appreciate what other people are up to instead.]]></summary></entry><entry><title type="html">February 2026</title><link href="http://simonekstrom.se/2026/02/26/feb-2026.html" rel="alternate" type="text/html" title="February 2026" /><published>2026-02-26T00:00:00+00:00</published><updated>2026-02-26T00:00:00+00:00</updated><id>http://simonekstrom.se/2026/02/26/feb-2026</id><content type="html" xml:base="http://simonekstrom.se/2026/02/26/feb-2026.html"><![CDATA[<p><img src="/images/feb26/denoised.png" alt="Balls" /></p>

<p>Last post I mentioned that I am terrible at denoising, so what better way to spend this month than with denoising.</p>

<!--more-->

<h2 id="noise">Noise</h2>

<p><img src="/images/feb26/noise.png" alt="Noise" /></p>

<p>One obvious challenge with Monte Carlo methods is the trade-off between accuracy and computational cost. This is a real-time application so we have our deadlines and casting rays is crazy expensive. The characteristics of the noise are a bit different depending on the signal we are trying to reconstruct. Here I will write about ray-cast shadows produced for the direct lighting and the incoming radiance for the indirect lighting. There is a lot of good material on this topic, such as this <a href="https://alain.xyz/blog/ray-tracing-denoising">blog post</a> by Alain Galvan.</p>

<h2 id="direct-light">Direct light</h2>

<p>I will not go that deep into the specifics as there already is much freely available material out there (like the <a href="https://pbr-book.org/">PBR book</a>), written by smarter people than me. But let us assume you light your world only using direct lighting.</p>

<p><img src="/images/feb26/direct_light_noisy.png" alt="Direct light" /></p>

<p>Not that exciting, is it? Any surfaces not facing the light source would be completely dark. This is also an important aspect of direct lighting. Naively applying just analytical lights would not account for shadows cast by objects. There are multiple approaches to estimate these shadows (e.g. shadow maps) but what I have opted for, and the source of noise, is raycasting.</p>

<p>Since the sun is not a true point light we get soft shadows. This means the shadow is not actually binary, but rather a function of how much of the light is occluded (see <a href="https://en.wikipedia.org/wiki/Umbra,_penumbra_and_antumbra">penumbra</a>). This is where the noise comes in. We treat the sun as a disk and for each pixel we cast a ray at a random point on this disk. If you do this enough times and keep track of the fraction of rays that reach the sun you will end up with a soft shadow. However, we do not have time so we cast a single random ray towards the sun per pixel.</p>

<p><img src="/images/feb26/shadow_noise.png" alt="Noisy shadow" /></p>

<p>Above is what we get from the shadow tracer each frame. A binary mask telling us whether the ray hit the light source or not.</p>

<h2 id="indirect-light">Indirect light</h2>

<p><img src="/images/feb26/old.png" alt="Reference scene" /></p>

<p>Direct lighting on its own is not that exciting. Neither is just adding a constant ambient term to the output like seen above. Global illumination is all the rage nowadays and it is one of these things you did not know you were missing until you actually see it in action. In short, global illumination is about simulating the indirect lighting in a scene, so light that has bounced around the scene before hitting the object.</p>

<p>Global illumination is tricky though and there are many ways of solving it (e.g. <a href="https://jcgt.org/published/0008/02/01/">DDGI</a>, <a href="https://www.advances.realtimerendering.com/s2009/Light_Propagation_Volumes.pdf">LPV</a>, <a href="https://advances.realtimerendering.com/s2022/SIGGRAPH2022-Advances-Lumen-Wright%20et%20al.pdf">Lumen</a>). Inspired by the full potato GI in <a href="https://www.youtube.com/watch?v=jusWW2pPnA0">Tiny Glade</a> I went out to make an even more potato solution. We cast rays randomly in a hemisphere to determine the incoming radiance and then we pray the denoiser can clean it up.</p>

<h2 id="denoising">Denoising</h2>

<p>Now we have two very noisy signals we need to denoise but we also have two properties we can exploit. The signal has both temporal and spatial coherence. The lighting is unlikely to change for a pixel over time and pixels on the same surface will likely have similar incoming light. Thus, a temporal filter allows us to accumulate samples over time if the camera does not move too much, and a spatial filter allows us to utilize samples from neighboring pixels if they belong to the same surface. The tricky part is the two IFs though. All the filters see is a 2D image so we need to be careful about what samples we actually can use.</p>

<h3 id="temporal-filter">Temporal filter</h3>

<p><img src="/images/feb26/temporal_filter.svg" alt="SVGF" /></p>

<p>If the camera has not moved too much we can likely accumulate new samples on top of the samples of previous frames. The tricky part here is that the camera is expected to move so you will need to reproject the history of samples on top of your current frame (such as in <a href="https://advances.realtimerendering.com/s2014/#_HIGH-QUALITY_TEMPORAL_SUPERSAMPLING">TAA</a>). This is simple enough if you have motion vectors for each pixel, but you are bound to get pixels where there is no valid history, such as edges of the frame or disoccluded areas.</p>

<p>In a perfect world the reprojection would only produce valid history; then it is just a matter of picking some strategy for accumulating the samples. I went for an exponential moving average. I record the number of samples collected and blend the history and the new samples according to <code class="language-plaintext highlighter-rouge">lerp(old, new, 1 / num_samples)</code>. I also have a constant <code class="language-plaintext highlighter-rouge">MAX_SAMPLES</code> for clamping <code class="language-plaintext highlighter-rouge">num_samples</code> which allows me to control how fast we accumulate samples.</p>

<p>However, the world is not perfect and a big part of this filter is actually determining whether history is valid or not. Edges of the screen are straightforward, we just check if we try to sample outside the history texture. The tricky part is that it is a 3D scene, and as the camera (or objects) move, we get disocclusions (surfaces previously not seen). Thankfully we do deferred rendering so I have a lot of information about the scene at my disposal and can compare the depth and the normal in an attempt to detect disocclusions.</p>

<p>This is a typical trade-off. Collect a large set of samples and you get less noise but you will also react slower to changes in the scene. If this is slow enough you will get a lot of noticeable artifacts for areas where the history is reset rapidly.</p>

<h3 id="spatial-filter">Spatial filter</h3>

<p>This filter is more like a blur filter. As mentioned, neighboring pixels likely hold the same incoming light (or lack thereof for shadows) and here we attempt to exploit this fact by aggregating together neighboring pixels. Here I went for two different approaches. The principle is the same but the two signals are a bit different so I will talk about this below.</p>

<p>Sadly we do not live in a perfect world and just as with the temporal filter it is not as simple as just blurring the image. You also need to consider how much to blur to not lose details and to make sure you do not blur across edges. The assumption is that a surface might have the same incoming light but the image contains many surfaces.</p>

<h3 id="shadow-mask">Shadow mask</h3>

<p><img src="/images/feb26/svgf_denoising.svg" alt="SVGF" /></p>

<p>Here I tried a bit of everything and in the end I ended up with something similar to <a href="https://research.nvidia.com/publication/2017-07_spatiotemporal-variance-guided-filtering-real-time-reconstruction-path-traced">SVGF</a>. For the most part it is identical but I took some short cuts.</p>

<p>The temporal part is similar to what I have already described but with one addition. We also track the variance of the signal. The idea is that we can use the variance to guide the spatial filter.</p>

<p>The spatial filter then uses a hierarchical <a href="https://jo.dreggn.org/home/2010_atrous.pdf">À-Trous wavelet filter</a> which uses our normals and depth to avoid filtering over edges. The variance we have collected is also used to control the degree of filtering. The idea is that areas with low variance, such as the shadow umbra, need less smoothing than areas with high variance (e.g. the penumbra). The first iteration of this filter is then what we keep as the history in the temporal filter for the next frame.</p>

<p>The tricky part here and with denoising in general is that there is a lot of tweaking involved. It all depends on your input signal and your scene and you have to tweak everything from your temporal accumulation to how to weight your spatial filter to get decent results.</p>

<p><img src="/images/feb26/shadow_denoise.png" alt="Denoised shadow" /></p>

<p>The penumbra looks a bit weird when looking at it like this so it might require some more tweaking. In the final render that is barely noticeable though.</p>

<h3 id="global-illumination">Global illumination</h3>

<p><img src="/images/feb26/gi_noise.png" alt="GI trace" /></p>

<p>The GI signal is a different beast. I tried the SVGF approach here as well and it looked straight-up terrible. One valuable thing to note about indirect lighting though is that it tends to be lower frequency. Visually I am way less worried about losing high frequency details and I am happy as long as I get a stable signal that is approximately correct.</p>

<p><img src="/images/feb26/reblur_denoising.svg" alt="ReBLUR" /></p>

<p>One key aspect of denoising the GI is that I first project it into first-order spherical harmonics, which lets us preserve directional information, and this is what we feed into the denoiser. This is also what Tiny Glade and <a href="https://developer.download.nvidia.com/video/gputechconf/gtc/2019/presentation/s9985-exploring-ray-traced-future-in-metro-exodus.pdf">Metro Exodus</a> did. The denoiser itself is also a bit more convoluted compared to SVGF. What I ended up with is similar to <a href="https://link.springer.com/chapter/10.1007/978-1-4842-7185-8_49">ReBLUR</a> but with some simplifications.</p>

<p>The temporal filter is as described before but with some blur of the input before we accumulate it. This is due to the high number of outliers in the GI signal.</p>

<p>The spatial filter is a bit different though. We no longer use the variance to guide the spatial filter since the variance does not tell us much when it comes to GI compared to shadows where we have clear high and low variance areas. Instead we use the number of accumulated samples to control the size of the filter. The sampling for the filter is a bit different as well, we use a randomly rotated Poisson disk and we sample in world space as opposed to screen space. Similar to SVGF we also use the blurred image from the previous frame as the history in the temporal filter.</p>

<p>There are also some tricks to deal with disocclusions since the lack of samples is very noticeable in those areas. The ReBLUR paper suggests using a mip-chain but I went with just an ugly hack where I just collect neighboring samples. In principle it is the same, just more stupid.</p>

<h3 id="putting-it-together">Putting it together</h3>

<p>Until now we have been rendering everything at half-resolution just because the raytracing is fairly expensive. So the first thing we need to do before actually putting everything together is to upsample our denoised targets. Here I use a simple <a href="https://en.wikipedia.org/wiki/Bilateral_filter">bilateral filter</a>. Using the normal and depth of the scene we can reliably upsample the image while keeping the edges.</p>

<p><img src="/images/feb26/denoised.png" alt="Denoised scene" /></p>

<p>After all tracing, denoising, and upsampling we can finally composite the scene. We can compare our path traced reference (left) to the new render (right) and see that we are getting fairly close.</p>

<p><img src="/images/feb26/ref_vs_new.png" alt="Reference scene" /></p>

<p>A visual difference is the ambient occlusion. One drawback of the heavy denoising is that we lose a lot of detail in the lighting so I am still relying on <a href="https://en.wikipedia.org/wiki/Screen_space_ambient_occlusion">SSAO</a> which lacks the <a href="https://developer.download.nvidia.com/video/gputechconf/gtc/2019/presentation/s9985-exploring-ray-traced-future-in-metro-exodus.pdf">visual depth of RTAO</a>. I actually collect the hit distance during the GI trace so I could potentially attempt to denoise and reconstruct RTAO separately from the GI but that is something for the future.</p>

<p><img src="/images/feb26/old_vs_new.png" alt="Reference scene" /></p>

<p>The old render (left) with the constant ambient term looks very dull in comparison to the new (right).</p>

<p><img src="/images/feb26/cornell.png" alt="Cornell" /></p>

<p>Coolest thing though, is that you can even see the bounce light in a Cornell box!</p>

<p>There is still better benchmarking needed but looking at some preliminary numbers, rendering the ball scene spends about 1.5 ms per frame on the GI trace and 0.5 ms on denoising at 2880x1620 (full res) on an RTX 3090. The sun shadow is about 0.5 ms in total (raycasting and denoising).</p>

<h3 id="limitations">Limitations</h3>

<p>It is worth nothing that even if we get some cool bounce lighting like in the Cornell box above, we still only do a single bounce so it will never look as good as a proper path tracer. This is not a deal breaker though as it can still look great. The issue is with noise and denoising artifacts. This whole thing was the result of a lot of experimentation and I expect that I will have to continue experimenting. The biggest issue now is dealing with indoor scenes.</p>

<p><img src="/images/feb26/indoor.png" alt="Indoor" /></p>

<p>Indoor scenes are especially tricky due to the low amount of useful signal. While it is cool to see that I can now light a scene just using light-emitting voxels instead of analytical lights, you definitely see the lost details of the shadows caused by the denoiser.</p>

<p><img src="/images/feb26/indoor_reference.png" alt="Indoor" /></p>

<p>In the reference, the shadows, just like the previously mentioned AO, are a lot more defined.</p>

<p><img src="/images/feb26/noisy-roof.png" alt="Noise" /></p>

<p>It is hard to spot in screenshots but there are also clear cases where the denoiser fails due to the low signal. Like in the image above you see some odd shifting patterns in the roof and this is even more noticeable when not looking at a still image. One issue here is that we rely on our random hemisphere rays actually hitting the light-emitting voxels. We might be able to reduce the noise by using <a href="https://lisyarus.github.io/blog/posts/multiple-importance-sampling.html">importance sampling</a> like they did for the <a href="https://link.springer.com/content/pdf/10.1007/978-1-4842-7185-8_47.pdf">Quake 2 raytracing demo</a>.</p>

<h3 id="ai">AI</h3>

<p>There are of course modern AI-based approaches for denoising out there such as NVIDIAs <a href="https://research.nvidia.com/labs/adlr/DLSS4/">DLSS</a> or AMDs <a href="https://gpuopen.com/learn/neural_supersampling_and_denoising_for_real-time_path_tracing/">Neural supersampling and denoising</a>. However, I decided against them for now since you limit yourself to specific hardware. I might look into this more in the future.</p>

<h2 id="claude">Claude</h2>

<p>In parallel to the work on the lighting I have continued experimenting with Claude Code with some mixed results.</p>

<h3 id="physics">Physics</h3>

<p>I hinted briefly at Claude implementing a physics engine last month. Well, I made Claude implement it again, and again… After some micro-management and a detailed plan outlined by myself it actually managed to implement something that resembles actual game physics. There is a lot of weird dancing going on though.</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/t_r1F6oUKWo?si=3UDLwW6-uLZU1hLC" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>It implements physics similar to how <a href="https://teardowngame.com/">Teardown</a> does it, with per-voxel collisions. Objects are just occupancy masks in 3D telling whether a voxel is solid or air. Then thanks to some ideas from <a href="https://www.youtube.com/watch?v=tZP7vQKqrl8">Dennis Gustafsson</a> it is actually feasible to calculate collisions per voxel (spoiler: you do not actually have to).</p>

<p>Have not had time to stress test this or anything but it is nice to now have the skeleton to actually be able to do so.</p>

<h2 id="whats-next">What’s next</h2>

<p>I am happy with how it turned out for outdoor scenes but there is still some work to do indoors so I will fiddle around some more with the denoising. I have some ideas on what to try next. Other than that I might actually look a bit more into the physics. I recently read this captivating <a href="https://gafferongames.com/">blog series</a> by Glenn Fiedler so maybe I should add networking as well!</p>

<p>I also have this half-insane fully vibe coded project related to my previous <a href="https://github.com/simeks/zynth/">synth project</a> which I might continue directing. I will cover this in the next post if it actually goes somewhere.</p>]]></content><author><name>Simon Ekström</name></author><summary type="html"><![CDATA[Last post I mentioned that I am terrible at denoising, so what better way to spend this month than with denoising.]]></summary></entry><entry><title type="html">January 2026</title><link href="http://simonekstrom.se/2026/01/25/jan-2026.html" rel="alternate" type="text/html" title="January 2026" /><published>2026-01-25T00:00:00+00:00</published><updated>2026-01-25T00:00:00+00:00</updated><id>http://simonekstrom.se/2026/01/25/jan-2026</id><content type="html" xml:base="http://simonekstrom.se/2026/01/25/jan-2026.html"><![CDATA[<p><img src="/images/jan26/screenshot-2026-01-19.png" alt="Terrain" /></p>

<!--more-->

<p>Plenty of stuff to showcase this month so lets jump straight into it.</p>

<h2 id="voxel-structure">Voxel structure</h2>

<p>New year, new me. New me realized old me had no clue what he was doing. It might be fairer to say that the requirements have changed but I’m also the one setting the requirements. I started thinking about how to do <a href="https://en.wikipedia.org/wiki/Level_of_detail_(computer_graphics)">LODs</a> using my old system and after trying some different approaches I started feeling like I was fitting a problem to a solution rather than the other way around. And here we are, a somewhat new solution to a new problem.</p>

<p>On the CPU the voxels are structured like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>World
→ Grid of Chunks
    → Grid of Bricks
        → Voxels
</code></pre></div></div>
<p>Loading and streaming to the GPU is done per chunk, but the brick map approach allows me to save some memory by not wasting space on too many empty voxels.</p>

<p>For editing in the world I request a view of the voxels, where I perform my changes to an intermediate buffer before committing them all at once to the storage. This allows me to be a bit more efficient in how I manage chunks and bricks:</p>

<div class="language-zig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="n">view</span> <span class="o">=</span> <span class="n">world</span><span class="p">.</span><span class="nf">beginEdit</span><span class="p">(</span><span class="n">begin</span><span class="p">,</span> <span class="n">end</span><span class="p">);</span>
<span class="n">view</span><span class="p">.</span><span class="nf">set</span><span class="p">(</span><span class="n">pt</span><span class="p">,</span> <span class="p">.</span><span class="py">stone</span><span class="p">);</span>
<span class="n">world</span><span class="p">.</span><span class="nf">endEdit</span><span class="p">(</span><span class="n">view</span><span class="p">);</span>
</code></pre></div></div>

<p><img src="/images/jan26/screenshot-2026-01-18.png" alt="Broken 64tree" /></p>

<p>To actually render this I stream any changes to the GPU. This step also includes compressing the chunks into a <a href="https://dubiousconst282.github.io/2024/10/03/voxel-ray-tracing/">sparse 64-tree</a> to make them smaller and more efficient to traverse. The rendering then has a top level acceleration structure (TLAS) which is just a grid of chunks. The raycasting traverses the TLAS in a <a href="https://en.wikipedia.org/wiki/Digital_differential_analyzer_(graphics_algorithm)">DDA manner</a> to determine which chunks are relevant to traverse. This might look like above if you get it wrong.</p>

<p>This is very similar to what I did before, just some minor tweaks. The main addition is the LODs. Taking some inspiration from Mike Turitzin’s <a href="https://www.youtube.com/watch?v=il-TXbn5iMA">great video</a> on a game engine based on SDFs, I decided to try out <a href="https://dl.acm.org/doi/abs/10.1145/1186562.1015799">geometry clipmaps</a>. What this means is that I now have a nested set of chunk grids, one for each LOD. Each grid is centered at the camera and contains the same number of chunks. You then have a different scale per level, trading resolution for larger coverage.</p>

<p><img src="/images/jan26/screenshot-2026-01-24.png" alt="LODs" /></p>

<p>And Bam! We have LODs. Don’t mind the artifacts, nested ray traversal is difficult, okay? Traversing the TLAS was a bit tricky to get right. The main issue is that since chunks are streamed in and we always want to display something, we need to pick the best chunk available for every cell during traversal.</p>

<p><img src="/images/jan26/screenshot-2026-01-23.png" alt="LODs" /></p>

<p>Did I mention it’s difficult to get the traversal right at times?</p>

<p>To make my life a bit easier I have limited editing and foliage to LOD0 (closest to camera). But the neat thing about this is that for lighting I can just hook up the new TLAS traversal and it just works. I will have to look into the performance later since it is a bit wasteful to do full lighting for pixels we barely see though. But I’m doing an overhaul of the lighting so that will come later!</p>

<p><img src="/images/jan26/screenshot-2026-01-25.png" alt="LODs" /></p>

<p>In the end I got something that looks good enough. It’s way cheaper to render, generate, and stream to the GPU.</p>

<h2 id="lighting">Lighting</h2>

<p>One thing that has been frustrating is lighting. It’s fairly easy to get something that looks good enough outdoors if you don’t care about realistic lighting. However, the moment you step indoors, such as into caves, all immersion is gone. That constant ambient term you lazily set to simulate indirect lighting screws you badly in a cave that is supposed to be pitch black.</p>

<p><img src="/images/jan26/screenshot-2026-01-24-3.png" alt="LODs" /></p>

<p>In the past I have used ray-based occlusion which kinda works. But in true stochastic fashion it’s a battle between spending time taking samples (casting rays) and dealing with low quality output. Noise like above is my nemesis, not only does it look bad in itself but it affects other filters along the pipeline (such as TAA) making things even worse. Let me tell you, there were times when I would rather let the player run at 5 fps than having to deal with more noise. In short, I’m terrible at denoising so let’s try fixing that.</p>

<p><img src="/images/jan26/screenshot-2026-01-23-2.png" alt="LPV" /></p>

<p>I first thought that maybe I could cheat the noise by trying another approach, <a href="https://morgan3d.github.io/articles/2019-04-01-ddgi/">DDGI</a> and light probes. Like shown above, you place a set of probes around the world and sample the light through these. This seemed to have been successfully implemented by <a href="https://www.youtube.com/watch?v=L1vhle74AEU">Douglas</a> in a similar engine to mine. However, I gave up quickly. To be fair, I didn’t spend that much time on it. It’s a neat idea but there are a lot of caveats, like where to place probes. I might continue on this track later but I got slightly distracted by <a href="https://www.youtube.com/watch?v=jusWW2pPnA0">Tiny Glade</a>.</p>

<p>There is a certain elegance to throwing around rays like a madman and if you know what you are doing it looks really good (e.g. Teardown and Tiny Glade). Back to noise I go! I have gathered a handful of references for better denoising so let’s see if there is something I can do. The Tiny Glade GI solution seems particularly interesting.</p>

<p><img src="/images/jan26/screenshot-2026-01-24-2.png" alt="Path tracer" />
<img src="/images/jan26/screenshot-2026-01-24-4.png" alt="Path tracer" /></p>

<p>Before ripping out the whole rendering pipeline I implemented a very simple path tracer that I can toggle to use as reference. It looks great but it’s completely useless for a game since it takes several seconds to converge to something that looks good. I don’t necessarily need to mimic it completely but it’s still good to have an understanding of what to expect.</p>

<p>Now I’m deep into an overhaul of the lighting pipeline, taking some inspiration both from Tiny Glade and <a href="https://developer.download.nvidia.com/video/gputechconf/gtc/2019/presentation/s9985-exploring-ray-traced-future-in-metro-exodus.pdf">Metro Exodus</a>. Let’s see where that takes me.</p>

<h2 id="misc">Misc</h2>

<h3 id="sound">Sound</h3>

<iframe width="560" height="315" src="https://www.youtube.com/embed/ofbFnTzVgcc?si=24Qtvsl-Wkmm15f3" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>I also tasked Claude with implementing a system for audio. It required some supervision, like telling it to use <a href="https://github.com/zig-gamedev/zaudio.git">zaudio</a> and exactly what API I wanted. But now I have sound in the game!</p>

<h3 id="gameplay">“Gameplay”</h3>

<iframe width="560" height="315" src="https://www.youtube.com/embed/xehQI-XjBAY?si=K2trUH-pXR6kPzkQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>Here’s a short video to showcase the game aspects. Sorry for the quality though, my screen recorder went haywire and I have probably not touched video editing since I made a frag movie for CS 1.6.</p>

<h3 id="models">Models</h3>

<p><img src="/images/jan26/screenshot-2026-01-20.png" alt="Models" /></p>

<p>I implemented “models” into the game. A model in this case is just a small voxel structure not bound to the voxel terrain, such as something produced in <a href="https://ephtracy.github.io/">MagicaVoxel</a>. It can be transformed arbitrarily and I will need it to render things such as characters. Just as with the terrain, they are raycasted and they are integrated with the raycasting TLAS (kinda), meaning lighting and such just works as expected. The implementation is stupidly naive though and I will have to revisit this if I want more models in the world, like using a proper TLAS. The Vulkan ray tracing extension might help but that’s a big step.</p>

<h3 id="terrain-generation">Terrain generation</h3>

<p>I wrote a small framework for describing scenes based on an SDF, so now I write them something like this:</p>

<div class="language-zig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">fn</span> <span class="n">scene</span><span class="p">(</span><span class="n">p</span><span class="p">:</span> <span class="n">Vec3</span><span class="p">)</span> <span class="n">Sample</span> <span class="p">{</span>
    <span class="k">const</span> <span class="n">floor</span> <span class="o">=</span> <span class="n">plane</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="mf">4.0</span><span class="p">).</span><span class="nf">as</span><span class="p">(.</span><span class="py">grass</span><span class="p">);</span>
    <span class="k">const</span> <span class="n">spheres</span> <span class="o">=</span> <span class="n">sphere</span><span class="p">(</span>
        <span class="n">opRep</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="o">.</span><span class="p">{</span><span class="mi">16</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">16</span><span class="p">})</span> <span class="o">-</span> <span class="n">Vec</span><span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">0</span><span class="p">},</span>
        <span class="mf">4.0</span><span class="p">,</span>
    <span class="p">).</span><span class="nf">as</span><span class="p">(.</span><span class="py">dirt</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">opUnion</span><span class="p">(</span><span class="n">floor</span><span class="p">,</span> <span class="n">spheres</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The chunk generator then automatically handles determining what chunks to build, etc. This makes it really quick to test stuff.</p>

<p><img src="/images/jan26/screenshot-2026-01-19.png" alt="Terrain" /></p>

<p>Above I recreated my old procedural test terrain with this new system. I really should spend some time trying to make a terrain I might actually want to use in a game but procedural generation is such a deep rabbit hole.</p>

<h3 id="physics">Physics</h3>

<p><img src="/images/jan26/screenshot-2026-01-23-3.png" alt="Physics" /></p>

<p>For fun I tasked Claude Code to implement a physics engine while I was working on the graphics. It took a few tries but it actually produced something that almost works! The remaining work to get it functional should not be underestimated though.</p>

<h3 id="graphics">Graphics</h3>

<p>While restructuring my voxels I took the opportunity to also do some general graphics improvements, working on things such as TAA, SSAO, fog, and sun lighting. I basically rebuilt the rendering flow, from outlining the pipeline to slowly integrating the new voxel structure, so below you see a neat timeline of screenshots.</p>

<p><img src="/images/jan26/screenshot-2025-12-13.png" alt="Graphics" /></p>

<p>First thing I wanted to improve was TAA.</p>

<p><img src="/images/jan26/screenshot-2025-12-15.png" alt="Graphics" /></p>

<p>I think this was just a simple test scene while I was setting up the gbuffer. No lighting yet though.</p>

<p><img src="/images/jan26/screenshot-2026-01-10.png" alt="Graphics" /></p>

<p>Putting the voxel pass back in.</p>

<p><img src="/images/jan26/screenshot-2026-01-12.png" alt="Graphics" /></p>

<p>Added sunlight and shadows again, and SSAO from the looks of it.</p>

<p><img src="/images/jan26/screenshot-2026-01-14.png" alt="Graphics" /></p>

<p>One significant change from before is that I now store voxel materials separate from the chunk BLAS and here I was setting that up.</p>

<p><img src="/images/jan26/screenshot-2026-01-17.png" alt="Graphics" /></p>

<p>I think I made some attempt for fancy terrain, and failed.</p>

<p><img src="/images/jan26/screenshot-2026-01-17-2.png" alt="Graphics" /></p>

<p>To control the color of voxel materials I have a big texture atlas which the rendering samples. Here I was setting that atlas up.</p>

<p><img src="/images/jan26/screenshot-2026-01-18-2.png" alt="Graphics" /></p>

<p>And finally, I added the foliage back. This was actually slightly easier than before as my new voxel structure made it easier to place grass billboards as this is all done on the GPU.</p>

<h2 id="whats-next">What’s next</h2>

<p>As mentioned, I’ll definitely look more into the lighting. I think something to prioritize is to make sure caves look good in terms of lighting. Then I think I have everything to be able to try out some gameplay ideas. And I will definitely let Claude continue trying things out. Maybe not physics or lighting (I tried…) but I have other ideas. I have noticed that as long as it’s high level tasks and you provide a solid foundation it gets things mostly correct.</p>]]></content><author><name>Simon Ekström</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">2025</title><link href="http://simonekstrom.se/2026/01/06/2025-retro.html" rel="alternate" type="text/html" title="2025" /><published>2026-01-06T00:00:00+00:00</published><updated>2026-01-06T00:00:00+00:00</updated><id>http://simonekstrom.se/2026/01/06/2025-retro</id><content type="html" xml:base="http://simonekstrom.se/2026/01/06/2025-retro.html"><![CDATA[<p>Christmas got a bit busy so I completely forgot about my December update. Let’s summarize the year instead, here are some thoughts from last year.</p>

<!--more-->

<h2 id="highlights">Highlights</h2>

<ul>
  <li>Made progress on the “game”. It has been a lot of iteration on the voxel systems, but it’s moving in the right direction. Most fun has been learning more about all the graphics aspects (foliage, lighting, denoising, etc.).</li>
  <li><a href="https://github.com/simeks/zynth">zynth</a> was a cool side-project. It actually works and I learned a bunch of stuff: audio, drivers, Wayland, immediate mode GUIs, and more.</li>
  <li>Went full-time Linux for the desktop at home. Games have always been the big blocker but Valve has solved that for the most part. I still miss the Visual Studio debugger and I guess there are better tools than GDB but it’s enough for now. Looking forward to the <a href="https://github.com/EpicGamesExt/raddebugger">RAD Debugger</a> coming to Linux though.</li>
  <li>Made some very minor open source contributions (<a href="https://github.com/zig-gamedev/zgui">zgui</a>, <a href="https://github.com/zig-gamedev/zglfw">zglfw</a>, <a href="https://github.com/simeks/zig-vma">zig-vma</a>, <a href="https://github.com/simeks/simeks-zig">simeks-zig</a>). It’s an area where I want to do more, but I tend to focus on contributions that help myself and not doing it just because I can.</li>
</ul>

<h2 id="ai">AI</h2>

<p>I’ve experimented a bit with various AI tools that exist nowadays and here are some brief thoughts:</p>

<h3 id="auto-complete">Auto-complete</h3>

<p>This isn’t for me. It just makes me lazy and slow. You do that mental pause to wait for the autocomplete way too often and when I’m at the stage of writing code I tend to already know what to write.</p>

<h3 id="agents">Agents</h3>

<p>I’ve tried Cursor, Claude Code, and Codex. Cursor was mostly annoying and it felt like I spent most of my time trying to make it not fail on simple project setup stuff (likely due to my own skill issues). I also really don’t feel like using yet another editor. Claude Code and Codex are more my taste. They are great for quick prototypes but the code quality is lacking. The code it produces tends to have a very limited shelf life and after a point you can’t really iterate on it without too much handholding. Another thing I noticed, if you want to use it in an existing project; you need a critical mass of existing code for it to learn your conventions, but if you have too much it tends to get overwhelmed. For now I might let it prototype but I will likely go in manually and refactor or pick the things I like.</p>

<h3 id="chatgptclaude">ChatGPT/Claude</h3>

<p>Probably what I use the most. It’s great for confirming things you already kinda knew, or to search for literature on topics. It manages to find odd blog posts I would never have found otherwise, and they are usually right on the money. You still need to be wary about what the LLM tells you though. It’s not too uncommon it misinterprets the references it collects.</p>

<h2 id="month-in-screenshots">Month in screenshots</h2>

<p>This year I started doing monthly posts where I mainly focus on showing screenshots. This was a great idea and I will definitely continue. It’s a bit difficult when you work on less visual stuff but these posts are a lot quicker to write than technical posts. This is mainly for my own sake anyway.</p>

<p><img src="/images/july25/Screenshot_2025-07-24-1.jpg" alt="Billboard grass" width="90%" height="90%" /></p>

<p>Above might be screenshot of the year. Working on the grass was a lot of fun and this part was implemented while visiting Gotland for the summer vacation.</p>

<h2 id="puzzles">Puzzles</h2>

<p>December is nice; not only did I get Advent of Code, but I also saw that <a href="https://fra.se/nyheter/nyheter/nyhetsarkiv/news/eliasklaradefrasdoldautmaning.5.6b4b0ae819565f845bb15d.html">FRA posted a puzzle</a>. It was released earlier in the year, but it seems only one person had solved it. I managed to do parts 1-4 without that much of a hassle, but I hit a brick wall on the last part, so I gave up. It sucks, but I have learned <strong>a lot</strong> about hiding data in a certain medium so I will see that as a win.</p>

<p>I feel puzzles are a great thing. You tend to practice the same part of your brain as with your other projects but the objective is far more concrete. Hopefully you manage to solve it and not only did you learn something but you also got a feeling of success! The danger is of course that you won’t let it go if you ever hit that wall.</p>

<h2 id="side-quests">Side quests</h2>

<p>I also like to get side-tracked and it usually starts with some idea in my head that I can’t let go of, and then it just continues on with me exploring the idea. I learn a lot during these periods and they tend to keep the spark going. Struggling with the same problem for too long isn’t great for motivation. But the rabbit holes are endless so you must be careful to avoid going all the way to writing <a href="https://github.com/simeks/simonos">your own OS</a>…</p>

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

<p>The large boosts of productivity or creativity I get tend to come at a price, and that debt was due in December. You are hyper-focused, the brain is working at 100%, and all of a sudden, all that energy turns against you. It’s like hitting a wall. You question everything and nothing seems fun anymore. I managed to direct my focus on some easy wins at work to keep me going, but even then it’s not a great mental state to be in. Christmas helped a bit with draining that surplus energy, and I can feel my motivation is coming back. I have another week off from work now so that’s a week of doing everything, or nothing. It’s all up to me!</p>

<h2 id="whats-next">What’s next?</h2>

<p>I’m hyped for 2026. I’m a better programmer and problem-solver than a year ago and I hope I’m able to say the same thing next year. I haven’t set any long-term plans for the year but short-term I’m back to voxels so I’m hoping for some cool screenshots next post!</p>]]></content><author><name>Simon Ekström</name></author><summary type="html"><![CDATA[Christmas got a bit busy so I completely forgot about my December update. Let’s summarize the year instead, here are some thoughts from last year.]]></summary></entry><entry><title type="html">November 2025</title><link href="http://simonekstrom.se/2025/11/29/nov-2025.html" rel="alternate" type="text/html" title="November 2025" /><published>2025-11-29T00:00:00+00:00</published><updated>2025-11-29T00:00:00+00:00</updated><id>http://simonekstrom.se/2025/11/29/nov-2025</id><content type="html" xml:base="http://simonekstrom.se/2025/11/29/nov-2025.html"><![CDATA[<p><img src="/images/nov25/synth.png" alt="Synth GUI" width="90%" height="90%" /></p>

<!--more-->

<p>This month I managed to align quite well with the goals I setup for myself last month. I released a <a href="https://github.com/simeks/simeks-zig">package of goodies</a> that I can use for prototyping and I gave my <a href="https://github.com/simeks/zynth">synth</a> an actual GUI. But there were plenty of side quests this month.</p>

<h2 id="wayland">Wayland</h2>

<p>Thanks to updating some packages I hit a very frustrating issue with GLFW. I use Wayland on my machine and GLFW deals with all window related stuff. For some unclear reason I got a weird stretchy letter box, which I figured must be related to the window management.</p>

<p><img src="/images/nov25/stretchy.png" alt="Stretchy" width="90%" height="90%" /></p>

<p>I consulted ChatGPT which wasn’t much help and it ended up blaming me and my code instead. I also tried out <a href="https://libsdl.org/">SDL</a>, which didn’t have the issue, so I started to suspect some bug in GLFW. It tickled that part of the brain and I got curious whether I would be able fix it. However, I quickly realized I knew nothing about <a href="https://wayland.freedesktop.org/">Wayland</a>,</p>

<p>What better way to learn about something than doing it yourself? After reading about protocols, compositors, scanners, etc. I managed to piece together my <a href="https://github.com/simeks/simeks-zig/blob/master/src/os/Window.zig">own window handling</a>. It’s not as nice and feature complete as using something like GLFW, but it works!</p>

<p>Someone managed to figure out a <a href="https://github.com/hyprwm/Hyprland/discussions/12200#discussioncomment-14852931">workaround</a> for the GLFW issue so I could revert back to GLFW again. It’s very nice feeling to understand the code underneath you though, so I will probably stick with my own solution (until the day I need to support more platforms).</p>

<h2 id="gui">GUI</h2>

<p><img src="/images/nov25/example-gui.png" alt="Example GUI" width="90%" height="90%" /></p>

<p>One issue solved, on to the next. I continued experimenting with my own immediate mode GUI. It’s very rewarding to work on as you see the results directly in front of you, but some things sure looks easier than they actually are.</p>

<p>One problem I looked into was layouting. The prototype I had before was a lot of just hand-feeding absolute positions to into a draw list. I wanted something more automatic, but how do you layout something without knowing the full picture beforehand. One example I encountered was determining the size of a panel. You queue a panel, then you queue the contents of the panel, and not until this is done do you know the size of the panel. I just opted for a 2 pass approach where I don’t render things directly, so I can update the panel command as soon as I know its actual size.</p>

<p>Talking of order of draw commands, another challenge is dealing with popups like dropdown menus. Luckily I know beforehand that I only need to support one level of overlapping elements so I went with just having a separate command queue that I ensure is drawn on top of everything else.</p>

<p>This <a href="https://rxi.github.io/microui_v2_an_implementation_overview.html">blog post</a> was a very interesting read on the subject.</p>

<h2 id="fonts">Fonts</h2>

<p>Initially I just had hardcoded bitmasks representing characters. It was fine for just uppercase letters and some special characters but it quickly got messy. Since I wanted to use the GUI in the game as well, I figured I should add proper fonts.</p>

<p><img src="/images/nov25/example-gui-font.png" alt="Example GUI new font" width="90%" height="90%" /></p>

<p>I didn’t really feel like writing my own truetype rasterizer or even depending on an existing one in the library itself so I added a <a href="https://github.com/simeks/simeks-zig/blob/master/src/gui/Atlas.zig">pre-baked atlas</a> instead. It’s basically just a TGA image and a <a href="https://ziglang.org/documentation/master/#toc-import">ZON file</a>. I picked TGA because its one of the simpler parsers to <a href="https://github.com/simeks/simeks-zig/blob/master/src/core/tga.zig">implement</a> but I can still use it in most other software the deals with images. ZON I picked mostly because I like being able to just <code class="language-plaintext highlighter-rouge">@import</code> the data file.</p>

<p>Fonts are tricky, I’ve went for pixely font hoping it would be a bit simpler but there were still some challenges getting it to look right, like aliasing.</p>

<h2 id="zig-package">Zig Package</h2>

<p>All this work went into an <a href="https://github.com/simeks/simeks-zig">package</a> I just call <code class="language-plaintext highlighter-rouge">simeks-zig</code> (in lack of a better name). This package is basically a set of tools mainly for myself to implement things like the synth. It’s provides a set of modules</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">core</code>: Various core tools</li>
  <li><code class="language-plaintext highlighter-rouge">gpu</code>: Vulkan wrapper for graphics, it provides some high level concepts on top of Vulkan, such as a less verbose API and limited resource management.</li>
  <li><code class="language-plaintext highlighter-rouge">gui</code>: GUI libray mentioned above.</li>
  <li><code class="language-plaintext highlighter-rouge">math</code>: Math library</li>
  <li><code class="language-plaintext highlighter-rouge">os</code>: For now only the window wayland wrapper.</li>
</ul>

<p>They are far from complete but it’s a place for me to collect various things that I might need.</p>

<h2 id="synth">Synth</h2>

<p><img src="/images/nov25/synth.png" alt="Synth GUI" width="90%" height="90%" /></p>

<p>Using the package I managed to get a GUI going for the <a href="https://github.com/simeks/zynth">synth project</a> with ease. I also played around with adding an envelope for the volume and some filtering. It was nice to have a smaller project to work against when setting up the package.</p>

<h2 id="game">Game</h2>

<p><img src="/images/nov25/game.png" alt="Game" width="90%" height="90%" /></p>

<p>I moved over the game project so that it now runs without <a href="https://github.com/zig-gamedev/zglfw">zglfw</a> and <a href="https://github.com/zig-gamedev/zgui">zgui</a>. This is really nice as I’ve learned that trying out new Zig features is a bit tricky when having dependencies. I’ve helped a bit trying to keep them up to date with new Zig releases but sometimes you just want to quickly try something out on a Zig dev build. I’ll likely migrate back when things get more stable but I will enjoy the freedom while I can.</p>

<p>Migrating wasn’t that difficult, I only had a few contact points for third party libraries and the <code class="language-plaintext highlighter-rouge">gpu</code> module is basically the same one as I was using in the game already.</p>

<p>I did however decide to rehaul my hot-reloading a bit. It was a bit of a long shot and I haven’t gotten so far to determine if it was a success or not. The issue with my previous system was that it got very frustrating to deal with the layer between the host (executable) and the game (reloaded .so). It was an annoying barrier and putting something on the wrong side would cause a lot of pain down the line.</p>

<p>The idea I had now was to put as much as possible within the reloadable module. Just allocate the memory in the host and as you reload you just patch the memory in the new module. Can’t be that hard as long as I never change the memory layout?</p>

<p>Boy was I wrong. I spent a lot of time getting it to work, and I still have random failures every now and then. Some of the issues I encountered:</p>

<ul>
  <li>Function pointers: VTables are commonly used with interfaces in Zig, such as in the <code class="language-plaintext highlighter-rouge">Allocator</code>, and they are problematic since they might point to functions in the library you just unloaded.</li>
  <li>Global variables: This is mostly my fault, but if you have global variables in a module you will have to patch these as well.</li>
</ul>

<p>So we’ll see how this goes. There are more clever ways of doing this but not sure how much time I’m willing to put into this.</p>

<h2 id="whats-next">What’s next</h2>

<p>Sebastian Aaltonen is currently working on a hobby project very similar to mine and he posts about it on <a href="https://x.com/SebAaltonen">X</a>. This has really re-ignited the voxel flame so I hope to work more on that. But Christmas and <a href="https://adventofcode.com/">Advent of Code</a> is coming up so I’m certain I won’t be bored!</p>]]></content><author><name>Simon Ekström</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">October 2025</title><link href="http://simonekstrom.se/2025/10/30/oct-2025.html" rel="alternate" type="text/html" title="October 2025" /><published>2025-10-30T00:00:00+00:00</published><updated>2025-10-30T00:00:00+00:00</updated><id>http://simonekstrom.se/2025/10/30/oct-2025</id><content type="html" xml:base="http://simonekstrom.se/2025/10/30/oct-2025.html"><![CDATA[<p><img src="/images/oct25/screenshot-3.png" alt="Synth GUI" width="90%" height="90%" /></p>

<!--more-->

<p>I started the month with a bit of a break. Given that the full-time job that I have sometimes can be quite demanding, it’s nice to be able to just let go of any expectations you have on yourself when you get home. Even if my hobby projects all are driven by genuine passion, sometimes nothing beats doing nothing.</p>

<h2 id="synthesizer">Synthesizer</h2>

<p>I quickly got the itch back though. One thing I have been spending my morning walks to the office thinking about is fun ways to integrate music into a game. And what do you need to make music? An instrument! This lead me into exploring <a href="https://github.com/simeks/zynth">software synthesizers</a>. This was a fun project, I (re-)learned some signal analysis, I learned about <a href="https://www.freedesktop.org/wiki/Software/PulseAudio/">pulseaudio</a>, read up on the <a href="https://en.wikipedia.org/wiki/WAV">WAV file format</a>, and I even learned about the fun stuff you can do with a <a href="https://zig.news/lhp/want-to-create-a-tui-application-the-basics-of-uncooked-terminal-io-17gm">terminal</a>.</p>

<h2 id="gui">GUI</h2>

<p>Another thing I learned is that a synthesizer is no fun without the knobs. You want to hear the sound change as you turn a knob, and this was hard to capture through the terminal. This pulled me into my next adventure, GUI! I’ve used a lot of <a href="https://github.com/ocornut/imgui">Dear ImGui</a> in the past and it’s great! I contemplated using it here as well, but I needed something way simpler and to be honest, I got curious… So I started prototyping my own immediate GUI library.</p>

<p><img src="/images/oct25/screenshot-1.png" alt="Early widgets" width="90%" height="90%" /></p>

<p>Screenshot above is an early iteration. I got simple text rendering working and imlemented some common widgets and of course, knobs. It’s not fancy, it’s not fast, but it actually renders correctly and the input works!</p>

<p><img src="/images/oct25/screenshot-2.png" alt="Synth keys" width="90%" height="90%" /></p>

<p>Given that I want to use the GUI for a synth, I also implemented actual keys. Don’t mind the missing <code class="language-plaintext highlighter-rouge">#</code>.</p>

<p><img src="/images/oct25/screenshot-3.png" alt="Synth GUI" width="90%" height="90%" /></p>

<p>In the end I actually got something that I think might be useful. It has not been hooked up to anything yet but the library is simple enough to use, just some final tweaks then I will try to implement it with the synth. I’m working on packaging this library and the underlying Vulkan wrapper to make it available on github. This is mainly to make me able to still keep the synth repo open, but more on that in a later post.</p>

<h2 id="codex">Codex</h2>

<p>I also tried out <a href="https://openai.com/codex/">Codex</a> for the first time. In general I’ve been underwhelmed by AI tools. I use ChatGPT every now and then to get a second opinion and the thinking models are actually good for finding references. But other tools, such as <a href="https://github.com/features/copilot">Github Copilot</a> and <a href="https://cursor.com/">Cursor</a> have mostly been annoying. I installed Codex CLI and got impressed how smooth it was to get running. It immediately started chugging along on the first task I gave it. It checked compile errors, it ran tests, and produced actual functioning code.</p>

<p>It was not all great though, as complexity grew it required a lot more handholding and it frequently got stuck in “Fix A, Break B -&gt; Fix B, Break A”-type loops. You also get a bit lazy. Initially I had such a flow with the prototyping that I didn’t even check the produced code but when I did I realized I’ll have to rewrite large portions of it. One fun example was a text renderer it wrote, it had a comptime function that translated every character in a string into actual draw calls, and it created one draw call for every pixel in the character…</p>

<h2 id="voxels">Voxels</h2>

<p><img src="/images/oct25/screenshot-4.png" alt="Voxels" width="90%" height="90%" /></p>

<p>I didn’t completely forget about the voxels. I’ve been working a bit on setting up a sandbox for prototyping. It was a bit tiresome having to rewrite certain game systems every time I wanted to prototype a new idea for the voxels, so I ripped out the rendering part of it. Now I can more quickly prototype and profile some ideas I have.</p>

<h2 id="whats-next">What’s next</h2>

<p>In order of level of commitment:</p>

<ul>
  <li>Package and release the GUI + Vulkan backend.</li>
  <li>Actually hook up GUI with the synth.</li>
  <li>Put GUI into game.</li>
  <li>Back to voxels.</li>
</ul>]]></content><author><name>Simon Ekström</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">September 2025</title><link href="http://simonekstrom.se/2025/09/28/sep-2025.html" rel="alternate" type="text/html" title="September 2025" /><published>2025-09-28T00:00:00+00:00</published><updated>2025-09-28T00:00:00+00:00</updated><id>http://simonekstrom.se/2025/09/28/sep-2025</id><content type="html" xml:base="http://simonekstrom.se/2025/09/28/sep-2025.html"><![CDATA[<p><img src="/images/sep25/denoised.png" alt="Denoised" width="90%" height="90%" /></p>

<!--more-->

<p>This month has been a bit of everything just to keep the ball rolling. I have mostly focused on small annoyances, some where maybe lower hanging than others.</p>

<h2 id="lighting">Lighting</h2>

<p>As I left the lighting half-finished it was time to pay the piper. Previous implementation traded performance for quality by just having a high number of lighting samples. This means there was naturally less noise but the cost was very high. So I set out to improve the performance by reducing the sampling and implement better denoising.</p>

<p><img src="/images/sep25/noisy.png" alt="Noisy" width="90%" height="90%" /></p>

<p>Above his how it looks with the noise. This is with 2 samples for the ambient occlusion, 1 sample for the sun light, and with lighting applied at half the resolution.</p>

<p><img src="/images/sep25/denoised.png" alt="Denoised" width="90%" height="90%" /></p>

<p>And after the denoising applied. Here I just compute the lighting of a pixel by averaging over a neighborhood, taking the surface normal and depth into account to not blur edges (<a href="https://en.wikipedia.org/wiki/Bilateral_filter">bilateral filtering</a>). Samples are also accumulated over time to make it even smoother (temporal filtering).</p>

<p>This made everything run smoothly on my macbook. It also made the job easier for the temporal anti-aliasing and now the world is more pleasant to walk around in. There are some subtle issues left though, like low frequency noise and small artifacts in corners due to the raycasting.</p>

<h2 id="grass">Grass</h2>

<p>To put some life into the picture I set out to add some simple animation to the grass.</p>

<video style="width:100%;height:100%;autoldisplay:block;" autoplay="" loop="" muted="" playsinline="">
  <source src="/images/sep25/transform-mess.mp4" type="video/mp4" />
  Your browser does not support the video tag.
</video>

<p>This is how it started. As always, ordering when applying transforms is important!</p>

<video style="width:100%;height:100%;autoldisplay:block;" autoplay="" loop="" muted="" playsinline="">
  <source src="/images/sep25/animated-grass.mp4" type="video/mp4" />
  Your browser does not support the video tag.
</video>

<p>And this is how it ended.</p>

<h2 id="hot-reloading-textures">Hot-reloading textures</h2>

<p>As I did some work on the grass, including using my limited artistic talent to shape and color it, my frustration with having to restart the game over and over grew. So why not just do the same as with shaders, lets hot-reload!</p>

<video style="width:100%;height:100%;autoldisplay:block;" autoplay="" loop="" muted="" playsinline="">
  <source src="/images/sep25/hotreload.mp4" type="video/mp4" />
  Your browser does not support the video tag.
</video>

<p>I used the <a href="https://ziglang.org/learn/build-system/">Zig build system</a> and the handy <code class="language-plaintext highlighter-rouge">--watch</code> feature to create a build step for assets that automatically builds assets as they are changed. Then the game listens for changes in the built assets and reloads if needed. Now the actual build step is very simple, but I wrote a simple parser for the <a href="https://www.openraster.org/index.html">OpenRaster</a> format (basically just unzipping a .png) as a proof of concept.</p>

<h2 id="misc">Misc</h2>

<p>Here are just a collection of smaller things I worked on.</p>

<p><img src="/images/sep25/tools.png" alt="Tools" width="90%" height="90%" /></p>

<p>For the actual game part I have implemented tools as a way for the player to interact with the world. I took some time to reimplement this system to make them easier to implement. For now they don’t provide much in terms of gameplay but I have the basics for modifiying the voxels and I made myself a marker/ruler tool for some debugging purposes.</p>

<p><img src="/images/sep25/lines.png" alt="Lines" width="90%" height="90%" /></p>

<p>Implementing the tools above I identified some problems with the raycasting and to debug those I quickly put together rough grid view to visualize the voxel data structure (chunks and bricks). Lines are frickin’ hard to deal with.</p>

<h2 id="whats-next">What’s next</h2>

<p>I have two goals in mind for the near term:</p>

<ul>
  <li>Procedurally adding objects like trees to the world.</li>
  <li>Setup a file format for the voxel world. Mostly motivated by me wanting to play around some more with <a href="https://github.com/ziglang/zig/pull/24329">writergate</a>, but there is also a point to being less dependent on the procedural generation when testing stuff.</li>
</ul>

<p>First I need to deal with some annoyances when working with the voxel structure. I got the ball rolling with the tools system but there are still some loose ends.</p>]]></content><author><name>Simon Ekström</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">August 2025</title><link href="http://simonekstrom.se/2025/08/31/aug-2025.html" rel="alternate" type="text/html" title="August 2025" /><published>2025-08-31T00:00:00+00:00</published><updated>2025-08-31T00:00:00+00:00</updated><id>http://simonekstrom.se/2025/08/31/aug-2025</id><content type="html" xml:base="http://simonekstrom.se/2025/08/31/aug-2025.html"><![CDATA[<p><img src="/images/aug25/screenshot-2025-08-05.png" alt="Bleh" width="90%" height="90%" /></p>

<!--more-->

<p>This month my objective was to further work on the procedural world generation. There were still some loose ends when it came to the foliage system and I wanted to improve upon the framework I have for experimenting with procedural generation. The hot reloading I implemented initially has been very valuable, especially for things like the world generation. I make a change, save the file, and the change pops up live in my game. However, the issue is that so far the world generation has been slow, which delays the feedback loop.</p>

<h2 id="voxels">Voxels</h2>

<p>I have mentioned in past posts that I use a <a href="https://dubiousconst282.github.io/2024/10/03/voxel-ray-tracing/">64-tree</a> to represent my voxels. This is great for the GPU side of things. It is a pain for editing voxels though, since you need to traverse the tree a lot. So I made some changes. Instead of having to deal with one 64-tree per chunk (64x64x64 voxels) on the CPU, I opted for keeping a big <a href="https://studenttheses.uu.nl/handle/20.500.12932/20460">brickmap</a> for the whole world, holding 16x16x16-sized bricks, which makes edits and generation a breeze. As I stream things to the GPU I build the trees on the fly hierarchically. If a single brick has changed, I only need to rebuild the sub-tree representing that brick. As a bonus this worked very well for foliage as well, I just tell the system to rebuild the foliage for a brick by providing the root node of the corresponding sub-tree.</p>

<p>Now with everything working I can say it was well worth it, but it was an adventure to get there. Here is some screenshots of my journey:</p>

<p><img src="/images/aug25/screenshot-2025-08-03.png" alt="Bleh" width="90%" height="90%" /></p>

<p><img src="/images/aug25/screenshot-2025-08-05.png" alt="Bleh" width="90%" height="90%" /></p>

<p>I think most of my bugs was like the ones above, you have weird bit shifts all over the place. If I remember correctly there were 2 main causes, either me providing incorrect offsets when writing the 64-tree (which in reality is just a stream of bytes). Or, the fact that GLSL provides a horrible developer experience, I utilize 64 bit integers a lot, and they are a bit wonky.</p>

<p><img src="/images/aug25/screenshot-2025-08-10.png" alt="Bleh" width="90%" height="90%" /></p>

<p>Since I now also pack everything into a single tree, there were also some issues getting that thing right. Transforming world positions into brick index, into voxel index, etc.</p>

<p><img src="/images/aug25/screenshot-2025-08-17.png" alt="Bleh" width="90%" height="90%" /></p>

<p>Then I needed the foliage system to place nice as well.</p>

<p><img src="/images/aug25/screenshot-2025-08-10-2.png" alt="3D noise" width="90%" height="90%" /></p>

<p>Now on to the fun stuff! Previously, I’ve mostly used a SDF scene I wrote myself because I haven’t bothered to go back to actual procedural terrain. Here is a buggy view of some 3D noise. The bug itself is not really related to previous bugs, it just had to do with the noise being too high frequency for the “smart” parameters I setup to not generate more than what is needed.</p>

<p><img src="/images/aug25/screenshot-2025-08-18.png" alt="Heightmap" width="90%" height="90%" /></p>

<p>Here is a more reasonable example of procedural terrain. In this case it’s just a heightmap.</p>

<p><img src="/images/aug25/screenshot-2025-08-18-2.png" alt="Ridged noise" width="90%" height="90%" /></p>

<p>Some ridged fractal noise. In the screenshots all the procedural terrain is just rock but I have some code setup for the coloring as well. It is still a bit slow though so I really want to tighten that feedback loop first.</p>

<h2 id="profiler">Profiler</h2>

<p><img src="/images/aug25/screenshot-2025-08-31.png" alt="Profiler" width="90%" height="90%" /></p>

<p>One important aspect of making things faster is actually knowing how fast (or slow) something is. So another thing I worked on this month was a very simple low-overhead profiler. It is similar to <a href="https://github.com/wolfpld/tracy">tracy</a> but without all the features. I just mark a block of code like this:</p>
<div class="language-zig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
    <span class="n">profiler</span><span class="p">.</span><span class="nf">beginZone</span><span class="p">(</span><span class="s">"load_chunk"</span><span class="p">);</span>
    <span class="k">defer</span> <span class="n">profiler</span><span class="p">.</span><span class="nf">endZone</span><span class="p">();</span>
    <span class="c">// Do stuff</span>
<span class="p">}</span>
</code></pre></div></div>
<p>and it collects statistics on how long that block of code ran. It works in a multi-threaded environment as well, which was needed since I generate chunks in a background thread.</p>

<p>I have no fancy visualizations yet, but we’ll see if I ever implement those. Tracy is a lot better for that. But I do get some nice live stats, and a summary when I close the program.</p>

<p>I also started collecting some more stats on memory usage for my voxels, a bit messy but has been very useful:</p>

<p><img src="/images/aug25/screenshot-2025-08-31-2.png" alt="Voxel memory" width="90%" height="90%" /></p>

<h2 id="slang">Slang</h2>

<p>Did I mention that GLSL isn’t great? One thing that has gotten more and more frustrating as the shaders get more complicated is dealing with GLSL. Using <a href="https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_buffer_device_address.html">Buffer device address</a> in Vulkan is a lot of boilerplate, which was fine up to a point. Even more frustrating is that I have found myself often wrangle the language to make the compiler produce the <a href="https://www.khronos.org/spirv/">SPIR-V</a> I want. Since I develop on Mac sometimes I use <a href="https://github.com/KhronosGroup/MoltenVK">MoltenVK</a>, which has its owns set of quirks that I need to work around.</p>

<p>In comes <a href="https://shader-slang.org/">Slang</a>. It’s a more modern language for shaders that also compiles to SPIR-V (amongst others). I wanted to see if it could make the developer experience a bit better so I ported all my shaders to Slang. I haven’t dared to touch all the shiny features it provides compared to GLSL but I’m already very happy with it.</p>

<p>Ironically I haven’t been able to fully get it to work on macos yet. I did the mistake of updating the OS so now Xcode has blocked me out of using the graphics debugger until the new version of Xcode releases. I might be able to solve this by changing the build process to target older macos but my first attempts failed so I gave up.</p>

<h2 id="summary">Summary</h2>

<p>This type of refactor can be a bit soul crushing but it’s also very exciting. You get the chance to actually do some problem solving and learn from your past mistakes. It’s a balancing act though, having visual feedback helps a lot in keeping yourself motivated but if you get stuck with code that won’t compile for too long, you just feel like giving up.</p>

<p>So, what’s next? Vacation ended in August and the autumn is closing in so I will probably try to focus on something high reward for next month to keep the motivation up. This means I should probably not spend that much time on the procedural generation. Adjusting parameters to get something you are happy with is a never-ending struggle and it tends to drain you, but we’ll see.</p>]]></content><author><name>Simon Ekström</name></author><summary type="html"><![CDATA[]]></summary></entry></feed>