Jeff Smith
banner
jeffsmith.bsky.social
Jeff Smith
@jeffsmith.bsky.social
Game designer and developer @ Incoming Games. Shadow of Aya coming soon. https://www.shadowofaya.com
This turned out to be “Grizzly Bear”. If you want to know how my week is going
October 30, 2025 at 4:50 AM
As a final flourish, I add a simple particle system to simulate ash. And with that, the effect is done—until it’s not. Iteration continues, and the needs of tomorrow may outweigh the needs of today.

But that is another problem! One at a time.
October 17, 2025 at 4:44 PM
In the way these things sometimes happen, this works perfectly the first try.
October 17, 2025 at 4:44 PM
I realize "heat distortion” doesn’t fully describe what we're after. Ideally it would obscure/fuzz the image as well as bend it. A blur seems like it would do the trick, but we want it only sparingly, and probably synced with the distortion. Can we reuse the mask, with the alpha as the blur radius?
October 17, 2025 at 4:44 PM
Until it works! In the end I add a vertical blur to let the distortion “rise” off the lava. I also tweak the noise for less intensity, and add a cosine wave to the original sine wave, using a different amplitude and frequency. I’m happy with the distortion pattern now, but something’s still missing.
October 17, 2025 at 4:44 PM
So much of this kind of work is iteration. You have an idea, you test the idea, you modify the idea, you test again. This thing I thought would work is too subtle, what if I multiply it? My code is broken, what error did I get? One problem at a time, over and over, and over again
October 17, 2025 at 4:44 PM
We’ll try a blur so that the mask softens and expands. This looks much nicer in this view, but will certainly need adjustment for the final effect. I can already guess that it'll need the gamma boosted, or a directional blur of some kind
October 17, 2025 at 4:44 PM
Let’s switch views to see the mask channel itself. We can see our perlin noise texture, and how it has the harsh edge of the lava tiles.
October 17, 2025 at 4:44 PM
Returning to our heat filter—and—it works! Well, it masks to the right layer. We still have to figure out this distortion effect. And the next thing I want to try is expanding the mask—looking at it now, it's become too subtle, only visible around the lava edges, in a way that’s easy to miss.
October 17, 2025 at 4:44 PM
(Note: that code assumes your values for znear, zfar, and zfrom match the values used in your calls to matrix_build_lookat and matrix_build_projection_ortho)
October 17, 2025 at 4:44 PM
This was a long detour, but we learned something, and that’s worth it in my book. (It also helped me solve a bug in an unrelated part of the code—a topic for another time.) Our updated originalDepth calculation now looks like this:
October 17, 2025 at 4:44 PM
But looking again, it’s obvious the `from` values also set translation, since this is where I’m passing my `view_x` and `view_y` values. It’s one of those obvious mistakes that goes unchallenged because “it works”, until it doesn’t
October 17, 2025 at 4:44 PM
I think I had some vague assumption that these were used to calculate *just* a rotation matrix, and that their actual position was irrelevant. Which is sort of half right, in that this is true of the `to` values
October 17, 2025 at 4:44 PM
Turns out this code I set up once and promptly forgot about is relevant again! The `matrix_build_lookat` function takes a `zfrom` and `zto`, which I’ve set to -10 and 0, respectively.
October 17, 2025 at 4:44 PM
Something about the number 10 is tickling my brain. I seem to recall a 10 somewhere related to camera setup, so let’s take a look in the camera code. Say, what’s this random -10 value?
October 17, 2025 at 4:44 PM
Hang on. The Lava layer should be at 5282, but in the shader output it’s 5292. And the layer that *is* visibly distorting, Rocks_2, is at 5272, not 5282 like the shader says. So every layer is off by 10 for some reason...?
October 17, 2025 at 4:44 PM
The values look okay—they’re in the expected range and have the expected distribution. It sure seems like the math and precision are fine. So what then. Let’s do a sanity check in the logs to see what depth we’re setting the layers to.
October 17, 2025 at 4:44 PM
Let’s see what’s going on. My guesses are either a math error on my part (likely, and maybe an easy fix) or insufficient precision in the depth buffer (unlikely, and maybe impossible). Using my new tool, I can get the depth as interpreted by the shader at each pixel, and, uhh, huh. It looks… right?
October 17, 2025 at 4:44 PM
And so. On the shader we pack our value into the rgba channels of the output pixel. We then read the target surface as a buffer, and reverse the math to get back what we put in. Hardly efficient, but fine for a debugging tool—and lets us peek at exactly what values our shader is working with!
October 17, 2025 at 4:44 PM
The latter is the tricky bit: it’s notoriously hard to get data out of a shader; it’s simply not what they’re built for. What they are built for is drawing images, and since an image is a grid of data, we should be able to output our converted depth value per-pixel, provided it will fit in 32 bits
October 17, 2025 at 4:44 PM
Time for my most loved/hated task: building debug tools. I ended up making a pair of shaders that work in conjunction:
1) to output a visually parsable representation of the depth buffer, and
2) to encode the depth value in a way we can read back after rendering.
October 17, 2025 at 4:44 PM
That did not do the thing! At first glance I thought it did nothing at all, probably I screwed something up and broke the shader. But looking closer, it *is* distorting, and it is masking—just not on the right layer. So something is up with how we’re reading or writing the depth value, maybe
October 17, 2025 at 4:44 PM