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.hIcon  =  LoadIcon(hInstance,"PROGRAM")
    wce.hCursor  =  0
    wce.hbrBackground  =  %NULL
    wce.lpszMenuName  =  %NULL
    wce.lpszClassName  =  VARPTR(szclassname)
    wce.hIconSm  =  LoadIcon(hInstance,BYVAL %IDI_APPLICATION)

    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
         glloadidentity

         gluperspective 45,wid/hei,1,2000
         glmatrixmode %gl_modelview
         glloadidentity
         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
    pp.dwlayermask =0
    pp.dwvisiblemask =0
    pp.dwdamagemask =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
    glloadIdentity
    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
     glbegin %gl_quads
        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
     glbegin %gl_quads
        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
     glbegin %gl_quads
        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
     glbegin %gl_quads
        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
     glbegin %gl_quads
        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
     glbegin %gl_quads
        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
    Key=getAsyncKeystate(%vk_Z)            'Move ahead
    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
------------------