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