Lab09
Lab09
Lab 09
MATERIAL COVERED
• Defining functions with parameters and results
Notes:
• Make sure your TA has recorded your mark before leaving.
• Remember that you only need to do one of the three levels (Bronze, Silver, or Gold) to obtain 2/2
on the lab, although you are encouraged to do as many as you can.
• In this lab, the Gold exercise cannot be done without first completing the Silver exercise. The
Bronze exercise is independent.
• The Silver and Gold exercises are not as difficult as they appear. The Silver exercise can be done
by adding only 9 lines of code. The Gold requires only 10-15 more.
3D! Computer screens are not capable of doing real 3D – they’re flat!
Anything “3D” you see on a computer screen is an illusion –
completely faked. There are a lot of great ways to fake 3D, and to trick
people’s eyes into seeing 3D. One of the most fundamental ways is by
simulating perspective:
• Objects that are further away appear to be smaller
• Objects that move further away appear to converge.
Look at the railroad image. Because of perspective, the tracks appear
to get closer together as they get further away. The point where they
appear to meet each other in the distance is the “vanishing point”.
Also, the trees in the distance are not much bigger in the picture than a
single railroad tie in the foreground. Our brains pick up on these cues
and we know that this is actually a 3D scene because we know that the
tree is really much bigger than the tie.
We will draw a 3D grid of “spheres” (circles when drawn in 2D), as shown in the image on the
next page. Start with the file Lab9SilverTemplate.pde, which will provide the necessary
constant mentioned below, as well as the setup and draw functions.
We have used “flat” 2D (x,y) coordinates a lot. To use 3D, we need to add a z
coordinate to get (x,y,z). Look at the diagram to the right. Our usual X axis goes to
the right across your screen, and the Y axis goes downward on your screen. The
new Z axis goes straight into your screen, away from you. These are virtual
coordinates – they’re not measured in pixels, and our screen does not really have a
z axis, so what we need to do is project our virtual 3D points (x,y,z) onto normal
2D canvas points (px,py). (“px” means “projected x” or maybe “pixels x”).
[Note: Processing has a built-in way to handle 3D points, but we won’t use that here. We’ll stick to
what we know, and do it ourselves. You can investigate Processing’s 3D capabilities on your own,
if you’re interested.]
The basic idea behind perspective projection is to simply divide the x and y coordinates by the z
coordinate to get px and py. That way, the larger the z coordinate is, the more px and py will
decrease, and appear to move toward the “vanishing point” (0,0) – as the railroad tracks did.
But if we just divide by z itself, our px and py coordinates
will get very small very quickly and you’ll only see a
cluster of dots at the center of the screen. Use the constant
PERSPECTIVE (set at 0.002) to control the amount of
perspective (how fast px and py converge as z gets larger).
Divide x and y by PERSPECTIVE*z instead of just z to get
px and py. Object sizes (such as the diameter of a circle or
sphere) also must be divided by PERSPECTIVE*z in the
same way, since objects also must appear smaller when
they are farther away.
Note that because PERSPECTIVE is a very small number
(0.002), any z value less than 500 will cause things to
grow, not shrink. Our illusion works best when we look at
objects far away “into the screen”, not directly in front of
our noses, and so we’ll usually want z values that are quite large compared to x and y.
Write a function void drawProjectedCircle(float x, float y, float z, float
diam) which draw a “sphere” (a small circle, really) with its centre at the 3D point (x,y,z), and a
diameter of diam. Use the projection method described above to modify x, y, and diam before
drawing the circle. Add an offset so that the “vanishing point” (0,0) is moved to the centre of the
canvas.
There is one potential problem here. If you happen to get z=0, you will divide by 0 and the result
will be Infinity! (At z=0 things are so close that they appear to be infinitely large – they’re right
in the middle of your eyeball! And z<0 is even worse – beyond infinity?) You need what is called
a “clipping plane” in graphics. Your drawProjectedCircle function shouldn’t attempt to draw
(or even do the calculations) for any circle unless z>0! Just ignore such circles. They're “behind
your head” and you can't see them.
Now write a function void drawDotGrid(int minValue, int maxValue, int spacing,
float diam), to draw a three-dimensional cube of spheres (circles) with diameter diam units.
Use three nested for loops to generate a cube of all possible (x,y,z) points in a grid. Every one of
the three coordinates should be a value between minValue and maxValue, spaced spacing units
apart. For example, drawDotGrid(-35,35,10,1) should use the values -35, -25, -15, -5, +5,
+15, +25, and +35 for x, y, and z, in all possible combinations (a total of 8*8*8 = 512 points). This
function should use drawProjectedCircle to do all of the drawing. Since we want large z
values, and we also want to be able to use the mouse to zoom in and out, simply add mouseX to
the z coordinates before you call drawProjectedCircle to draw the circles.
Once your Silver exercise is working, add 3D rotations to
it, controlled by the mouse!
First, add a constant “zoom factor” of 150 to all z co-
ordinates, instead of mouseX as was done in the Silver
program. We’ll need the mouse to do other things, and 150
will “push” the cube just far enough “into the screen” to
give it a nice size. You could experiment with other
values, too. (You were going to use a named constant,
right?)
To rotate a 2D point (x,y) an angle of θ radians around the
centre of rotation (0,0), giving a new point (rx,ry), the
math is not very complicated (rx is short for “rotated x”):
𝑟𝑥 = 𝑥 𝑐𝑜𝑠(𝜃) + 𝑦 𝑠𝑖𝑛(𝜃)
𝑟𝑦 = −𝑥 𝑠𝑖𝑛(𝜃) + 𝑦 𝑐𝑜𝑠(𝜃)
To rotate a 3D point (x,y,z) around the X axis (think of grabbing the X axis and spinning it), just
leave the x coordinate unchanged, and use the above formulae with y and z instead of x and y.
Similarly, to rotate the point around the Y axis, use the formulae with x and z, leaving y untouched.
Using the formulae exactly as they are above will spin around the Z axis.
Write a function void rotate(float theta, float a, float b) which will rotate a point
(a,b) around the point (0,0) by an angle of theta radians, using the formulae above, giving a new
point (newA,newB). This function could be used with x and y, or x and z, or y and z, so let’s just
call them a and b to make them generic. Now we run into a problem with returning results from
functions – only one value can be returned, and we need to return two (newA and newB). There is a
way to solve this, but it’s not covered in COMP 1010, so instead create two global variables newA
and newB and put the answers there. It’s not the “proper” way to do it, but it will work.
Now modify the drawDotGrid function to use the mouse to spin the cube. Before drawing each
circle, use your rotate function to spin it mouseX/100.0 radians around the Y axis, and then
(height-mouseY)/100.0 radians around the X axis. (If this seems backwards, just think about it
and visualize it – it’s correct.) The spinning must be done before you add the “zoom” factor of 150
to the z co-ordinate.
You should now be able to “grab” the cube and spin it with the mouse.