BCX Raytrace Demo

Started by MrBcx, September 07, 2024, 12:32:25 AM

Previous topic - Next topic

MrBcx

Here is my QuickPixels version. 

It builds a 2k image (1920 x 1080 x 32 bit) in less than 1 second

Load it into BED, compile, run, be amazed  ;)



GUI "Raytrace", PIXELS
OPTION BASE 1
CONST DEMOWIDTH = 1920
CONST DEMOHEIGHT= 1080

SUB FORMLOAD
    GLOBAL AS HWND Form1, Canvas
    Form1 = BCX_FORM("Raytrace in progress ... please wait", 0, 0, DEMOWIDTH-1000, DEMOHEIGHT-500)
    Canvas = BCX_BITMAP(0, Form1, 0, 0, 0, DEMOWIDTH, DEMOHEIGHT)
    CENTER Form1
    SHOW Form1
    CALL Render
END SUB

BEGIN EVENTS
END EVENTS



SUB Render
    CONST spheres = 6
    DIM AS SINGLE c[spheres, 3]
    DIM AS SINGLE r[spheres]
    DIM AS SINGLE q[spheres]
    DIM AS DWORD cl[4]

    ' Screen dimensions and vars for width, height, scaling factor
    DIM AS SINGLE scrw, scrh, w, h, s

    ' Variables for the rendering loop
    DIM AS SINGLE x, y, z, dx, dy, dz, dd, sc, nx, ny, nz, nn, l, u, v
    DIM AS SINGLE px, py, pz, pp, bb, aa
    DIM AS INT n, ba
    DIM AS DWORD ik

    scrw = DEMOWIDTH    ' Screen width;  adjust as desired
    scrh = DEMOHEIGHT   ' Screen height; adjust as desired

    ' Calculate half-width and half-height for screen center
    w = scrw / 2
    h = scrh / 2
    s = 0.0

    ' Initialize color lookup table

    cl[1] = RGB(0, 0, 0)
    cl[2] = RGB(34, 24, 24)
    cl[3] = RGB(65, 0, 0)   ' Dark Red
    cl[4] = RGB(0, 0, 0)

    ' Initialize sphere positions and properties
    c[1, 1] = -0.3 : c[1, 2] = -0.8  : c[1, 3] = 3.0  : r[1] = 0.6  : q[1] = r[1] * r[1]
    c[2, 1] = 0.9  : c[2, 2] = -1.4  : c[2, 3] = 3.5  : r[2] = 0.35 : q[2] = r[2] * r[2]
    c[3, 1] = 0.7  : c[3, 2] = -0.45 : c[3, 3] = 2.5  : r[3] = 0.4  : q[3] = r[3] * r[3]
    c[4, 1] = -0.5 : c[4, 2] = -0.3  : c[4, 3] = 1.5  : r[4] = 0.15 : q[4] = r[4] * r[4]
    c[5, 1] = 1.0  : c[5, 2] = -0.2  : c[5, 3] = 1.5  : r[5] = 0.1  : q[5] = r[5] * r[5]
    c[6, 1] = -0.1 : c[6, 2] = -0.2  : c[6, 3] = 1.25 : r[6] = 0.2  : q[6] = r[6] * r[6]



    '===================================
    '  Supports my FastPixels Library
    '===================================
    LOCAL RayBmp AS HBITMAP
    LOCAL Myhdc AS HDC
    RayBmp = NEWBMP
    Myhdc = MAKEHDC(RayBmp)
    IF NULL = Myhdc THEN EXIT SUB
    '===================================
    OpenBmpData(Myhdc)
    '===================================



    ' Main rendering loop
    FOR INT i = 1 TO scrh
        FOR INT j = 0 TO scrw - 1

            x =  0.3
            y = -0.5
            z =  0
            ba = 3
            dx = j - w
            dy = h - i
            dz = (scrh / 480) * 640
            dd = dx * dx + dy * dy + dz * dz

            RayLoop:
            n = -(y >= 0  OR dy <= 0)
            IF n = 0 THEN s = -y / dy

            FOR INT k = 1 TO spheres
                ' Calculate distances and check for intersections
                px = c[k, 1] - x
                py = c[k, 2] - y
                pz = c[k, 3] - z
                pp = px * px + py * py + pz * pz
                sc = px * dx + py * dy + pz * dz

                IF sc <= 0 THEN ITERATE

                bb = sc * sc / dd
                aa = q[k] - pp + bb
                IF aa <= 0 THEN ITERATE

                sc = (SQR(bb) - SQR(aa)) / SQR(dd)
                IF sc < s OR n < 0 THEN
                    n = k
                    s = sc
                END IF
            NEXT k

            IF n < 0 THEN
                SetPixelFast(j, scrh - i, RGB2BGR(16 + (dy * dy / dd) * 240))
                ITERATE
            END IF

            ' Reflect rays and continue tracing
            dx = dx * s
            dy = dy * s
            dz = dz * s
            dd = dd * s * s
            x += dx
            y += dy
            z += dz

            IF n <> 0 THEN
                nx = x - c[n, 1]
                ny = y - c[n, 2]
                nz = z - c[n, 3]
                nn = nx * nx + ny * ny + nz * nz
                l = 2 * (dx * nx + dy * ny + dz * nz) / nn
                dx = dx - nx * l
                dy = dy - ny * l
                dz = dz - nz * l
                GOTO RayLoop
            END IF

            ' Check for shadows and plot the pixels
            FOR INT k = 1 TO spheres
                u = c[k, 1] - x
                v = c[k, 3] - z
                IF u * u + v * v <= q[k] THEN
                    ba = 1
                    EXIT FOR
                END IF
            NEXT k

            ' Determine plot color based on conditions
            IF (x - INT(x) > 0.5) = (z - INT(z) > 0.5) THEN
                ik = cl[ba]
            ELSE
                ik = cl[ba + 1]
            END IF
            SetPixelFast(j, scrh - i, RGB2BGR(ik))
        NEXT j
    NEXT i
    BCX_SET_TEXT(Form1, "Raytrace Completed")


    '===================================
    ' Support for my FastPixels Library
    '===================================
    CloseBmpData(Myhdc)
    RayBmp = MAKEBMP(Myhdc)
    '===================================
    SET_BCX_BITMAP2(Canvas, RayBmp)
END SUB



'========================================================================
' By Kevin Diggins       Quick Pixels GDI library     June 19, 2024
'                              MIT License
'========================================================================

GLOBAL QP_BmpSize AS DWORD
GLOBAL QP_PixelBuffer[8300000] AS DWORD  ' Support 2k screens @ 32-bit
GLOBAL QP_hBmp AS HBITMAP
GLOBAL QP_MemHDC AS HDC
GLOBAL QP_BmpWidth, QP_BmpHeight

FUNCTION OpenBmpData(hdc AS HDC) AS BOOL
    IF QP_hBmp THEN
        DeleteObject(QP_hBmp)
        DeleteDC(QP_MemHDC)
    END IF

    QP_MemHDC = CreateCompatibleDC(hdc)
    IF NOT QP_MemHDC THEN
        MSGBOX "Error: CreateCompatibleDC failed."
        FUNCTION = FALSE
    END IF

    QP_hBmp = CreateCompatibleBitmap(hdc, GetDeviceCaps(hdc, HORZRES), GetDeviceCaps(hdc, VERTRES))
    IF NOT QP_hBmp THEN
        DeleteDC(QP_MemHDC)
        MSGBOX "Error: CreateCompatibleBitmap failed."
        FUNCTION = FALSE
    END IF

    IF NOT SelectObject(QP_MemHDC, QP_hBmp) THEN
        MSGBOX "Error: SelectObject failed."
        DeleteObject(QP_hBmp)
        DeleteDC(QP_MemHDC)
        FUNCTION = FALSE
    END IF

    IF NOT BitBlt(QP_MemHDC, 0, 0, GetDeviceCaps(hdc, HORZRES), GetDeviceCaps(hdc, VERTRES), hdc, 0, 0, SRCCOPY) THEN
        MSGBOX "Error: BitBlt failed."
        DeleteObject(QP_hBmp)
        DeleteDC(QP_MemHDC)
        FUNCTION = FALSE
    END IF

    LOCAL bmpInfo AS BITMAP
    IF NOT GetObject(QP_hBmp, SIZEOF(bmpInfo), &bmpInfo) THEN
        MSGBOX "Error: GetObject failed."
        DeleteObject(QP_hBmp)
        DeleteDC(QP_MemHDC)
        FUNCTION = FALSE
    END IF

    QP_BmpWidth = bmpInfo.bmWidth
    QP_BmpHeight = bmpInfo.bmHeight
    QP_BmpSize = QP_BmpWidth * QP_BmpHeight * (bmpInfo.bmBitsPixel / 8)

    IF QP_BmpSize > SIZEOF(QP_PixelBuffer) THEN
        MSGBOX "Error: QP_PixelBuffer size needs to be increased or choose a smaller bitmap."
        DeleteObject(QP_hBmp)
        DeleteDC(QP_MemHDC)
        FUNCTION = FALSE
    END IF

    LOCAL bmi AS BITMAPINFO
    bmi.bmiHeader.biSize = SIZEOF(BITMAPINFOHEADER)
    bmi.bmiHeader.biWidth = QP_BmpWidth
    bmi.bmiHeader.biHeight = -QP_BmpHeight
    bmi.bmiHeader.biPlanes = 1
    bmi.bmiHeader.biBitCount = bmpInfo.bmBitsPixel
    bmi.bmiHeader.biCompression = BI_RGB

    IF GetDIBits(hdc, QP_hBmp, 0, QP_BmpHeight, &QP_PixelBuffer, &bmi, DIB_RGB_COLORS) THEN
        FUNCTION = TRUE
    ELSE
        MSGBOX "Error: GetDIBits failed."
        DeleteObject(QP_hBmp)
        DeleteDC(QP_MemHDC)
        FUNCTION = FALSE
    END IF
END FUNCTION

SUB CloseBmpData(hdc AS HDC)
    IF NOT QP_hBmp THEN
        MSGBOX "Error: CloseBmpData detected QP_hBmp is corrupted."
        EXIT SUB
    END IF

    LOCAL bmi AS BITMAPINFO
    bmi.bmiHeader.biSize = SIZEOF(BITMAPINFOHEADER)
    bmi.bmiHeader.biWidth = QP_BmpWidth
    bmi.bmiHeader.biHeight = -QP_BmpHeight
    bmi.bmiHeader.biPlanes = 1
    bmi.bmiHeader.biBitCount = 32
    bmi.bmiHeader.biCompression = BI_RGB
    bmi.bmiHeader.biSizeImage = QP_BmpSize

    DIM RAW result AS LONG
    result = SetDIBits(QP_MemHDC, QP_hBmp, 0, QP_BmpHeight, &QP_PixelBuffer, &bmi, DIB_RGB_COLORS)
    IF result = 0 THEN
        MSGBOX "Error: SetDIBits returned zero scanlines."
        EXIT SUB
    END IF

    BitBlt(hdc, 0, 0, QP_BmpWidth, QP_BmpHeight, QP_MemHDC, 0, 0, SRCCOPY)

    DeleteObject(QP_hBmp)
    QP_hBmp = NULL
    DeleteDC(QP_MemHDC)
    QP_MemHDC = NULL
END SUB


FUNCTION GetPixelFast(x AS INTEGER, y AS INTEGER) AS DWORD
    IF x < 0 OR x >= QP_BmpWidth OR y < 0 OR y >= QP_BmpHeight THEN
        FUNCTION = RGB(0, 0, 0)
    END IF

    DIM RAW pixelOffset AS DWORD
    pixelOffset = x + (y * QP_BmpWidth)
    FUNCTION = QP_PixelBuffer[pixelOffset]
END FUNCTION


SUB SetPixelFast(x AS INTEGER, y AS INTEGER, rgbColor AS DWORD)
    IF x < 0 OR x >= QP_BmpWidth OR y < 0 OR y >= QP_BmpHeight THEN
        EXIT SUB
    END IF

    DIM RAW pixelOffset AS DWORD
    pixelOffset = x + (y * QP_BmpWidth)
    QP_PixelBuffer[pixelOffset] = rgbColor 
END SUB


FUNCTION RGB2BGR(RGBValue AS DWORD) AS DWORD
    DIM Red AS BYTE, Green AS BYTE, Blue AS BYTE
    Red = (RGBValue >> 16) AND &HFF
    Green = (RGBValue >> 8) AND &HFF
    Blue = RGBValue AND &HFF
    FUNCTION = (Blue << 16) OR (Green << 8) OR Red
END FUNCTION


FUNCTION RGB2BRG(RGBValue AS DWORD) AS DWORD
    DIM Red AS BYTE, Green AS BYTE, Blue AS BYTE
    Red = (RGBValue >> 16) AND &HFF
    Green = (RGBValue >> 8) AND &HFF
    Blue = RGBValue AND &HFF
    FUNCTION = (Blue << 16) OR (Red << 8) OR Green
END FUNCTION


FUNCTION RGB2RBG(RGBValue AS DWORD) AS DWORD
    DIM Red AS BYTE, Green AS BYTE, Blue AS BYTE
    Red = (RGBValue >> 16) AND &HFF
    Green = (RGBValue >> 8) AND &HFF
    Blue = RGBValue AND &HFF
    FUNCTION = (Red << 16) OR (Blue << 8) OR Green
END FUNCTION


FUNCTION RGB2GBR(RGBValue AS DWORD) AS DWORD
    DIM Red AS BYTE, Green AS BYTE, Blue AS BYTE
    Red = (RGBValue >> 16) AND &HFF
    Green = (RGBValue >> 8) AND &HFF
    Blue = RGBValue AND &HFF
    FUNCTION = (Green << 16) OR (Blue << 8) OR Red
END FUNCTION


FUNCTION RGB2GRB(RGBValue AS DWORD) AS DWORD
    DIM Red AS BYTE, Green AS BYTE, Blue AS BYTE
    Red = (RGBValue >> 16) AND &HFF
    Green = (RGBValue >> 8) AND &HFF
    Blue = RGBValue AND &HFF
    FUNCTION = (Green << 16) OR (Red << 8) OR Blue
END FUNCTION


dragon57

Whoa! Are you going to update your above code box?

MrBcx

Quote from: dragon57 on September 07, 2024, 07:16:23 PM
I used POV-Ray for several projects back in the day. I still dabble with 3D modeling and rendering. I must see if I can extend this. Many thanks for posting this.

Glad you like it.

I just completed replacing the two BCX_PSET statements with two SetPixelFast statements from my
QuickPixels library.  I never noticed this before but I needed to swap RGB to BGR in my library, in order
to match the colors of the BCX_PSET statements.

Anyway, using my QuickPixels library, I can now generate a 2k image (1920 x 1080 x 32 bit) in less than 1 second.

Pretty happy about that  ;D   


dragon57

I used POV-Ray for several projects back in the day. I still dabble with 3D modeling and rendering. I must see if I can extend this. Many thanks for posting this.

MrBcx

#5
Thanks everyone ...

I used to like tinkering with the Persistence of Vision Raytracer, hence the fascination with my demo.

I added a section below my code at the top of this thread titled:
     Explanation of variable names
               for inquiring minds.


Ref:  http://www.povray.org/










Pietro54

Awesome, only takes 7 seconds on my HP 250 G7 Intel(R) Core(TM) i3-8130U CPU @ 2.20GHz - Win 11 Pro.

Thanks MrBcx

airr

Nice, takes about 4 seconds on MY Dell i7-10700T.

AIR.

dragon57


MrBcx

#1
Not as impressive as Armando porting BCX and BED to ARM but still pretty neat.
It takes about 10 seconds to render the drawing on my Dell i7.

The original SPEC Basic code is included as a block of comments at the end of my BCX listing.
As ugly as the SPEC Basic is to look at, converting it to BCX was quite a challenge for me.
ChatGPT helped some but getting it to actually work correctly cost me a few hours of hair pulling.



GUI "Raytrace", PIXELS
OPTION BASE 1
CONST DEMOWIDTH = 1024
CONST DEMOHEIGHT=  720

SUB FORMLOAD
    GLOBAL AS HWND Form1, Canvas
    Form1 = BCX_FORM("Raytrace in progress ... please wait", 0, 0, DEMOWIDTH, DEMOHEIGHT)
    Canvas = BCX_BITMAP(0, Form1, 0, 0, 0, DEMOWIDTH, DEMOHEIGHT)
    CENTER Form1
    SHOW Form1
    CALL Render
END SUB

BEGIN EVENTS
END EVENTS



SUB Render
    CONST spheres = 6
    DIM AS SINGLE c[spheres, 3]
    DIM AS SINGLE r[spheres]
    DIM AS SINGLE q[spheres]
    DIM AS DWORD cl[4]

    ' Screen dimensions and vars for width, height, scaling factor
    DIM AS SINGLE scrw, scrh, w, h, s

    ' Variables for the rendering loop
    DIM AS SINGLE x, y, z, dx, dy, dz, dd, sc, nx, ny, nz, nn, l, u, v
    DIM AS SINGLE px, py, pz, pp, bb, aa
    DIM AS INT n, ba
    DIM AS DWORD ik

    scrw = DEMOWIDTH    ' Screen width;  adjust as desired
    scrh = DEMOHEIGHT   ' Screen height; adjust as desired

    ' Calculate half-width and half-height for screen center
    w = scrw / 2
    h = scrh / 2
    s = 0.0

    ' Initialize color lookup table

    cl[1] = RGB(0, 0, 0)
    cl[2] = RGB(34, 24, 24)
    cl[3] = RGB(65, 0, 0)   ' Dark Red
    cl[4] = RGB(0, 0, 0)

    ' Initialize sphere positions and properties
    c[1, 1] = -0.3 : c[1, 2] = -0.8  : c[1, 3] = 3.0  : r[1] = 0.6  : q[1] = r[1] * r[1]
    c[2, 1] = 0.9  : c[2, 2] = -1.4  : c[2, 3] = 3.5  : r[2] = 0.35 : q[2] = r[2] * r[2]
    c[3, 1] = 0.7  : c[3, 2] = -0.45 : c[3, 3] = 2.5  : r[3] = 0.4  : q[3] = r[3] * r[3]
    c[4, 1] = -0.5 : c[4, 2] = -0.3  : c[4, 3] = 1.5  : r[4] = 0.15 : q[4] = r[4] * r[4]
    c[5, 1] = 1.0  : c[5, 2] = -0.2  : c[5, 3] = 1.5  : r[5] = 0.1  : q[5] = r[5] * r[5]
    c[6, 1] = -0.1 : c[6, 2] = -0.2  : c[6, 3] = 1.25 : r[6] = 0.2  : q[6] = r[6] * r[6]

    ' Setup for BCX persistent image
    DIM RAW hDestDC AS HDC
    hDestDC = STARTDRAW(Canvas)

    ' Main rendering loop
    FOR INT i = 1 TO scrh
        FOR INT j = 0 TO scrw - 1

            x = 0.3
            y = -0.5
            z = 0
            ba = 3
            dx = j - w
            dy = h - i
            dz = (scrh / 480) * 640
            dd = dx * dx + dy * dy + dz * dz

            RayLoop:
            n = -(y >= 0  OR dy <= 0)
            IF n = 0 THEN s = -y / dy

            FOR INT k = 1 TO spheres
                ' Calculate distances and check for intersections
                px = c[k, 1] - x
                py = c[k, 2] - y
                pz = c[k, 3] - z
                pp = px * px + py * py + pz * pz
                sc = px * dx + py * dy + pz * dz

                IF sc <= 0 THEN ITERATE

                bb = sc * sc / dd
                aa = q[k] - pp + bb
                IF aa <= 0 THEN ITERATE

                sc = (SQR(bb) - SQR(aa)) / SQR(dd)
                IF sc < s OR n < 0 THEN
                    n = k
                    s = sc
                END IF
            NEXT k

            IF n < 0 THEN
                BCX_PSET(0, j, scrh - i,  16 + (dy * dy / dd) * 240, hDestDC)
                ITERATE
            END IF

            ' Reflect rays and continue tracing
            dx = dx * s
            dy = dy * s
            dz = dz * s
            dd = dd * s * s
            x += dx
            y += dy
            z += dz

            IF n <> 0 THEN
                nx = x - c[n, 1]
                ny = y - c[n, 2]
                nz = z - c[n, 3]
                nn = nx * nx + ny * ny + nz * nz
                l = 2 * (dx * nx + dy * ny + dz * nz) / nn
                dx = dx - nx * l
                dy = dy - ny * l
                dz = dz - nz * l
                GOTO RayLoop
            END IF

            ' Check for shadows and plot the pixels
            FOR INT k = 1 TO spheres
                u = c[k, 1] - x
                v = c[k, 3] - z
                IF u * u + v * v <= q[k] THEN
                    ba = 1
                    EXIT FOR
                END IF
            NEXT k

            ' Determine plot color based on conditions
            IF (x - INT(x) > 0.5) = (z - INT(z) > 0.5) THEN
                ik = cl[ba]
            ELSE
                ik = cl[ba + 1]
            END IF

            BCX_PSET(0, j, scrh - i, ik, hDestDC)
        NEXT j
    NEXT i
    ENDDRAW(Canvas, hDestDC)
    BCX_SET_TEXT(Form1, "Raytrace Completed")
END SUB



'********************************************************************
' This is the SPEC Basic listing that I used to create the BCX Demo.
' It was not easy.       MrBcx  Sept 6, 2024         PUBLIC DOMAIN
'********************************************************************
'10 PAPER 0: INK 15: CLS:
' palette 16, 255, 255, 255:
' palette 32, 0, 192, 255:
' palette 255, 0, 0, 192:
' rainbow 16 to 32:
' rainbow 32 to 255
'20 read spheres
' DIM c(spheres, 3), r(spheres), q(spheres), cl(4):
' w = scrw/2, h = scrh/2, s = 0:
' cl(1) = 6, cl(2) = 1,
' cl(3) = cl(1)+8, cl(4) = cl(2)+8
'30 FOR k = 1 TO spheres:
'    READ c(k, 1), c(k, 2), c(k, 3), r:
'    r(k) = r, q(k) = r*r:
' NEXT k
'40 data 6:
' DATA -0.3, -0.8, 3, 0.6
'50 DATA 0.9, -1.4, 3.5, 0.35:
' data 0.7, -0.45, 2.5, 0.4:
' data -0.5, -0.3, 1.5, 0.15:
' DATA 1.0, -0.2, 1.5, 0.1:
' DATA -0.1, -0.2, 1.25, 0.2
'60 FOR i = 1 TO scrh:
'    FOR j = 0 TO scrw-1
'70       x = 0.3, y = -0.5, z = 0, ba = 3:
'       dx = j-w, dy = h-i, dz = (scrh/480)*600,
'       dd = dx*dx+dy*dy+dz*dz
'80       n = -(y>=0 OR dy<=0):
'       IF n = 0 THEN
'          s = -y/dy
'90       FOR k = 1 TO spheres:
'          px = c(k, 1)-x, py = c(k, 2)-y, pz = c(k, 3)-z,
'          pp = px*px+py*py+pz*pz,
'          sc = px*dx+py*dy+pz*dz:
'          IF sc<=0 THEN
'             GO TO 120
'100          bb = sc*sc/dd,
'          aa = q(k)-pp+bb:
'          IF aa<=0 THEN
'             GO TO 120
'110          sc = (SQR bb-SQR aa)/SQR dd:
'          IF sc<s OR n<0 THEN
'             n = k, s = sc
'120       NEXT k
'130       IF n<0 THEN
'          plot ink 16+(dy*dy/dd)*240;j, scrh-i:
'          go to 200
'140       dx = dx*s, dy = dy*s, dz = dz*s, dd = dd*s*s,
'       x+ = dx, y+ = dy, z+ = dz:
'       IF n<>0 THEN
'           nx = x-c(n, 1), ny = y-c(n, 2), nz = z-c(n, 3),
'          nn = nx*nx+ny*ny+nz*nz,
'          l = 2*(dx*nx+dy*ny+dz*nz)/nn,
'          dx = dx-nx*l, dy = dy-ny*l, dz = dz-nz*l:
'          GO TO 80
'160       FOR k = 1 TO spheres:
'          u = c(k, 1)-x,
'          v = c(k, 3)-z:
'          IF u*u+v*v<=q(k) THEN
'             ba = 1:
'             go to 180
'170       NEXT k
'180       IF (x-INT x>.5) = (z-INT z>.5) THEN
'          ik = cl(ba)
'       else
'          ik = cl(ba+1)
'190       plot ink ik;j, scrh-i       
'200    NEXT j:
' NEXT i



Explanation of variable names



*******
Summary
*******

This code is a basic ray-tracing algorithm for rendering spheres in a 3D space
onto a 2D screen.  Each sphere's position and radius are defined in 3D space,
and rays are cast from the camera to each pixel on the screen.  The algorithm
checks for intersections with spheres and calculates reflections, shadows, and
shading for realistic effects.


*******************************
Variable List with Descriptions
*******************************

CONST spheres = 6 
' Number of spheres in the scene

DIM AS SINGLE c[spheres, 3] 
' Array to hold the center coordinates (x, y, z) of each sphere

DIM AS SINGLE r[spheres] 
' Array to hold the radius of each sphere

DIM AS SINGLE q[spheres] 
' Array to hold the square of the radius of each sphere (used for distance calculations)

DIM AS DWORD cl[4] 
' Array to hold the color values for shading (RGB format)

' Screen dimensions and variables for half-width, half-height, and scaling factor
DIM AS SINGLE scrw, scrh, w, h, s 
' scrw, scrh: Screen width and height 
' w, h: Half of screen width and height, used for centering the rendering 
' s: Scaling factor for ray tracing steps (set to 0.0 initially)

' Variables for the rendering loop and ray tracing calculations
DIM AS SINGLE x, y, z, dx, dy, dz, dd, sc, nx, ny, nz, nn, l, u, v 
' x, y, z: Current ray position 
' dx, dy, dz: Direction of the ray from the camera to the screen 
' dd: Distance squared from the ray origin 
' sc: Dot product for determining the intersection distance 
' nx, ny, nz: Normal vector components at the point of intersection 
' nn: Length squared of the normal vector 
' l: Reflection factor used for calculating reflections 
' u, v: Temporary variables for distance calculations in shadows

DIM AS SINGLE px, py, pz, pp, bb, aa 
' px, py, pz: Vector from ray origin to a sphere center 
' pp: Squared length of the above vector 
' bb: Projection length squared of the above vector on the ray 
' aa: Difference between the squared radii (used to determine if the ray intersects)

DIM AS INT n, ba 
' n: Index of the sphere hit by the ray 
' ba: Base color index used for shading (1 to 3)

DIM AS DWORD ik 
' ik: Color value used for plotting pixels

scrw = DEMOWIDTH   
' Screen width; set to a constant or pre-defined variable

scrh = DEMOHEIGHT   
' Screen height; set to a constant or pre-defined variable

' Calculate half-width and half-height for screen center
w = scrw / 2   
' Half-width of the screen

h = scrh / 2   
' Half-height of the screen

s = 0.0 
' Initialize scaling factor for ray tracing steps

' Initialize color lookup table with different shades
cl[1] = RGB(0, 0, 0)   
' Color for the base shade (black)

cl[2] = RGB(34, 24, 24)   
' Dark color used for shadows or darker areas

cl[3] = RGB(65, 0, 0)   
' Dark red color for spheres or reflections

cl[4] = RGB(0, 0, 0)   
' Additional shade (same as the first)

' Initialize sphere positions and properties (center coordinates and radius)
' For each sphere: c[x, y, z] - Center coordinates; r - Radius; q - Radius squared

c[1, 1] = -0.3 : c[1, 2] = -0.8  : c[1, 3] = 3.0  : r[1] = 0.6  : q[1] = r[1] * r[1] 
c[2, 1] = 0.9  : c[2, 2] = -1.4  : c[2, 3] = 3.5  : r[2] = 0.35 : q[2] = r[2] * r[2] 
c[3, 1] = 0.7  : c[3, 2] = -0.45 : c[3, 3] = 2.5  : r[3] = 0.4  : q[3] = r[3] * r[3] 
c[4, 1] = -0.5 : c[4, 2] = -0.3  : c[4, 3] = 1.5  : r[4] = 0.15 : q[4] = r[4] * r[1] 
c[5, 1] = 1.0  : c[5, 2] = -0.2  : c[5, 3] = 1.5  : r[5] = 0.1  : q[5] = r[5] * r[5] 
c[6, 1] = -0.1 : c[6, 2] = -0.2  : c[6, 3] = 1.25 : r[6] = 0.2  : q[6] = r[6] * r[6] 

' Setup for BCX persistent image (handle for drawing context)
DIM RAW hDestDC AS HDC 
' hDestDC: Handle to the device context for drawing

hDestDC = STARTDRAW(Canvas)   
' Begin drawing on the provided canvas

' Main rendering loop - loops through each pixel on the screen
FOR INT i = 1 TO scrh 
    ' Loop through each row of pixels

    FOR INT j = 0 TO scrw - 1 
        ' Loop through each column of pixels

        ' Initialize ray origin and direction
        x = 0.3 : y = -0.5 : z = 0 
        ' Ray origin in space

        ba = 3 
        ' Base color index (default)

        dx = j - w : dy = h - i : dz = (scrh / 480) * 640 
        ' Direction from camera to the pixel on the screen

        dd = dx * dx + dy * dy + dz * dz 
        ' Squared distance from the ray origin to the current point

        ' Rest of the ray tracing and shading logic goes here...