The Basic of
3 D G R A P H I C S
For QBasic
Written by Aaron Severn
December 19, 1997
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Table of Contents
-----------------
0 Disclaimer
1 Introduction to 3D Space
2 Drawing a 3D Object on a 2D Monitor
3 3D Rotations
4 Sample Program
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
0 Disclaimer
------------
I assume no responsibility for any harm that comes from using the material
contained in this document to you, your computer, or anything relating to
your existence. No warranty is provided or implied with this information.
This document is provided as is.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1 Introduction to 3D Space
--------------------------
To many of you, the concept of 3D space is unfamiliar, despite the fact
that we live in it everyday. The most important thing to remember when
thinking 3D is that objects that are far away appear smaller than those
that are close. This is obvious to us, our brain has learned to interpret
that fact, however it's not so obvious to a computer. You've got some work
to do if you want to turn a 2D monitor into a 3D world.
First of all, let's look at graphing in space. 3D space (R3) looks
something like this on a graph.
Z Where Y is the horizontal axis
| Z is the vertical axis
| X is the axis coming out of the screen
|í
|_______ é is the angle in the XY plane
/ Y í is the angle from the Z axis
/ é
/
/
X
The symbols above will be used throughout this text.
The two problems we have to deal with are this:
1. How do you create the effect of depth on a flat screen?
2. How do you rotate points in space?
Now that we've got the basis, let's answer number one.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
2 Drawing a 3D Object on a 2D Monitor
-------------------------------------
The answer to this problem is surprisingly simple. First think about
what's going on. Let's say we have a transparent box on the screen, we want
the side that is closest to us to be bigger than the side that is farther
away. So how can we do this, simple, divide the points making up the far
away side by the distance to that side.
So what does that mean for plotting points in 3D. We start off with three
coordinates, but we want two, so divide two of the coordinates by the other
one. Here are the formulas:
x2D = 256 * (x3D / (z3D + zCenter)) + xCenter
y2D = 256 * (y3D / (z3D + zCenter)) + yCenter
In the above formulas, (x2D, y2D) is the point that will be plotted on the
screen, (x3D, y3D, z3D) is the point in 3D space, xCenter and yCenter
represent the location of the center of the object on the screen, and zCenter
represents the location of the 3D center of the screen or the location of
you, the viewer (this value is usually 256). Both equations are multiplied
by 256 to relocate the object adjusted to the viewer's perspective.
Using those formulas makes it possible to draw a 3D object on a 2D screen.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
3 3D Rotations
--------------
A 3D object that just sits on the screen looking pretty gets boring after
a while. It's alot more interesting if you can have it moving, or maybe
rotating. So how do we do that. Well, let me start by saying that the
math involved here is pretty complicated. I'm no expert in it myself so I
won't waste your time with a bad explanation. I'll just give you the final
formulas and you can make spinning boxes all night.
First of all you will need the two angles that can be found in the diagram
in section 1. They were theta(é) and phi(í). We will also need to deal with
rho(p) which represents the distance to the point from the origin (0,0,0).
Now I'll just jump straight to the formulas. In the formulas listed below,
(xO, yO, zO) represent the original location of the point in 3D space and
(xR, yR, zR) represent the rotated point.
xR = -xO * SIN(é) + yO * COS(é)
yR = -xO * COS(é) * SIN(í) - yO * SIN(é) * SIN(í) - zO * COS(í) + p
zR = -xO * COS(é) * COS(í) - yO * SIN(é) * COS(í) + zO * SIN(í)
Nice, aren't they. Well, if you can copy those down right you'll be able
to write a nice 3D rotating program. But I won't just dump this on you and
run, check out the example below.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
4 Sample Program
----------------
NOTE: Some lines in the sample program have been divided into two parts so
they can fit on the screen, these should be obvious. If you want to
run this program make sure you restore these cut lines to one single
line.
DEFINT A-Z
TYPE pnt 'type for each 3D point
x AS INTEGER 'x coord (horizontal)
y AS INTEGER 'y coord (vertical)
Z AS INTEGER 'z coord (into the screen)
p AS INTEGER 'dist from center of object
END TYPE
numLines = 12 - 1
DIM lO(numLines, 1) AS pnt 'Original line coords
DIM lR(numLines, 1) AS pnt 'Rotated coords
DIM scrX(numLines, 1) 'screen x coord
DIM scrY(numLines, 1) 'screen y coord
DIM oldX(numLines, 1) 'old x coord for erasing
DIM oldY(numLines, 1) 'old y coord for erasing
DIM s!(359) 'trig tables
DIM c!(359)
CONST PI = 3.141592
FOR i = 0 TO 359 'create sine and cosine
s!(i) = SIN(i * (PI / 180)) 'look up tables to speed up
c!(i) = COS(i * (PI / 180)) 'the math
NEXT
' Read two points instead of one.
FOR i = 0 TO numLines
READ lO(i, 0).x, lO(i, 0).y, lO(i, 0).Z, lO(i, 0).p
READ lO(i, 1).x, lO(i, 1).y, lO(i, 1).Z, lO(i, 1).p
NEXT
SCREEN 13
CLS
xCenter = 160: yCenter = 100: zCenter = 256
theta = 0: phi = 0
thetaRot = 2: phiRot = 2
justStarted = 1
DO
FOR i = 0 TO numLines
' Save the old values of x and y so we can erase the balls later.
oldX(i, 0) = scrX(i, 0): oldY(i, 0) = scrY(i, 0)
oldX(i, 1) = scrX(i, 1): oldY(i, 1) = scrY(i, 1)
' Rotate both points on each axis.
lR(i, 0).x = -lO(i, 0).x * s!(theta) + lO(i, 0).y * c!(theta)
lR(i, 0).y = -lO(i, 0).x * c!(theta) * s!(phi) - lO(i, 0).y * s!(theta)
* s!(phi) - lO(i, 0).Z * c!(phi) + lO(i, 0).p
lR(i, 0).Z = -lO(i, 0).x * c!(theta) * c!(phi) - lO(i, 0).y * s!(theta)
* c!(phi) + lO(i, 0).Z * s!(phi)
lR(i, 1).x = -lO(i, 1).x * s!(theta) + lO(i, 1).y * c!(theta)
lR(i, 1).y = -lO(i, 1).x * c!(theta) * s!(phi) - lO(i, 1).y * s!(theta)
* s!(phi) - lO(i, 1).Z * c!(phi) + lO(i, 1).p
lR(i, 1).Z = -lO(i, 1).x * c!(theta) * c!(phi) - lO(i, 1).y * s!(theta)
* c!(phi) + lO(i, 1).Z * s!(phi)
' Translate both points from 3D to 2D.
IF (lR(i, 0).Z + zCenter) <> 0 THEN
scrX(i, 0) = 256 * (lR(i, 0).x / (lR(i, 0).Z + zCenter)) + xCenter
scrY(i, 0) = 256 * (lR(i, 0).y / (lR(i, 0).Z + zCenter)) + yCenter
END IF
IF (lR(i, 1).Z + zCenter) <> 0 THEN
scrX(i, 1) = 256 * (lR(i, 1).x / (lR(i, 1).Z + zCenter)) + xCenter
scrY(i, 1) = 256 * (lR(i, 1).y / (lR(i, 1).Z + zCenter)) + yCenter
END IF
NEXT i
' Erase the old lines.
WAIT &H3DA, 8
IF justStarted = 0 THEN
FOR i = 0 TO numLines
LINE (oldX(i, 0), oldY(i, 0))-(oldX(i, 1), oldY(i, 1)), 0
NEXT i
END IF
' Draw the new lines.
FOR i = 0 TO numLines
LINE (scrX(i, 0), scrY(i, 0))-(scrX(i, 1), scrY(i, 1)), 11
NEXT i
theta = (theta + thetaRot) MOD 360
phi = (phi + phiRot) MOD 360
justStarted = 0
LOOP UNTIL INKEY$ = CHR$(27)
' Lines are stored in format (X1,Y1,Z1,p1)-(X2,Y2,Z2,p2)
DATA -50,50,50,1,50,50,50,1
DATA 50,-50,50,1,50,50,50,1
DATA 50,50,-50,1,50,50,50,1
DATA -50,-50,50,1,-50,50,50,1
DATA -50,50,-50,1,-50,50,50,1
DATA -50,-50,50,1,50,-50,50,1
DATA -50,50,-50,1,50,50,-50,1
DATA -50,-50,-50,1,50,-50,-50,1
DATA -50,-50,-50,1,-50,50,-50,1
DATA 50,-50,-50,1,50,-50,50,1
DATA 50,-50,-50,1,50,50,-50,1
DATA -50,-50,-50,1,-50,-50,50,1