Posted: 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.
Sponsor Sponsor
Cervantes
Posted: Tue Oct 17, 2006 9:09 am Post subject: (No 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.
Windsurfer
Posted: Tue Oct 17, 2006 3:29 pm Post subject: (No 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.
Cervantes
Posted: Tue Oct 17, 2006 4:35 pm Post subject: (No 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.
Clayton
Posted: Tue Oct 17, 2006 4:41 pm Post subject: (No 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^^
[Gandalf]
Posted: Tue Oct 17, 2006 5:52 pm Post subject: (No 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...
zylum
Posted: Tue Oct 17, 2006 6:19 pm Post subject: (No subject)
doh! i was actually in the process of writing a 3d tutorial
windsurfer > all but your second code snippet have errors / warnings.
Cervantes
Posted: Tue Oct 17, 2006 6:53 pm Post subject: (No 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.
Sponsor Sponsor
Windsurfer
Posted: Tue Oct 17, 2006 7:19 pm Post subject: (No 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?
[Gandalf]
Posted: Tue Oct 17, 2006 7:20 pm Post subject: (No 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.
Cervantes
Posted: Tue Oct 17, 2006 8:30 pm Post subject: (No 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?
Danjen
Posted: 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?
Cervantes
Posted: Wed Nov 22, 2006 2:36 pm Post subject: (No 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.
Wolf_Destiny
Posted: Wed Nov 22, 2006 3:34 pm Post subject: (No 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
[Gandalf]
Posted: Wed Nov 22, 2006 5:11 pm Post subject: (No 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?