[HN Gopher] ZX Spectrum Raytracer
       ___________________________________________________________________
        
       ZX Spectrum Raytracer
        
       Inspired by a recent article in HN about raytracing on some ancient
       architecture, I realized that I've implemented many a raytracer in
       my life, but never one for my first love, the ZX Spectrum. So I
       fixed the glitch.  Trigger warning: the source code includes GO TO
       statements. Because that's how we did it in the 80s! Enter at your
       own peril.
        
       Author : ggambetta
       Score  : 136 points
       Date   : 2024-01-24 15:20 UTC (7 hours ago)
        
 (HTM) web link (gabrielgambetta.com)
 (TXT) w3m dump (gabrielgambetta.com)
        
       | kinard wrote:
       | Amazing stuff, love coming back to speccy code.
        
       | fipar wrote:
       | Very nice!
       | 
       | This was almost my first computer (almost because it was actually
       | a TK-90) so it's nice to see it here, I guess it was a common
       | starting point if you were in a family with a computer in the 80s
       | in UY :)
       | 
       | While I know it's not the same (plus I'm a backend guy so I know
       | nothing about graphics, a fact that may change in the near future
       | as my daughter is set to study animation & videogames after she's
       | done with high school next year) but your last version made me
       | think of Batman for the Spectrum. I remember thinking that game's
       | graphics were magical compared with most of the other games for
       | that computer at the time.
        
         | ggambetta wrote:
         | Batman is a good example of avoiding attribute clashing by
         | going monochrome :) But I have the same memories, huge sprites
         | and smooth animation, it was pretty great!
        
         | AndrewStephens wrote:
         | I loved that isometric Batman game. I spent an enjoyable 3
         | weeks of after school hours beating it on the Amstrad. It was
         | just so ... weird.
        
       | lowbloodsugar wrote:
       | Nutter. Love it.
        
       | foobarian wrote:
       | The thing I loved about Spectrum was game loading graphics from
       | tape. Spectrum had a very strange video memory layout, so the
       | bitmap would load first into different thirds of the screen (but
       | still all monochrome). And then at the end the attributes (which
       | were much smaller due to the 8x8 size) would load almost
       | instantly, "painting" the monochrome image in a final splash! So
       | this is very nostalgic for sure.
       | 
       | And the funny thing is, I like the spectrum renders better than
       | the "perfect" ones from a modern computer :-)
        
         | praptak wrote:
         | I also remember that there existed cool loaders that were able
         | to paint the image in arbitrary order. It was pretty impressive
         | because it required doing calculations in the tight loop which
         | calculated the delay between slopes of the signal which came
         | from tape.
        
           | ggambetta wrote:
           | I remember a loader where you could _play a game of
           | Mastermind_ while the game was loading. Magic. Blew my mind,
           | it 's the kind of thing that wasn't supposed to be possible,
           | yet it was...
        
             | becurious wrote:
             | There is something like a 171 clock cycle delay in the
             | regular tape loaded routine between looking for edge
             | transitions in the audio signal. You just break your
             | program up into pieces that fits into that. I did one that
             | did a countdown timer and broke it into exactly the loop
             | delay but I suspect the tape loading would be tolerant to a
             | little more inexactness.
        
       | stevekemp wrote:
       | There _is_ the possibility to define functions, via "DEF FN":
       | 10 DEF FN s(x)=x*x:          20 PRINT FN s(3)
       | 
       | Of course it is limited, as ZX Spectrum BASIC is limited, but it
       | is an option.
        
         | ggambetta wrote:
         | Sure. I meant functions in the modern programming sense of the
         | word, not in the mathematical sense.
        
       | cglace wrote:
       | My favorite class in college was building a raytracer from
       | scratch and slowly adding features and optimizing. I always
       | wanted to keep adding on to the raytracer I made but never found
       | the time.
        
         | Keyframe wrote:
         | Hence Ray Tracing in One Weekend by raytracing royalty prof.
         | Shirley himself https://raytracing.github.io/
        
           | cglace wrote:
           | Thanks, I think I might try to work through those with my
           | son.
        
             | Marazan wrote:
             | It's an excellent book which I have worked through twice,
             | once in Python/Numpy and once in Pico-8
        
       | roskelld wrote:
       | It would be fun to see a set of frames rendered out and stitched
       | together to make an animated sequence. 17 hours per frame. It'll
       | be like Pixar where you'd need a render farm of Speccy's to knock
       | it out.
        
         | MenhirMike wrote:
         | One could "cheat" by running this in an emulator of a massively
         | overclocked ZX Spectrum to cut down on the render time and
         | still get an authentic end result.
        
           | giantrobot wrote:
           | Or fan out the frames to a bunch of emulator instances.
        
         | s_gourichon wrote:
         | You mean, like this?
         | https://www.youtube.com/watch?v=SJ79CdUMfVA
         | 
         | Also, not exactly the same type or rendering, but basically
         | word for word what you wrote: the full-screen fully animated
         | parts appear to be "a set of frames rendered out and stitched
         | together to make an animated sequence"
         | 
         | https://www.youtube.com/watch?v=o59LrpzGUaE
        
       | Anotheroneagain wrote:
       | IDK, but it seems super wasteful: Why not compute RDX and RDX*RDX
       | outside the inner loop? Why not precompute COX, COY, COZ and EQC
       | once per scene?
        
         | ggambetta wrote:
         | (COX, COY and COZ) vary per pixel and per sphere, hence so does
         | EQC, so can't really compute them once per scene, unless I'm
         | missing something?
         | 
         | RDX yes, and I did that (and a lot more) in the later
         | iterations where performance started to become an issue.
        
           | Anotheroneagain wrote:
           | I don't see why they would vary? They must remain constant
           | for each object.
        
             | ggambetta wrote:
             | If you're talking about the first iteration, you're right
             | (ROX, ROY, ROZ) is hardcoded to (0, 0, 0), but I decided to
             | implement a general algorithm anyway, because performance
             | wasn't a problem at that point.
             | 
             | If you can find optimizations that don't obliterate code
             | readability for the fifth iteration
             | (https://gabrielgambetta.com/zx-raytracer-5-src.html), I'm
             | all ears :)
        
       | hoc wrote:
       | Waiting on the port for HP calculators in RPL...
       | 
       | (did a 2d morphing animator and a simple cad tool back then. also
       | straight forward base algs that you can keep adding to, adding
       | some more, and some more...)
       | 
       | Great write-up. Thanks. Love the Spectrum and its direct token
       | input despite never owning one.
        
       | jng wrote:
       | Beautiful, I love it, congratulations! I started my programming
       | journey with Basic on a ZX Spectrum +, then Z80 assembly
       | language, then 8086 and the rest from there... 40 years of
       | programming and my appreciation for my original platform is still
       | there. Thank you for the tribute.
        
         | ggambetta wrote:
         | Similar story here. The Spectrum holds a very special place in
         | my heart :)
        
       | rahimnathwani wrote:
       | The penultimate screenshot looks at least as good as the output
       | from VU-3D, a commercial raytracer for the ZX Spectrum. Pretty
       | impressive!
       | 
       | https://worldofspectrum.org/software?id=0008953
        
       | Joeboy wrote:
       | > each 8x8-pixel block can show one or two different colors, but
       | never three or more
       | 
       | That's not _entirely_ true. If you modify the colour attributes
       | between the drawing of each row, you can get eight separate
       | 2-colour palettes in an 8x8 block. I believe I saw somewhere that
       | people had managed this horizontally as well somehow.
        
         | varjag wrote:
         | Though it is naturally out of realm of Spectrum BASIC the
         | author had this exercise with.
        
           | Joeboy wrote:
           | Indeed, but they mention the possibility of redoing it in
           | assembly.
        
       | shever73 wrote:
       | This is glorious, thank you for sharing. I also started coding on
       | the ZX Spectrum and can still remember the Z80 assembler opcodes.
       | 
       | You make a good point about both the simplicity and immediacy of
       | the environment, which made it so easy to just start coding.
        
       | mbitsnbites wrote:
       | > The ZX Spectrum has a 3.5 MHz Z80 processor (1,000 times slower
       | than current computers)
       | 
       | Actually, it's much, much slower than that.
       | 
       | The Z80 takes at least one clock cycle to add or subtract an
       | 8-bit number (sometimes several clock cycles, depending on
       | addressing mode). For a 32-bit number those clock cycles add up
       | quickly.
       | 
       | In contrast, a modern 3.5GHz CPU can add or subtract several
       | 64-bit numbers every clock cycle
       | 
       | And don't get me started on multiplication or floating-point
       | arithmetic. A modern CPU would be thousands times faster, even if
       | it was running at the same clock speed.
       | 
       | Thus, I think that the feat is even more impressive than
       | presented!
        
         | adamredwoods wrote:
         | http://www.andreadrian.de/oldcpu/Z80_number_cruncher.html
        
       | s_gourichon wrote:
       | Excellent! A similar program, apparently from 3 years ago, did
       | similar things, in Basic also, on the Amstrad CPC, a machine with
       | the same processor, a little more RAM and more (27) colors or
       | shades of green, depending on the screen it was hooked to.
       | 
       | * colored spheres (like you did)
       | 
       | * (one) checkerboard surface
       | 
       | * self-shadowing (like you did)
       | 
       | * cast shadows (like you did)
       | 
       | * partial or total specular reflection!
       | 
       | See https://www.cpc-power.com/index.php?page=detail&num=19283
       | 
       | The webpage displays the color (left) and green (right) versions
       | on hovering your pointer over the ribbon (above). Image 1 and 2
       | are okay in both cases, image 3 is clearly tuned for color
       | screen, image 4 for green screen.
       | 
       | Or direct links to the relevant variants:
       | 
       | https://www.cpc-power.com/extra_lire_fichier.php?extra=cpcol...
       | 
       | https://www.cpc-power.com/extra_lire_fichier.php?extra=cpcol...
       | 
       | https://www.cpc-power.com/extra_lire_fichier.php?extra=cpcol...
       | 
       | https://www.cpc-power.com/extra_lire_fichier.php?extra=cpcol...
       | 
       | https://www.cpc-power.com/extra_lire_fichier.php?extra=cpcol...
       | 
       | https://www.cpc-power.com/extra_lire_fichier.php?extra=cpcol...
        
         | ggambetta wrote:
         | That's pretty impressive. The main difficulty with reflections
         | in the ZX Spectrum is not so much the limited color palette,
         | which can be dealt with, but the 2-color limit per 8x8 block.
         | It's already a big issue with just lights and shadows as it is
         | :-/
        
           | s_gourichon wrote:
           | > the 2-color limit per 8x8 block. It's already a big issue
           | with just lights and shadows as it is :-/
           | 
           | Well, you impressed me (and others) with your simple bet that
           | "hey, what if I simply choose the two most popular colors"
           | and just code that in simple BASIC. The same could be done
           | with the shading. It would probably pick a good compromise
           | when bright yellow meets dark red: by choosing to have the
           | few should-be-red pixels become yellow, you avoid a bigger
           | obviously-square-grid-based clash of colors.
           | 
           | Next step: error diffusion dithering would IMHO be a good
           | bet. But maybe that becomes really to much for Basic.
           | 
           | I've been considering doing Raytracing on the Amstrad CPC,
           | but in C. Perhaps using integers only, for speed.
           | 
           | Done that instead: https://cpcitor.itch.io/just-get-9
           | 
           | Hey, found this https://bbcmic.ro/?t=9ctpk via "BBC BASIC
           | raytracer in 432 characters (mastodon.me.uk)"
           | https://news.ycombinator.com/item?id=39023056#39023768
        
       | Angostura wrote:
       | I have absolutely no recollection of being able to put multiple
       | statements on a line, separated by : - is that just my old brain
       | forgetting stuff, or is there something else going on here?
        
         | becurious wrote:
         | It's your brain. You could always do that in ZX Basic
        
           | donio wrote:
           | Starting with the Spectrum to be precise. The ZX-81 version
           | didn't have that yet.
        
       ___________________________________________________________________
       (page generated 2024-01-24 23:00 UTC)