Copy Link
Add to Bookmark
Report

Intro to 3D Graphics Programming (Volume 1)

eZine's profile picture
Published in 
Demo News extra
 · 5 years ago

 
=------------------------------[Intro to 3D Graphics Programming]--[Kiwidog]-=

_____Greetings

Hello everyone,

As my first code article since joining the Hornet crew, I've decided to
write an introduction into 3D graphics coding. Trixter informed me that
DemoNews has never had a 3D article, so it seemed like a good idea. :-)

This will not be just one article - far from it. In fact, it will take
quite a few articles to cover all the information I want to cover (after
finding out about space limitations on article size I'm allowed to write,
I've had to chop up the series into smaller pieces). But expect a minimum
ten or so articles coming your way...

This is the full version of the DemoNews 114 article, which means you've
got the supplement file (that's good, because it has example source, too :)
The source included is the same example, but in three different files,
depending on what language and compiler you use.
WC_EX1.C - Watcom C version (32-bit DOS extended)
BC_EX1.C - Borland C version
TP_EX1.PAS - Turbo Pascal version

This series will assume you've never done 3D graphics (or "Vectors" as
the scene likes to call it) before. I'm going to try this on a very basic
level, and if you're already familiar with the essentials, you might want
to hit PgDn a few times. Eventually, I'll write about more complicated 3D
issues in later articles. But let's take it one step at a time...

_____What You Need To Know First

I'm assuming you have a knowledge of basic algebra, i.e. early high school
level math. If you've had some Trigonometry already, that will help out,
but I'll try to cover the basics of what you need from that as well. But,
more important than any one particular requirement, you have to like math,
or at least a willingness to like it.

MATH IS ONLY BORING IF YOU DON'T HAVE A USE FOR IT.

Basically, 3D coding is not really about programming... it's almost entirely
about math. The amount of time you spend thinking about what you're doing
is generally a lot longer than the time you actually spend pounding the keys
writing the code.

If you don't think you like math, and you're a coder, chances are you
probably _DO_ like math and just don't know it. :-) I'm serious... a lot
of times when you hate a subject in school, you don't really hate it, you
just think you hate it because you hate the way it's taught. Once you get
down to finding a purpose for all that "useless" knowledge.... it becomes
a heck of a lot of fun. So if you like math, and you like coding graphics,
you've come to the right place. If you like coding graphics but don't like
math... give it a chance; you may find out something new about yourself. :-)

_____What You Don't Need to Know First

Okay, generally speaking, the chain of math courses taught in high school
and college (or their European equivalents, which I can't really speak for
but I can take a guess) goes something like this:

1. Algebra 2. Geometry 3. Analytical Algebra 4. Trigonometry
5. Pre-Calculus 6. Calculus 7. Linear Algebra 8. Vector Geometry
9. Multivariable Calculus 10. Differential Equations 11. Even more
complicated issues that I haven't gotten to yet...

You need to know up to #3 to make use of any of this. #4 would be helpful,
although I'll cover a few things here in the beginning to get you going,
if you've never taken Trig before.

Pre-Calc and Calc aren't directly needed for this series, although you'll
find it useful later on in life when you want to make really cool movements
for your vectors. And if you know through #7, life is a breeze; you can
forget most of this beginning crap. :) Anything after that I'm not going to
cover by itself, but you'll find it helpful if you know it, as you try more
innovative ideas with your vector code. But still, it's not necessary at
all for the little I'm going to be able to teach you. :)

_____What Will Be Covered In This Series

Whew, it's a whopper. Here's the series layout as I'm planning it so far.
As you can tell, the first couple articles are for the _very_ beginners,
like the kind who know how to code, but have never even touched anything
with 3D before. After the first few, it'll get more interesting though,
so bear with me (if you have some 3D experience already, you can forget
reading the first few articles; they won't do you any good).

1. Basic Trig functions, 3D Coordinate to 2D Screen Projection.
2. 2D and 3D Rotations & Other Transformations.
3. Dot and Cross Products, Backface Removal and Lightsourcing.
4. Polygon Fills - Flat and Lambert
5. Polygon Fills - Gouraud and Phong
6. Polygon Fills - Affine Texture Mapping
7. Polygon Fills - Perspective Texture Mapping, Perspective Correction
8. Spherical and Cylindrical Coordinates
9. Polygon Fills - Bump Mapping
10. BSP Trees and Spacial Partitioning
11. Polygon Fills - Reflection Mapping
12. Alternative Camera Positioning and Motion
13. Path Management, Linear and Bezier Curve Movement
14. Basic Inverse Kinematics
15. (Unknown - I'm not sure how much further I can take it without risking
losing the few tricks I have up my sleeve for my future :)

It's quite likely that the later articles will be split into separate
articles as well (I doubt I can fit both Gouraud and Phong, for example,
into one article). So the numbers are probably going to get higher and
higher. It will probably be around the time of NAID or afterward that
the series ends.

Get ready for a crash course in 3D... :-)


_____Section One - Basic Trigonometric Functions


Okay, If you've taken Trig before, skip this section. If you haven't,
you're probably thinking that this long title doesn't sound too friendly.
Don't worry, it sounds a whole lot worse than it is.

The main principles of Trigonometry come from the first part of the name,
"Trigon", which pretty much means a triangle. Everything in Trig is based
on a few functions which deal with triangles. There are six of these
functions...

Sine, Cosine, Tangent, Secant, Cosecant, and Cotangent
Abbreviated: Sin, Cos, Tan, Sec, Csc, and Cot.

... but in fact, they all boil down to the first two, Sine and Cosine.

Now I assume you're all familiar with functions, like [f(x) = 2x] and so
forth (BTW I'll enclose functions in [] on occasion throughout this doc).
Well the Trig functions are all functions using angles, for example
[f(x) = sin(x)] or even [f(x) = 2 sin(x) - 5 cos(x)], and so on.
In these cases, x is an angle, either in degrees or radians. I'm going
to stick with degrees for this explanation, since radians are too complex
for me to discuss right now.

Okay, so now you're thinking, "Great... Sine and Cosine are functions using
angles. But what do they _DO_?" Well, here's the rundown.

Get out a piece of paper. Go ahead, I'll wait. Now on that piece of paper,
draw yourself a pair of X and Y coordinate axes, like you normally would for
a graph. Make it big, so you give yourself lots of room.

Now draw a circle around the origin. Make it as good a circle as you can
sketch. Now whatever size you drew your circle, assume that's a radius
of 1. So where the circle hits your X and Y axes, label those points as
X=1 on the right, Y=1 on the top, X=-1 on the left, and Y=-1 on the bottom.

Okay, with this circle, we need some angles. Using your good ol' 360-degree
system, label those four points with their angles, where 0 degrees is facing
the right and they go counterclockwise. So 0 is at the right, 90 is up,
180 is left, and 270 is down.

Now knowing that's how the angles go, draw a line from the origin out to the
circle at about, oh, 60 degrees (up and to the right, more tall than wide).
At that point on the circle, draw a line STRAIGHT DOWN to the X axis. What
you should have now is a triangle inside the circle, with one side on the
X axis, one going straight up, and one at a 60 degree angle.

Okay, it's time for a little guessing game. Assuming that the circle has
a radius of 1 like you drew it, how tall is that vertical side of the
triangle? It's almost as tall as the circle, but not quite... more than
half the height of the circle, maybe around 3/4 the height, something like
that.

What was your guess? Probably somewhere between 0.7 and 0.9, I would think.
It should look about that tall. Whatever your guess is, write it down, and
keep it handy... it's going to be very important.

Now do the same thing for the bottom horizontal side of the triangle, the
one along the X axis. How long is that from the looks of it? It appears
around half the width of the circle, so you'd probably guess around 0.5 or
so. Anyway, write that one down too.

Okay, So you've got this triangle inside this circle, with the longest side
at an angle of 60 degrees and a length of 1 (the radius of the circle),
and two sides going straight vertical and horizontal, with lengths somewhere
near the numbers you guessed.

I'm gonna pull out my calculator, which supports Trig functions. Lemme
punch up the numbers....

Sin(60) = 0.8660254
Cos(60) = 0.5000000

Notice any correlation? You should... they're the lengths of the sides you
were guessing! :-) That, quite simply, is what the Sin and Cos functions
are... if you have a circle with radius 1, and a line at any angle from
the origin to the circle, then the Sine of that angle is the Y-component
(height) of that line, and the Cosine of the angle is the X-component
(width) of the line.

Other examples? Well now that you know what the Sin and Cos functions mean,
what are the Sin and Cos of 90 degrees? Well let's see... that's the line
going straight up from the origin, along the Y axis. The height is
the height of circle, exactly. And the width... well, there is no width,
since it's not going left or right whatsoever.

Sure enough,

Sin(90) = 1.0000000
Cos(90) = 0.0000000

Likewise, the trend continues.....

Angle Sine Cosine
0 0 1
90 1 0
180 0 -1
270 -1 0

Which demonstrates a very important point about these functions

Fact: The Sine and Cosine functions will never go less than -1,
and never greater than 1.

... and that makes sense, considering the circle has a radius of 1.
(Terminology: The circle you drew is often called the "unit circle" by
math books and teacher-type people).

Now by looking at the circle, you can see how the height changes less and
less as you approach Sin=1 or Sin=-1, and likewise the width changes less
and less as you approach Cos=1 or Cos=-1. It turns out that the Sin and
Cos functions are not linear whatsoever... and because of that, calculating
them for any given angle is a real pain in the rear. Sure, you have the
occasional angles like 90 and 180 which have 1s and 0s, or 30 and 60 which
have one of the components exactly 0.5, but most of the time you have values
that are not the nicest of numbers.

For this reason, you often hear 3D coders referring to "Sine Tables", which
are precalculated tables holding the Sin and Cos values for every angle in
their system (in this example, a 360 degree system. It turns out in coding
that a 256 degree system is much more convenient, for reasons you can
probably guess). These do-it-once tables turn an otherwise painful
calculation into a simple memory look-up. :-)

(BTW - earlier I mentioned in addition to Sin and Cos the Tan, Sec, Csc
and Cot functions... these can all be created from Sin and Cos by the
following: Tan(x) = Sin(x)/Cos(x)
Sec(x) = 1/Cos(x)
Csc(x) = 1/Sin(x)
Cot(x) = Cos(x)/Sin(x) = 1/Tan(x)
Most of those aren't very useful in vector coding, with the exception of
Tangent... it will come in handy at times. Everything else isn't too
important for now, but keep it on the back burner for future reference).

Okay, so now you've learned what you need to know from Trig... that the
Sin and Cos functions can be used to find the X and Y parts of a line at
any given angle. You just take the length of the line, multiply the Sin
or Cos (whichever you're looking for, Y or X respectively) by the line's
length, and wham you've got your result. This will be used EXTENSIVELY
in the rest of the doc and in 3D in general, as almost everything depends
on these components.

There is certainly a lot more to trigonometry, and entire books have been
dedicated to the subject. My highly over-simplified version here should
hopefully be enough to get your foot in the door. If you want more
complex and detailed info, I'd highly suggest a good math textbook covering
the subject (Trig is often grouped in with pre-calculus texts, incase you
have a difficult time finding a separate book). But this should be good
enough for the time being. :)

All set? Then let's get out of this plane of thought (sorry, very bad pun)
and get into some three-space nitty gritty, starting with...


_____Section Two - The 3D Cartesian Coordinate System


Okay, you're undoubtedly already used to doing graphics in 2D using the X
and Y axes; it's what you've been doing in algebra all along. Well using
X and Y is the 2D standard of the Cartesian coordinate system, and likewise,
we need to add another axis of depth for 3D - The Z axis. But where does
this Z axis go?

Well X and Y are perpendicular to each other in the 2D plane. It's the same
as the plane of your screen, for example. Well in 3D, we can get depth by
putting the Z axis perpendicular to that entire plane altogether, going
either into or out of your screen.

A common term for a line or plane perpendicular to another plane is
"orthogonal". That is, we want a Z axis that's orthogonal to the XY screen
plane.

But in which direction? It can either go into or out of the plane, so which
one is right? Well this is really entirely by convention... the standard
way is to use a "right handed" orientation. What's a right handed
orientation? Okay, hold up your right hand in front of your face. Point
your thumb toward your nose. Now look at your fingers... they curl
counterclockwise. This is the basis behind a right-handed system. When you
have the XY plane, the two axes for X and Y are to the right and going up,
respectively. So if you start your hand at the first axis, X, and curl your
right hand's fingers in the direction of the second axis, Y, your thumb
will point out toward your nose.

So the Z axis goes out of the screen, toward you, by a right-handed system
(for the same reason, if you used the YX plane instead of the XY plane,
the curl would be in the opposite direction and Z would go into the screen
instead).

Cheesy ASCII graph....

Y
|
|
| Screen Plane
|
|
-------X
/
/
/
Z


The graph above is the way we're going to do it in this doc. All 3D
Cartesian really is, is just the same coordinate system you're used to, but
with the added dimension so you can locate a point anywhere in space and
not just on a plane. End of story (You might find later that other
coordinate systems, like spherical and cylindrical, can help out with some
routines... but I'll go over those in time).

Short section, eh? There's more, but not much. :-) Well, now with the 3D
system in your grasp, it's time to put it to use, and get some 3D points to
show up on-screen.


_____Section Three - Perspective Projection


Now we've got this nice XYZ spacial coordinate system... but how do we take
advantage of it and get some stuff on-screen from it? In 2D, it was easy...
you just used the same X axis as in your coordinates, and the Y axis was the
same only flipped upside down (since as you know, in coding it's easier if
the Y axis goes down, for calculation reasons). But how are we going to
take this Z thing into account and make things look right?

The way we do it is by "Perspective Projection". Perspective is a pretty
basic idea which you see continuously in your life.... Things further away
from you look smaller than things closer up. Pretty obvious fact that you've
lived with throughout your existence. :)

Well all we have to do is turn that simple principle into math...

English way: "Things further away from you look smaller than things
closer up."

Math way: "The projected size of any object in the eye is inversely
proportional to its distance from the eye".

It's more "math-like", but still makes sense, right? They both mean the
same thing more or less, but the second phrase is more easily turned into
equation form... the kind we can put into our code.

Okay, by our ASCII graph of the coordinate system, your eye lies right along
our Z axis, staring down in the negative direction (the Z axis goes out of
your screen, and you're looking in). So an object's, or point's, distance
from the eye is going to have a lot to do with the Z coordinate of that
point.

So the first thing we need to settle is, if our eye (or in our case, the
video screen) is sitting somewhere along the Z axis.... where along the Z
axis is it? In truth, this is entirely up to you... the screen is just
a "camera", and we're trying to find a convenient place for it to sit.
All we know is that it goes along the positive Z axis.

It turns out for calculation reasons that a very effective place to put the
camera is at Z=256. You'll see why in a minute... (and no, none of this
is carved in stone. Cameras can go anywhere and view in any direction,
it's just that the math gets more complicated).

Okay, now we've got the camera at (0,0,256) and pointing in the negative
direction. That means that our Cartesian origin is exactly 256 units away
from us, right along our line of sight. The next thing we need to make up
is a frame of reference for X and Y.... How big, visually, is a unit along
X or Y? Well we can use any set of values we see fit, but just to make it
easier on the brain, we want to use something we're already used to...
something intuitive...

How about 1 pixel? It's easy to compute, since it's the same as your
screen plane. Okay, so we know _somewhere_ viewable along this axis, X and
Y are going to mean 1 pixel units.

But where along the Z axis? If things get larger as they get closer and
smaller as they get further away, where's a good distance to say where the
unit/pixel plane sits?

Heck, why not just use the Z=0 plane? After all, we know the origin is
perfectly visible (256 units down the -Z axis) so the origin is easily in
reach, and probably will make it easy to calculate our perspective viewing.

Okay, we've got all we need to set up some perspective equations. Now let's
do some simple examples in our heads so we can find out where these
equations are coming from...

In 320x200 resolution (you can use any resolution you want, really, but this
is just for demonstration), the center point of your screen is at (160,100).
Makes sense, and you're probably very used to this by now. Well, we're
viewing along the -Z axis, so that's where our axis is going down. Right
along the line of that pixel. No up, no down, no left, no right. That's it.

Now we know that at Z=0, Every X and Y unit mean one pixel. So where would
(5,5,0) be positioned? Well, it's the same as our screen plane, so there's
no distance adjustments to do. It's 5 to the right of the origin, so
ScrX=160+5 = 165. And it's 5 above the origin, which is in the opposite
direction of our "screen plane" Y, so that's ScrY=100-5 = 95. You've got
your projected point.

So what to do with points not on Z=0? Howsabout (5,5,128)? Well if we
think about it, Z=0 is 256 units distance from the screen. Well Z=128 is
256-128 = 128 units distance from the screen. Exactly half of the distance.

Half the distance? That makes it twice the size! :-) So instead of adding
5 to X and subtracting 5 from Y, you'd use 10 instead for each axis. That
would give us screen coords (170,90) for this point. Same 3D X and Y as in
the first example, but the fact that it's so much closer makes it look
further from the center of the screen... the essence of perspective. :)

Now in general, our equations go something like this

ScrX = ( Lens*X / Distance ) + CenterX
ScrY = CenterY - ( Lens*Y / Distance )

I'll explain each piece. First, ScrY _would_ be the same basic equation
as ScrX, except that we have to do the subtraction instead because Y goes
down onscreen, not up. Anyway, we have a few variables here...

ScrX and ScrY are the screen point of the 3D point we're trying to project.
I figure you guessed that one already. :-)

Distance is the distance of the point from the eye. As we discussed before,
this has a _direct_ correlation to the Z coordinate of our 3D point. Now
since we're on the positive Z axis but viewing in the negative direction,
and we know that at Z=0 the distance is 256, then any point where Z > 0
will have a distance < 256. Likewise, if Z < 0, the point will be further
away than the origin, and distance > 256. This makes distance a very
simple value:

Distance = (256-Z)

Pretty easy, eh? :-) Now, Lens is a multiplier used to give your projection
the right "field of view" that you want. You can manipulate Lens to give
the projection a very narrow range of vision, or to give it the classic
"wall-eye" view as well. But in general, a really messed-up field of view
will make people want to vomit. It's not natural.

So we want to set it to something that will "feel" natural. Well, from our
calculations and the fact that we want a unit/pixel relationship at Z=0,
we can get our Lens factor immediately... 256. It's also a nice multiplier,
as you can use bit-shifts to get it, instead of a costly MUL instruction.

And finally, CenterX and CenterY are positive integers that match to the
center X and Y point onscreen; 160 and 100 in this case (the Center value
for any resolution is just the resolution/2, which makes sense).

So, from there, our equations boil down to

ScrX = ( 256*X / (256-Z) ) + 160
ScrY = 100 - ( 256*Y / (256-Z )

The first thing you should recognize is that we don't need to calculate
(256-Z) twice. Also, if you use fixed point, you can calculate the divide
only once as well, but that's for a later article (I'm not focusing on
optimizations in this one). Anyway, let's work out one of our previous
examples using these equations and see if we get the same result. How about
the (5,5,128) one... let's see...

Distance = 256-Z = 256-128 = 128

ScrX = ( 256*X / 128 ) + 160
= ( 256*5 / 128 ) + 160
= 10 + 160
= 170

ScrY = 100 - ( 256*Y / 128 )
= 100 - ( 256*5 / 128 )
= 100 - 10
= 90

Yup, same answer! And these equations go quite easily into code. :-)

Now you'll find that using straight integer math with these equations will
work well at first, but there are added advantages to using fixed point
math as well. If you're familiar with fixed point, you should have few
problems trying to adapt this to it, speeding things up in the process.
If you're not familiar with it, don't sweat it; I'll cover it in an
upcoming article.

Well, looks like that's the end of this article... make sure to check out
the sample source in Pascal and C within this supplement, and try a few
experiments out on your own! You'll want to get very familiar with
projection as we get into more advanced stuff later on... and since it's
at least a week until the next DemoNews, you've got the time, so take
advantage of it. :-)

Next issue, it'll be time to start on our path to not just getting these
points up there, but getting them to move. Straight movement left, right,
up, down, in and out is easy... you just add or subtract from X, Y, or Z.
But rotation, that's another story.

Until next time,


Chris Hargrove
a.k.a. Kiwidog, of Terraformer & Hornet
Coding & Organization
email: <kiwidog@vt.edu>

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT