Rotating Points, Antialiasing and Smoother Animations
=====================================================
Tutorial By Tom Rathbone 23/8/99
======================== =======
E-mail: tom.rathbone@advanced-internet.co.uk
====== chilliwilli_99@yahoo.com
Website: http://come.to/chilliwilli/
=======
Requirements: QB compiler (pref QBasic 4.5)
============
This is the second of my tutorials on 3d routines in QB. In the last tutorial i covered the basics of converting a 3d co-ordinate into a relevant screen position and gave you a subroutine for PSETing 3d points. As i said at the start of the first tutorial these are the techniques which i use and which i found easiest to learn/teach. I make no claims about these techniques or any of my code being the best way to program 3d in QBasic.
In this tutorial i shall attempt to teach the maths involved in 3d rotation as well as two graphic techniques commonly used in 3d and games programming.
All i ask in return for this information is that you send me a copy of anything you produce using the stuff contained in this tutorial.
Rotations.
=========
This is where a good knowledge of trig and some knowledge of matrices could come in help full. The rotation maths which i use came straight out of my A-level matrices text book. What we must do is multiple our co-ordinate matrix by a rotational transformation matrix which i shall tell you in a minute. There are three possible ways to rotate an object in 3d space. Once about each axis, X,Y and Z. This means that we need to apply three seperate transformations to our 3d co-ordinates.
To rotate a 2d co-ordinate about ø degrees we would use the following matrix multiplication.
[x] [cos ø -sin ø] [x2]
[y]*[sin ø cos ø]=[y2]
Where x and y are the original co-ordinates, x2 and y2 are the new co-ordinates and ø is the angle to rotate in radians.
A note on radians: For those who don't know already. One revolution is equal to 2PI radians. This means that one revolution/360degs is equal to approximately 6.218rads.
To convert degrees to radians use the following:- d/360 *2PI =r where d is degrees, r is radians and PI is the mathematical constant approx equal to 3.142.
If you haven't fully understood the above then don't worry. Here is the same sum in QB.
x2 = (x * COS(rotZ!)) - (y * SIN(rotZ!))
y2 = (x * SIN(rotZ!)) + (y * COS(rotZ!))
This will rotate any point we subject to the formula about the Z axis by rot! radians. We know have the rotation formula for one of our axis now the other two.
x3 = (x2 * COS(rotY!)) - (z * SIN(rotY!))
z2 = (x2 * SIN(rotY!)) + (z * COS(rotY!))
y3 = (y2 * COS(rotX!)) - (z2 * SIN(rotX!))
z3 = (y2 * SIN(rotX!)) + (z2 * COS(rotX!))
If we input X,Y and Z in to the formulas we get back the now rotated values X3,Y3 and Z3. Now to use these formulas. There are two ways we can do this. Either we can create a function which we can use to return the rotated values of the points or we can include then in our graphic routines. Both methods have their advantages though i prefer to use the second option. Lets apply the formula to the XPSET subroutine we created last tutorial and make a new subroutine which we shall call XRPSET.
Declare the sub in the main module.
----------------------------------------------------------
DECLARE SUB XRPSET (x3d!,y3d!,z3d!,rotx!,roty!,rotz!,col%)
----------------------------------------------------------
Create a new sub routine and input the following.
----------------------------------------------------------
SUB XRPSET (x3d!, y3d!, z3d!,rotx!,roty!,rotz!,col%)
zoom = 200
depth = 40
x2! = (x3d! * COS(rotZ!)) - (y3d! * SIN(rotZ!))
y2! = (x3d! * SIN(rotZ!)) + (y3d! * COS(rotZ!))
x3! = (x2! * COS(rotY!)) - (z3d! * SIN(rotY!))
z2! = (x2! * SIN(rotY!)) + (z3d! * COS(rotY!))
y3! = (y2! * COS(rotX!)) - (z2! * SIN(rotX!))
z3! = (y2! * SIN(rotX!)) + (z2! * COS(rotX!))
x2d% = zoom * (x3! / (z3! + depth)) + 160
y2d% = zoom * (y3! / (z3! + depth)) + 100
pset (x2d%, y2d%), col%
END SUB
----------------------------------------------------------
Using the subroutine
====================
Now take the cube plotting example from the first tutorial and replace XPSETs with XRPSETs.
e.g.
XPSET 10,10,10,4
becomes
XRPSET 10,10,10,rotx!,roty!,rotz!,4
Try setting different values for the rotations and see how each axis effects the cube. You might like to see if you could write a program to show the cube spinning on all the axis.
Anti-aliasing
=============
This technique is very important when working in low resolutions such as Mode 13. It allows you to create an effect of smoother animation. Take for example the common starfield. Due to the size of the pixels in Mode 13 the animation can seem jumpy and the transitions between pixels can seem sudden. What is needed is a way to plot pixels between the integer values that are the pixel's screen co-ordinates. The answer as i'm sure you have already guessed is to use anti-aliasing. When you use anti-aliasing and try to set a pixel between integer values it calculates the area covered of each of the four pixels it could possible be touching. The proportion of the pixel covered relates directly to intensity of that pixel. In effect the dot is being shared between the pixels using area to decide how much of the intensity each pixel takes. Again don't worry if you don't understand whats going on. To see antialiasing in action see my 3D waves demo on my website (URL at top of tutorial).
I shan't bother deriving the maths behind this routine as I never bothered to myself in the first place and frankly once you have a working version of this sub you don't need to worry about how it works.
NB. Antialiasing will only work once a gradient palette has been set up.
I shall explain how to use the palette statement properly in another tutorial but for now use this code to set up a gradient palette in mode 13.
--------------------------------------------------
FOR p = 1 to 63
PALETTE p, p + 256 * p + 65536 * p
NEXT
--------------------------------------------------
This will set colours 1 to 63 up as a greyscale.
The Antialias Routine
=====================
--------------------------------------------------
SUB aaset (x!, y!, c%)
x1% = FIX(x!)
y1% = FIX(y!)
x2% = x1% + 1
y2% = y1% + 1
xm! = x! - x1%
ym! = y! - y1%
c1% = (1 - xm!) * (1 - ym!) * c%
c2% = xm! * (1 - ym!) * c%
c3% = (1 - xm!) * ym! * c%
c4% = xm! * ym! * c%
pset (x1%, y1%), c1%
pset (x2%, y1%), c2%
pset (x1%, y2%), c3%
pset (x2%, y2%), c4%
END SUB
--------------------------------------------------
Smoother Animations
===================
One technique is to wait for the screens vertical retrace before drawing to or clearing a screen to do this simply use the following line.
WAIT &H3DA, 8
Another technique is to use a screen buffer. This is merely going to be a brief introduction to screen buffers as i shall cover them in full in a later tutorial i have planned based on the DirectQB library.
For now i shall simply show you how to use the PCOPY function in QBasic.
This function will only work in Screen 9. So unfortunately you will be limited to 16 colors and 640*480 resolution.
Set the screen mode to 9 as follows
SCREEN 9,0,1,0
This means that all graphic and text commands will write to screen page 1 and the monitor will show screen page 0. Now we can draw all of our graphics and when ready we can copy page 1 onto page 0 and it will appear as if all graphics are drawn in an instant.
when we are ready to display a frame we must use the following line.
PCOPY 1,0
=====================================================
Thats it for this tutorial. More to come very soon.
=====================================================