ZX Spectrum Raytracer

This is the example output and source code for the 2nd iteration of the ZX Spectrum Raytracer, with better handling of attribute clashes.

1 BRIGHT 1: CLS

5 LET ROX = 0
6 LET ROY = 0
7 LET ROZ = 0
8 LET TMIN = 0
9 LET TMAX = 10000

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

30      DIM C(64)
31      LET CI = 1
32      DIM A(8)

120     REM --- For each 8x8 block, collect the pixel colors and their counts ---
125     FOR U = X TO X+7
126       FOR V = Y TO Y+7

130         LET RDX = (U - 128) / 256
131         LET RDY = (V - 88) / 256
132         LET RDZ = 1

140         GO SUB 1000
141         LET C(CI) = COL
142         LET CI = CI + 1
143         LET A(COL+1) = A(COL+1) + 1

160       NEXT V
161     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 FCOL = MFI - 1

207     LET II = MFI: LET MFC = 0: LET MFI = 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 SCOL = MFI - 1

259     REM --- If there's only one color, paint the whole block --
260     IF SCOL <> -1 THEN GO TO 300
270     POKE 22528 + X/8 + 32*(21-Y/8), 64 + FCOL * 8
280     GO TO 500

300     REM --- Otherwise 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) <> FCOL THEN PLOT INK SCOL; PAPER FCOL; U, V
321         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

1000 REM ===== TraceRay =====
1001 REM Params: (ROX, ROY, ROZ): ray origin; (RDX, RDY, RDZ): ray direction; (TMIN, TMAX): wanted ranges of t
1002 REM Returns: COL: pixel color

1010 LET COL = -1: LET MINT = 0

1100 RESTORE 9000
1102 FOR S = 1 TO NS

1200    LET COX = ROX - SCX
1201    LET COY = ROY - SCY
1202    LET COZ = ROZ - SCZ

1210    LET EQA = RDX*RDX + RDY*RDY + RDZ*RDZ
1211    LET EQB = 2*(RDX*COX + RDY*COY + RDZ*COZ)

1220    LET DISC = EQB*EQB - 4*EQA*EQC
1230    IF DISC < 0 THEN GO TO 1500

1240    LET T1 = (-EQB + SQR(DISC)) / 2*EQA
1241    LET T2 = (-EQB - SQR(DISC)) / 2*EQA

1250    IF T1 >= TMIN AND T1 <= TMAX AND (T1 < MINT OR COL = -1) THEN LET COL = SCOL: LET MINT = T1
1300    IF T2 >= TMIN AND T2 <= TMAX AND (T2 < MINT OR COL = -1) THEN LET COL = SCOL: LET MINT = T2

1500 NEXT S

1999 IF COL = -1 THEN LET COL = 0
2000 RETURN

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

8998 REM ===== Sphere data =====
8999 REM Sphere count, followed by (SCX, SCY, SCZ, SRAD, 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, 6