Smart questions
Smart answers
Smart people
INTELLIGENT WORK FORUMS
FOR COMPUTER PROFESSIONALS

Member Login




Remember Me
Forgot Password?
Join Us!

Come Join Us!

Are you a
Computer / IT professional?
Join Tek-Tips now!
  • Talk With Other Members
  • Be Notified Of Responses
    To Your Posts
  • Keyword Search
  • One-Click Access To Your
    Favorite Forums
  • Automated Signatures
    On Your Posts
  • Best Of All, It's Free!

Join Tek-Tips
*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.

Donate Today!

Do you enjoy these
technical forums?
Donate Today! Click Here

Posting Guidelines

Promoting, selling, recruiting, coursework and thesis posting is forbidden.
Jobs from Indeed

Link To This Forum!

Partner Button
Add Stickiness To Your Site By Linking To This Professionally Managed Technical Forum.
Just copy and paste the
code below into your site.

QBasic FAQ

Graphical Techniques in QB

3D Graphics Fundamentals
Posted: 24 Dec 00

3D Graphics Fundamentals
------------------------


So you want to do 3D graphics? First, you have to understand the math. There's no other way to do it. The concepts behind the graphics are not difficult, however. What we're dealing with is basically the transformation of points from this:

       |y
       |
       |   /z
       |  /
       | /
       |/
-------*------
      /|     x
     / |
    /  |
   /   |
       |


.. to this:

                ___________
               / =======  /|
  ____________/_=======__/_|______
 /                               /|
+-------------------------------+ |
|  .-------------------------.  | |
| |             |y            | | |
| |             |             | | |
| |             |             | | |
| |             |             | | |
| | ------------*------------ | | |
| |             |           x | | |
| |             |             | | |
| |             |             | | |
| |             |             | | |
|  '-------------------------'  | |
|  o o o o o o             [0]  |/
+----------__--------___--------+
          / /._____.\  /\
         /____________/ /
        /_____________\/


If we take a closer look at what we're given to work with -- that is, the 3D space, or the so-called 'worldspace', in which our points are originally represented, from top-down, we see this:

               |z
               |
               |
               |
               |
               |
               |
---------------*--------------
               |y            x
               |
               |
               |
               |
               |
               |


The Y axis is no longer visible since it extends directly upwards from the diagram, away from your screen. Now, let's place a typical point into worldspace:

               |z
               |
               |
               |---P(x,y,z)
               |   |
               |   |
               |   |
---------------*--------------
               |y            x
               |
               |
               |
               |
               |
               |


The point P is at coordinates (x,y,z). Now, the concept of what we want to do is that we want to place the point on the screen so that it will be along the line of sight between the eye and the real point P, in the hopes that we can fool the eye into believing that it IS the point P. To do this, we need some representation of the screen in our 3D world. This is easier than it sounds, because the screen is nothing more than a section of a plane -- perfectly flat. Things become substantially simpler if we restrict the screen to be parallel to the XY plane. Our diagram now looks like this:

               |z
               |
               |
               |     P(x,y,z)
               |
               |
          |---------|
               |
               |
---------------*---------------
               |y             x
               |
               |
               |
               |
               |
               |
               |
               |


Two points are important in setting up the line-of-sight between the eye and the point P, and those are the eye and the point P themselves. We know where the point P is, but we haven't declared where the eye is yet. Having placed the screen like this, somewhat away from the XY plane, we can use the origin as the point that we're projecting from. Representing this in the diagram gives us this:

               |z
               |
               |
               |-----P(x,y,z)
               |    /
               |   /
          |-------S-|
               | /
               |/
---------------E---------------
               |y             x
               |
               |
               |
               |
               |
               |
               |
               |


.. where S is the point whose coordinates we need to know to place the pixel on the screen in the line-of-sight from E to P. Getting the coordinates of this point is easy. We know that the 'z' coordinate is the same as the screen, and we can put the screen wherever we want to. For simplicity, we can make this value 1. Getting the X and Y coordinates is a little bit more complicated, but still not hard. Notice that a system of similar triangles has been set up: the triangle with ES as hypotenuse has the same angles as the triangle with EP as hypotenuse. This means that there is proportionality between the edges of the two triangles. To simplify things, let's add two points to the diagram:

               |z
               |
               |
               K-----P(x,y,z)
               |    /
               |   /
          |----J--S-|
               | /
               |/
---------------E---------------
               |y             x
               |
               |
               |
               |
               |
               |
               |
               |


Using these 5 points, and denoting the distance between two points by specifying the two points (eg, EP for the distance from the eye to the point), the following proportionalities can be observed:

  JS      ES      EJ
 ---- == ---- == ----
  KP      EP      EK


We can construct a similar system as viewed from the right-hand side:

               |y
               |
               |
               |     P(x,y,z)
               |    /|
               | --/ |
               |  S  |
               | /|  |
               |/ |  |
---------------E--J--K---------
               |x |           z
               |  |
               |  |
               | ---
               |
               |
               |
               |
               |


Because of this, the above proportionalities can be expanded to:

  Sx      Sy      Sz
 ---- == ---- == ----
  Px      Py      Pz


Since we know that Sz -- the Z coordinate of the screen -- is 1, we can re-arrange these proportionalities into the following equations:

         Px
  Sx == ----
         Pz

         Py
  Sy == ----
         Pz


Thus, the conversion from worldspace to the screen, which is referred to as viewspace, is a straightforward set of divisions. All that remains to be attended to are minor technicalities, such as the dimensions of the screen. It's not as simple as saying that one unit is one pixel, because by simplifying Sz as 1, that would require that the user be 1 pixel away from the screen, which is not the typical scenario. Assuming that we're dealing with SCREEN 13, which is 320x200x256 with roughly 1 mm per side on the pixels, the user is usually about 75 cm away from the monitor, which translates to 750 pixels. With larger screens, the user usually places themselves farther away, and with smaller monitors, the user usually sits closer to the monitor, so any inequities in pixel size with respect to monitor size tend to balance out proportionally and can be ignored. Now, if the user is 750 pixels away from the screen, the point on the screen S has a z value of 1, that means that the unit of z in worldspace is equivalent to 750 pixels. Since the system uses the same units for x and y as it does for z, the screen's boundaries in worldspace must be (-160 / 750, -100 / 750) to (160 / 750, 100 / 750). The x and y coordinates of S are thus scaled down by 750 from the corresponding screen coordinates, and they must be multiplied by 750 to get these values. The distance 75 cm is arbitrary, and it can be modified or calculated from angles to get different fields of vision, or FOVs (like in Quake). Once you have the coordinates, you can do whatever you want with them. Some sample code to demonstrate this follows. As usual, the code is optimized for readability, rather than speed.


CONST PixelsFromEyeToCentreOfScreen# = 750

CONST SphereStripes% = 32
CONST SphereDotsPerStripe% = 24
CONST NumDots% = SphereStripes% * SphereDotsPerStripe%

CONST SphereRadius# = 1
CONST SphereCentreX# = 0
CONST SphereCentreY# = 0
CONST SphereCentreZ# = 8
TYPE pointType
 x AS DOUBLE
 y AS DOUBLE
 z AS DOUBLE
 oldScreenX AS INTEGER
 oldScreenY AS INTEGER
END TYPE
DECLARE SUB
makeSphere (dot() AS pointType)
DIM dot(1 TO NumDots%) AS pointType
makeSphere dot()
SCREEN 13
DO
 '0.003 radians == 17.2 degrees
 rotation# = rotation# + .003
 IF rotation# > 6.283185 THEN rotation# = rotation# - 6.283185

 FOR i% = 1 TO NumDots%
  thisX# = dot(i%).x
  thisY# = dot(i%).y
  thisZ# = dot(i%).z

  'rotate point around Y axis at Sphere Center (X,Y,Z)
  rotatedX# = COS(rotation#) * (thisX# - SphereCentreX#) - SIN(rotation#) * (thisZ# - SphereCentreZ#) + SphereCentreX#
  rotatedY# = thisY#
  rotatedZ# = SIN(rotation#) * (thisX# - SphereCentreX#) + COS(rotation#) * (thisZ# - SphereCentreZ#) + SphereCentreZ#

  'translate to screen coordinates
  pointSX# = rotatedX# / rotatedZ#
  pointSY# = rotatedY# / rotatedZ#

  screenX% = 160 + pointSX# * PixelsFromEyeToCentreOfScreen#
  screenY% = 100 - pointSY# * PixelsFromEyeToCentreOfScreen#

  'do a little bit of depth queuing (very simple =D)
  IF rotatedZ# > SphereCentreZ# THEN
   colour% = 8 'dark gray
  ELSEIF rotatedZ# > SphereCentreZ# - SphereRadius# / 2 THEN
   colour% = 7 'light gray
  ELSE
   colour% = 15 'white
  END IF

  'draw pixel
  PSET (screenX%, screenY%), colour%

  'erase old pixel in a fairly flicker-free way
  IF (dot(i%).oldScreenX <> screenX%) OR (dot(i%).oldScreenY <> screenY%) THEN
   PSET
(dot(i%).oldScreenX, dot(i%).oldScreenY), 0
  END IF

  'store new pixel's position for next time
  dot(i%).oldScreenX = screenX%
  dot(i%).oldScreenY = screenY%
 NEXT i%
LOOP UNTIL INKEY$ <> ""
SCREEN 0: WIDTH 80, 25

SUB makeSphere (dot() AS pointType)
FOR i% = 1 TO SphereStripes%
 heading# = 6.283185 * i% / SphereStripes%
 FOR j% = 1 TO SphereDotsPerStripe%
  pitch# = 3.141592 * (j% - 1) / (SphereDotsPerStripe% - 1) - 1.570796
  thisDotX# = SphereRadius# * SIN(heading#) * COS(pitch#)
  thisDotY# = SphereRadius# * SIN(pitch#)
  thisDotZ# = SphereRadius# * COS(heading#) * COS(pitch#)
  thisDotIndex% = (i% - 1) * SphereDotsPerStripe% + j%
  dot(thisDotIndex%).x = thisDotX# + SphereCentreX#
  dot(thisDotIndex%).y = thisDotY# + SphereCentreY#
  dot(thisDotIndex%).z = thisDotZ# + SphereCentreZ#
 NEXT j%
NEXT i%
END SUB

Back to QBasic FAQ Index
Back to QBasic Forum

My Archive

Close Box

Join Tek-Tips® Today!

Join your peers on the Internet's largest technical computer professional community.
It's easy to join and it's free.

Here's Why Members Love Tek-Tips Forums:

Register now while it's still free!

Already a member? Close this window and log in.

Join Us             Close