<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Get Info: #rendering</title>
    <description>Posts tagged “rendering” — Blog of independent game and app developer Matt Sephton. Featuring vintage Macintosh, game development, digital artwork, Japanese esoterica, video game reviews, hacks and tips, and much more.</description>
    <link>https://blog.gingerbeardman.com/tag/rendering/</link>
    <atom:link href="https://blog.gingerbeardman.com/tag/rendering/index.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Wed, 15 Jul 2026 17:33:15 +0000</pubDate>
    <lastBuildDate>Wed, 15 Jul 2026 17:33:15 +0000</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>

    
      
        <item>
          <title>OpenSCAD to Sprite Sheet workflow</title>
          <description>&lt;p&gt;I just released the “OpenSCAD to Spritesheet” workflow I created for &lt;a href=&quot;/tag/dailydriver/&quot;&gt;Daily Driver&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/gingerbeardman/openscad-spritesheet&quot;&gt;github.com/gingerbeardman/openscad-spritesheet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s a Frankenstein mish-mash of a Makefile and several shell scripts that evolved over many months/years. Initial rendering is done using OpenSCAD, and post-processing is done using ImageMagick. Model poses and rendering variations are controlled by variables in either the shell script or passed through to the model. The whole process is optimised to do as much in parallel as I could figure. More info at the link above! 🚗💨&lt;/p&gt;

&lt;h2 id=&quot;post-processing&quot;&gt;Post Processing&lt;/h2&gt;

&lt;p&gt;After exporting all frames there is some &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;image magick&lt;/code&gt; work to process the files as follows:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;stitch frames together into a single sprite sheet&lt;/li&gt;
  &lt;li&gt;split sprite sheet into RGBA channels&lt;/li&gt;
  &lt;li&gt;process channels to recolour and dither as required&lt;/li&gt;
  &lt;li&gt;recombine processed channels into new sprite sheet image&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can read about that in a &lt;a href=&quot;/2021/06/05/channelling-rgb-into-1bit/&quot;&gt;previous blog post&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;benchmarks&quot;&gt;Benchmarks&lt;/h2&gt;

&lt;p&gt;A full build of 36 cars is as follows:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;3GHz 6-core Intel Mac mini 32GB
    &lt;ul&gt;
      &lt;li&gt;100% CPU for ~26 minutes&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;M1 Pro 10-core MacBook Pro 16GB
    &lt;ul&gt;
      &lt;li&gt;70% CPU for ~9 mins&lt;/li&gt;
      &lt;li&gt;about 3x speedup&lt;/li&gt;
      &lt;li&gt;approx 16 seconds per car&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s parallel 3D rendering, PNG writing &amp;amp; compositing &amp;amp; processing, and copying of ~140K files (which takes up ~0.5GB of disk space).&lt;/p&gt;

&lt;h2 id=&quot;example-model&quot;&gt;Example Model&lt;/h2&gt;

&lt;p&gt;Not to scale! Sizes of features are exagerated to allow for them to appear correct when rendered at a very small size.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/openscad-spritesheet-model-car.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;example-output&quot;&gt;Example Output&lt;/h2&gt;

&lt;p&gt;990 frames each for car and shadow, total of 1980 frames per sprite sheet. Each sprite sheet takes up about ~400KB of RAM on Playdate, and only one is loaded at a time.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/posts/openscad-spritesheet-car-table-38-38.png&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/openscad-spritesheet-car-table-38-38.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Wed, 04 Oct 2023 15:19:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/10/04/openscad-to-sprite-sheet/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/10/04/openscad-to-sprite-sheet/</guid>
        </item>
      
    
      
        <item>
          <title>Daily Driver: Club GTi</title>
          <description>&lt;p&gt;My &lt;a href=&quot;/2023/06/07/gti-cub-supermini-festa/&quot;&gt;GTi Club: Supermini Festa!&lt;/a&gt; obsession has bled into my game &lt;a href=&quot;/tag/dailydriver/&quot;&gt;Daily Driver&lt;/a&gt;… the “Club GTi” car features smaller wheels than the standard size, more roll during turning, and lots of detail and decoration to show off the rendering workflow I spent so many hours on. A good choice for the autotesting/motorkhana/gymkhana stages.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-club-gti.gif#playdate&quot; alt=&quot;GIF&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Here’s the 3D model, created in &lt;a href=&quot;https://openscad.org&quot;&gt;OpenSCAD&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-club-gti-model.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Tue, 08 Mar 2022 12:32:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2022/03/08/club-gti/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2022/03/08/club-gti/</guid>
        </item>
      
    
      
        <item>
          <title>Daily Driver: How big is a sprite sheet?</title>
          <description>&lt;p&gt;Since the early days of the project I’ve been generating the car sprites by rotating the 3D model 360 degrees in a sequence of 32 steps. It works well and I hadn’t given it much thought. Until over on the &lt;a href=&quot;https://discord.gg/zFKagQ2&quot;&gt;unofficial Playdate Discord server&lt;/a&gt; user Jono showed of a hot-dog trick rendered using 64 steps which looked super smooth! This got me thinking about increasing the number of rotation steps used in my workflow for Daily Driver.&lt;/p&gt;

&lt;h2 id=&quot;code-changes&quot;&gt;Code changes&lt;/h2&gt;

&lt;p&gt;I had to do a bit of code changing as I had used the value &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;32&lt;/code&gt;, or the angular equivalent of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;11.25&lt;/code&gt; (degrees), in a few places throughout my code. Once I’d changed those to refer to a single variable I was set. I can now run the game with sprites of any number of steps.&lt;/p&gt;

&lt;h2 id=&quot;rendering-changes&quot;&gt;Rendering changes&lt;/h2&gt;

&lt;p&gt;Similar changes were also needed in my rendering workflow, moving any hardcoded steps or angle values into a variable. After replacing hard-coded magic numbers with variables, I spent way too long figuring out why angles were being rounded down to the nearest integer. Eventually I realised that in shell scripts the calculation &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$( 360/64 )&lt;/code&gt; gives an integer result of 5, whereas  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$( 360.0/64 )&lt;/code&gt; gives the more accurate floating point result of 5.625.&lt;/p&gt;

&lt;p&gt;Interestingly, I also needed to increase my filename pattern from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%03d&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%04d&lt;/code&gt; as there are now thousands of images being processed.&lt;/p&gt;

&lt;h2 id=&quot;how-high&quot;&gt;How high?&lt;/h2&gt;

&lt;p&gt;After making the necessary changes to my workflow it is very easy to render a new set of sprites. But what number of steps to use? My immediate thought was to simply double it to 64, which is higher than the frame rate I’m using and the result was definitely smoother.&lt;/p&gt;

&lt;p&gt;After this &lt;a href=&quot;https://twitter.com/mrgan&quot;&gt;Neven Mrgan&lt;/a&gt; asked if I’d explored whether there was a performance limit or upper boundary? I hadn’t but it sounded like a fun thing to explore.&lt;/p&gt;

&lt;p&gt;But why go higher? Well, even if we have more frames of animation that than refresh rate, we can still gain in terms of accuracy. The more steps there are the more discrete angles we can show the car pointing. That was good enough reason for me to try it out.&lt;/p&gt;

&lt;p&gt;I spent some time exporting a sprite at 32, 64, 90, 128, 180, 256, 360-steps to compare not only the smoothness of the animation but also other related implications.&lt;/p&gt;

&lt;h2 id=&quot;selected-results&quot;&gt;Selected results&lt;/h2&gt;

&lt;p&gt;Most modern browsers are well-behaved enough to only play an Animated GIF if it is on the page. So you’ll probably need to force reload the page (hold shift and click reload) to synchronise one or more of the below animations.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-rotation-steps-32.gif#playdate&quot; alt=&quot;32 rotation steps&quot; title=&quot;Above: 32 rotation steps. After I&apos;ve mentioned looking for the &amp;quot;judder&amp;quot; you won&apos;t be able to unsee it&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-rotation-steps-64.gif#playdate&quot; alt=&quot;64 rotation steps&quot; title=&quot;Above: Even with twice as many rotation steps there&apos;s still a slight judder&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-rotation-steps-90.gif#playdate&quot; alt=&quot;90 rotation steps&quot; title=&quot;Above: 90 rotation steps give us one sprite every 4 degrees, judder becomes less noticable&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-rotation-steps-180.gif#playdate&quot; alt=&quot;180 rotation steps&quot; title=&quot;Above: 180 rotation steps give us one sprite every 2 degrees, judder is even less noticable&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-rotation-steps-360.gif#playdate&quot; alt=&quot;360 rotation steps&quot; title=&quot;Above: 360 rotation steps give us one sprite for every degree, as good as it gets&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;stats&quot;&gt;Stats&lt;/h2&gt;

&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;steps #&lt;/th&gt;
        &lt;th&gt;render time Secs&lt;/th&gt;
        &lt;th&gt;frames #&lt;/th&gt;
        &lt;th&gt;width PX&lt;/th&gt;
        &lt;th&gt;pixels W×H&lt;/th&gt;
        &lt;th&gt;png KB&lt;/th&gt;
        &lt;th&gt;pdt KB&lt;/th&gt;
        &lt;th&gt;ram Bytes&lt;/th&gt;
        &lt;th&gt;ram MB&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;32&lt;/td&gt;
        &lt;td&gt;30s&lt;/td&gt;
        &lt;td&gt;704&lt;/td&gt;
        &lt;td&gt;1216&lt;/td&gt;
        &lt;td&gt;1016576&lt;/td&gt;
        &lt;td&gt;48&lt;/td&gt;
        &lt;td&gt;39&lt;/td&gt;
        &lt;td&gt;147404&lt;/td&gt;
        &lt;td&gt;0.15&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;64&lt;/td&gt;
        &lt;td&gt;59s&lt;/td&gt;
        &lt;td&gt;1408&lt;/td&gt;
        &lt;td&gt;2432&lt;/td&gt;
        &lt;td&gt;2033152&lt;/td&gt;
        &lt;td&gt;64&lt;/td&gt;
        &lt;td&gt;78&lt;/td&gt;
        &lt;td&gt;294807&lt;/td&gt;
        &lt;td&gt;0.29&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;90&lt;/td&gt;
        &lt;td&gt;82s&lt;/td&gt;
        &lt;td&gt;1980&lt;/td&gt;
        &lt;td&gt;3420&lt;/td&gt;
        &lt;td&gt;2859120&lt;/td&gt;
        &lt;td&gt;111&lt;/td&gt;
        &lt;td&gt;107&lt;/td&gt;
        &lt;td&gt;414572&lt;/td&gt;
        &lt;td&gt;0.41&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;128&lt;/td&gt;
        &lt;td&gt;114s&lt;/td&gt;
        &lt;td&gt;2816&lt;/td&gt;
        &lt;td&gt;4864&lt;/td&gt;
        &lt;td&gt;4066304&lt;/td&gt;
        &lt;td&gt;143&lt;/td&gt;
        &lt;td&gt;140&lt;/td&gt;
        &lt;td&gt;589614&lt;/td&gt;
        &lt;td&gt;0.59&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;180&lt;/td&gt;
        &lt;td&gt;159s&lt;/td&gt;
        &lt;td&gt;3960&lt;/td&gt;
        &lt;td&gt;6840&lt;/td&gt;
        &lt;td&gt;5718240&lt;/td&gt;
        &lt;td&gt;174&lt;/td&gt;
        &lt;td&gt;188&lt;/td&gt;
        &lt;td&gt;829145&lt;/td&gt;
        &lt;td&gt;0.83&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;256&lt;/td&gt;
        &lt;td&gt;192s&lt;/td&gt;
        &lt;td&gt;5632&lt;/td&gt;
        &lt;td&gt;9728&lt;/td&gt;
        &lt;td&gt;8132608&lt;/td&gt;
        &lt;td&gt;209&lt;/td&gt;
        &lt;td&gt;227&lt;/td&gt;
        &lt;td&gt;1179228&lt;/td&gt;
        &lt;td&gt;1.18&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;360&lt;/td&gt;
        &lt;td&gt;285s&lt;/td&gt;
        &lt;td&gt;7920&lt;/td&gt;
        &lt;td&gt;13680&lt;/td&gt;
        &lt;td&gt;11436480&lt;/td&gt;
        &lt;td&gt;240&lt;/td&gt;
        &lt;td&gt;264&lt;/td&gt;
        &lt;td&gt;1658290&lt;/td&gt;
        &lt;td&gt;1.66&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;

&lt;/div&gt;

&lt;h2 id=&quot;charts&quot;&gt;Charts&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/daily-driver-rotation-steps-charts.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;implications&quot;&gt;Implications&lt;/h2&gt;

&lt;p&gt;The increase from 32-steps to 360-steps is echoed by an increase of around a factor of ten across each of rendering time, number of frames and memory usage.&lt;/p&gt;

&lt;p&gt;In the above table we can see that related stats increase pretty much linearly, with a slight downward trend for file sizes and a slight upward trend for everything else.&lt;/p&gt;

&lt;p&gt;The increased smoothness is great, and very easy to notice. The benefits of the increased smoothness becomes less noticeable with each increase in step count. However, it’s important to remember there are other implications:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Rendering time&lt;/li&gt;
  &lt;li&gt;File size&lt;/li&gt;
  &lt;li&gt;Memory usage&lt;/li&gt;
  &lt;li&gt;Performance in-game&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;rendering-time&quot;&gt;Rendering time&lt;/h3&gt;
&lt;p&gt;This increases from what I would call a quick 30 seconds per render at 32-steps, to an extremely slow 284 seconds (4m45s) at 360-steps. For a full set of renders it’s not as easy as multiplying that number, there is some overhead that goes with it. This means a real world increase from ~20 minutes to multiple hours, which could be a problem. Cars aren’t generally re-rendered very frequently but when I do render them I do it many times as I make minor tweaks. So, at this point all numbers of steps are on the table but with a preference for those with shorter rendering times.&lt;/p&gt;

&lt;h3 id=&quot;file-size&quot;&gt;File size&lt;/h3&gt;
&lt;p&gt;Playdate’s compiled image format is very similar in file size to an optimised PNG. The file size increases by slightly more than double as the number of steps is doubled. Even the largest size at 360-steps is only 264KB (0.26MB) which seems OK in the grand scheme of things. All numbers of steps are on the table.&lt;/p&gt;

&lt;h3 id=&quot;memory-usage&quot;&gt;Memory usage&lt;/h3&gt;
&lt;p&gt;As we can see by the table below, memory usage is quite optimised on Playdate. The system is clever enough to trim transparent areas from cells of sprite sheets and store the offset of the image instead of the complete raw bitmap data. For this example sprite memory usage is roughly &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(W×H÷8)*1.16&lt;/code&gt; rather than the expected &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(W×H÷8)*2.0&lt;/code&gt;. The car sprite is my largest occupier of memory. Only one is loaded at a time and even at 360-steps it fits comfortably in the Playdate’s 16MB RAM. All numbers of steps are on the table.&lt;/p&gt;

&lt;h3 id=&quot;performance-in-game&quot;&gt;Performance in-game&lt;/h3&gt;
&lt;p&gt;I’m targeting a refresh rate of 60fps on the device, so it’s important for me to check that any changes to rendering don’t have an adverse effect on performance. At 32-steps the car sprite was changed roughly every second update, with 64-steps and higher the car sprite will be changed every update. Looking at performance in broad terms, there does seem to be a small hit when using the large numbers of steps. It doesn’t prevent me hitting 60fps, but it does leave me less time to do other things I have planned.&lt;/p&gt;

&lt;h2 id=&quot;final-choice&quot;&gt;Final choice?&lt;/h2&gt;
&lt;p&gt;If you’ve read this far you’re probably wanting to know my choice. How many steps did I choose? Well, the jury is out. This is the sort of thing I’ll have to live with for a while before I settle on a choice.&lt;/p&gt;

&lt;p&gt;I’ll probably end up going somewhere in the middle with a choice that gives increased smoothness and accuracy, renders fairly quickly and doesn’t affect performance in-game.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 07 Aug 2021 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2021/08/07/how-big-is-a-sprite-sheet/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2021/08/07/how-big-is-a-sprite-sheet/</guid>
        </item>
      
    

  </channel>
</rss>
