home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 300-399 / ff384.lzh / NorthC / Example1.LZH / cRender / render.doc < prev    next >
Text File  |  1990-08-30  |  11KB  |  238 lines

  1. (c) 1990 S.Hawtin.
  2.  
  3. Permission is granted to copy this file provided
  4.  1) It is not used for commercial gain
  5.  2) This notice is included in all copies
  6.  3) Altered copies are marked as such
  7.  
  8. No liability is accepted for the contents of the file.
  9.  
  10.   This document explains how the "render.c" program works, well it gives 
  11. some hints as to what is going on.  This document is split into two parts, 
  12. first the theory, then the actual code itself.
  13.  
  14. Theoretical Stuff
  15.  
  16.   If you know about three dimensional graphics, or if you are not 
  17. interested I suggest that you skip this bit.
  18.  
  19.   The position of any point in space can be specified by three numbers, 
  20. these three numbers can be collected into a "vector", for example [14 7 
  21. 21] gives us a point that is 14 along 7 across and 21 up.  We can specify 
  22. the shape of a solid object by first saying where the vertices of the 
  23. object are, then by specifying triangles to connect the vertices 
  24. together.  So for example could describe a simple object with
  25.  
  26.     4            ; Number of vertices
  27.     0  0  0      ; Vertex 0 at x=0 y=0 z=0
  28.     80 0  0      ; Vertex 1
  29.     0  80 0      ; Vertex 2
  30.     0  0  80     ; Vertex 3
  31.     4            ; Number of triangles
  32.     3 2 1        ; Triangle 0 connects vertex 3 2 1
  33.     2 3 0        ; Triangle 1
  34.     1 0 3        ; Triangle 2
  35.     0 1 2        ; Triangle 3
  36.  
  37. remember in 'C' the first item in any list is usually item number 0.  The 
  38. order of the vertices in each triangle is important, I will tell you why later.
  39.  
  40.   Now once we have loaded this object into memory we want to display it on 
  41. the screen, this is simple, all we have to do is draw all the triangles in 
  42. the object and there the object will be.  But you may ask how to we draw 
  43. the triangle, ([80 0 0],[0 80 0],[0 0 80]) for example.  Well the simplest 
  44. way to draw it is to ignore the last component of the vector, just draw 
  45. the triangle ([80 0],[0 80],[0 0]).
  46.  
  47.   The first problem with this simple approach is the fact that we want 
  48. faces that are nearer to us to obscure the faces that are further away.  
  49. We need to sort the faces according to how far away they are and draw the 
  50. nearest ones last.  Although this sounds like the right thing to do it 
  51. suffers from one minor drawback, the fact that it doesn't work.  The cases 
  52. where it doesn't work are rare enough that I will ignore them.
  53.  
  54.   The second problem with this simple approach is that while a view down 
  55. the z axis is entirely accurate, it does lack a certain sparkle, it fails 
  56. to convey the shape of the object.  Well we can get around this problem by 
  57. using two different coordinate systems, the first set of coordinates is 
  58. fixed relative to the object, the second set is fixed relative to the 
  59. screen.  Now when we want to display a triangle we must
  60.  
  61.     Transform the vertices to screen coordinates
  62.     Sort the triangles according to their z positions
  63.     Draw the triangles just using the x,y positions
  64.  
  65. all well and good you say, but how do we turn [80 0 0] into a screen 
  66. position[x y z]?  The answer is that we use matrix maths.
  67.  
  68.   To transform a logical position into a screen position we must multiply 
  69. the logical position vector by a "transformation matrix".  If you 
  70. understood that sentence you don't need to bother with the next bit, for 
  71. us mortals however.
  72.  
  73.   A transformation matrix is a 3x3 square of numbers that transforms 
  74. vectors from one coordinate system to another.  So if we have a logical 
  75. position [X Y Z] then we can work out its screen position from
  76.  
  77.              |A B C|
  78.    [X Y Z] * |D E F| = [A*X+D*Y+G*Z  B*X+E*Y+H*Z  C*X+F*Y+I*Z]
  79.              |G H I|
  80.  
  81. all we need to do is work out the transformation matrix.  In "real" three 
  82. dimensional graphics the transformation matrix is 4x4, if you are 
  83. interested in why this should be you should look it up, however for my 
  84. simple system a 3x3 matrix is complicated enough.
  85.  
  86.   So how does a self confessed matrix illiterate work out the right 
  87. matrix?  Well I could have worked it out from first principles, or then 
  88. again maybe not, but actually I looked up the matrix in a book, "The 
  89. fundamentals of three-dimensional computer graphics" by Watt if you are 
  90. interested.  What I wanted was a matrix that would leave the origin in the 
  91. same place but rotate the axis by "th" in one direction and "ph" in 
  92. another, the matix is
  93.  
  94.     |-sin(th)  -cos(th)*cos(ph)  -cos(th)*sin(ph)|
  95.     |                                            |
  96.     | cos(th)  -sin(th)*cos(ph)  -sin(th)*sin(ph)|
  97.     |                                            |
  98.     |   0          sin(ph)            -cos(ph)   |
  99.  
  100. so we now can be even more specific about how to draw our object
  101.  
  102.     Work out th and ph
  103.     Work out the transformation matrix
  104.     Multiply all the vertex positions
  105.     Sort the triangles according to their z positions
  106.     Draw the triangles just using the x,y positions
  107.  
  108. so now can we get on with the coding?  Well no, the next problem that 
  109. comes up is the fact that we want to draw each face a different colour.
  110.  
  111.   The reason for this is easy to see, if you look around you will see that 
  112. all three dimensional objects look different according to the light, if we 
  113. want to create a solid object on the screen, even a white one, we must 
  114. draw it with many different colours.
  115.  
  116.   So we must use a simple model of the illumination of the object, the 
  117. simplest possible model is the "cosine" model, this assumes that the light 
  118. source is in a certain direction.  The amount of light reflected by the 
  119. surface is proportional to the cosine of the angle between the light 
  120. source and the normal to the surface.  There, who said three dimensional 
  121. graphics was complicated.
  122.  
  123.   The normal to a surface is a vector that is perpendicular to the 
  124. surface, it shows which way the surface is facing.  The amount of light 
  125. hitting the surface will obviously be at a maximum when the surface is 
  126. pointing directly at the light, and will be zero if the light is shining 
  127. along the surface.  A little bit of thought, and a lot of tilting large 
  128. books under standard lamps, will convince you that the cosine rule 
  129. previously stated is correct.
  130.  
  131.  *** WARNING ***  The following paragraph contains serious hand
  132.  *** WARNING ***  waving and should therefore not be trusted.
  133.  
  134.   Well now, I can hear you say, how do we find the cosine of the angle 
  135. between two vectors, let alone the normal to the surface.  Well luckily 
  136. there is this thing called the "dot product", if we have vectors V (say [X 
  137. Y Z]) and U (say [I J K]) the "dot product" is 
  138.  
  139.     V.U = X*I+Y*J+Z*K = |V|*|U|*cos(th)
  140.  
  141. where "|V|" is the length of V and "th" is the angle between the vectors.  
  142. Well isn't that convenient.  But even better than that, the "cross 
  143. product" of two vectors
  144.  
  145.     VxU = [J*Z-Y*K K*X-I*Z I*Y-J*X]
  146.  
  147. gives a vector that is perpendicular to both the original vectors, we can 
  148. work out the normal of any surface if we have two vectors in the surface, 
  149. so if our triangle is ([X1 Y1 Z1],[X2 Y2 Z2],[X3 Y3 Z3]) we know that the vectors 
  150.  
  151.     [X2-X1 Y2-Y1 Z2-Z1] and [X3-X1 Y3-Y1 Z3-Z1] 
  152.  
  153. are both in the surface then the normal to the surface is given by
  154.  
  155.     [Y1(Z3-Z2)+Y2(Z1-Z3)+Y3(Z2-Z1)
  156.      Z1(X3-X2)+Z2(X1-X3)+Z3(X2-X1)
  157.      X1(Y3-Y2)+X2(Y1-Y3)+X3(Y2-Y1)]
  158.  
  159. but of course the question is which normal, the surface has two, one 
  160. pointing into the object the other pointing out.  This is why the order of 
  161. the vertices in the triangles is important, if we get it wrong the program 
  162. will think the face is pointing the wrong way and will get the 
  163. illumination all wrong.
  164.  
  165.   There is another advantage to having the normal vector, if the z 
  166. component of the normal vector is positive, that is the face is pointing 
  167. away from the screen, and we don't need to draw it.  This is because there 
  168. must be a closer face pointing towards us.  So with this final refinement 
  169. we can draw our object with the following steps
  170.  
  171.     Work out th and ph
  172.     Work out the transformation matrix
  173.     Throw out faces pointing the wrong way
  174.     Cross multiply all the vertex positions
  175.     Work out the illumination on each face
  176.     Sort the triangles according to their z positions
  177.     Draw the triangles just using the x,y positions
  178.  
  179. and that is what the CRender program does.
  180.  
  181.   There are a number of things you can do to improve the display, for 
  182. example the illumination model is very simple, you could add shadows.  The 
  183. edges of the triangles could use some anti-aliasing.
  184.  
  185. Practical Stuff
  186.  
  187.   The theoretical section skims over a number of rather interesting 
  188. problems, so if we go through the "render.c" file I will point some of 
  189. them out.  You can look at this file and "render.c" at the same time in a 
  190. number of ways, obviously one option is to print one or both files, 
  191. another option is to use "memacs" from the "1.3 Extras" disk, then select 
  192. "Window-Split Window" from the menu to look at both files at the same time.
  193.  
  194.   Right the first bit of "render.c" reads in the include files, as well as 
  195. the system include files we read "3D.h", this defines things like 
  196. "Vector", "Vertex" and "Triangle".
  197.  
  198.   Next we have some variables that control what is being displayed, these 
  199. are directly controlled by the user, the program uses these global 
  200. variables to control its behaviour.
  201.  
  202.   Next the global variables, these are where the program stores things 
  203. that are needed all over the place, for example the object description, 
  204. the current transformation matrix and so on.  Things to note, firstly the 
  205. object description doesn't take much space, space is allocated when the 
  206. program runs, this means we don't have to guess what the largest object is 
  207. when we write the program.  Next the transformation matrix is 4x4, a real 
  208. matrix, however this program only uses the top left 3x3 area.
  209.  
  210.   Now we get into the functions, trans_vertex() transforms a vertex from 
  211. logical to screen coordinates, notice that we put the result into a long, 
  212. this is because at one of the intermediate steps the number will be more 
  213. than sixteen bits wide and we don't want any overflow.
  214.  
  215.   The cmpz() function compares the z coordinate of the centres of two 
  216. triangles, the function is not directly called within the program, the 
  217. qsort() call in display_object() uses the function to sort the triangles 
  218. by z position.
  219.  
  220.   The display_object() function does most of the work in the program, in 
  221. the algorithm it does the 
  222.  
  223.     Throw out faces pointing the wrong way
  224.     Cross multiply all the vertex positions
  225.     Work out the illumination on each face
  226.     Sort the triangles according to their z positions
  227.     Draw the triangles just using the x,y positions
  228.  
  229. parts.  If you follow the function you will see that it goes through these 
  230. five steps.
  231.  
  232.   The init_triangle() function takes an initial triangle and works out the 
  233. position of the centre, and the normal vector.  These two only need to be 
  234. worked out once, when the triangle is initially loaded.
  235.  
  236.   The read_file() routine reads an object description file and creates all 
  237. the data structures that the program needs, this bit of code is rather 
  238. important but also boring.