Mandelbrot Explorer

Started by MrBcx, November 17, 2024, 08:36:32 PM

Previous topic - Next topic

MrBcx

Here is a hardware accelerated (OpenGL) version in less than 200 lines.


'====================================================
' Mandelbrot Explorer (BCX + OpenGL)
' By MrBcx + ChatGPT, Aug 2025        MIT License
' Left Click - zoom in,  Right Click - zoom out
'====================================================

GUI "Mandelbrot_Explorer_GL", PIXELS

#include <GL/gl.h>
#include <GL/glu.h>

$PRAGMA comment(lib, "opengl32.lib")
$PRAGMA comment(lib, "glu32.lib")

CONST WIN_W = 1200
CONST WIN_H =  900
CONST MAX_ITER = 256

GLOBAL AS HWND Form1
GLOBAL AS DOUBLE CenterX, CenterY, Scale
GLOBAL AS HDC hdc
GLOBAL AS HGLRC hglrc

' Pixel buffer for glDrawPixels
GLOBAL ImageBuf[WIN_W * WIN_H * 3] AS UBYTE


SUB FORMLOAD
    Form1 = BCX_FORM("Mandelbrot Explorer OpenGL", 0, 0, WIN_W, WIN_H)
    MODSTYLE(Form1, 0, WS_MAXIMIZEBOX | WS_MINIMIZEBOX, FALSE)
    CENTER Form1
    SHOW Form1

    ' Initial fractal settings
    CenterX = -0.5
    CenterY = 0.0
    Scale   = 3.0 / WIN_W

    InitOpenGL()
    RenderMandelbrot()
END SUB


BEGIN EVENTS
    SELECT CASE CBMSG
        CASE WM_QUIT, WM_CLOSE, WM_DESTROY
        CleanupOpenGL()
        END

        CASE WM_LBUTTONDOWN
        HandleZoom(LOWORD(CBLPARAM), HIWORD(CBLPARAM), TRUE)

        CASE WM_RBUTTONDOWN
        HandleZoom(LOWORD(CBLPARAM), HIWORD(CBLPARAM), FALSE)
    END SELECT
END EVENTS



SUB InitOpenGL()
    '====================================================
    ' OpenGL setup
    '====================================================
    DIM AS PIXELFORMATDESCRIPTOR pfd = _
    { SIZEOF(PIXELFORMATDESCRIPTOR), 1, _
    PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, _
    PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 8, 0, _
    PFD_MAIN_PLANE, 0, 0, 0, 0}

    hdc = GetDC(Form1)
    DIM AS INT pixelFormat = ChoosePixelFormat(hdc, &pfd)
    SetPixelFormat(hdc, pixelFormat, &pfd)
    hglrc = wglCreateContext(hdc)
    wglMakeCurrent(hdc, hglrc)

    glViewport(0, 0, WIN_W, WIN_H)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluOrtho2D(0, WIN_W, 0, WIN_H)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

    ' background black
    glClearColor(0.0, 0.0, 0.0, 1.0)
END SUB


SUB CleanupOpenGL()
    wglMakeCurrent(NULL, NULL)
    wglDeleteContext(hglrc)
    ReleaseDC(Form1, hdc)
END SUB


SUB RenderMandelbrot()
    '====================================================
    ' Render Mandelbrot into ImageBuf
    '====================================================
    DIM AS INT px, py, iter
    DIM AS DOUBLE zx, zy, zx2, zy2, cx, cy
    DIM AS DOUBLE temp
    DIM AS DOUBLE hue
    DIM AS UBYTE r, g, b

    FOR py = 0 TO WIN_H - 1
        cy = CenterY + (py - WIN_H/2) * Scale
        FOR px = 0 TO WIN_W - 1
            cx = CenterX + (px - WIN_W/2) * Scale
            zx = 0
            zy = 0
            zx2 = 0
            zy2 = 0
            iter = 0

            DO WHILE (zx2 + zy2 <= 4.0 AND iter < MAX_ITER)
                temp = zx2 - zy2 + cx
                zy = 2*zx*zy + cy
                zx = temp
                zx2 = zx*zx
                zy2 = zy*zy
                iter = iter + 1
            LOOP

            IF iter = MAX_ITER THEN
                r = 0 : g = 0 : b = 0
            ELSE
                DIM AS DOUBLE mu, hue
                mu = iter - LOG(LOG(SQR(zx2+zy2))) / LOG(2.0)
                hue = mu / MAX_ITER
                HSVtoRGB(hue, 1.0, 1.0, &r, &g, &b)
            END IF

            DIM AS INT idx = (py * WIN_W + px) * 3
            ImageBuf[idx+0] = r
            ImageBuf[idx+1] = g
            ImageBuf[idx+2] = b
        NEXT
    NEXT

    glClear(GL_COLOR_BUFFER_BIT)
    glRasterPos2i(0, 0)
    glDrawPixels(WIN_W, WIN_H, GL_RGB, GL_UNSIGNED_BYTE, &ImageBuf[0])
    SwapBuffers(hdc)
END SUB


SUB HSVtoRGB(h AS DOUBLE, s AS DOUBLE, v AS DOUBLE, r AS *UBYTE, g AS *UBYTE, b AS *UBYTE)
    '====================================================
    ' HSV to RGB helper
    '====================================================
    DIM AS DOUBLE i, f, p, q, t
    DIM AS INT hi

    h = h * 6
    hi = INT(h)
    f = h - hi
    p = v * (1 - s)
    q = v * (1 - f * s)
    t = v * (1 - (1 - f) * s)

    SELECT CASE hi MOD 6
        CASE 0
        *r = v*255 : *g = t*255 : *b = p*255
        CASE 1
        *r = q*255 : *g = v*255 : *b = p*255
        CASE 2
        *r = p*255 : *g = v*255 : *b = t*255
        CASE 3
        *r = p*255 : *g = q*255 : *b = v*255
        CASE 4
        *r = t*255 : *g = p*255 : *b = v*255
        CASE 5
        *r = v*255 : *g = p*255 : *b = q*255
    END SELECT
END SUB


SUB HandleZoom(x AS LONG, y AS LONG, zoomIn AS INT)
    '====================================================
    ' Mouse zoom handler
    '====================================================
    ' Convert screen (x,y) to fractal coords
    DIM AS DOUBLE fx, fy
    fx = CenterX + (x - WIN_W/2) * Scale
    fy = CenterY + (WIN_H/2 - y) * Scale

    ' Recenter at click point
    CenterX = fx
    CenterY = fy

    IF zoomIn THEN
        Scale = Scale * 0.5
    ELSE
        Scale = Scale * 2.0
    END IF

    RenderMandelbrot()
END SUB


MrBcx

Quote from: jbk on November 17, 2024, 10:29:24 PM
thanks MrBcx
love fractals  ;D

Thanks ... I'm pretty happy with it too.

Getting Claude to help me out with the nuanced color gradients was the toughest part.

jbk


MrBcx

#1
This is a very lean Mandelbrot fractal explorer which uses my Quick-Pixels library.

All in, it's less than 300 lines of code and compiles with all the usual compilers.

Left Click to zoom in, Right click to zoom out.