Computer Science Canada [Tutorial] 3D graphics

Author:  Windsurfer [ Mon Oct 16, 2006 9:14 pm ]
Post subject:  [Tutorial] 3D graphics

I have written this as more a discussion with myself... or towards an imaginary person. Not really a "tutorial" wording.

Okay, so there comes a point in a programmers course (once we learn graphics) that we sit down and think, "I should make something awesome. Something mind blowing. Something... three-Dee!!!"
*cough* okay, well, at least i did.

Of course, then you wonder... how can you draw 3D? Draw.Line only accepts things in 2-D.... hrm.

Well, what you could do, is visualise the screen as a window (well, at least that's what i did) and realise that what you see in the window is simply a representation of a 3D world.

So, if you think of what you have been drawing in 2D as simply the "glass" on this window, then the new dimension would be the axis going into and coming out of the screen. But how do you get your monitor to draw that axis?

Well, obviously, you can't directly . So you figure out how this new dimension related to your screen.

When you look at the real world, everyone knows that as objects get further from you, they appear to diminish in size. The amount they get smaller, one may have noticed, is not linear, meaning if an object goes x distance from you, it will not be x size. Instead something else happens.

If an object moves z amount away from you, it will appear 1/z (that's one divided by zed). Let me explain in detail.

If you were to draw a ball (okay, a circle) at point x,y on the screen, you would simply use some code that expresses how do draw this ball.
Eg.
 code: const BALL_SIZE := 20 var x ,y: int := 100 %arbitrary place on screen proc drawBall (x : int, y : int) Draw.FillOval(x, y, BALL_SIZE, BALL_SIZE, black) end drawBall drawBall(x,y)

Now, what that does should be fairly obvious. It draws a circle at (x,y) or 100,100.

Now, what we really want to do here is draw a ball at... say... (100,100,10). That 10 would be the z coordinate.

Well, it's quite simple to figure out where the ball would be on the screen, actually. It's really basically dividing the x and y coordinates by the z coordinate.

Surprised? All this talk about 3D and that's it?
Sort of. Well, yes, but more like that's the foundation.... well, you'll see.

So you start off with this. You apply this new idea to drawing the ball, and you can find that you can make a procedure that can spit out the x and y coordinates.
 code: procedure make2D (x, y, z : real, var temp_x, temp_y : int)     temp_x := round (x / z)     temp_y := round (y / z) end make2D

Well, cool. Now lets draw the ball at 100,100,10
10 is the new axis, the z cooridinate.

 code: const BALL_SIZE := 20 var x, y : int := 100 var z : real := 10 function size2D (size, z : real) : int     result round (size / z) end size2D procedure make2D (x, y, z : real, var temp_x, temp_y : int)     temp_x := round (x / z)     temp_y := round (y / z) end make2D proc drawBall (x : real, y : real, z : real)     var temp1, temp_2 : int     make2D (x, y, z, temp1, temp_2)     var size : int := size2D (BALL_SIZE, z)     Draw.FillOval (temp1, temp_2, size, size, black) end drawBall drawBall (x, y, z)

Notice how i made the size2D function by simply dividing the size by the z coordinate. It's actually the same idea.

Now, you may be thinking it's not that great... well, visually it's not. Yet. Let's add some control to it to make it more interesting.

 code: Mouse.ButtonChoose ("multibutton") const BALL_SIZE := 20 var x, y : int := 100 var z : real := 10 var temp_zvel : real := 0 % velocity of the ball function size2D (size, z : real) : int     result round (size / z) end size2D procedure make2D (x, y, z : real, var temp_x, temp_y : int)     temp_x := round (x / z)     temp_y := round (y / z) end make2D proc drawBall (x : real, y : real, z : real)     var temp1, temp_2 : int     make2D (x, y, z, temp1, temp_2)     var size : int := size2D (BALL_SIZE, z)     Draw.FillOval (temp1, temp_2, size, size, black) end drawBall var button : int := 0 loop     Mouse.Where (x, y, button)     if button = 1 then         temp_zvel += 0.03     end if     if button = 100 then         temp_zvel += -0.03     end if     z += temp_zvel     drawBall (x, y, z)     delay(50) end loop

Whoa! What just happened?
Well, i simply made the x and y coordinates of the ball the same as the mouse... and I made the z coordinate a variable that is controlled with the mouse buttons. That's all. Can you see how it's 3D now?

This is just the beginning. I will folow up with drawing a cube later.

Author:  Cervantes [ Tue Oct 17, 2006 9:09 am ]
Post subject:

Nice, Windsurfer!

The style of the tutorial is good. The most important thing with tutorials is that they are A) correct and B) easy to read. There are many different ways of achieving B), and you've got one of them.

One comment: Your make3D "procedure" should really be a function.
 code: procedure make3D (x, y, z : real, var temp_x, temp_y : int)     temp_x := round (x / z)     temp_y := round (y / z) end make3D

That kind of blatant side-effect is just frightening to me. Instead,
 code: type pos2d :     record         x, y : real     end record     function make2D (x, y, z : real) : pos2d     var p : pos2d     p.x := x / z     p.y := y / z     result p end make2D

You'll notice I also changed the function name to "make2D", as I think that's a better reflection of what we're doing. I also used reals to represent the coordinates returned by the function, to allow for better accuracy. These values can be rounded when drawing.

 Author: Windsurfer [ Tue Oct 17, 2006 3:29 pm ] Post subject: I actually tried to keep the coding as dumbed down as possible... without getting into any horrible coding practices. Admittedly, a record in that situation works very well, however, it may confuse a lot of beginners. And yes, i did mislabel that procedure... I'm going toi change it now.

 Author: Cervantes [ Tue Oct 17, 2006 4:35 pm ] Post subject: I think it reasonable to assume that anyone wishing to tackle 3D graphics has types under their belt. And I mean the sane people wishing to tackle 3D graphics. There's a lot of noobs who would want to tackle 3D graphics before even learning 2D. Besides, creating procedures that modify the values of the variables you give them is a horrible coding practice. Sure, it works, but it only works because you wrote that procedure, and you know exactly how it manipulates the variables. If someone wants to use your subroutine, that someone has to also know exactly how it manipulates the variables it's given. He wouldn't want to use your procedure because he's unsure of what awful things your procedure might do to his precious variables. A function, however, doesn't modify the state of the program. Instead, it computes new values. This last statement is fundamental to functional programming (not programming with functions in Turing, but the paradigm itself -- programming in languages like LISP, Scheme, O'Caml, and Haskell). In pure functional languages, we don't even have variables. That might seem like it'd be a bad thing, but it's not, since functional languages are based on the computation of new values instead of the modification of old values.

 Author: Clayton [ Tue Oct 17, 2006 4:41 pm ] Post subject: Well really, a beginner shouldnt really be going straight into 3D graphics, so I personally, would keep the coding as neat and tidy as possible. Some tips: Formatting - Could use a lot of work really, I found it kind of hard to know exactly where you might find something, use headers, titles etc to your advantage here. Explanation - I kinda got lost on some of the topics, 3D graphics requires a lot of explanation to do something well, and needs to be explained well in order for someone to absord and learn properly. Tips and Hints - Some people find some things help with things even if they aren't really meant to be. Maybe give some of these if you have any (these include preferences on something etc). Perhaps a Beginner, Intermediate, Advanced sort of level Tutorial would be good too So far though, good stuff! EDIT: DOH! Cervantes beat me to it, ^^What he said^^

 Author: [Gandalf] [ Tue Oct 17, 2006 5:52 pm ] Post subject: Freakman wrote:^^What he said^^ And yet you and he said two completely different things. O_o Cervantes wrote:Besides, creating procedures that modify the values of the variables you give them is a horrible coding practice. Sure, it works, but it only works because you wrote that procedure, and you know exactly how it manipulates the variables. If someone wants to use your subroutine, that someone has to also know exactly how it manipulates the variables it's given. He wouldn't want to use your procedure because he's unsure of what awful things your procedure might do to his precious variables. A function, however, doesn't modify the state of the program. Instead, it computes new values. Definately an understandable point, but then, you're saying that passing by reference is always horrible coding practice? Hrm? Or is it just that it doesn't follow the model behind functional programming...

 Author: zylum [ Tue Oct 17, 2006 6:19 pm ] Post subject: doh! i was actually in the process of writing a 3d tutorial windsurfer > all but your second code snippet have errors / warnings.

 Author: Cervantes [ Tue Oct 17, 2006 6:53 pm ] Post subject: [Gandalf] wrote: Cervantes wrote:Besides, creating procedures that modify the values of the variables you give them is a horrible coding practice. Sure, it works, but it only works because you wrote that procedure, and you know exactly how it manipulates the variables. If someone wants to use your subroutine, that someone has to also know exactly how it manipulates the variables it's given. He wouldn't want to use your procedure because he's unsure of what awful things your procedure might do to his precious variables. A function, however, doesn't modify the state of the program. Instead, it computes new values. Definately an understandable point, but then, you're saying that passing by reference is always horrible coding practice? Hrm? Or is it just that it doesn't follow the model behind functional programming... Allowing subroutines the ability to modify the values of the variables you give them is horrible. This is a concept that is strictly forbidden in pure functional programming languages. But that's not the reason I condemn it in Turing. I condemn it in Turing because it is a terrible idea to give so much of your control away. I want to clarify with you: [Gandalf] wrote:you're saying that passing by reference is always horrible coding practice Are you referring to passing a variable to a subroutine as we usually do (passing the value of the variable as an argument such that the parameters within the subroutine become that value) or are you referring to actually giving the memory address of the variable to the subroutine so the subroutine can go ahead and butcher that location in memory if it wants to? zylum wrote:doh! i was actually in the process of writing a 3d tutorial See, this is why you should all coordinate in the Improving Tutorials thread. Author: Windsurfer [ Tue Oct 17, 2006 7:19 pm ] Post subject: D'Oh! i hate that... supid C programming... and i thought i checked everything... And about this function thing... Weird, my programming teacher is pushing passing pointers. He would rather we had functions modify those, and return an int for error checking. (Maybe that's just a C thing?) And I'm sorry I did this tutorial before you... perhaps i could see what you have written/done so far, and we could collaberate?

 Author: [Gandalf] [ Tue Oct 17, 2006 7:20 pm ] Post subject: Cervantes wrote:I want to clarify with you: [Gandalf] wrote:you're saying that passing by reference is always horrible coding practice Are you referring to passing a variable to a subroutine as we usually do (passing the value of the variable as an argument such that the parameters within the subroutine become that value) or are you referring to actually giving the memory address of the variable to the subroutine so the subroutine can go ahead and butcher that location in memory if it wants to? The latter, of course. Yes, there is the potential for unsafe/unpredictable code, but that doesn't change the fact that it's useful on occassion, right? Or is it completely pointless and should be avoided at all costs? Meh, I don't really have anything else to say, but it just surprised me that you would completely condemn such a... key ability in programming languages.

 Author: Cervantes [ Tue Oct 17, 2006 8:30 pm ] Post subject: [Gandalf] wrote: Meh, I don't really have anything else to say, but it just surprised me that you would completely condemn such a... key ability in programming languages. A key ability? What does this approach offer that functions (returning user defined types if necessary) do not?

 Author: Danjen [ Wed Nov 22, 2006 11:32 am ] Post subject: Wait a minute Hold on, I'm kind of a noob in turing, and I was wondering, why would you use functions as opposed to procedures, and why would you use records. Also, what should / would variable Z be set to in the previous program?

 Author: Cervantes [ Wed Nov 22, 2006 2:36 pm ] Post subject: Much of the time we write subroutines to compute a value from some given values. We might, for example, write a subroutine to compute somebody's taxes. It makes sense to use a function for this. The value returned by the function is the amount of tax owed. On the other hand, using a procedure makes no sense, because it doesn't return a value. Instead, you'd have to directly output the value, which makes the procedure kind of silly, because you never really know what the taxes owed are. The only way you could know is if you're looking at the output screen. Your program certainly doesn't know. But it could know, using procedures, if you allow the procedure to modify some global variables. This has two problems: first, if the procedure can modify that global variable that represents tax owed by person A, how much more power does it have? What's to stop it from modifying other global variables? It could ruthlessly change the state of a program. Second, while coding, we have to distinctly remember what variable is being changed by the procedure, then refer to it. There's none of that confusion with functions. Using records is often a good idea because it groups data that should be grouped. For example, consider having a bunch of points. Each point has an x and y component. We could use two arrays, each of length n, one for x values and one for y values. However, this is messy. When working with this, we have to assume that the i'th element of the x array and the i'th element of the y array are supposed to go together. What if things get out of order? Using records allows us to have a single array of points, where each point has an x and y value. No confusion here.

 Author: Wolf_Destiny [ Wed Nov 22, 2006 3:34 pm ] Post subject: Since this thread got bumped... ...I've been making a 3d game - or at least the engine for it. I've got heightmaps, walkable zones and room boundaries. Different polygon types (character and terrain) and external model files. Since it also have rotation I needed a sorting algorithm. The one I'm using (a rough approximation of the Painter's Algorithm (or depth sorting)) isn't working very well for me; at all. I've seen programs with sorting in them and I've tried to understand Zylum's 3d engine and its sorting. Unfortunately as far as I can tell it just sorts based on the average depth. Which is what mine attempts to do. My question is whether anyone knows a good yet fast sorting algortihm I could use, or if depth sorting just doesn't work for quadrilateral polygons and if it only works for triangles. ~Wolf_Destiny

 Author: [Gandalf] [ Wed Nov 22, 2006 5:11 pm ] Post subject: Cervantes wrote:[Gandalf] wrote: Meh, I don't really have anything else to say, but it just surprised me that you would completely condemn such a... key ability in programming languages. A key ability? What does this approach offer that functions (returning user defined types if necessary) do not? Convenience, maybe? Also, by "key" I meant something more along the lines of "common". Wolf_Destiny, as I recall zylum's 3D engine was quite good. Why don't you try asking him for a bit of help in understanding the sorting?

 Author: BenLi [ Thu Jan 25, 2007 12:43 pm ] Post subject: RE:[Tutorial] 3D graphics can somone expand upon this tutorial? I understand the ball thingie, but applying it into any sort of real situation would be harder. Zylum, why don't you explain your entry for the "20 lines" contest?

Author:  CodeMonkey2000 [ Wed Jul 18, 2007 6:01 pm ]
Post subject:  RE:[Tutorial] 3D graphics

I don't get it. Why does the x and y values change as you move along the z axis? Why does the z value affect the position of the ball? It should only affect the size (in my mind anyway).
 code: Mouse.ButtonChoose ("multibutton") const BALL_SIZE := 20 var x, y : int := 100 var z : real := 10 var temp_zvel : real := 0 % velocity of the ball function size2D (size, z : real) : int     if z <= 0 then         result round (size)     end if     result round (size / z) end size2D proc drawBall (x : real, y : real, z : real)     var size : int := size2D (BALL_SIZE, z)     Draw.FillOval (round (x), round (y), size, size, black) end drawBall var button : int := 0 loop     Mouse.Where (x, y, button)     if button = 1 then         temp_zvel += 0.03     elsif button = 100 then         temp_zvel += -0.03     else         temp_zvel := 0     end if     z += temp_zvel     drawBall (x, y, z)     delay (50)     cls end loop

Is this considered 3D?

 Author: HellblazerX [ Thu Jul 19, 2007 10:35 am ] Post subject: RE:[Tutorial] 3D graphics CodeMonkey, you're right in that the z-value doesn't affect the position of the ball. What it does affect is the size of the ball, as well as the size of the space between the z-axis and the ball. As the ball gets smaller, so does that space, and the ball appears to be moving closer to the z-axis, when in reality it isn't.

 Author: CodeMonkey2000 [ Thu Jul 19, 2007 2:47 pm ] Post subject: RE:[Tutorial] 3D graphics Why does the space between the z-axis and the ball also appear to be getting bigger/smaller?

 Author: zylum [ Thu Jul 19, 2007 3:06 pm ] Post subject: RE:[Tutorial] 3D graphics perspective

 Author: Gooie [ Thu Jan 03, 2008 1:02 am ] Post subject: Re: [Tutorial] 3D graphics Ok, so far this tutorial hasn't helped at all, actually it's really confusing. I think It needs a serious revamp. I'll try to write some of it, but anyone feel free to toss in any knowledge. I am just starting with 3D. So I only know a few of the subjects listed below. I've seen some 3D engines on CompSci, and I think that It would be great if some of those people could give up there secrets. So here are some areas needed to be covered. All of them together give the illusion of depth. -relative size. Objects further away appear smaller. -relative speed. Objects further away appear to move slower. -z-axis blocking. Objects nearer appear in front of objects further away. -haze. Objects further away are obscured by particles in the air. -perspective. The apparent dimensions of an object taper as they get further away. Relative size and speed are also a result of perspective. -light reflection/shading. The way light reflects from an object indicates its three-dimensional shape, and its position in relation to the light source. I got this list from a website that explains 3D in great detail, for Shockwave. Translations could really help. http://www.jmckell.com/

Author:  andrew. [ Thu Jun 26, 2008 6:13 pm ]
Post subject:  RE:[Tutorial] 3D graphics

Don't know if I can do this, but I have created my own "3D thingy". It's basically a basketball and it drops to the ground. I read this, but it wasn't much help, so I just kind of made it up. Also, it may be my computer, but the end product of Windsurfer's tutorial doesn't do much. I think mine is much better, if I don't say so myself.

 Turing: setscreen ("graphics:800,600,nobuttonbar,offscreenonly") const gravity := 3 var ballmass : int := 150 var ballx, bally, ballz : int var direction : int := 1 var chars : array char of boolean ballx := maxx div 2 bally := maxy div 2 ballz := 50 loop     Input.KeyDown (chars)     cls     % Checking if the ball hit the ground     if not ballmass = 0 then         % If the ball didn't hit the ground, then make it drop         ballz := ballz - (gravity * direction)         % If the ball is at the ground then reverse the direction. Same for the other one.         if ballz <= 20 then             ballmass := ballmass div 4 * 3             direction *= -1         elsif ballz >= ballmass then             direction *= -1         end if     end if     if chars (KEY_UP_ARROW) then         bally += 3     elsif chars (KEY_DOWN_ARROW) then         bally -= 3     elsif chars (KEY_LEFT_ARROW) then         ballx -= 3     elsif chars (KEY_RIGHT_ARROW) then         ballx += 3     elsif chars (' ') then         ballz := 150         ballmass := 150     end if     % Draw the ball     drawfilloval (ballx, bally, ballz, ballz, black)     drawfilloval (ballx, bally, ballz - 2, ballz - 2, 42)     drawarc (ballx + ballz - (ballz div 4), bally, ballz - (ballz div 3), ballz - (ballz div 3), 90, 270, black)     drawarc (ballx - ballz + (ballz div 4), bally, ballz - (ballz div 3), ballz - (ballz div 3), 270, 90, black)     drawline (ballx - ballz, bally, ballx + ballz, bally, black)     put "Press space to drop the ball again."     View.Update     delay (5) end loop

P.S. Some of my variable names don't make sense, but I couldn't think of anything better at the time.

EDIT: Forgot to comment it.

 Author: apomb [ Fri Jun 27, 2008 8:25 am ] Post subject: RE:[Tutorial] 3D graphics Thats not exactly 3D, Andrew. there's no perspective, the ball is simply a 2D object. where is this third dimension? Moving a circle seemingly into the screen is hardly 3D visuals. Oh, and P.S. this is a TUTORIAL, not a submissions thread. just to point that out.

 Author: SNIPERDUDE [ Sat Jun 28, 2008 1:29 pm ] Post subject: RE:[Tutorial] 3D graphics Ok, I've MASTERED 2D graphics, and might as well start tampering around with 3D. I've noticed that in your tutorial WindSurfer that the Z coordinate always moves towards (0, 0). What if I wanted it (my (0, 0) coordinate) to move towards the center instead of lower left? So no matter where the X or Y coordinates are on the screen changing the Z will make it move closer to the middle... I'll experiment a bit and see if I can come up with anything.

 Author: andrew. [ Sun Jun 29, 2008 9:38 pm ] Post subject: RE:[Tutorial] 3D graphics Oh well, I tried.

 Author: SNIPERDUDE [ Mon Jun 30, 2008 5:40 pm ] Post subject: RE:[Tutorial] 3D graphics I've almost got it... still working on it though

 Author: Lucas [ Thu Mar 24, 2011 8:14 pm ] Post subject: RE:[Tutorial] 3D graphics setscreen ("graphics: 1200; 800") setscreen ("offscreenonly") var Cubes : int := 50 var x, y, z : array 1 .. Cubes of array 0 .. 8 of real var dx, dy : array 1 .. Cubes * 8 of int var clr : array 1 .. Cubes of int var Vx := 600 var Vy := -200 var Vz := 400 var l : int := 80 var dump : string (1) var mx, my, dump2 : int var dix, diz : int loop for a : 1 .. Cubes x (a) (0) := Rand.Int (-350, 3750) y (a) (0) := Rand.Int (100, 1800) z (a) (0) := Rand.Int (-3550, 3350) clr (a) := Rand.Int (1, 13) for b : 1 .. 8 if b = 1 or b = 2 or b = 5 or b = 6 then x (a) (b) := x (a) (0) + l else x (a) (b) := x (a) (0) - l end if if b = 1 or b = 4 or b = 5 or b = 8 then y (a) (b) := y (a) (0) + l else y (a) (b) := y (a) (0) - l end if if b < 5 then z (a) (b) := z (a) (0) - l else z (a) (b) := z (a) (0) + l end if end for end for loop exit when hasch mousewhere (mx, my, dump2) dix := (600 - mx) div 50 diz := (400 - my) div 50 for a : 1 .. Cubes for b : 1 .. 8 x (a) (b) := x (a) (b) - dix z (a) (b) := z (a) (b) - diz dx (((a - 1) * 8) + b) := (((200 / (y (a) (b) + 200)) * (x (a) (b) - Vx)) + Vx) div 1 dy (((a - 1) * 8) + b) := (((200 / (y (a) (b) + 200)) * (z (a) (b) - Vz)) + Vz) div 1 end for drawline (dx (((a - 1) * 8) + 1), dy (((a - 1) * 8) + 1), dx (((a - 1) * 8) + 2), dy (((a - 1) * 8) + 2), clr (a)) drawline (dx (((a - 1) * 8) + 2), dy (((a - 1) * 8) + 2), dx (((a - 1) * 8) + 3), dy (((a - 1) * 8) + 3), clr (a)) drawline (dx (((a - 1) * 8) + 3), dy (((a - 1) * 8) + 3), dx (((a - 1) * 8) + 4), dy (((a - 1) * 8) + 4), clr (a)) drawline (dx (((a - 1) * 8) + 4), dy (((a - 1) * 8) + 4), dx (((a - 1) * 8) + 1), dy (((a - 1) * 8) + 1), clr (a)) drawline (dx (((a - 1) * 8) + 5), dy (((a - 1) * 8) + 5), dx (((a - 1) * 8) + 6), dy (((a - 1) * 8) + 6), clr (a)) drawline (dx (((a - 1) * 8) + 6), dy (((a - 1) * 8) + 6), dx (((a - 1) * 8) + 7), dy (((a - 1) * 8) + 7), clr (a)) drawline (dx (((a - 1) * 8) + 7), dy (((a - 1) * 8) + 7), dx (((a - 1) * 8) + 8), dy (((a - 1) * 8) + 8), clr (a)) drawline (dx (((a - 1) * 8) + 8), dy (((a - 1) * 8) + 8), dx (((a - 1) * 8) + 5), dy (((a - 1) * 8) + 5), clr (a)) drawline (dx (((a - 1) * 8) + 1), dy (((a - 1) * 8) + 1), dx (((a - 1) * 8) + 5), dy (((a - 1) * 8) + 5), clr (a)) drawline (dx (((a - 1) * 8) + 2), dy (((a - 1) * 8) + 2), dx (((a - 1) * 8) + 6), dy (((a - 1) * 8) + 6), clr (a)) drawline (dx (((a - 1) * 8) + 3), dy (((a - 1) * 8) + 3), dx (((a - 1) * 8) + 7), dy (((a - 1) * 8) + 7), clr (a)) drawline (dx (((a - 1) * 8) + 4), dy (((a - 1) * 8) + 4), dx (((a - 1) * 8) + 8), dy (((a - 1) * 8) + 8), clr (a)) end for View.Update delay (0) cls end loop getch (dump) end loop I think that that is a good example that combines depth/perspective

Author:  SNIPERDUDE [ Thu Mar 24, 2011 8:30 pm ]
Post subject:  Re: [Tutorial] 3D graphics

code tags in the future please, it makes it easier to read. Especially language-specific.
 code: [syntax="turing"]code here[/syntax]

 :