Code:
```'
' This little program is a demo of OpenGL's GluLookAt function, which is like a camera
' that you can orient at any angle to view a 3D world.
' Use the arrow keys to rotate the camera up, down, left, or right.
' Use "A" or "S" to spin the camera
' Use "Z" or "X" to move ahead or backwards.
' You'll need the OpenGL Include files to compile this program.  There's a link to these
' files somewhere on the PowerBasic website.
'
' I just started to get into OpenGL a couple weeks ago and quickly realized that to program
' a 3D world you need to know some math.  More than what knew to program my game
' POLYGON WAR in which the game takes place on a plane but is flipped perpendicular to the
' monitor to give depth perception.
' So I educated myself by looking at the camera
' tutorials at www.gametutorials.com  and so here's my first program in OpenGL.
'
' Joel Patterson        www.spiritone.com\~joeltr

#COMPILE EXE "ROCKS"
#INCLUDE "WIN32API.INC"
#INCLUDE "GL.INC"
#INCLUDE "GLU.INC"
#INCLUDE "GLAUX.INC"

%rocks=1000

\$classname="Spinning cubes in true 3D space."

FUNCTION WINMAIN (BYVAL hInstance  AS LONG,_
BYVAL hPrevInstance AS LONG,_
lpCmdLine           AS ASCIIZ PTR,_
BYVAL iCmdShow      AS LONG) AS LONG

DIM Xcube(%rocks) AS GLOBAL SINGLE
DIM Ycube(%rocks) AS GLOBAL SINGLE
DIM Zcube(%rocks) AS GLOBAL SINGLE
DIM XcubeAngle(%rocks) AS GLOBAL SINGLE
DIM YcubeAngle(%rocks) AS GLOBAL SINGLE
DIM ZcubeAngle(%rocks) AS GLOBAL SINGLE
DIM CubeSize(%rocks) AS GLOBAL SINGLE

GLOBAL Xposition AS SINGLE    'Position of your Camera
GLOBAL Yposition AS SINGLE
GLOBAL Zposition AS SINGLE

GLOBAL Xview AS SINGLE        'Direction of your Camera
GLOBAL Yview AS SINGLE
GLOBAL Zview AS SINGLE

GLOBAL Xup AS SINGLE          'Up Vector
GLOBAL Yup AS SINGLE
GLOBAL Zup AS SINGLE

GLOBAL Xhorz AS SINGLE        'Horizontal vector.
GLOBAL Yhorz AS SINGLE
GLOBAL Zhorz AS SINGLE

GLOBAL Xdir AS SINGLE         'Direction of movement
GLOBAL Ydir AS SINGLE
GLOBAL Zdir AS SINGLE

GLOBAL XvVector AS SINGLE     'Temporary vector.
GLOBAL YvVector AS SINGLE
GLOBAL ZvVector AS SINGLE

GLOBAL XvAxis AS SINGLE       'Arbitrary axis to move around.
GLOBAL YvAxis AS SINGLE
GLOBAL ZvAxis AS SINGLE

GLOBAL Magnitude AS SINGLE

GLOBAL Msg      AS tagMsg
LOCAL wce      AS WndClassEx
LOCAL szClassName AS ASCIIZ*80
GLOBAL hWnd     AS DWORD
LOCAL htimer   AS DWORD
LOCAL myclass  AS STRING

RANDOMIZE TIMER
szClassName=\$ClassName

wce.cbSize  =  SIZEOF(wce)
wce.style  =  %null
wce.lpfnWndProc  =  CODEPTR(WndPRoc)
wce.cbClsExtra  =  0
wce.cbWndExtra  =  0
wce.hInstance  =  hInstance
wce.hCursor  =  0
wce.hbrBackground  =  %NULL
wce.lpszClassName  =  VARPTR(szclassname)

RegisterClassEx wce

hWnd=CreateWindowEx(%ws_ex_appwindow,_            'the fullscreen window
\$classname,_
\$classname,_
%ws_overlappedwindow OR %ws_visible,_
0,_
0,_
640,_
480,_
%hwnd_desktop,_
BYVAL 0,_
hInstance,_
BYVAL %NULL)

showwindow hWnd, %sw_normal
UpdateWindow hWnd

DO WHILE PeekMessage(Msg, %NULL, 0, 0, %PM_NOREMOVE)
IF GetMessage(Msg, %NULL, 0, 0) THEN
TranslateMessage Msg
DispatchMessage Msg
ELSE
EXIT FUNCTION
END IF
LOOP

FUNCTION = msg.wParam

END FUNCTION

FUNCTION WndProc (BYVAL hWnd AS LONG, BYVAL wMsg AS LONG,_
BYVAL wParam AS LONG, BYVAL lParam AS LONG) EXPORT AS LONG

STATIC holdpen AS DWORD
GLOBAL PS AS paintstruct
LOCAL t AS DWORD              'looping variables.
LOCAL temp AS SINGLE
LOCAL temp2 AS SINGLE
LOCAL T1 AS INTEGER
LOCAL t2 AS INTEGER
GLOBAL hdc AS DWORD
GLOBAL g_hdc AS LONG
GLOBAL Hrc AS LONG
GLOBAL wid AS SINGLE,hei AS SINGLE
LOCAL lp AS LONG

SELECT CASE wMsg

CASE %WM_CREATE
hdc=getdc(hwnd)
g_hdc=hdc
CALL setupPixelFormat(hdc)
Hrc=wglCreateContext(hdc)
wglMakeCurrent hdc,hrc

Xposition=0     'Initial Position of camera.
Yposition=0
Zposition=0

Xview=0         'Initial Direction of camera.
Yview=0
Zview=-1

Xup=0:Yup=1:Zup=0     'Initial Up Position of camera.

glpointsize 1
glenable %gl_point_smooth
glenable %gl_cull_face
glcullface %gl_back
glfrontface %gl_ccw
glenable %gl_depth_test

FOR lp=0 TO %rocks           'Initial position of cubes
Xcube(lp)=RND(-1000,1000)
Ycube(lp)=RND(-1000,1000)
Zcube(lp)=RND(-1000,1000)
XcubeAngle(lp)=RND(0,359)
YcubeAngle(lp)=RND(0,359)
ZcubeAngle(lp)=RND(0,359)
CubeSize(lp)=RND(1,30)
NEXT lp

EXIT FUNCTION

CASE %WM_PAINT

CALL DoGraphics()
EXIT FUNCTION

CASE %WM_CLOSE
wglMakeCurrent hdc,%null
wglDeleteContext hrc

PostQuitMessage 0
EXIT FUNCTION

CASE %WM_SYSCOMMAND
IF wparam=%sc_close THEN
postquitmessage 0
END IF

CASE %wm_size
wid=LOWRD(lparam)
hei=HIWRD(lparam)

glviewport 0,0,wid,hei
glmatrixmode %gl_projection

gluperspective 45,wid/hei,1,2000
glmatrixmode %gl_modelview
EXIT FUNCTION

CASE %WM_DESTROY                   'Exit back to windows.

PostQuitMessage 0
EXIT FUNCTION

END SELECT
FUNCTION=DefWindowProc(hWnd,wMsg,wParam,lParam)

END FUNCTION

SUB setuppixelformat(BYVAL hdc AS DWORD)

GLOBAL PP AS pixelformatdescriptor
GLOBAL Npixelformat AS DWORD

PP.nsize =SIZEOF(PP)
pp.nversion =1
pp.dwflags =%pfd_draw_to_window OR %pfd_support_opengl OR %pfd_doublebuffer
pp.ipixeltype =%pfd_type_rgba
pp.ccolorbits =32
pp.credbits =0
pp.credshift =0
pp.cgreenbits =0
pp.cgreenshift =0
pp.cbluebits =0
pp.cblueshift =0
pp.calphabits =0
pp.calphashift =0
pp.caccumbits =0
pp.caccumredbits =0
pp.caccumgreenbits =0
pp.caccumbluebits =0
pp.caccumalphabits =0
pp.cdepthbits =16
pp.cstencilbits =0
pp.cauxbuffers =0
pp.ilayertype =%pfd_main_plane
pp.breserved =0

npixelformat=ChoosePixelFormat(hdc,PP)
SetpixelFormat hdc,npixelformat,PP

END SUB
'
'  *Set Camera and draw spinning cubes.
'
SUB DoGraphics

LOCAL t AS DWORD
LOCAL temp AS SINGLE
LOCAL temp2 AS SINGLE
LOCAL lp AS LONG

CALL GetKeyboard

glclear %gl_color_buffer_bit OR %gl_depth_buffer_bit
gluLookAt Xposition,Yposition,Zposition,_
Xview,Yview,Zview,Xup,Yup,Zup

FOR lp=0 TO %rocks
XcubeAngle(lp)=XcubeAngle(lp)+0.5
IF Xcubeangle(lp)>360 THEN Xcubeangle(lp)=0
Ycubeangle(lp)=Ycubeangle(lp)+1
IF Ycubeangle(lp)>360 THEN Ycubeangle(lp)=0
Zcubeangle(lp)=Zcubeangle(lp)+1.5
IF Zcubeangle(lp)>360 THEN Zcubeangle(lp)=0

CALL DrawCube(Xcube(lp),Ycube(lp),Zcube(lp),_
Xcubeangle(lp),Ycubeangle(lp),Zcubeangle(lp),_
cubesize(lp))
NEXT lp

swapbuffers g_hdc
END SUB
'
' * Draw a cube oriented at any angle
'
SUB DrawCube(BYVAL Xpos AS SINGLE,BYVAL Ypos AS SINGLE,BYVAL Zpos AS SINGLE,_
BYVAL Xangle AS SINGLE,BYVAL Yangle AS SINGLE,BYVAL Zangle AS SINGLE,_
BYVAL big AS SINGLE)

glpushmatrix
gltranslatef Xpos,Ypos,Zpos   'Position the cube
glrotatef Xangle,1,0,0        'Rotate around X axis
glrotatef Yangle,0,1,0        'Rotate around Y axis
glrotatef Zangle,0,0,1        'Rotate around Z axis
glscalef big,big,big          'Size of cube
glcolor3f 1,1,1
glvertex3f -0.5,-0.5,0.5     'front face
glvertex3f 0.5,-0.5,0.5
glvertex3f 0.5,0.5,0.5
glvertex3f -0.5,0.5,0.5
glend
glcolor3f 1,0.5,1
glvertex3f -0.5,-0.5,-0.5   'back face
glvertex3f -0.5,0.5,-0.5
glvertex3f 0.5,0.5,-0.5
glvertex3f 0.5,-0.5,-0.5
glend
glcolor3f 0.5,1,1
glvertex3f 0.5,-0.5,0.5   'right face
glvertex3f 0.5,-0.5,-0.5
glvertex3f 0.5,0.5,-0.5
glvertex3f 0.5,0.5,0.5
glend
glcolor3f 1,1,0.5
glvertex3f -0.5,-0.5,0.5  'left face
glvertex3f -0.5,0.5,0.5
glvertex3f -0.5,0.5,-0.5
glvertex3f -0.5,-0.5,-0.5
glend
glcolor3f 0.5,1,0.5
glvertex3f -0.5,-0.5,0.5  'top face
glvertex3f -0.5,-0.5,-0.5
glvertex3f 0.5,-0.5,-0.5
glvertex3f 0.5,-0.5,0.5
glend
glcolor3f 1,0.5,0.5
glvertex3f -0.5,0.5,0.5  'bottom face
glvertex3f 0.5,0.5,0.5
glvertex3f 0.5,0.5,-0.5
glvertex3f -0.5,0.5,-0.5
glend
glpopmatrix
END SUB
'
' * Get keyboard input
'
SUB GetKeyBoard

LOCAL Key AS WORD

Key=getAsyncKeystate(%vk_left)         'Rotate left.
IF Key>255 THEN
CALL GetHorzVec(Xview-Xposition,Yview-Yposition,Zview-Zposition,_
Xup,Yup,Zup)
CALL GetupVec(Xview-Xposition,Yview-Yposition,Zview-Zposition,_
Xhorz,Yhorz,Zhorz)
CALL rotateview(0.01,Xup,Yup,Zup)
END IF
Key=getAsyncKeystate(%vk_right)        'Rotate right.
IF Key>255 THEN
CALL GetHorzVec(Xview-Xposition,Yview-Yposition,Zview-Zposition,_
Xup,Yup,Zup)
CALL GetupVec(Xview-Xposition,Yview-Yposition,Zview-Zposition,_
Xhorz,Yhorz,Zhorz)
CALL rotateview(-0.01,Xup,Yup,Zup)
END IF
Key=getAsyncKeystate(%vk_down)        'Pitch up.
IF Key>255 THEN
CALL GetHorzVec(Xview-Xposition,Yview-Yposition,Zview-Zposition,_
Xup,Yup,Zup)
CALL GetupVec(Xview-Xposition,Yview-Yposition,Zview-Zposition,_
Xhorz,Yhorz,Zhorz)
CALL rotateview(0.01,Xhorz,Yhorz,Zhorz)
END IF
Key=getAsyncKeystate(%vk_up)           'Pitch down.
IF Key>255 THEN
CALL GetHorzVec(Xview-Xposition,Yview-Yposition,Zview-Zposition,_
Xup,Yup,Zup)
CALL GetupVec(Xview-Xposition,Yview-Yposition,Zview-Zposition,_
Xhorz,Yhorz,Zhorz)
CALL rotateview(-0.01,Xhorz,Yhorz,Zhorz)
END IF
IF Key>255 THEN
CALL MoveCamera(1)
END IF
Key=getAsyncKeystate(%vk_X)            'Move back
IF Key>255 THEN
CALL MoveCamera(-1)
END IF
Key=getAsyncKeystate(%vk_A)            'Roll left.
IF Key>255 THEN
CALL GetHorzVec(Xview-Xposition,Yview-Yposition,Zview-Zposition,_
Xup,Yup,Zup)
CALL spinview(0.01,xview-xposition,yview-yposition,zview-zposition)
END IF
Key=getAsyncKeystate(%vk_S)            'Roll right.
IF Key>255 THEN
CALL GetHorzVec(Xview-Xposition,Yview-Yposition,Zview-Zposition,_
Xup,Yup,Zup)
CALL spinview(-0.01,xview-xposition,yview-yposition,zview-zposition)
END IF
END SUB
'
' Move camera towards or back from your view vector.
'
SUB MoveCamera(BYVAL speed AS SINGLE)

XvVector=Xview-Xposition
YvVector=Yview-Yposition
ZvVector=Zview-Zposition

Xposition=Xposition+(XvVector*speed)
Yposition=Yposition+(YvVector*speed)
Zposition=Zposition+(ZvVector*speed)

Xview=Xview+(XvVector*speed)
Yview=Yview+(YvVector*speed)
Zview=Zview+(ZvVector*speed)

END SUB
'
' Rotate Camera (Get New Position of View Vector)
'
SUB RotateView(BYVAL angle AS SINGLE,BYVAL Xaxis AS SINGLE,BYVAL Yaxis AS SINGLE,BYVAL Zaxis AS SINGLE)

LOCAL Xnew AS SINGLE
LOCAL Ynew AS SINGLE
LOCAL Znew AS SINGLE
LOCAL CosAngle AS SINGLE
LOCAL SinAngle AS SINGLE

XvVector=Xview-Xposition
YvVector=Yview-Yposition
ZvVector=Zview-Zposition

CosAngle=COS(angle)
SinAngle=SIN(angle)

Xnew=(CosAngle+(1-CosAngle)*xaxis*xaxis)*XvVector
Xnew=Xnew+((1-CosAngle)*xaxis*yaxis-zaxis*SinAngle)*YvVector
Xnew=Xnew+((1-CosAngle)*xaxis*zaxis+yaxis*SinAngle)*ZvVector

Ynew=((1-CosAngle)*xaxis*yaxis+zaxis*SinAngle)*XvVector
Ynew=Ynew+(CosAngle+(1-CosAngle)*yaxis*yaxis)*YvVector
Ynew=Ynew+((1-CosAngle)*yaxis*zaxis-xaxis*SinAngle)*ZvVector

Znew=((1-CosAngle)*xaxis*zaxis-yaxis*SinAngle)*XvVector
Znew=Znew+((1-CosAngle)*yaxis*zaxis+xaxis*SinAngle)*YvVector
Znew=Znew+(CosAngle+(1-CosAngle)*Zaxis*Zaxis)*ZvVector

Xview=Xposition+Xnew
Yview=Yposition+Ynew
Zview=Zposition+Znew

END SUB
'
' Get Perpendicular vector from View Vector and Up Vector
'
SUB GetHorzVec(BYVAL Xvec AS SINGLE,BYVAL Yvec AS SINGLE,BYVAL Zvec AS SINGLE,_
BYVAL Xvec2 AS SINGLE,BYVAL Yvec2 AS SINGLE,BYVAL Zvec2 AS SINGLE)

'Find vector perpendicular to two given vectors.

Xhorz=((Yvec*Zvec2)-(Zvec*Yvec2))
Yhorz=((Zvec*Xvec2)-(Xvec*Zvec2))
Zhorz=((Xvec*Yvec2)-(Yvec*Xvec2))

'Calculate magnitude of vector (length)

Magnitude=SQR(Xhorz*Xhorz+Yhorz*Yhorz+Zhorz*Zhorz)

'Calculate Normal of vector (set length to 1)

Xhorz=Xhorz/Magnitude
Yhorz=Yhorz/Magnitude
Zhorz=Zhorz/Magnitude
END SUB
'
' Get Perpendicular vector from view & horizontal vectors
'
SUB GetUpVec(BYVAL Xvec AS SINGLE,BYVAL Yvec AS SINGLE,BYVAL Zvec AS SINGLE,_
BYVAL Xvec2 AS SINGLE,BYVAL Yvec2 AS SINGLE,BYVAL Zvec2 AS SINGLE)

'Find vector perpendicular to two given vectors.

Xup=(Yvec*Zvec2)-(Zvec*Yvec2)
Yup=(Zvec*Xvec2)-(Xvec*Zvec2)
Zup=(Xvec*Yvec2)-(Yvec*Xvec2)

'Calculate magnitude of vector (length)

Magnitude=SQR(Xup*Xup+Yup*Yup+Zup*Zup)

'Calculate Normal of vector (set length to 1)

Xup=-Xup/Magnitude
Yup=-Yup/Magnitude
Zup=-Zup/Magnitude
END SUB
'
' Spin Camera (Get New Position of Up Vector.  Up vector spins around view vector.)
'
SUB SpinView(BYVAL angle AS SINGLE,BYVAL Xaxis AS SINGLE,BYVAL Yaxis AS SINGLE,BYVAL Zaxis AS SINGLE)

LOCAL Xnew AS SINGLE
LOCAL Ynew AS SINGLE
LOCAL Znew AS SINGLE
LOCAL CosAngle AS SINGLE
LOCAL SinAngle AS SINGLE

CosAngle=COS(angle)
SinAngle=SIN(angle)

Xnew=(CosAngle+(1-CosAngle)*xaxis*xaxis)*Xup
Xnew=Xnew+((1-CosAngle)*xaxis*yaxis-zaxis*SinAngle)*Yup
Xnew=Xnew+((1-CosAngle)*xaxis*zaxis+yaxis*SinAngle)*Zup

Ynew=((1-CosAngle)*xaxis*yaxis+zaxis*SinAngle)*Xup
Ynew=Ynew+(CosAngle+(1-CosAngle)*yaxis*yaxis)*Yup
Ynew=Ynew+((1-CosAngle)*yaxis*zaxis-xaxis*SinAngle)*Zup

Znew=((1-CosAngle)*xaxis*zaxis-yaxis*SinAngle)*Xup
Znew=Znew+((1-CosAngle)*yaxis*zaxis+xaxis*SinAngle)*Yup
Znew=Znew+(CosAngle+(1-CosAngle)*Zaxis*Zaxis)*Zup

Xup=Xnew
Yup=Ynew
Zup=Znew
END SUB```
------------------