ZX Spectrum Raytracer

This is the example output and source code for the 3rd iteration of the ZX Spectrum Raytracer, with performance improvements.

   1 BRIGHT 1: CLS
   3 LET DX = 0: LET DY = 0: LET DZ = 1: GO SUB 1000: REM The order of declaration of variables makes the program faster (!)
   4 DIM C(64)

  20 FOR X = 0 TO 255 STEP 8
  25   FOR Y = 0 TO 175 STEP 8

  39     REM --- Fast approximate path: if all 4 corners of this 8x8 block are the same color, draw the whole block with that color ---
  40     LET DX = (X - 128) / 256:     LET DY = (Y - 88) / 256:     GO SUB 1000: LET CTL = COL
  41     LET DX = (X - 128 + 7) / 256:                              GO SUB 1000: LET CTR = COL
  42                                   LET DY = (Y - 88 + 7) / 256: GO SUB 1000: LET CBR = COL
  43     LET DX = (X - 128) / 256:                                  GO SUB 1000: LET CBL = COL
  50     IF CTL = CBR AND CTL = CTR AND CTL = CBL THEN POKE 23200 + X/8 - 4*Y, 64 + COL*8: GO TO 500

 100     REM --- For each 8x8 block, collect the pixel colors and their counts ---

 111     LET CI = 1
 112     DIM A(8)

 125     FOR U = X TO X+7
 126       LET DX = (U - 128) / 256

 130       FOR V = Y TO Y+7

 140         IF CI =  1 THEN LET COL = CTL: GO TO 160
 141         IF CI =  8 THEN LET COL = CBL: GO TO 160
 142         IF CI = 57 THEN LET COL = CTR: GO TO 160
 143         IF CI = 64 THEN LET COL = CBR: GO TO 160

 150         LET DY = (V - 88) / 256
 151         GO SUB 1000

 160         LET A(COL+1) = A(COL+1) + 1
 161         LET C(CI) = COL
 162         LET CI = CI + 1

 170       NEXT V
 171     NEXT U

 199     REM --- Find the most and second most frequent colors in this 8x8 block ---
 201     LET MFC = 0
 202     FOR C = 1 TO 8
 203       IF A(C) > MFC THEN LET MFC = A(C): LET MFI = C
 204     NEXT C
 205     LET FC = MFI - 1

 207     LET II = MFI: LET MFC = 0
 208     FOR C = 1 TO 8
 209       IF C <> II AND A(C) > MFC THEN LET MFC = A(C): LET MFI = C
 210     NEXT C
 211     LET SC = MFI - 1

 300     REM --- Set the PAPER to the most frequent color, and draw everything else in the second most frequent color --
 301     LET CI = 1
 310     FOR U = X TO X+7
 311       FOR V = Y TO Y+7

 320         IF C(CI) <> FC THEN PLOT INK SC; PAPER FC; U, V
 325         LET CI = CI + 1

 350       NEXT V
 351     NEXT U

 500   NEXT Y
 505   GO SUB 3000: PRINT AT 0, 0; TIME
 510 NEXT X

 520 STOP


 990 REM ===== TraceRay =====
 991 REM Params: (DX, DY, DZ): ray direction
 992 REM Returns: COL: pixel color
 993 REM Optimizations: ray origin hardcoded to (0, 0, 0); (TMIN, TMAX) hardcoded to (0, +inf)

1000 LET MT = 1E10
1020 LET A = 2*(DX*DX + DY*DY + DZ*DZ)

1100 RESTORE 9000
1101 READ NS
1102 FOR S = 1 TO NS

1110    READ CX, CY, CZ, SR, SC
1211    LET B = 2*(DX*CX + DY*CY + DZ*CZ)
1212    LET C = (CX*CX + CY*CY + CZ*CZ) - SR

1220    LET D = B*B - 2*A*C
1230    IF D < 0 THEN GO TO 1500
1231    LET D = SQR(D)

1240    LET T = (B + D) / A
1241    IF T > 0 AND T < MT THEN LET COL = SC: LET MT = T
1250    LET T = (B - D) / A
1251    IF T > 0 AND T < MT THEN LET COL = SC: LET MT = T

1500 NEXT S

1999 IF MT = 1E10 THEN LET COL = 0
2000 RETURN


2999 REM ===== Get timestamp in seconds =====
3000 LET TIME = (65536*PEEK 23674 + 256*PEEK 23673 + PEEK 23672) / 50
3001 RETURN


8998 REM ===== Sphere data =====
8999 REM Sphere count, followed by (CX, CY, CZ, RADIUS*RADIUS, COLOR)
9000 DATA 4
9001 DATA  0, -1, 4, 1, 2
9002 DATA  2,  0, 4, 1, 1
9003 DATA -2,  0, 4, 1, 4
9004 DATA 0, -5001, 0, 5000^2, 6