home *** CD-ROM | disk | FTP | other *** search
/ The Datafile PD-CD 3 / PDCD_3.iso / utilities / utilst / zed3d_pcx / 3D next >
Text File  |  1995-04-22  |  133KB  |  3,253 lines

  1.                                 
  2.    A tutorial for 2d and 3d vector and texture mapped graphics
  3.                           Version 0.50á
  4.  
  5.  
  6. c 1994, 1995 by Sŵbastien Loisel
  7.  
  8. Note
  9. This document is provided "as is", without guaranty of ANY kind.
  10. This document is copyrighted 1994 by Sŵbastien Loisel. It may be
  11. distributed freely as long as no fee except shipping and handling
  12. is charged for it. Including this document in a commercial
  13. package requires the written permission of the author, Sŵbastien
  14. Loisel.
  15.  
  16. If you want to reach me, see the end of this file.
  17.  
  18.  
  19.      ____________________
  20.  
  21.  
  22.  
  23. 1    An introduction to 3d transformations
  24.  
  25.  
  26.  
  27. First, let's introduce the idea of projections. We have to
  28. realize that we are trying to view a 3d world through a 2d
  29. ®window¯. Thus, one full dimension is lost and it would be absurd
  30. ot think that we can reconstitute it wholly and recognizably with
  31. one less dimension. However, there are many forms of depth-cueing
  32. that we introduce in a 2d image to help us recognize that 3d
  33. dimension. Examples include but are not limited to reduced
  34. lighting or visibility at a distance, reduced size at a distance,
  35. out-of-focus blur, etc...
  36.  
  37. Our media is a screen, ideally flat, and the 3d world being
  38. represented is supposed to look as real as possible. Let's look
  39. at how the eyes see things:
  40.  
  41.  
  42.  
  43. Figure 1
  44.  
  45.  
  46. The projection rays are the beams of light that reflect off the
  47. objects and head to your eye. Macroscopically, they are straight
  48. lines. If you're projecting these beams on a plane, you have to
  49. find the intersection of these beams with the plane.
  50.  
  51.  
  52.  
  53. Figure 2
  54.  
  55. The projection points are thus the intersection of the projection
  56. rays with the projection plane. Of course, projection points
  57. coming from projection lines originating from behind an object
  58. are obviously hidden, and thus comes the loss of detail from 3d
  59. to 2d. (i.e. closer objects can hide objects that lie further
  60. away from the eye).
  61.  
  62. The objects can be defined as a set of (x,y,z) points in 3d space
  63. (or 3space). Thus, a filled sphere can be defined as
  64. x**2+y**2+z**2<=1. But we need to simplify the problem right away
  65. because of machine limitations. We can safely assume the eye is
  66. always outside any object, thus we do not need do define the
  67. interior. [Note: a**b stands for ®a raised to the bth power¯,
  68. thus, x**2 is x squared]
  69.  
  70. Now let us define a projection onto a plane. Many different
  71. projections could be defined, but the one that interests us is
  72. the following for it's simplicity. Projecting takes any given
  73. point (x,y,z) and localizes it on an (u,v) coordinate system.
  74. I.e. space (x,y,z) is mapped onto a plane (u,v). Let's transform
  75. (x,y,z) first. Multiplying (x,y,z) by a constant keeps it on a
  76. line that passes through (0,0,0) (the origin). We will define
  77. that line as the projection ray for our projection. Since all of
  78. these lines pass through the origin, we thus define the origin as
  79. our eye. Let the constant be k. Thus our transformation as of now
  80. is k(x,y,z). Now we want to set everything into a plane. If we
  81. select k as being 1/z, assuming z is nonzero, we get a projected
  82. point of (1/z)(x,y,z). This becomes (x/z, y/z, 1). Thus all
  83. points of any (x,y,z) value except z=0 will be projected with a
  84. value z'=z/z of one. Thus all points lie in the plane z=1. This
  85. projection thus fits our need for a projection with linear
  86. projector through the eye on a plane surface.
  87.  
  88. If you didn't get all that right away, you can always look it up
  89. later. The important thing to remember now is this: to project
  90. from (x,y,z) onto (u,v) we decide to use
  91.  
  92.      u=x/z
  93.      v=y/z
  94.  
  95.  
  96. But, I hear you say, this means the eye is facing parallel to the
  97. z axis, centered on the origin and is not banked. What if that is
  98. not what I desire?
  99.  
  100. First, it is obvious that if the eye is at location (a,b,c) it
  101. will not affect what one would see to move EVERYTING including
  102. the eye by (-a,-b,-c). Then the eye will be centered.
  103.  
  104. Second, and almost as obvious, if the eye is rotated around the
  105. x,y and z axis by agles of rx, ry and rz respectively, you can
  106. rotate EVERYTHING by the exact opposite angles, which will orient
  107. the eye correctly without changing what the eye would see.
  108.  
  109. Translation (moving) is a simple addition. I.e. (x,y,z)+(a,b,c)
  110. becomes (x+a,y+b,z+c). Additions are fairly quick. But there is
  111. the problem of the rotations.
  112.  
  113.  
  114.  
  115.  
  116. 1.1 Trigonometry
  117.  
  118.  
  119. We will do it in two dimensions first.
  120.  
  121.  
  122.  
  123. Figure 3
  124.  
  125.  
  126. We have the point defined by (x,y) and we want to rotate it with
  127. a counterclockwise angle of b to (x',y'). See figure 3. The
  128. radius r stays the same throughout the rotation, as per
  129. definition, rotation changes the angle but not distance to
  130. origin. Let r be the radius (not shown on figure 3). We need to
  131. express x' and y' as functions of what we know, that is x, y and
  132. b. The following can be said:
  133.  
  134. y'=sin(a+b)r
  135. x'=cos(a+b)r
  136.  
  137. With the identities sin(a+b)=sin(a)cos(b)+sin(b)cos(a) and
  138. cos(a+b)=cos(a)cos(b)-sin(a)sin(b), we substitute.
  139.  
  140. y'=rsin(a)cos(b)+rcos(a)sin(b)
  141. x'=rcos(a)cos(b)-rsin(a)sin(b)
  142.  
  143. But from figure 3 we know that
  144.  
  145. rsin(a)=y and
  146. rcos(a)=x
  147.  
  148. We now substitute:
  149.  
  150. y'=ycos(b)+xsin(b)
  151. x'=xcos(b)-ysin(b)
  152.  
  153. A special note: you do not normally calculate sinuses and
  154. cosinuses in real time. Normally, you build a lookup table, that
  155. is, an array. This array contains the precalculated value of
  156. sin(x) for all values of x within a certain range. That is, if
  157. the smallest possible angle in your model is on half of a degree,
  158. then you need a sinus table containing 720 entries, of which each
  159. entry is the value of the sinus of the corresponding angle. You
  160. can even note that a sinus is highly symmetric, that is, you need
  161. the values of sin(x) only for x between 0 and 90 degrees.
  162. Furthermore, you do not need a cosine table, as cos(x)=sin(x+90
  163. degrees). This and fixed point mathematics may become obsolete as
  164. floating point processors become faster and faster. A good
  165. algorithm for calculating a sinus can be very quick today on a
  166. reasonably high performance platform, and it shouldn't use
  167. powers.
  168.  
  169. With that said, we will approach briefly 3d transformations.
  170.  
  171. In 3d, all rotations can be performed as a combination of 3
  172. rotations, one about the x axis, one about the y axis and one
  173. about the z axis. If you are rotating about the z axis, use the
  174. exact same equation as above, and leave the z coordinate
  175. untouched. You can extrapolate the two other transformations from
  176. that.
  177.  
  178. Note that rotating first about the x axis and then about the y
  179. axis is not the same as rotating about the y axis and then the x
  180. axis. Consider this: point (0,0,1) rotated 90ø about the x axis
  181. becomes (0,1,0). Then, rotated 90ø about the y axis it remains
  182. there. Now, rotating first about the y axis yields (1,0,0), which
  183. rotated then about the x axis stays at (1,0,0), thus the order of
  184. rotations is important [(0,1,0) and (1,0,0) are not the same].
  185.  
  186. Now, if you are facing west (x axis, towards negative infinity).
  187. The user pulls the joystick up. You want the ship to dive down as
  188. a result of this. You will have to rotate the ship in the xy
  189. plane, which is the same as rotating about the z axis. Things get
  190. more complicated when the ship is facing an arbitrary angle.
  191.  
  192. Here I include a document authored by Kevin Hunter. Since he did
  193. a very nice job of it, I didn't see a reason to re-do it,
  194. especially when considering the fact that it would probably not
  195. have been as tidy. :-) So, from here to chapter two has been
  196. entirely written by Kevin Hunter.
  197.  
  198.  
  199.  
  200.  
  201.      _____________________
  202.  
  203.  
  204.  
  205.  
  206. Unit Vector Coordinate Translations
  207. By Kevin Hunter
  208.  
  209.  
  210. One of the more common means of doing "world to eye" coordinate
  211. transformations involves maintaining a system of "eye" unit
  212. vectors, and using a property of the vector dot product.  The
  213. advantage of this approach, if your "eye" frame of reference is
  214. rotated with respect to the "world" frame of reference, is that
  215. you can do all the rotational calculation as simple projections
  216. to the unit vectors.
  217.  
  218. The dot product of two vectors yields a scalar quantity:
  219.  
  220.      R dot S = |R| |S| cosa
  221.  
  222. where |R| and |S| are the lengths of the vectors R and S,
  223. respectively, and a is the angle between the two vectors.  If S
  224. is a unit vector (i.e. a vector whose length is 1.0), this
  225. reduces to
  226.  
  227.      R dot S = |R| cosa
  228.  
  229. However, a bit of geometry shows that |R| cosa is the portion of
  230. the R vector that is parallel to the S vector.  (|R| sina is the
  231. part perpendicular).  This means that if R is a vector with its
  232. origin at the eye position, then R can be converted from "world"
  233. space into "eye" space by calculating the dot product of R with
  234. the eye X, Y, and Z unit vectors.  Getting a "eye origin" vector
  235. is trivial - all you need to do is subtract the "world"
  236. coordinates for the eye point from the "world" coordinates for
  237. the point to be transformed.  If all this sounds a bit foggy,
  238. here's a more concrete example, along with a picture.  To make
  239. the diagram easier, we'll consider the two-dimensional case
  240. first.
  241.  
  242.                                 
  243. Figure 3a
  244.  
  245. In this figure, the "world" coordinates have been shown in their
  246. typical orientation.  The "eye" is displaced up and left, and
  247. rotated to the right.  Thus, the EyeX vector would have world-
  248. space coordinates something like (0.866, -0.500) [I've picked a
  249. 30 degree rotation just for the numbers, although my less-than-
  250. perfect drawing talents don't make it look like it).  The EyeY
  251. vector, which is perpendicular to the EyeX vector, has world-
  252. space coordinates (0.500, 0.866).  The eye origin point is at
  253. something like (1, 2).
  254. Based on these numbers, any point in world space can be turned
  255. into eye-relative coordinates by the following calculations:
  256.  
  257.      EyeSpace.x   = (Px - 1.0, Py - 2.0) dot (0.866, -0.500)
  258.              = (0.866Px - 0.866) + (-0.500Py + 1.00)
  259.              = 0.866Px - 0.500Py + 0.134
  260.      EyeSpace.y   = (Px - 1.0, Py - 2.0) dot (0.500, 0.866)
  261.              = (0.500Px - 0.500) + (0.866Py - 1.732)
  262.              = 0.500Px + 0.866Py - 2.232
  263.  
  264. The simple way of implementing this transformation is a two-step
  265. approach.  First, translate the point by subtracting the eye
  266. origin.  This gives you the eye-relative vector.  Then, perform
  267. each of the three dot products to calculate the resulting X, Y,
  268. and Z coordinates in the eye space.  Doing this takes the
  269. following operations for each point:
  270.  
  271.      1.Three subtractions to translate the point
  272.      2.Three multiplications and two additions for the X
  273.        coordinate
  274.      3.Three multiplications and two additions for the Y
  275.        coordinate
  276.      4.Three multiplications and two additions for the Z
  277.        coordinate
  278.  
  279. Since additions and subtractions cost the same in virtually any
  280. system, this reduces to a total of nine multiplications and nine
  281. additions to translate a point.
  282.  
  283. If you're striving for performance, however, you can make this
  284. run a little better.  Note, in the two-dimensional example above,
  285. that we were able to combine two constants that fell out of the
  286. second line into a single constant in the third line.  That last
  287. constant turns out to be the dot product of the particular eye
  288. coordinate vector and the negative of the eye origin point.  If
  289. you're smart, you can evaluate this constant once at the
  290. beginning of a set of point transformations and use it over and
  291. over again.
  292.  
  293. This doesn't change the total number of operations - the three
  294. subtractions you take away at the beginning you put back later.
  295. The advantage, however, is that the constant is known ahead of
  296. time, which may let you keep it in a register or cache area.
  297. Basically, you've reduced the number of constants needed to
  298. transform the coordinates and the number of intermediate values,
  299. which may let you keep things in registers or the cache area.
  300. For example, this approach lets you use a stacked-based FPU in
  301. the following manner:
  302.  
  303.      push P.x
  304.      push UnitVector.x
  305.      mult
  306.      push P.y
  307.      push UnitVector.y
  308.      mult
  309.      add
  310.      push P.z
  311.      push UnitVector.z
  312.      mult
  313.      add
  314.      push CombinedConstant
  315.      add
  316.      pop  result
  317.  
  318. This algorithm never has more than three floating point constants
  319. on the FPU stack at a time, and doesn't need any of the
  320. intermediate values to be removed from the FPU.  This type of
  321. approach can cut down on FPU-to-memory transfers, which take
  322. time.
  323.  
  324. Of course, if you don't have a stack-based FPU, this second
  325. approach may or may not help.  The best thing to do is to try
  326. both algorithms and see which works better for you.
  327.  
  328.  
  329.  
  330. Maintaining Systems of Unit Vectors
  331.  
  332. Spinning In Space
  333. To effectively use the system discussed in the previous section,
  334. you need to be able to generate and maintain a set of eye-space
  335. unit vectors.  Generating the vectors is almost never a problem.
  336. If nothing else, you can use the world X, Y and Z vectors to
  337. start.  Life gets more interesting, however, when you try to
  338. maintain these vectors over time.
  339.  
  340. Translations of the eye point are never a problem.  Translations
  341. are implemented by adding or subtracting values to or from the
  342. eye origin point.  Translations don't affect the unit vectors.
  343. Rotations, however, are another matter.  This kind of makes
  344. sense, since handling rotations is what maintaining this set of
  345. vectors is all about.
  346.  
  347. To be effective, the set of three unit vectors have to maintain
  348. two basic properties.  First, they have to stay unit vectors.
  349. This means that we can't let their lengths be anything other than
  350. 1.0.  Second, the three vectors have to stay mutually
  351. perpendicular.
  352.  
  353. Ensuring that a vector is a unit vector is very straight-forward
  354. conceptually.  All you need to do is to calculate the length of
  355. the vector and then divide each of its individual coordinates by
  356. that length.  The length, in turn, can be easily calculated as
  357. SQRT(x^2 + y^2 + z^2).  The only interesting part of this is the
  358. SQRT() function.  We'll come back to that one later.
  359.  
  360. Keeping the set of vectors perpendicular takes a bit more work.
  361. If you restrict rotations of the vector set to sequences of
  362. rotations about the individual vectors, it's not too hard to do a
  363. decent job of this.  For example, if you assume that, in eye
  364. space, Z is to the front, X is to the right and Y is down (a
  365. common convention since it lets you use X and Y for screen
  366. coordinates in a 0,0 == top left system), rotation of the eye to
  367. the right involves rotating around the Y axis, with Z moving
  368. toward X, and X toward -Z.  The calculations here are very
  369. simple:
  370.  
  371.      Znew = Zoldcosa + Xoldsina
  372.      Xnew = Xoldcosa + (-Zold)sina
  373.  
  374. Of course, while you're doing this you need to save at least the
  375. original Z vector so that you don't mess up the X calculation by
  376. overwriting the Z vector before you get a chance to use it in the
  377. second equation, but this is a detail.
  378.  
  379. In a perfect world, when you're done with this rotation, X, Y,
  380. and Z are still all unit vectors and still mutually
  381. perpendicular.  Unfortunately, there's this thing called round-
  382. off error that creeps into calculations eventually.  With good
  383. floating point implementations, this won't show up quickly.  If
  384. you're having to do fixed-point approximations (for speed, I
  385. assume) it will show up a lot faster, since you're typically
  386. storing many fewer mantissa bits than standard FP formats.
  387.  
  388. To get things back square again there are a couple of things you
  389. can do.  The most straight forward is to use the cross product.
  390. The cross product of two vectors yields a third vector which is
  391. guaranteed (within your round-off error) to be perpendicular to
  392. both of the input vectors.  The cross product is typically
  393. represented in determinant form:
  394.                    x   y   z
  395.          A cross   Ax  Ay  Az
  396.          B =
  397.                    Bx  By  Bz
  398.  
  399. In more conventional format, the vector resulting from A cross B
  400. is
  401.  
  402.      (AyBz - AzBy, AzBx - AxBz, AxBy - AyBx)
  403.  
  404. One thing about the cross product is that you have to watch out
  405. for the sign of things and the right-handedness or left-
  406. handedness of your coordinate system.  A cross B is not equal to
  407. B cross A - it's equal to a vector in exactly the opposite
  408. direction.  In a right-handed coordinate system, X cross Y is Z,
  409. Y cross Z is X, and Z cross X is Y.  In a left-handed coordinate
  410. system, the arguments of the crosses have to be exchanged.
  411.  
  412. One tactic, then, is to rotate one of the vectors, and then,
  413. instead of rotating the other, to recalculate the other using the
  414. cross product.  That guarantees that the third vector is
  415. perpendicular to the first two.  It does not, of course,
  416. guarantee that the vector that we rotated stayed perpendicular to
  417. the stationary vector.  To handle this, we could then adjust
  418. either the stationary vector or the one we originally rotated by
  419. another cross product if we wanted to.  In practice, this
  420. frequently isn't necessary, because we rarely are rotating only
  421. about one axis.  If, for example, we pitch and then roll, we can
  422. combine rotations and crosses so that no vector has undergone
  423. more than one or two transformations since it was last readjusted
  424. via a cross product.  This keeps us from building up too much
  425. error as we go along.
  426.  
  427. One other thing to remember about the cross product.  The length
  428. of the generated vector is the product of the lengths of the two
  429. input vectors.  This means that, over time, your calculated
  430. "cross" vectors will tend to drift away from "unitness" and will
  431. need to be readjusted in length.
  432.  
  433. Faster!
  434. It is unusual in interactive graphics for absolute and unwavering
  435. precision to be our goal.  More often (much more often) we're
  436. willing to give up a tiny bit of perfection for speed.  This is
  437. particularly the case with relatively low-resolution screens,
  438. where a slight error in scale will be lost in pixel rounding.  As
  439. a result, we'd like a measure of how often we need to readjust
  440. the perpendicularity of our unit vectors.  As it happens, our
  441. friend the dot product can help us out.
  442.  
  443. Remember that we said that the dot product of two vectors
  444. involves the cosine of the angle between them.  If our unit
  445. vectors are actually perpendicular, the dot product of any pair
  446. of them should then be zero, since the cosine of a right angle is
  447. zero.  This lets us take the approach of measuring just how badly
  448. things have gotten out of alignment, and correcting it only if a
  449. tolerable threshold has been exceeded.  If we take this approach,
  450. we "spend" three multiplications and two additions, hoping to
  451. save six multiplications and three additions.  If we're within
  452. our tolerance more often than not, we're in good shape.
  453.  
  454. Foley et al point out that we can make this tradeoff work even
  455. better, however.  Remember that the dot product represents the
  456. projection of one vector along another.  Thus, if we find that X
  457. dot Y is not zero, the resulting value is the projection of the X
  458. vector along the Y vector (or the Y vector along the X vector, if
  459. you want).  But if we subtract from the X vector a vector
  460. parallel to the Y vector whose length is this projection, we end
  461. up with X being perpendicular to Y again.  The figure may help to
  462. make this clearer
  463.                                 
  464. Figure 3b
  465.  
  466. This lets use the following algorithm:
  467.      A = X dot Y
  468.      if (abs(A) > threshold)
  469.      {
  470.           X -= Y times A
  471.      }
  472. This approach uses three multiples and two adds if we don't need
  473. to adjust, and six multiplies and five adds if we do.  This
  474. represents a savings of three multiplications over the "test and
  475. then cross product" approach.  Note that, like the cross product
  476. approach, this technique will tend to change the length of the
  477. vector being adjusted, so vectors will need to be "reunitized"
  478. periodically.
  479.  
  480. Perfection?
  481. If we want to use our unit vector system, but we need to model
  482. rotations that aren't around one of our unit axes, life gets more
  483. difficult.  Basically, except in a very few circumstances (such
  484. as if the rotation is around one of the world axes) the
  485. calculations are much harder.  In this case, we have at least two
  486. options.  The first is to approximate the rotation by incremental
  487. rotations that we can handle.  This has to be done carefully,
  488. however, because rotations are not interchangeable - rotating
  489. about X then Y doesn't give the same answer as rotating around Y
  490. then X unless the angles are infinitely small.
  491.  
  492. The second option is to use quaternions to do our rotations.
  493. Quaternions are a very different way of representing rotations
  494. and rotational orientations in space.  They use four coordinates
  495. rather than three, in order to avoid singularities, and can model
  496. arbitrary rotations very well.  Unfortunately, I'm not completely
  497. up on them at this point, so we'll have to defer discussion of
  498. them until some future issue when I've had a chance to do my
  499. studying.
  500.  
  501.  
  502.  
  503. Efficient Computation of Square Roots
  504.  
  505. We mentioned earlier that we'd need to look at square roots to
  506. "unitize" vectors.
  507.  
  508. Let's assume for the moment that we need to calculate the square
  509. root of a number, and we don't have the hardware to do it for us.
  510. Let us assume further than multiplications and divisions are
  511. cheap, comparitively speaking.  How can we go about calculating
  512. the square root of a number?
  513.  
  514. [Note:  Intel-heads like me think this is an unnatural situation,
  515. but it does, in fact, occur out there.  For example, the Power PC
  516. can do FP add/subtract/multiply/divide in a single cycle, but
  517. lacks sqrt() or trig function support.]
  518.  
  519. Isaac Strikes Again
  520. Perhaps the simplest approach is to use Newton's method.
  521. Newton's method is a technique for successively approximating
  522. roots to an equation based on the derivative of that equation.
  523. To calculate the square root of N, we use the polynomial equation
  524. x2 - N = 0.  Assuming that we have a guess at the square root,
  525. which we'll call xi, then next approximation can be calculated by
  526. the formula
  527.  
  528.      xi+1 = xi + (N - xi2) / (2xi)
  529.  
  530. This begs two questions.  First, how do we get started?  Second,
  531. when do we get there?  To answer the first part, Newton's method
  532. works really well for a well-behaved function like the square
  533. root function.  In fact, it will eventually converge to the
  534. correct answer no matter what initial value we pick.  To answer
  535. the second part, "Well, it all depends on where you start."
  536.  
  537. To get an idea of how this iteration performs, let's expand it a
  538. bit.  Let's assume that our current guess (xi) is off by a value
  539. "e" (in other words, the square root of N is actually xi + e.)
  540. In this case,
  541.  
  542.      xi+1    = xi + ((xi + e)2 - xi2) / (2xi)
  543.         = xi + (xi2 +2exi + e2 - xi2) / (2xi)
  544.         = xi + (2exi + e2) / (2xi)
  545.         = xi + e + (e2 / 2xi)
  546.  
  547. But since "xi + e" is the answer we were looking for, this says
  548. that the error of the next iteration is "e2/2xi".  If the error
  549. is small, the operation converges very quickly, because the
  550. number of accurate bits doubles with each iteration.  If the
  551. error is not small, however, it's not so obvious what happens.
  552. I'll spare you the math, and simply tell you that if the error is
  553. large, the error typically halves each iteration (except for the
  554. first iteration, in which the error can actually increase).  So,
  555. while a bad initial guess won't prevent us from finding an
  556. answer, it can slow us down a lot.
  557.  
  558. So how do we find a good initial guess?  Well, in many cases, we
  559. have a good idea what the value ought to be.  If we're re-
  560. unitizing a vector, we suspect that the vector we're working with
  561. is already somewhere near a length of 1.0, so an initial guess of
  562. 1.0 will typically work very well.  In this situation, four
  563. iterations or so usually gets you a value as accurate as you
  564. need.
  565.  
  566. What if we're trying to convert a random vector into a unit
  567. vector, or just taking the square root of a number out of the
  568. blue?  In this case, we can't be sure that 1.0 will be a very
  569. good guess.  Here are a few things you can do:
  570.  
  571.      1.If you have easy access to the floating point number's
  572.        exponent, halve it.  The square root of A x 2M is SQRT(M)
  573.        x 2M/2.  Using A x 2M/2 is a really good first
  574.        approximation.
  575.      2.If you are taking the length of a vector (x,y,z),
  576.        abs(x)+abs(y)+abs(z) won't be off by more than a factor
  577.        of 2 - also a good start.
  578.      3.It turns out that the operation will converge faster if
  579.        the initial guess is higher than the real answer.  If all
  580.        else fails, using N/2 will converge pretty quickly.
  581.  
  582. Don't go crazy trying to get a good first guess.  Things will
  583. converge fast enough even if you're somewhat off that a fast
  584. guess and an extra iteration may be faster than taking extra time
  585. to refine your guess.
  586.  
  587. If You Don't Like Newton...
  588.  
  589. If you're looking for a really accurate answer, Newton's method
  590. may not be for you.  First, you can't, in general, just iterate a
  591. fixed number of times.  Rather, you have to check to see if the
  592. last two iterations differ by less than a threshold, which adds
  593. calculation time to each iteration.  If you're in fixed-point
  594. land, the assumption we made earlier that multiplications were
  595. cheap may not be the case.  In this case, there is an alternate
  596. closed-form algorithm you can use that will result in the "exact"
  597. answer in a predefined amount of time.  This algorithm also uses
  598. an incremental approach to the calculation.
  599.  
  600. Since (x + a)2 = x2 + 2xa + a2, we can iterate from x2 to (x+a)2
  601. by adding 2xa + a2 to our working sum.  If a is2N, however, this
  602. translates into adding 2N+1 x + 22N.  Since we can multiply by 2N
  603. by shifting to the left by N bits, we can take a square root by
  604. use of shifts and subtractions.  To take the square root of a 32-
  605. bit number, for example, this results in an algorithm that looks
  606. something like this:
  607.  
  608.      remainder = valueSquared;
  609.      answer = 0;
  610.      for (iteration = N; iteration >= 0; iteration--)
  611.      {
  612.           if (remainder >= (answer << iteration+1) + (1 <<
  613.      iteration * 2))
  614.           {
  615.                remainder -= (answer << iteration+1) + (1 <<
  616.      iteration * 2);
  617.                answer += 1 << iteration;
  618.           }
  619.      }
  620.  
  621. A "real" algorithm won't repeatedly perform the shifts on the 5th
  622. line.  Rather, an incremental approach can be taken in which the
  623. answer and "1 bit" is kept in pre-shifted form and shifted to the
  624. right as the algorithm proceeds.  With appropriate temporary
  625. variable space, this algorithm shifts two values and does two
  626. subtractions and either one or zero add or "or" per iteration,
  627. and uses one iteration for each two bits in the original value.
  628.  
  629. This algorithm can be used very effectively on integer values,
  630. and can also be adapted to take the square root of the mantissa
  631. portion of a floating-point number.  In the latter case, care has
  632. to be taken to adjust the number into a form in which the
  633. exponent is even before the mantissa square root is taken, or the
  634. answer won't be correct.
  635.  
  636. Table It
  637. If you don't need absolute precision, there is a table-driven
  638. square root algorithm that you can use as well.  The code for the
  639. 32-bit FP format ("float") is in Graphics Gems, and the 64-bit FP
  640. format ("double") is in Graphics Gems III.  The source code is
  641. online at ftp:://Princeton.edu/pub/Graphics/GraphicsGems.  (Note
  642. the capital `G'.  There is also a /pub/graphics (small `g') which
  643. is not what you want...)
  644.  
  645. The way this algorithm works is to reach inside the IEEE floating
  646. point format.  It checks the exponent to see if it is odd or
  647. even, modifies the mantissa appropriately for odd exponents, and
  648. then halves the exponent.  The mantissa is then converted to a
  649. table lookup with a shift and mask operation, and the new
  650. mantissa found in a table.  The version in Graphics Gems III is
  651. set up to allow you to adjust the size of the lookup table, and
  652. thus the accuracy of the operation.
  653.  
  654. Whether or not this method is appropriate for you depends on your
  655. tolerance for accuracy vs. table size tradeoffs.  The algorithm
  656. runs in constant time, like the previous one, which is an
  657. advantage in many situations.  It's also possible to combine
  658. them, using a table lookup to get the first N bit guess, and then
  659. finishing it out using the bit shifting approach above.
  660.  
  661. Trig Functions
  662. Unfortunately, trig functions like sine and cosine don't lend
  663. themselves nicely to methods like the square root algorithms
  664. above.  One of the reasons for this is that sine and cosine are
  665. interrelated.  If you try to use Newton's method to find a
  666. cosine, you need to know the sine for your guess.  But to figure
  667. out the sine, you need cosine.  Department of Redundancy
  668. Department.
  669.  
  670. One alternate approach you can take is to use a Taylor series.  A
  671. Taylor series is a polynomial that will approximate a sine or
  672. cosine to any desired accuracy.  The problem with this is that
  673. the Taylor series for sine and cosine don't converge really
  674. quickly.  They don't do too bad, but you still have to do a bunch
  675. of computation.
  676.  
  677. A better approach in this case is probably to use a combination
  678. of computation and a look-up table, like the last square root
  679. algorithm.  Since sine and cosine are cyclic, and interrelated,
  680. you only really need to be able to calculate one of them over the
  681. range 0 - p/2.  Given this, the rest of the values can be
  682. determined.  Depending on how much calculation you want to do,
  683. how much table space you want to spend, and how accurate your
  684. answer needs to be, you can break up the region above into a
  685. fixed number of regions, and use some form of interpolation
  686. between points.  If you're in Floating Point land, over a
  687. relatively short region, you can fit a quadratic or cubic
  688. function to either the sine or cosine function with pretty good
  689. accuracy.  This gives you a second or third order polynomial to
  690. evaluate and a little bit of table lookup overhead.  For all but
  691. the lowest accuracy cases, this will probably net out faster than
  692. calculating the number of Taylor terms you'd need.
  693.  
  694. If you're in Fixed Point Land, you may be better off with a table
  695. lookup and linear interpolation, rather than using a higher-order
  696. polynomial.  For comparable accuracy, this approach will probably
  697. need more table space, but if you're avoiding floating point,
  698. you'll probably be avoiding integer multiplications as well.
  699. (Particularly because you have the decimal point adjustments to
  700. worry about).  The linear interpolation can be reduced to a
  701. single multiply by storing delta information in the table, so all
  702. you have to do is multiply the bits you stripped off while
  703. building the table index (the "fractional part" by this delta
  704. value and add it to the table entry.
  705.  
  706. One trick you can use in either case to make your table lookups
  707. easier is to measure your angles in 216 units rather than radians
  708. or degrees. 216 units basically consist of the following mapping:
  709.      Degrees Radians     216 units
  710.      0         0         0
  711.      45        p/4       0x2000
  712.      90        p/2       0x4000
  713.      180       p         0x8000
  714.      270       3p/2      0xC000
  715.      359.999   2p-e      0xFFFF
  716. The neat thing about this set of units is that they wrap just the
  717. way that radians do (and degrees are supposed to).  They give you
  718. resolution of about 0.005 degrees, and they make table lookups
  719. very easy, since you can shift-and-mask them down into table
  720. indices very easily.
  721.  
  722. Interestingly enough, this is something I'd devised for myself
  723. late last year (feeling very proud of myself) only to see the
  724. technique published in Embedded Systems a month or so later.
  725. Just goes to show you...
  726.  
  727.  
  728.  
  729.  
  730.  
  731.      ____________________
  732.  
  733.  
  734.  
  735.  
  736.  
  737. 2    Polygon drawing on a raster display in two dimensions
  738.  
  739.  
  740. The second main problem is to display a filled polygon on a
  741. screen. We will discuss that problem here. First, let us approach
  742. the 2d polygon.
  743.  
  744. We will define a polygon as a set of points linked by segments,
  745. with a defined inner region and outer region. The inner region
  746. need not be in a single piece.
  747.  
  748. Our rule for determining if a point is in a given region is to
  749. draw a line from infinity to that point. If that line crosses an
  750. odd number of segments (edges), the point is within the polygon.
  751. Otherwise it is not.
  752.  
  753.  
  754.  
  755. Figure 4
  756.  
  757.  
  758. Drawing a line from infinity to any of the gray areas of the star
  759. will cross an odd number of edges. In the white areas, it will
  760. cross an even number of edges. Try it.
  761.  
  762.  
  763.  
  764. Figure 5
  765.  
  766. The polygon shown in a, b and c are all the same. In a, the inner
  767. region has been shaded, but this has been omitted in b and c for
  768. clarity.
  769.  
  770. The polygon shown in a can be listed as vectors, as in b or c.
  771. Let us agree that both versions are equally satisfying. In b we
  772. enumerated the edges in a counterclockwise continuous fashion,
  773. meaning that the head of each vector points to the tail of
  774. another. In c, we made each vector point generally downward, or
  775. more strictly, each vector's topmost point is its tail. These
  776. vector are noted (a,b)-(c,d) where (a,b) is the location of the
  777. tail of the vector, and (c,d) is the location of the head (arrow)
  778. of the vector. In diagram c, for all vectors d<b (or b>d, either
  779. way). (Note, we will see what to do with b=d later. That being a
  780. special case, we will not discuss it now).
  781.  
  782. As we are drawing on a screen (supposedly), it would be
  783. interesting to be able to color each pixel once and only once,
  784. going in horizontal or vertical lines (as these are easy to
  785. compute). We will choose horizontal lines for hardware reasons.
  786. Thus, we start scanning from infinitely upwards to infinitely
  787. downwards using horizontal scanlines (e.g., the ®first¯ scanline
  788. is way up, and the last is way down). Now, our screen being
  789. finite, we need only to draw those lines that are on screen. At
  790. each pixel, we will draw an imaginary line from the infinite to
  791. the left (horizontal line) to the current pixel. If it crosses an
  792. odd number of edges, we color it. Otherwise, we don't.
  793.  
  794. This means that from infinite left to the first edge, we do not
  795. color. Then, from first edge to second edge, we color. Then, from
  796. second edge to third edge, we do not color. From third edge to
  797. fourth edge, we color. And so on. If you want, you can always
  798. draw a background bitmap left from the first edge, from second
  799. edge to third edge, etc.... Thus, you could put the picture of a
  800. beautiful blue sky behind at the same time you're drawing a
  801. polygon.
  802.  
  803. Now, let's start that over again. We are coming from infinitely
  804. upward and drawing. We do not need to consider every edge, only
  805. those that are on the current scanline (horizontal line). We will
  806. thus build an inactive edge list (those edges that have not yet
  807. been used in the drawing process) and an active edge list (those
  808. edges that are currently in use to draw the polygon, i.e. that
  809. intercept the current scanline). Thus, at first, all edges (named
  810. m,n,o,p in figure 5) would be in the inactive edge.
  811.  
  812. As the algorithm reaches the topmost line of the polygon, any
  813. relevant edge will be transfered from the inactive edge list to
  814. the active edge list. When an edge becomes unused (we're lower
  815. than the lowermost scanline), we remove it from the active list
  816. and throw it to the dogs. (# grin #)
  817.  
  818. Now you will realize that at each scanline we have to check every
  819. edge in the inactive edge list to see if we need to transfer them
  820. to the active edge list. To accelerate that, we can sort the
  821. inactive edge list in decreasing order. Thus, the ordered list
  822. would become {m,p,n,o}. Then, we need only to check the first
  823. vector at every scanline. Since they point downward, we need only
  824. to check the first point of it.
  825.  
  826. If you get any horizontal lines in the edge list, remove them
  827. from the list. Horizontal lines can be identified easily as b=d.
  828. Proving that it works is left as an exercise (# muhaha! #).
  829.  
  830. The polygon drawing algorithm (scan-line algorithm).
  831.  
  832. I=inactive edges list, initially all edges
  833. A=active edges list, initally empty
  834.  
  835. sort I with highest first endpoint first
  836.  
  837. for each scanline i starting from the top of the screen
  838.      does the first edge in I start at scan line i?
  839.           Yes, transfer all edges that should start now from I to
  840. A
  841.      find x value of all edges in A for current scanline (see the
  842. line algorithm below)
  843.      sort all edges in A with increasing x-intercept value (the
  844. thing we jest calculated)
  845.  
  846.      previous_x_intercept<-leftmost pixel on screen
  847.      for all edges n in A, counting by two, do
  848.           draw background between previous_x_intercept and edge n
  849. in A
  850.           fill polygon between edge n and edge n+1 in A
  851.           previous_x_intercept<-x intercept of edge n+1
  852.      end for
  853.  
  854.      draw background for the rest of the scan line (from
  855. previous_x_intercept to end of line)
  856.      for all edges for which we have reached the bottom
  857.           remove the edge from A
  858.      end for
  859. end for
  860.  
  861.  
  862.  
  863.  
  864. 2.1 A line algorithm for the polygon drawing algorithm
  865.  
  866.  
  867. This section discusses a somewhat shortened version of
  868. Bresenham's line drawing algorithm. It is optimized for drawing
  869. filled polygons.
  870.  
  871. You assuredly realize you need to calculate the x coordinate of
  872. the intersection of a line with an horizontal line. Well consider
  873. the line equation that follows:
  874.  
  875.      x=dy/dx x y + k
  876.  
  877. If the line is defined with the vector (a,b)-(c,d), then we
  878. define dy as d-b, dx as c-a. Say x0 is the x coordinate of the
  879. topmost point of the segment.
  880.  
  881. x0=a
  882.  
  883. Say xn is the x intercept value of the segment for the nth line
  884. from the top. Then, xn can be defined as follows:
  885.  
  886. xn=dy/dx x n + a
  887.  
  888. Say we know xn-1 and want to know xn based on that knowledge.
  889. Well it is obvious that:
  890.  
  891. xn-x(n-1)=dy/dx x n + a - [dy/dx x (n-1) + a]
  892. xn-x(n-1)=dy/dx
  893. xn=dy/dx+x(n-1)
  894.  
  895. Thus, knowing xn-1 and adding dy/dx yields xn. We can thus do an
  896. addition instead of a multiply and an add to calculate the new x
  897. intercept value of a line at each scanline. An addition been at
  898. least 5 times as fast as an add, usually more in the 15 times
  899. range, this will speed things up considerably. However, it might
  900. annoy us to use floating point or fixed point, even more so with
  901. roundoff error.
  902.  
  903. To this end, we have found a solution. dy/dx can be expressed as
  904. a+b/d, b<d. a is the integer part of dy/dx. Substituting, we find
  905. that
  906.  
  907. xn=a+b/d+xn-1
  908.  
  909. Thus, we always add a at each scanline. Furthermore, when we add
  910. a sufficient amount of b/d, we will add one more to xn. We will
  911. keep a running total of the fraction part. We will keep the
  912. denominator in mind, and set the initial numerator (when n=0) to
  913. 0. Then, at each line we increase the numerator by b. If it
  914. becomes greater than d, we add one to xn and decrease the
  915. numerator by d. In short, in pseudocode, we draw a segment from
  916. (x0,y0) to (x1,y1) this way:
  917.  
  918. dx=x1-x0
  919. dy=y1-y0
  920. denominator=dy
  921. numerator=dx modulo dy
  922. add=dx divided (integer) dy
  923. running=0
  924. x=x0
  925. for each line (0 to n) do
  926.      x=x+add
  927.      running=running+numerator
  928.      if running>=denominator then
  929.           running=numerator-denominator
  930.           x=x+1
  931.      end if
  932.      do whatever you want with line here, such as drawing a pixel
  933. at location (x,line)
  934. next line
  935.  
  936.  
  937.  
  938.      __________________________
  939.  
  940.  
  941.  
  942.  
  943. 3    3d polygon drawing
  944.  
  945.  
  946.  
  947. The first step is to realize that a projected 3d line with our
  948. given projection becomes a 2d line, that is it stays straight and
  949. does not bend. That can be easily proved. The simplest proof is
  950. the geometric one: the intersection of two planes in 3d is a line
  951. in the general case. The projector all lie in the plane formed by
  952. the origin ant the line to be projected, and the projection plane
  953. is, well, a plane. Thus, the projection is contained in a line.
  954.  
  955. This means that all the boundaries of a polygon can be projected
  956. as simple lines. Only their endpoints need be projected. Then,
  957. you can simply draw a line between the two endpoints of each
  958. projected edge (or more precisely, draw a 2d polygon with the
  959. given endpoints).
  960.  
  961. However, there comes the problem of overlapping polygons.
  962. Obviously, the color of a pixel must be that of the closest
  963. polygon in that point (well for our objective, that suffices).
  964. But this means that we must determine the distance of each
  965. polygon that has the possibility to be drawn in the current
  966. pixel, and that for the whole image. That can be time consuming.
  967.  
  968. We will make a few assumptions. First, we will assume no polygons
  969. are interpenetrating; this means that to come in front of another
  970. polygon, a polygon must go around it. This means that you only
  971. need to determine what polygon is topmost when you cross an edge.
  972.  
  973. Less obviously, but equally useful, if from one scanline to the
  974. next, you encounter the same edges in the same order when
  975. considering left to right, you do not need to recalculate the
  976. priorities as it has not changed.
  977.  
  978. Inevitably, though, you will need to determine the depth of the
  979. polygon (z value) in a given pixel. That can be done rather
  980. easily if you carry with the polygon its plane equation in the
  981. form Ax+By+Cz+D=0. Remember that the projection ray equation for
  982. pixel (u,v) is x=zu and y=zv. Feed that x and y in the plane
  983. equation and you find that
  984.  
  985. Azu+Bzv+Cz+D=0
  986. z(Au+Bv+C)=-D
  987. z=-D/(Au+Bv+C)
  988.  
  989. Thus you can easily calculate the z coordinate for any plane in a
  990. given (u,v) pixel.
  991.  
  992. The cautious reader will have noticed that it is useful to do
  993. some of these multiplications and additions incrementally from
  994. scanline to scanline. Also note that if Au+Bv+C equals zero, you
  995. cannot find z unless D is also zero. These (u,v) coordinates
  996. correspond to the escape line of the plane of the polygon (you
  997. can picture it as the line of ®horizon¯ for the given plane). The
  998. projection ray is parallel to the plane, thus you never cross it.
  999.  
  1000. This brings me to another very important thing about planes.
  1001. Another way to define a plane is to give a normal vector. A
  1002. vector and a point of reference generate one and only one plane
  1003. perpendicular to the vector and passing through the point of
  1004. reference. Think of a plane. Think of a vector sticking up
  1005. perpendicular to it. That vector is called normal to the plane.
  1006. It can be easily proved that this vector is (A,B,C) with A,B,C
  1007. being the coefficients of the plane equation Ax+By+Cz=D. We might
  1008. want to normalize the vector (A,B,C) (make it length 1), or
  1009. divide it by SQRT(A2+B2+C2). But to keep the equation the same,
  1010. we must divide all member by that value. The equation thus
  1011. becomes A'x+B'y+C'z=D'. If we want to measure the distance
  1012. perpendicularly to the plane of a point (a,b,c), it can be shown
  1013. that this (signed) distance is A'a+B'b+C'c+D' (you can always
  1014. take the absolute value of it if you want an unsigned distance).
  1015. That might be useful for different reasons.
  1016.  
  1017. It is also notable that since the A, B and C coefficients are
  1018. also the coords of the normal vector, and that rotating a vector
  1019. can be done as we saw previously, it is easy to rotate the plane.
  1020. If the vector is normalized in the first place, it will stay
  1021. normalized after rotation. After rotation, you will ignore the D
  1022. component of the plane, but you do know the coordinates of a
  1023. point in the rotated plane. Any point in the polygon will fit the
  1024. bill. Thus, you can deduce D. In fact, when performing back-face
  1025. culling (see chapter 4), you will calculate D even as you are
  1026. performing some optimization.
  1027.  
  1028. Normally, you will build an inactive edge table (IET) in which
  1029. you will store all edges of all polygons to be rendered. Also
  1030. initialize an empty list called the active edge table (AET). In
  1031. the IET, make sure that the edges are sorted in increasing values
  1032. of y for the topmost endpoint of any edge. Also, as per the 2d
  1033. polygon drawing algorithm, discard any horizontal edge.
  1034.  
  1035. As you scan from the top of the screen to the bottom of the
  1036. screen, move the edges from the IET to the AET when you encounter
  1037. them. You do not need to check every edge in the IET as they are
  1038. sorted in increasing y order, thus you need only to check the
  1039. first edge on the IET.
  1040.  
  1041. As you are scanning from left to right, each time you cross an
  1042. edge, you toggle the associated polys on or off. That is, if poly
  1043. X is currently "off", it means you are not inside poly X. Then,
  1044. as you cross an edge that happens to be one of poly X's edge,
  1045. turn it "on". Of all polys that are "on", draw the topmost only.
  1046. If polys are not self-intersecting, you only need to recalculate
  1047. which one's on top when you cross edges. Your edge lists must
  1048. contain pointers to thir parent polygons. Here's a pseudo-code
  1049. for the algorithm:
  1050.  
  1051. Let polylist be the list of all poly
  1052. Put all edges of all polygon in IET
  1053. Empty AET
  1054. for Y varying from the topmost scanline to the last
  1055.      while the edge on top of the IET's topmost endpoint's Y
  1056. coodrinate equals Y do
  1057.           take the edge off the IET and insert it in the AET with
  1058. an insertion sort
  1059.           so that all edges in the AET are in increasing X
  1060. values.
  1061.      end_while
  1062.  
  1063.      discard any edge in AET for which the second endpoint's Y
  1064. value is Y
  1065.  
  1066.      previous_X=minus infinity
  1067.      current_poly=none
  1068.      for i=first edge through last edge in AET
  1069.           for X varying from previous_X to i's X coordinate
  1070.                draw a pixel at position (X,Y), attribute is taken
  1071. from current_poly
  1072.           end_for
  1073.           toggle polygons that are parent to i
  1074.           current_poly=find_topmost_poly()
  1075.      end_for
  1076.  
  1077.      update intercept values of all edges in AET and sort them by
  1078. increasing X intercept
  1079. end_for
  1080.  
  1081. A recent uproar about "s-buffers" made me explain this algorithm
  1082. a little more carefully. Paul Nettle wrote a FAQ explaining
  1083. something that he calls "s-buffering". It is essentially an
  1084. algorithm where, instead of calculating the spans on the fly as
  1085. above, you buffer them, that is, you precalculate all spans and
  1086. send them to a span buffer. Then you give the span buffer to a
  1087. very simple rasterizing function. It's a bit harder to take
  1088. advantage of coherence in that case (an example of coherence is
  1089. when we note that if the order of the edges do not change from
  1090. one scanline to the next, we don't need to recalculate which
  1091. spans are topmost, assuming polygons aren't interpenetrating).
  1092.  
  1093. Another very nice algorithm is the z-buffer algorithm, discussed
  1094. later. It allows interpenetrating polygon and permits mixing
  1095. rendering techniques in a rather efficient way (for real-time
  1096. rendering purposes).
  1097.  
  1098.  
  1099.  
  1100.  
  1101.      _________________________
  1102.  
  1103.  
  1104.  
  1105.  
  1106.  
  1107.  
  1108. 4    Data Structures
  1109.  
  1110.  
  1111.  
  1112.  
  1113. A very important aspect of 3d graphics is data structures. It can
  1114. speed up calculations by a factor of 6 and up, only if we put
  1115. some restriction on what can be represented as 3d entities.
  1116.  
  1117. Suppose that everything we have in the 3d world are closed
  1118. polyhedras (no self-crossing(s)!). Each polyhedra is constituted
  1119. of several polygons. We will need each polygon's normal and
  1120. vertexes. But how we list these is where we can improve speed.
  1121.  
  1122. It can be proved that each edge is shared by exactly two polygons
  1123. in a closed polyhedra. Thus, if we list the edges twice, once in
  1124. each polygon, we will do the transformations twice, and it will
  1125. take twice as long.
  1126.  
  1127. To that, add that each vertex is part of at least 3 points,
  1128. without maximum. If you list each point in every vertex it is
  1129. part of, you will do the transformations at least thrice, which
  1130. means it will take thrice the time. Three times two is six.
  1131. You're doing the same transformations at least six times over.
  1132.  
  1133. Thus, you need to list the vertexes in every polygon as pointers
  1134. to vertexes. This way, two polygons can share the same data for a
  1135. shared vertex, and will allow you to do the transformations only
  1136. once.
  1137.  
  1138. The same goes for edges, list the endpoints as pointers to
  1139. endpoints, thus you will spare yourself much runtime work.
  1140.  
  1141. Another interesting thing. All polygons have an interior and
  1142. exterior face in that model. Make it so that the normal to all
  1143. polygons point towards the exterior face. It is clear that, if
  1144. you are standing outside all objects, and all objects (polyhedra)
  1145. are closed, you cannot see an inside face. If, when transforming
  1146. polygons, you realize a polygon is facing away from the eye, you
  1147. do not to transform it, just skip it. To know if it's pointing
  1148. away from you do the following:
  1149.  
  1150. This is called back-face culling. Take the (a,b,c) vector from
  1151. the eye to any one endpoint of the polygon. (E.g., if the eye is
  1152. at (m,n,o) and a vertex of the poly is at (i,j,k), then the said
  1153. vector would be (i-m,j-n,k-o).) Take also (u,v,w) the normal
  1154. vector to the polygon. No matter what direction you are looking,
  1155. do the following operation:
  1156.  
  1157. au+bv+cw
  1158.  
  1159. If the result is positive, then it is facing away from you. If it
  1160. is negative, it is facing towards you. If it is null, it is
  1161. parallel to you. The operation is called scalar multiply of two
  1162. vectors. It is very notable that the above scalar multiplication
  1163. of two vectors is interestingly enough the D part of the plane
  1164. equation Ax+By+Cz=D.
  1165.  
  1166. You can also illuminate objects with a very distant light source
  1167. (e.g. the sun) very easily. Take the vector pointing away from
  1168. the light source (a,b,c) and the normal of the plane (u,v,w). Do
  1169. a scalar multiplication of both as above. The result can be
  1170. interpreted as the intensity of the lighting or shadow for the
  1171. polygon. The approximation is good enough to give interesting
  1172. results. You might also want to affect the shading of the polygon
  1173. according to its distance to origin, to yield a fog result.
  1174.  
  1175.  
  1176.  
  1177.  
  1178.      _______________________
  1179.  
  1180.  
  1181.  
  1182.  
  1183.  
  1184.  
  1185. 5    Texture mapping
  1186.  
  1187.  
  1188.  
  1189. What we are about to do would probably be more aptly named pixmap
  1190. mapping onto a plane. We want to take a pixmap (or bitmap,
  1191. although less appropriate a word, it is very popular) and ®stick¯
  1192. it to the surface of a polygon, cutting away any excess. The
  1193. result can be quite impressive. This, however, can be a painfully
  1194. slow operation. We will discuss here of the mathematics involved,
  1195. and after that, we will try to optimize them.
  1196.  
  1197. Let's give a polygon its own coordinate system, (i,j). Thus, all
  1198. points on the said polygon can be localized in (i,j), or (x,y,z),
  1199. or, once projected, in (u,v).
  1200.  
  1201.  
  1202.  
  1203. Figure 6
  1204.  
  1205. Figure 6 illustrates the problem. The blue plane is the
  1206. projection plane. The green triangle is a polygon we are
  1207. projecting on that plane and then scan-converting in (u,v)
  1208. coordinates. What we want to do is find, what (i,j) point
  1209. corresponds to any given (u,v) point when projected this way.
  1210. Then, the point (i,j) can be indexed in a pixmap to see what
  1211. color this pixel in (u,v) space should be.
  1212.  
  1213. Let the i and j vectors be expressed in (x,y,z) coordinates,
  1214. which they should normally be. We want a general solution.
  1215.  
  1216. i=(a,b,c)
  1217. j=(d,e,f)
  1218.  
  1219. Let also the origin of (i,j) space be pointed to in (x,y,z) by
  1220. the vector k=(m,n,o). Thus, a point at location (p,q) in (i,j)
  1221. space is the same as a point at location pi+qj+k in (x,y,z)
  1222. space. Furthermore, we have the projection ray defined as follow:
  1223. it shoots through point (r,s) in (u,v) space. This corresponds to
  1224. point (r,s,1) in (x,y,z) space. The equation of that ray is thus
  1225. t(r,s,1) where t can take any real value. Simplifying, we find
  1226. that the ray is (tr,ts,t), or
  1227.  
  1228.      x=tr
  1229. R:   y=ts
  1230.      z=t
  1231.  
  1232. or
  1233.      x=zr
  1234. R:   y=zs
  1235.      z=z
  1236.  
  1237. The point in the plane is
  1238.      x=pa+qd+m (1)
  1239. P:   y=pb+qe+n (2)
  1240.      z=pc+qf+o (3)
  1241.  
  1242. We want to find (p,q) as a function of (r,s). Here is what i did
  1243. in stupid MathCAD. [MathCAD flame deleted] I'm sorry I didn't use
  1244. the same variable names in MathCAD...
  1245.  
  1246. #######Begin picture of mathcad screen#########
  1247.  
  1248. ########End picture of mathcad screen#########
  1249.  
  1250. These equations for p and q can be optimized slightly for
  1251. computers. Since it is usually faster on a computer to do a(b+c)
  1252. than ab+ac, and that both are equivalent, we will try to
  1253. factorize a little.
  1254.  
  1255. p=(Mv+Nu+O)/(Pv+Qu+R)
  1256. where M,N,O,P,Q,R are as follows:
  1257. M=CXq-AZq
  1258. N=BZq-CYq
  1259. O=AYq-BXq
  1260. P=ZqXp-ZpXq
  1261. Q=YqZp-YpZq
  1262. R=YpXq-YqXp
  1263.  
  1264. They are all constants, so they need only to be calculated once
  1265. to draw the whole image.
  1266.  
  1267. Similarly for q, we have:
  1268. q=(Sv+Tu+U)/(Vv+Wu+X)
  1269. and
  1270. S=AZp-CXp
  1271. T=CYp-BZp
  1272. U=BXp-AYp
  1273. V=ZqXp-ZpXq
  1274. W=YQZP-YPZQ
  1275. X=YqXp-YpXq
  1276.  
  1277. All these constants should be calculated only once. Now we can
  1278. simplify a little with, knowing that P=V, Q=W, R=X.
  1279.  
  1280. p=(Mv+Nu+O)/(Pv+Qu+R)
  1281. q=(Sv+Tu+U)/(Pv+Qu+R)
  1282.  
  1283. This is noteworthy: (Q,P,R)(u,v,1)=uQ+vP+R, the denominator. But,
  1284. (Q,P,R)=(Xq,Yq,Zq)x(Xp,Yp,Zp). This cross-product of two vectors
  1285. will yield a vector that is perpendicular to the first two (i.e.
  1286. a normal to the plane). If these two vectors are of length 1,
  1287. then the cross product will also be of length 1. But we might
  1288. already have the normal from previous transformations (e.g. if
  1289. we're doing back-face culling). Then, we won't need to calculate
  1290. (Q,P,R) as it is the normal to the plane. Or we can deduce the
  1291. normal from these equations if we don't have it.
  1292.  
  1293. The denominator needs to be calculated only once per pixel, not
  1294. for both p and q. The numerator for p and q is different, though.
  1295. Furthermore, It is obvious that Mv+O are constant throughout a
  1296. constant-v line (horizontal line). Thus, it needs to be
  1297. calculated only once per line. We can spare ourselves the
  1298. multiply per pixel doing it incrementally. (We're still stuck
  1299. with a divide, though.) Example, let V=Mv+O, for a given v. Then,
  1300. when calculating Mv+Nu+O, we can use V+Nu. If we know V+Nua, then
  1301. we can calculate V+N(ua+1) as follow: V+Nua+N
  1302.  
  1303. Let W=V+Nua, then we get
  1304.  
  1305. W+N
  1306.  
  1307. That's a simple add instead of a multiply. But we can not get rid
  1308. of the divide, so we're stuck with a divide per pixel (and at
  1309. least two adds) per coordinate.
  1310.  
  1311. There is a way to solve this, however, and here's how to do it.
  1312.  
  1313. First, it is interesting to note the following. The plane
  1314. equation is Ax+By+Cz=D, and x=zu, y=zv, where (u,v) is the screen
  1315. coordinates of a point, and (x,y,z) is the corresponding world
  1316. coordinate of the unprojected point. Thus,
  1317.  
  1318. Auz+Bvz+Cz=D
  1319.  
  1320. What happens when z is a constant? Let us express v as a function
  1321. of u.
  1322.  
  1323. Bvz=-Auz-Cz+D
  1324. v=(-A/B)u+(-C/B)+D/(Bz)
  1325.  
  1326. Let M=-A/B, and N=d/(Bz)-C/B then we have
  1327.  
  1328. u=Mv+N
  1329.  
  1330. a linear equation. Furthermore, M is independant of z. Thus, a
  1331. slice of constant z in the plane is projected as a line on (u,v).
  1332. All of these slices project in lines that are all parallel to
  1333. each other (e.g. the slope, M, is independant of the slice
  1334. taken).
  1335.  
  1336. Now, a slice of constant z in the original Ax+By+Cz=D plane is a
  1337. line. Since z is constant throughout that line, all points of
  1338. that line are divided by the same number, independantly of (x,y).
  1339. Thus, the projected line is merely the original line scaled up or
  1340. down.
  1341.  
  1342. Up to now, we have been rendering polygons with a scanline
  1343. algorithm where the scanlines are horizontal. First, we have to
  1344. conceive that the scanlines need not be horizontal, merely
  1345. parallel. If we use a slope of M for the scanlines (M=-A/B, as
  1346. above), then we will be performing scans of constant z value. Two
  1347. advantages are very obvious. First, it is easy to calculate the z
  1348. value for a large number of pixel (it is constant throughout a
  1349. scanline) and second, texture mapping the scanline becomes a mere
  1350. scaling operation, which can be done incrementally. Furthermore,
  1351. M need be calculated only once for the whole polygon, while N
  1352. requires at most a divide and an add per scanline.
  1353.  
  1354. Note that if abs(A)>abs(B), you might want to express v as a
  1355. function of u instead of the above, so that the slope ends up as
  1356. being -B/A instead. (E.g., make certain that the slope is less
  1357. than or equal to 1 in absolute value). There is a degenerate
  1358. case: A=B=0. This is the case where the whole plane is of
  1359. constant z value. In that case, you can use horizontal scan lines
  1360. and just scale the texture by a factor of 1/z.
  1361.  
  1362. Thus, we're going to be rasterizing the polygon with
  1363. nonhorizontal scanlines of slope M. The equation of a scanline
  1364. can be expressed as:
  1365.  
  1366. v=Mu+Vi
  1367.  
  1368. Where Vi is the v coordinate when u is 0. All scanlines being
  1369. parallel, M never changes, and Vi identifies uniquely each and
  1370. every scanline. Say i=0 corresponds to the topmost scanline. Say
  1371. we want to find the intersection of the scanline with a polygon
  1372. edge. What we're really interested in is the u value of the
  1373. intersection of the two lines. The edge can be represented as
  1374. being:
  1375.  
  1376. v=Nu+K
  1377.  
  1378. Where K is the v value for the edge when u=0. Now, intersecting
  1379. the two yields
  1380.  
  1381. Nu+K=Mu+Vi
  1382. (N-M)u=(Vi-K)
  1383. u=(Vi-K)/(N-M)
  1384.  
  1385. That is, assuming N-M is not zero, which shouldn't be because
  1386. we're not including edges parallel to the scanlines in our scan
  1387. line algorithm, thus N-M is never zero.
  1388.  
  1389. Now, V(i+1) is (Vi)+1. When we go from scanline i to scanline
  1390. i+1, V is incremented by one. We thus get u', the intersection of
  1391. the two lines for the next scanline, as being:
  1392.  
  1393. u'=(Vi+1-K)/(N-M)
  1394. u'=(Vi-K)/(N-M)+1/(N-M)
  1395. u'=u+1/(N-M)
  1396.  
  1397. It is thus possible to calculate u from scanline to scanline
  1398. incrementally, bresenham stlye. To clarify things up a bit: if P1
  1399. and P2 delimit the edge, then let (p,q) be P2 minus P1 (that is,
  1400. p is the delta u and q is the delta v). Therefore, N is q/p (by
  1401. definition of slope). Now, we know M to be of the form -B/A (from
  1402. the plane equation, above). We need to calculate N-M for our
  1403. incremental calculations, which is relatively easy. We have
  1404.  
  1405. N-M=q/p-(-A/B)
  1406. N-M=q/p+A/B
  1407. N-M=(qB+Ap)/(Bp)
  1408.  
  1409. Thus, u' is really:
  1410. u'=u+Bp/(qB+Ap)
  1411.  
  1412. At one point in the algorithm, we will need to calculate which
  1413. scanline contains a particular point. As an example, when sorting
  1414. from topmost edge downwards, we need to use a definition of top
  1415. that is perpendicular to the scanline used. Then, we have the
  1416. slope of the scanline, which is, say, m. If the equation of the
  1417. line that interests us is:
  1418.  
  1419. y=mx+b
  1420.  
  1421. Then, we can sort the edges according to increasing b values in
  1422. the above equation. If we want to calculate b for point (i,j),
  1423. simply isolate b in the above equation.
  1424.  
  1425. This last bit is called constant-z texture mapping. It is not
  1426. particularly useful in the general case, for a number of reasons,
  1427. mainly added aliasing, and span generation problems. However, it
  1428. has a very well known and very used application. If we have
  1429. vertical walls and horizontal floors exclusively, and that the
  1430. eye is never banked, then the following is always true: the
  1431. constant-z lines are always either horizontal or vertical. For
  1432. example, the walls' constant-z lines are vertical while the
  1433. floors' and ceilings' are horizontal. So basically, you can
  1434. render floors and walls using the techniques described in this
  1435. chapter in a very straightforward manner, if you use both
  1436. horizontal and vertical scanlines. Just use the last bit about
  1437. constant-z texture mapping and optimize it for horizontal and
  1438. vertical scanlines. It is to note that looking up or down will
  1439. not affect the fact that walls will have vertical scan-lines and
  1440. floors and ceilings will have horizontal scan-lines.
  1441.  
  1442.  
  1443.  
  1444.  
  1445.      ________________________
  1446.  
  1447.  
  1448.  
  1449.  
  1450.  
  1451. 6    Of logarithms
  1452.  
  1453.  
  1454.  
  1455. If we have many multiplies or divides to make, little time and
  1456. lots of memory, and that absolute precision is not utterly
  1457. necessary, we may use the following tricks.
  1458.  
  1459. Assuming x and y are positive or null real numbers, and b a base
  1460. for a logarithm, we can do the following: (note: x**y means x
  1461. raised to the yth power, logby denotes base b logarithm of y).
  1462.  
  1463. logb(xy)=logbx+logby
  1464. b**logb(xy)=b**(logbx+logby)
  1465. xy=b**(logbx+logby)
  1466.  
  1467. Now, you might say that taking the log or the power of a value is
  1468. very time consuming, and it usually is. However, we will reuse
  1469. the trick that we used to do not so long ago, when pocket
  1470. computers were not available, we will make a logarithm table. It
  1471. is easy to store the values of all possible logaritms within a
  1472. finite, discrete range. As an example, if we are using 16 bit
  1473. fixed points or integer, we can store the result of a log of that
  1474. number as a table with 65536 entries. If each entry is 32 bits
  1475. wide, which should be sufficient, the resulting table will use
  1476. 256kb of memory. As for the power table, you can build another
  1477. lookup table using only the 16 most significant bits of the
  1478. logarithm, yielding a 16 or 32 bit wide integer or fixed point.
  1479. This will yield a 128 to 256kb table, for a grand total of under
  1480. 512kb. With today's machines, that should not be a problem. The
  1481. only problem is that you must then limit your scope to 16 bits
  1482. numbers. You realize of course that you can not multiply if one
  1483. or both of the numbers are negative. Well, you can do it this
  1484. way. Say x<0, then let u=-x, thus u>0. Then, xy=-uy, and uy you
  1485. can calculate with this method. Similarly,
  1486.  
  1487. x/y=b**(logbx-logby)
  1488.  
  1489. Thus divides are also a simple matter of looking it up in the
  1490. table and subtracting.
  1491.  
  1492. Powers are made much more simple also. I won't say anything long
  1493. winded about powers, but here is, in short:
  1494.  
  1495. logb(x**y)=y logbx
  1496. x**y=b**(y logbx)   (*)
  1497. And from there, even better yet:
  1498. x**y=b** ( b** [ logby+logb(logbx) ] )
  1499.  
  1500. For y=constant, some shortcuts are usually possible. Since ax can
  1501. sometimes be very quickly calculated with left shifts, you can
  1502. use (*) instead of the last equation. Very common example:
  1503. 320y=(256+64)y
  1504. 320y=256y+64y
  1505. 320y=(y<<8)+(y<<6)
  1506.  
  1507. Where x<<y denotes "x, left shifted y times". A left shift of y
  1508. is equivalent of multiplying by 2**y. The ancient egyptians only
  1509. knew how to multiply this way (at least, as far as I know, for a
  1510. while, like for the pyramids and stuff - I think they even
  1511. calculated an approximation of pi during that time, and they
  1512. couldn't even multiply efficiently! :-)
  1513.  
  1514.  
  1515.  
  1516.      ______________________
  1517.  
  1518.  
  1519.  
  1520.  
  1521. 7    More on data structures and clipping
  1522.  
  1523.  
  1524.  
  1525. Usually, the space viewed by the eye can be defined by a very
  1526. high square-based pyramid. Normally, it has no bottom, but in
  1527. this case it might have one for practical purposes. Let us say
  1528. that the eye has a width of vision (horizontally and vertically)
  1529. of 90 degrees. (Since it is square, that angle is slightly larger
  1530. when taken from one corner to the opposite.) Thus, we will see
  1531. 45ø to both sides, up and down. This means a slope of one. The
  1532. planes bounding that volume can be defined as x<z, x>-z, y<z and
  1533. y>-z. To a programmer with an eye for this, it means that it easy
  1534. to ®clip¯, i.e. remove obviously hidden polygons, with these
  1535. bounds. All polygons that do not satisfy either of these
  1536. inequalities can be discarded. Note that these polygons must fall
  1537. totally outside the bounding volume. This is called trivial
  1538. rejection.
  1539.  
  1540. For polygons that are partially inside, partially outside that
  1541. region, you may or may not want to clip (I am not sure yet of
  1542. which is more efficient - I suspect it depends on the situation,
  1543. i.e. number of polygons, number of edges in each polygon...)
  1544.  
  1545. There is also a slight problem with polygons that are partially
  1546. behind, partially in front of the eye. Let us give a little
  1547. proof. All of our polygon-drawing schemes are based on the
  1548. assumption that a projected line is also a line. Here is the
  1549. proof of that.
  1550.  
  1551. Let us consider the arbitrary line L, not parallel to the plane
  1552. z=0, defined parametrically as:
  1553.      x= at+b
  1554. L:   y= ct+d
  1555.      z= t
  1556.  
  1557. Now, let us define the projection P(x,y,z) as follows:
  1558.  
  1559. P(x,y,z): u=x/z
  1560.      v=y/z
  1561.  
  1562. Then, P(L) becomes
  1563.  
  1564. P(L):     u=(at+b)/t
  1565.      v=(ct+d)/t
  1566.  
  1567. We are using a segment here, not an infinite line. If no point
  1568. with t=0 needs to be projected, we can simplify:
  1569.  
  1570. P(L):     u=a+b/t
  1571.      v=c+d/t
  1572.  
  1573. Let us measure the slope of that projection:
  1574.  
  1575. du/dv=(du/dt) / (dv/dt) = (-b/t2)/(-d/t2)
  1576.  
  1577. Since t is not 0
  1578.  
  1579. du/dv=b/d, a constant, thus the parametric curve defined by P(L)
  1580. is a straight line if t is not zero.
  1581.  
  1582. However, if t is null, the result is indeterminate. We can try to
  1583. find the limits, which are pretty obvious. When t tends towards
  1584. zero+, then u will tend towards positive (or negative if b is
  1585. negative) infinity. If t tends towards zero-, then u will tend
  1586. towards negative (or positive, if b is negative) infinity. Thus,
  1587. the line escapes to one infinity and comes back from the other if
  1588. t can get a zero value. This means that if the segment crosses
  1589. the plane z=0, the projection is not a line per se.
  1590.  
  1591. However, if the line is parallel to z=0, L can be defined as
  1592. follows:
  1593.      x=at+b
  1594. L:   y=ct+d
  1595.      z=k
  1596.  
  1597. Where k is the plane that contains L. P(L) becomes
  1598.  
  1599. P(L):     u=(at+b)/k
  1600.      v=(ct+d)/k
  1601.  
  1602. The equations do not admit a null k. For a non-null k, we can
  1603. find the slope:
  1604.  
  1605. du/dv = (du/dt)/(dv/dt) = (a/k)/(c/k)
  1606. du/dv=a/c
  1607.  
  1608. Still a constant, thus P(L) is a line. However, if k is null, we
  1609. do not get a projection. As k gets close to zero, we find that
  1610. the projection lies entirely at infinity, except for the special
  1611. case b=d=0, which is an indetermination.
  1612.  
  1613. This will have a practical impact on our 3d models. If a cube is
  1614. centered on the eye, it will be distorted unless we somehow take
  1615. that into consideration. The best way to do it, with little
  1616. compromise and efficient coding, is to remove all parts of
  1617. polygons that lie in the volume defined by z<=0. Of course, if an
  1618. edge spans both sides of the plane z=0, you will have to cut it
  1619. at z=0, excluding everything defined by z<=0. Thus, you will have
  1620. to make sure the point of the edge with the smaller z coordinate
  1621. is strictly greater than 0. Normally, you would have to take the
  1622. next higher real number (whatever that might be!) but due to
  1623. hardware contraints, we will use any arbitrarily small number,
  1624. such as z=.001 or even z=.1 if you are building a real-time
  1625. something in which infinite precision is not required.
  1626.  
  1627. Lastly, you might not want to render objects that are very far
  1628. away, as they may have very little impact on the global scene, or
  1629. because you are using a lookup table for logarithms and you want
  1630. to make sure that all values are within range. Thus, you will set
  1631. an arbitrary maximum z for all objects. It might even be a fuzzy
  1632. maximum z, such as ®I don't wand any object to be centered past
  1633. z0¯. Thus, some objects may have some part farther than z0, but
  1634. that may be more efficient than the actual clipping of the
  1635. objects.
  1636.  
  1637. Moreover, you might want to make several polygon representations
  1638. of the objects, with different levels of details as measured by
  1639. the number of polygons, edges and vertices, and show the less
  1640. detailed ones at greater distances. Obviously, you would not
  1641. normally need to rasterize 10 polygons for an object that will
  1642. occupy a single pixel, unless you are doing precision rendering.
  1643. (But then, in that case, you'll have to change the whole
  1644. algorithm...)
  1645.  
  1646. You may want to perform your trivial rejection clipping quickly
  1647. by first rotating the handle coordinates of each polyehdra (the
  1648. handle coordinate is the coordinate of its local coordinates'
  1649. origin), and performing trivial rejection on them. As an example,
  1650. if you determine that all of a polyhedra is contained within a
  1651. sphere of radius r, and that this polyhedra is ®centered¯ at z=-
  1652. 3r, then it is obvious that it is entirely outside the view
  1653. volume.
  1654.  
  1655. Normally, the view volume is exactly one sixth of the total
  1656. viewable volume (if you do not take into account the bottom of
  1657. the pyramid). Thus, you will statistically eliminate five sixths
  1658. of all displayable polyhedras with such a clipping algorithm.
  1659.  
  1660. Let's make a brief review of our speed improvements over the
  1661. brute-force-transform-then-see-no-think algorithm. We multiplied
  1662. the speed by six with our structure of pointers to edges that
  1663. point to vertexes. Then, we multiply again by two (on average)
  1664. with back-face culling, and now by six again. The overall
  1665. improvement is a factor of 72! This shows you how a little
  1666. thinking pays. Note however that that last number is a bit skewed
  1667. because some operations do not take as long as some others, and
  1668. also by the fact that most polyhedra do not contain a nearly
  1669. infinite amount of vertices.
  1670.  
  1671. We have not taken into account several optimizations we
  1672. mentioned, such as the lessening of detail, a bottom to the view
  1673. volume and we did not compare with other kinds of algorithm. But
  1674. another appeal of this algorithm is that it draws once, and only
  1675. once to each pixel, thus allowing us to superimpose several
  1676. polygons with minimal performance loss, or make complicated
  1677. calculations for each pixel knowing that we will not do it twice
  1678. (or more!) for some pixels. Thus, this algorithm lends itself
  1679. very well to texture mapping (or more properly, pixmap mapping),
  1680. shading or others.
  1681.  
  1682. However, we have to recognize that this approach is at best an
  1683. approximation. Normally, we would have to fire projection rays
  1684. over the entire area covered by a pixel (an infinite number of
  1685. rays), which will usually amount to an integral, and we did not
  1686. perform any kind of anti-aliasing (more on that later), nor did
  1687. we filter our signal, nor did we take curved or interpenetrating
  1688. surfaces into account, etc... But it lends itself very well to
  1689. real-time applications.
  1690.  
  1691.  
  1692.  
  1693.  
  1694.  
  1695. 8    A few more goodies...
  1696.  
  1697.  
  1698.  
  1699. Anti-aliasing is something we invented to let us decrease the
  1700. contrast between very different pixels. More properly, it should
  1701. be considered an approximation of high frequencies in a
  1702. continuous image through intensity variations in the discrete
  1703. view space. Thus, to reduce the stairway effect in a diagonal
  1704. line, we might want to put some gray pixels.
  1705.  
  1706.  
  1707.  
  1708. Figure 7
  1709.  
  1710. Figure 7 demonstrates this. This diagonal line a was ®anti-
  1711. aliased¯ manually (I hear the scientists scream) in b, but it
  1712. should let you understand the intention behind anti-aliasing.
  1713.  
  1714. Scientists use filters, and so will we, but I will not discuss
  1715. fully the theory of filters. Let us see it that way. We first
  1716. generate a normal image, without any kind of anti-aliasing. Then
  1717. we want to anti-aliase it. What we do is take each pixel, modify
  1718. it so it resembles its non-anti-aliased neighbors a little more
  1719. and show that on screen.
  1720.  
  1721. Let us further imagine that how we want to do that specifically
  1722. is to take one eight of each of the four surrounding pixel
  1723. (north, south, east and west) and one half of the ®real¯ pixel,
  1724. and use the resulting color for the output pixel.
  1725.  
  1726. If our image is generated using a 256-color palette, there comes
  1727. a handy little trick. Let us imagine that we want to take one
  1728. half of color a and one half of color b and add them together.
  1729. Well, there are 256 possible values for a. And for each single
  1730. value of a, there are 256 values of b, for a total number of
  1731. 256x256 pairs, or 64k pairs. We can precalculate the result of
  1732. mixing one half a with one half b and store it in a matrix 64kb
  1733. large.
  1734.  
  1735. Now let us call the four surrounding pixels n,s,e,w and the
  1736. central point o. If we use the above matrix to mix n and s, we
  1737. get a color A that is one half n plus one half s, that is
  1738. A=n/2+s/2. Then, mix e and w as B=e/2+w/2. Then, mix A and B.
  1739. C=A/2+B/2=n/4+s/4+e/4+w/4. Lastly, mix C and o, and we get
  1740. D=o/2+n/8+s/8+e/8+w/8, the exact desired result. Now, this was
  1741. all done through a lookup table, thus was very fast. It can
  1742. actually be done in real time.
  1743.  
  1744. Thus, if you want to do this, you will first create the original,
  1745. un-anti-aliased image in a memory buffer, and then copy that
  1746. buffer as you anti-aliase it on screen. Obviously, it will be
  1747. slower a little, but you might want to consider the increased
  1748. picture quality. However, you will probably want to do this in
  1749. assembler, because it can be highly optimized that way (most
  1750. compilers get completely confused...)
  1751.  
  1752. Another very nice technique is this. For pixel (u,v) on screen,
  1753. mix equal parts of pixels (u,v), (u+1,v), (u,v+1), (u+1,v+1) in
  1754. your rendering buffer. It's as if the screen was half a pixel of
  1755. center in relation to the rendering buffer. You'll need to render
  1756. an extra row and column though.
  1757.  
  1758.      __________________
  1759.  
  1760.  
  1761. It is also interesting to note that the silhouette of a projected
  1762. sphere is a circle. The centre of the circle is the projection of
  1763. the centre of the sphere. However, the radius is NOT the
  1764. projection of the radius of the sphere. However, when the
  1765. distance is large enough when compared to the radius of the
  1766. sphere, this approximation is good enough.
  1767.  
  1768.      __________________
  1769.  
  1770.  
  1771. To determine if a point is contained inside a polyhedra, we
  1772. extend our test from the polygons like this: if a line drawn from
  1773. the point to infinity crosses an odd number of faces, the it is
  1774. contained, otherwise it is not.
  1775.  
  1776.      __________________
  1777.  
  1778.  
  1779. If you want to approximate a continuous spectrum with 256 colors
  1780. (it will not look perfect though), use base 6. That is, using the
  1781. RGB model (we could use HSV or any other), you will say that red,
  1782. green and blue can all take 6 different values, for a total
  1783. number of combinations of 216. If you add an extra value to green
  1784. or red, you get 252 colors, close to the maximum. However, 216 is
  1785. not bad and it leaves you 40 colors for system purposes, e.g.
  1786. palette animations, specific color requirements for, let's say,
  1787. fonts and stuff. Under Microsoft Windows, 20 colors in the
  1788. palette are already reserved by the system, but you still have
  1789. enough room for your 216 colors. By the way, I think it's stupid
  1790. that MS Windows does not allow you to do that as a standard
  1791. palette allocation. You have to define your own video driver to
  1792. enable programs that limit themselves to dithering to use those
  1793. colors. And that's living hell.
  1794.  
  1795. In addition, if the colors are arranged in a logical way, it will
  1796. be easier to find the closest match of the mix of two colors as
  1797. per anti-aliasing, above. (A bit like cheaper 24 bit color.)
  1798.  
  1799. Some people give 8 values to red and green, and 4 values to blue,
  1800. for a total of 256 colors, arguing that shades of blue are
  1801. perceived more difficultly. Personnally, I think the result is
  1802. not as good as the base six model I previously described.
  1803.  
  1804. Some other nice techniques: divide your palette in spans.
  1805. Example, allocate palette registers 16-48 to shades of blue. Use
  1806. only registers 20-44 in your drawings, but arrange so that it
  1807. goes from white-blue at color #16 through black-blue at color
  1808. #48. Then, shading is a simple matter of adding something to make
  1809. it darker, or subtracting to make it lighter. Make a few spans
  1810. for your essential colors, and maybe keep 30 for anti-aliasing
  1811. colors. These should not be used in your pixmaps. They could be
  1812. used only when performing final anti-aliasing of the rendered
  1813. picture. Pick these colors so to minimize visual artifacts. (E.g.
  1814. you have a blue span at 16-48, and a red span at 64-100. But you
  1815. don't have any purple, so if blue happens to sit next to red,
  1816. anti-aliasing will be hard to achieve. Then, just a bunch of your
  1817. special 30 colors for shades of purple.) Another particular trick
  1818. to increase the apparent number of colors (or rather, decrease
  1819. the artifacts created by too few colors), you could restrict your
  1820. intensity to lower values. E.g. make your darkest black/gray at
  1821. 25% luminosity, and your lightest white at 75%. This will make
  1822. your graphics look washed out, but your 256 colors will be spread
  1823. over a narrower spectrum.
  1824.  
  1825.  
  1826.  
  1827.  
  1828.      _______________________
  1829.  
  1830.  
  1831.  
  1832.  
  1833.  
  1834. 9    Sorting
  1835.  
  1836.  
  1837.  
  1838. Sorting becomes an important issue because we do it a lot in our
  1839. 3d rendering process. We will discuss here briefly two general
  1840. sorting algoritmthms.
  1841.  
  1842. The first sort we will discuss is the bubble sort. It is called
  1843. this way because the elements seem to ®float¯ into position as
  1844. the sort progresses, with ®lighter¯ elements floating up and
  1845. ®heavier¯ ones sinking down. Given the set S, composed of
  1846. elements s0, s1, ..., sn-1. This set has n elements. Let us also
  1847. suppose that we can compare two elements to know which of the two
  1848. is ®lighter¯, i.e. which one goes first. Let us also suppose that
  1849. we can exchange two consecutive elements in the set S. To sort
  1850. the set S, we will proceed as follow. Starting at element 0 up to
  1851. element n-2, we will compare each element to its successor and
  1852. switch if necessary. If no switches are made, the set is sorted.
  1853. Otherwise, repeat the process. In pseudocode, we have:
  1854.  
  1855. repeat the following:
  1856.      for i varying from 0 to n-2, repeat the following
  1857.           if si should go after si+1, then exchange si and si+1
  1858.      end of the loop
  1859. until no elements are switched in the for loop (i.e the set is
  1860. sorted)
  1861.  
  1862. This algorithm, while very inefficient, is very easy to
  1863. implement, and may be sufficient for relatively small sets (n of
  1864. about 15 or less). But we need a better algoritm for greater
  1865. values of n.
  1866.  
  1867. However, we have to keep in mind that we are sorting values that
  1868. are bounded; that is, these values have a finite range, and it is
  1869. indeed small, probably 16 bits or 32 bits. This will be of use
  1870. later. But let us introduce first the sort. It is called the
  1871. radix sort.
  1872.  
  1873. Assume we are give a set S of numbers, all ranging from 0 to 9999
  1874. inclusively (finite range). If we can sort all si according to
  1875. the number in the ones' place, then in the tens' place, then in
  1876. the hundreds' place, then thousands' space, we will get a sorted
  1877. list. To do this, we will make a stack for each of the 10
  1878. possible values a digit can take, then take each number in the S
  1879. set and insert them in the appropriate stack, first for the
  1880. rightmost digit. Then, we will regroup the 10 stacks and start
  1881. over again for the second rightmost digit, etc... This can be
  1882. done in pseudocode as follows:
  1883.  
  1884. divisor=1
  1885. while divisor<10000, do the following:
  1886.      for i varying from 0 to n-1, do the following:
  1887.           digit=(si divided (integer version) by divisor) modulo
  1888. 10
  1889.           put si on stack # digit
  1890.      end of for loop
  1891.      empty set S
  1892.      for i varying from 0 to 9, do the following:
  1893.           take stack # i and add it to set S
  1894.      end of for loop
  1895.      divisor=divisor x 10
  1896. end of while loop
  1897.  
  1898.  
  1899. Now, let us assume that we want to work with hexadecimal numbers.
  1900. Thus, we will need 16 stacks. In real life, what we want to sort
  1901. is edges or something like that. If we limit ourselves to 16384
  1902. edges (16k, 12 bits), then our stacks need to be able to fit
  1903. 16384 objects each. We could implement them as arrays and use
  1904. lots of memory. A better way is to use lists.
  1905.  
  1906.  
  1907.  
  1908. One could also use the quicksort algorithm, which has a
  1909. complexity of O(nxln(n)), which seems worse than n for this
  1910. particular problem. It is also a bit more tedious to code (in my
  1911. humble opinion). In our problem, we may not even need to sort for
  1912. 16 bits. The only place that we need a powerful sorting algorithm
  1913. is when sorting all edges from top to bottom and from left to
  1914. right. If we are certain that all coordinates are bound between,
  1915. say, 0 and 200 (e.g. for y coordinate), then we can sort on 8
  1916. bits. For the x coordinate, though, depending if we already
  1917. clipped or not, we may have to sort on the full 16 bits. When
  1918. using the z-buffer algorithm (decribed scantily elsewhere), you
  1919. want to do a z-sort, then drawing the polygons that are on top
  1920. FIRST so that you can avoid these nasty pixel writes.
  1921.  
  1922.  
  1923.  
  1924.  
  1925.  
  1926.      ______________________
  1927.  
  1928.  
  1929.  
  1930.  
  1931.  
  1932.  
  1933.  
  1934. 10   Depth-field rendering and the Z-buffer algorithm
  1935.  
  1936.  
  1937.  
  1938.  
  1939. You can approximate any 3d function z=f(x,y) with a depth field,
  1940. that is, a 2d matrix containing the z- values of f(x,y). Thus,
  1941. z=M[x][y]. Of course, if you want any precision at all, it has
  1942. somewhat large memory requirements. However, rendering this depth-
  1943. field is fairly efficient and can give very interesting results
  1944. for smooth, round surfaces (like a hill range or something).
  1945.  
  1946. To render a depth field, find the vector V from our eye to the
  1947. pixel in the projection plane in 3space. Shoot a ray, as before,
  1948. using that vector. Start at the eye and move in direction V a
  1949. little. If you find that you are still above the surface (depth
  1950. field), then keep going (add another V and so on) until you hit
  1951. or fall under the surface. When that happens, color that pixel
  1952. with the color of the surface. The color of the surface can be
  1953. stored in another matrix, C[x][y]. In pseudocode, we get:
  1954.  
  1955.  
  1956. M[x][y]=depth field
  1957. C[x][y]=color field
  1958. for each pixel to be rendered, do:
  1959.      V=projection ray vector
  1960.      P=Eye coordinates
  1961.      while (M[Px][Py]<Pz)
  1962.           P=P+V
  1963.      end while
  1964.      color the current pixel with C[Px][Py]
  1965.      if we are using z-buffer algorithm, then set Z[Px][Py] to Pz
  1966. (see below)
  1967. end for
  1968.  
  1969.  
  1970. Of course, you will want to break the loop when P is too far from
  1971. origin (i.e. when you see that nothing is close through that
  1972. projection ray, stop checking).
  1973.  
  1974. For vector graphics, we were forced to place our eye centered on
  1975. origin and looking towards positive z values. That forced us to
  1976. rotate everything else so the eye would be positioned correctly.
  1977. Now, obviously, we cannot rotate the whole depth field as it
  1978. would be very time- and memory-consuming. Instead, the eye now
  1979. can be centered anywhere (the initial value for P need not be
  1980. (0,0,0)) and the vector V can be rotated into place. Even better,
  1981. if we can find the vector W from the eye to the center of the
  1982. projection plane, and the two base vectors u and v in 3d for the
  1983. projection plane, all of the other vectors Vu,v wil be a linear
  1984. combination of the form: V=W+au+bv where (a,b) is the coordinate
  1985. of the pixel in the (u,v) coodinates system. If we are doing this
  1986. rendering with scan lines (as we most probably are doing), then
  1987. the above equation can be done incrementally, e.g. say Va,y=A,
  1988. then Va+1,y=A+u. No multiplications whatsoever.
  1989.  
  1990. The z coordinate of each pixel is readily available in the above
  1991. algorithm and it has a use. Let us store all the z coordinate
  1992. values in a matrix Z[a][b]. If we then want to add a flat image
  1993. with constant z value (say z=k) to the already rendered scene, we
  1994. can do it very easily by writing only to pixels for which
  1995. Z[a][b]>k. Thus, we can use another rendering technique along
  1996. with depth-field rendering. This is called the z-buffer
  1997. algorithm. Accelerating calculations for the z-buffer with
  1998. polygons, you might want to remove the divide-per-pixel. That is
  1999. fully discussed towards the end of the texture-mapping chapter.
  2000.  
  2001. A few optimizations are easily implemented. Let us assume we are
  2002. drawing from the bottom of the screen to the top, going in
  2003. columns instead of rows. First, if you are not banked (or banked
  2004. less than the steepest slope, e.g. no ®overhangs¯ are seen in the
  2005. picture) and you have rendered pixel a. Then you know how far
  2006. pixel a is from you. Then, then next pixel up, pixel b, will be
  2007. at least as far as pixel a. Thus, you do not need to fire a ray
  2008. starting from your eye to pixel b, you need only to start as far
  2009. as pixel a was. In fact, if you are not banked, you need only to
  2010. move your Pz coordinate up a little and keep going.
  2011.  
  2012. Therefore, if pixel a is determined to be a background pixel, so
  2013. will all the pixels above it.
  2014.  
  2015. You might want to render all of you polygons and other entities
  2016. using a z-buffer algorithm. As an example, say you have 3
  2017. polygons, A, B and C, and a sphere S. You could tell your
  2018. renderer to render poly A, which it would do right away, filling
  2019. the z-buffer at the same time. That is, for each screen pixel
  2020. that the buffer A covers, it would calculate the z-value of the
  2021. polygon in that pixel, then check with the z-buffer if it stands
  2022. in front of whatever is already drawn. If so, it would draw the
  2023. pixel and store the polygon's z-value for that pixel in the z-
  2024. buffer. Then render sphere S, which it would do right away again,
  2025. calculating the z-value for each pixel and displying only the
  2026. visible pixels using the z-buffer, and updating the z-buffer
  2027. accordingly. Lastly polygons B and C would be displayed.
  2028.  
  2029. What you gain by using this algorithm instead of a scan line
  2030. algorithm is obvious. Interpenetrating polygons behave correctly,
  2031. mixing rendering techniques is straightforward. There is but one
  2032. annoying thing: calculating the z value for a polygon in any
  2033. point on the screen involves a division, hence a division per
  2034. pixel if using z-buffer. But we can do better.
  2035.  
  2036. We are not really interested in the z value of a polygon in a
  2037. determined pixel, what we want really is to know what is visible.
  2038. To that purpose, we can obviously calculate z and show the object
  2039. with the smallest z value. Or we could evaluate z^2 and show the
  2040. one with the smallest z^2 value. If we want whatever has the
  2041. smallest z value, this is the same as saying that we want
  2042. whatever has the largest 1/z value. Fortunately 1/z is way easier
  2043. to calculate. Let the plane equation be Ax+By+Cz=D, and the
  2044. projection equations be u=x/z and v=y/z, or
  2045.  
  2046. Ax+By+Cz=D
  2047. x=uz
  2048. y=vz
  2049.  
  2050. or
  2051.  
  2052. A(uz)+B(vz+)Cz=D
  2053.  
  2054. or
  2055.  
  2056. z(Au+Bv+C)=D
  2057.  
  2058. (Au+Bv+C)/D=1/z
  2059.  
  2060. 1/z= (A/D)u+(B/D)v+(C/D)
  2061.  
  2062. Let M=A/D (a constant), N=B/D (another constant) and K=C/D (a
  2063. third constant), then
  2064.  
  2065. 1/z=Mu+Nv+K
  2066.  
  2067. As we can see plainly, we don't have a divide. This is linear so
  2068. can be calculated incrementally. E.g. for a given scanline (v=k),
  2069. the equation is
  2070.  
  2071. 1/z=Mu+Nk+K
  2072.  
  2073. Let P=Nk+K (a constant)
  2074.  
  2075. 1/z=Mu+P
  2076.  
  2077. A linear equation. When going from pixel u to pixel u+1, we just
  2078. need to add M to the value of 1/z. A single add per pixel.
  2079.  
  2080. There's also a double edged optimization that you can make, which
  2081. follows.
  2082.  
  2083. Sort your polygons in generally increasing z order (of course,
  2084. some polygons will have an overlap in z, but that's everybody's
  2085. problem with this algorithm - your solution is just as good as
  2086. anybody else's.) Now, you're scan-converting a polygon. If you
  2087. can find a span (e.g. a part of the current scanline) for which
  2088. both ends are behind the same convex object, then that span is
  2089. wholly hidden. As an example of this, if both enpoints of the
  2090. current span (on the scanline) are behind the same convex object,
  2091. then the current span is entirely hidden by that convex object.
  2092. If all of your objects are convex polygons, then you don't have
  2093. to check for convexity of the object. Another interesting example
  2094. is this:
  2095.  
  2096. If current span's left endpoint is hidden
  2097.      let A be the object that hides the left endpoint (of the
  2098. span)
  2099.      if A hides the right endpoint
  2100.           stop drawing the scanline
  2101.      else
  2102.           start drawing from the right end of the span,
  2103. leftwards, until object A is in front of the span
  2104.      end if
  2105. else
  2106.      if current span's right endpoint is hidden
  2107.           let B be the object that hides the right endpoint
  2108.           scan rightwards until object B is in front of the span
  2109.      else
  2110.           draw span normally
  2111.      end if
  2112. end if
  2113.  
  2114. This can be applied also to polyhedras if they are convex. This
  2115. last optimization should be relatively benign (e.g. I expect a
  2116. factor of two or some other constant from it).
  2117.  
  2118.  
  2119.  
  2120.  
  2121.  
  2122.      _____________________
  2123.  
  2124.  
  2125.  
  2126.  
  2127.  
  2128.  
  2129. 11   Bitmap scaling and mixing rendering techniques
  2130.  
  2131.  
  2132.  
  2133.  
  2134. Another popular technique is called bitmap (or more properly
  2135. pixmap) scaling. Say you are looking in a parallel direction to
  2136. the z axis, and you are looking at a picture with constant z.
  2137. Through the projection, you will realize that all that happens to
  2138. it is that it is scaled by a constant factor of 1/z. That is, if
  2139. you look at a photograph lying in the plane z=2, it will look
  2140. exactly the same (once projected) as the same photograph at z=1,
  2141. except that it will be twice as small. You could also do that
  2142. with the picture of a tree. From a great distance, the
  2143. approximation will be fairly good. When you get closer, you might
  2144. realize that the tree looks flat as you move. But nonetheless, it
  2145. can yield impressive results. Bitmap scaling often suffices for
  2146. ball-like objects. You could use it for an explosion, smoke,
  2147. spherical projectile, etc... The advantage of bitmap scaling is
  2148. that it has only one point, probably it's center, to transform
  2149. (i.e, translate and rotate into place in 3d), and scaling it is
  2150. very fast.
  2151.  
  2152. The advantages of such a technique are obvious. First, it is very
  2153. easy to scale a pixmap by a given factor. Second, the z value is
  2154. a constant, thus it can easily be incorporated in the above z-
  2155. buffer algorithm with depth-field rendering for very nice
  2156. results. However, it will be obvious to the careful observer that
  2157. you can not smoothly go around it, as it is always facing you the
  2158. same way (i.e. you cannot see the back door to a house if it is
  2159. rendered this way, because, obviously, the bitmap being scaled
  2160. does not have a back door).
  2161.  
  2162. Moreover, the depth-field rendering technique blends quite
  2163. smoothly with vector graphics. First, render the depth field in a
  2164. buffer B. Do a scan-line algorithm for the vector graphics, but
  2165. with an added twist: the ®background¯ bitmap is B, and use the z-
  2166. buffer algorithm to determine if a pixel in B lies in front of
  2167. the current polygon if any.
  2168.  
  2169. If you do decide to use depth-field rendering along with vector
  2170. graphics, and you decide not to bank your eye, then you can
  2171. eliminate one full rotation from all calculations. E.g. if your
  2172. eye is not banked, you do not need to rotate around the z axis.
  2173.  
  2174.  
  2175.  
  2176.  
  2177.      _________________________
  2178.  
  2179.  
  2180.  
  2181.  
  2182.  
  2183. Scaling a bitmap can be done fairly easily this way. The
  2184. transformation is like this: we want to take every (u,v) point
  2185. and send it into (u/s,v/s), where s is the scaling factor (s=2
  2186. means that we are halving the size of the bitmap). The process is
  2187. linear.
  2188.  
  2189. (i,j)=(u/s,v/s)
  2190. (is,js)=(u,v)
  2191.  
  2192. Thus, if we are at pixel (i,j) on screen, it corresponds to pixel
  2193. (is,js) in the bitmap. This can also be done incrementally. If
  2194. un=A, then un+1=A+s. No multiplications are involved. Thus, if we
  2195. are viewing a bitmap at z=2 according to the above said 3d
  2196. projections, then we can render it using a scan-line algorithm
  2197. with these equations and s=z=2.
  2198.  
  2199. If a bitmap has holes in it, we can precalculate continuous spans
  2200. in it; e.g., in an O, drawing a scanline through the center shows
  2201. you that you are drawing two series of pixels. If you want the
  2202. center to be transparent (allow a background to show through),
  2203. then you can divide each scanline of the O in two spans, one left
  2204. and one right, and render them as two separate bitmaps; e.g.,
  2205. break the O in ( and ) parts, both of which have no holes. Note
  2206. however that we have a multiplication at each beggining of a
  2207. span, so this might not be efficient. Or you could select a color
  2208. number as being the ®transparent¯ color, and do a compare per
  2209. pixel and not blit if it is the transparent color. Or you can
  2210. allocate an extra bitplane (a field of bit, one bit per pixel)
  2211. where each bit is either 1 for opaque (draw this pixel) or 0 for
  2212. transparent (do not draw this pixel).
  2213.  
  2214.  
  2215.  
  2216.  
  2217.  
  2218.      ______________________
  2219.  
  2220.  
  2221.  
  2222.  
  2223.  
  2224. Partially translucid primitives can be rendered pretty easily.
  2225. This is an old trick, you see. You can even scavenge the lookup
  2226. table you used for anti-aliasing. If you want to have a red glass
  2227. on a part of the screen, the lookup table will tell you how to
  2228. redden a color. That is, the item #c in the lookup table tells
  2229. you what color is color c with a little red added. The lookup
  2230. table need not be calculated on the fly, of course. It can be
  2231. calculated as part of your initialization or whatever.
  2232.  
  2233.  
  2234.  
  2235.  
  2236.      ______________________
  2237.  
  2238.  
  2239.  
  2240.  
  2241.  
  2242.  
  2243. 12   About automatically generating correctly oriented normals to
  2244. planes.
  2245.  
  2246.  
  2247.  
  2248. This bit is not meant to be a blindingly fast algorithm for
  2249. region detection. It is assumed that these calculations are made
  2250. before we start generating pictures. Our objective is to generate
  2251. a normal for each plane in a polyhedra oriented so that it points
  2252. outwards respectively to the surface. We assume all surfaces are
  2253. defined using polygons. But before determining normals for
  2254. planes, we will need to examine polygons in 3d.
  2255.  
  2256. Each polygon being constituted of a set of edges, we will later
  2257. require a vector for each edge that points in the polygon for any
  2258. edge. See below.
  2259.  
  2260.  
  2261.  
  2262. Figure 8
  2263.  
  2264. In figure 8, we can see in red the said vectors for all edges. As
  2265. we can see, the vectors do not necessarely point towards the
  2266. general center of mass. We could use the algorithm we used for
  2267. rendering polygons in two dimensions to determine the direction
  2268. of the arrows, but that is a bit complicated and involves trying
  2269. to find a direction that is not parallel to any of the above
  2270. lines. Thus, we will try another approach.
  2271.  
  2272. First, pick any point in the polygon. Then, from that point, take
  2273. the farthest point from it. See below:
  2274.  
  2275.  
  2276.  
  2277. Figure 9
  2278.  
  2279. (Please, do not measure it with a ruler :-) )
  2280. Now say we started with the point marked by the big red square.
  2281. Now, both point a and b are the farthest points from the red
  2282. square. You can pick any of the two. The objective here is to
  2283. find three consecutive points around the polygon for which the
  2284. inside angle is less than 180 degrees. If all three are on the
  2285. circle (worst case), their angle is strictly smaller than 180
  2286. degrees (unless they are all the same point, silly).
  2287.  
  2288. You could also pick the topmost point. If several are at the same
  2289. height, use the leftmost (or rightmost, whatever) one in the
  2290. topmost points.
  2291.  
  2292. Now we are certain that the small angle is the inside angle. Let
  2293. us draw the general situation:
  2294.  
  2295.  
  2296.  
  2297. Figure 10
  2298.  
  2299. Now, in a we see the above mentionned farthest point and the two
  2300. adjacent edges. If we take these edges as vectors as in b and
  2301. then sum them as in c, we find a point (pointed by the red vector
  2302. in c) which is necessarely on the in side for both edges. Now it
  2303. is easy to find a perpendicular vector for both edges and make
  2304. sure that they are in the good general direction. A way would be
  2305. to take the perpendicular vector and do a scalar multiplication
  2306. with the above red vector, the result of which must be strictly
  2307. positive. If it is not, the you must take the opposite vector for
  2308. perpendicular vector.
  2309.  
  2310. Now we know the situation for at least one edge. We need some way
  2311. to deduce the other edges' status from that edge's.
  2312.  
  2313. If we take Figure 10.c as our general case, the point at the end
  2314. of the red vector is not necessarely on the in side for both
  2315. edges. However, if it is on the in side for any of the two edges,
  2316. it is on the in side for both, and if it is on the out side for
  2317. any edge, it is on the out side for both.
  2318.  
  2319.  
  2320. Normally, we will never find two colinear edges in a polygon, but
  2321. if we do, it is fairly easy to solve it anyway (left as an
  2322. exercise).
  2323.  
  2324.  
  2325.  
  2326. Now, briefly, here is how to determine if a normal to a plane
  2327. points in the right direction. Pick the point with the greatest z
  2328. value (or x, or y). If many points have the same z value, break
  2329. ties with greatest x value, then greatest y value. Then, take all
  2330. connected edges. Make them into vectors, pointing from the above
  2331. point to the other endpoints (that is, the z component is less
  2332. than or equal to zero). Make them unit vectors, that is, length
  2333. of one. Find the vector with the most positive (or less negative)
  2334. z value. Break ties with the greates x value, and then greatest y
  2335. value. Take its associated edge. Now that edge is said to be
  2336. outermost.
  2337.  
  2338. Now, from that outermost edge, take the two associated polygons.
  2339. The edge has an associated vector for each connected polygon to
  2340. indicate the "in" direction. Starting from any of the two points
  2341. of the edge, add both these vectors. The resulting point is
  2342. inside. Adjust the normal so that it is on the right side of the
  2343. plane for both polygons.
  2344.  
  2345. Now we have the normal for at least one plane straight. We will
  2346. deduce correct side for the normal to the other polygons in a
  2347. similar manner than we did for the edges of a polygon, above.
  2348.  
  2349. Take two adjacent polygons, one for which you do know the normal,
  2350. the other for which you want to calculate the correct normal. Now
  2351. take a shared edge (any edge will do). That edge has two "in"
  2352. vectors for the two polygons. Starting from any of the two points
  2353. of the edge, add the two ®in¯ vectors. If the resulting point is
  2354. on the ®in¯ side for a plane, it is also on the ®in¯ side of the
  2355. other plane. If it is on the ®out¯ side of a plane, it is on the
  2356. ®out¯ side for both planes.
  2357.  
  2358.  
  2359.  
  2360. To get a general picture of what I'm trying to say, here's a
  2361. drawing. Basically, where two lines intersect, you get four
  2362. distinct regions. One region lies entirely on the ®in¯ side of
  2363. both lines and another region lies entirely on the ®out¯ side of
  2364. both lines (if the two lines define a polygon). The two other
  2365. regions are mixed region (they lie on the ®in¯ side of a line and
  2366. on the ®out¯ side of the other line). The ®mixed¯ regions may or
  2367. may not be part of the actual polygon, depending on whether the
  2368. angle is lesser or greater than 180 degrees.
  2369.  
  2370.  
  2371.  
  2372. Figure 11
  2373.  
  2374.  
  2375. So we are trying to find a point that is in the ®in¯ or ®out¯
  2376. region, but not in the ®mixed¯ region. If, as above, we take the
  2377. edges as vectors and add the together, we end up in the either
  2378. the ®in¯ region or the ®out¯ region, but not in the ®mixed¯
  2379. region. That can be proved very easily, geometrically (yet
  2380. strictly), but I don't feel like drawing it. :) Anyway, it should
  2381. be simple enough.
  2382.  
  2383.  
  2384.  
  2385.  
  2386.      __________________
  2387.  
  2388.  
  2389.  
  2390.  
  2391.  
  2392. 13   Reducing a polygon to a mesh of triangles
  2393.  
  2394.  
  2395.  
  2396. Now that's fairly simple. The simplest is when you have a convex
  2397. polygon. Pick any vertex. Now, take its two adjacent vertexes.
  2398. That's a triangle. In fact, that's your first triangle in your
  2399. mesh of triangles. Remove it from your polygon. Now you have a
  2400. smaller polygon. Repeat the same operation again until you are
  2401. left with nothing. Example:
  2402.  
  2403.  
  2404. Figure 12
  2405.  
  2406. For the above polygon, we pick the green edges in 1. The triangle
  2407. is shown in 2. When we remove it from the polygon, we're left
  2408. with what we see in 3.
  2409.  
  2410. This will take a n sided polygon and transform into n-2
  2411. triangles.
  2412.  
  2413. If the polygon is concave, we can subdivide it until it is convex
  2414. and here is how we do that.
  2415.  
  2416. Take any vertex where the polygon is concave (i.e. the angle at
  2417. that vertex is more than 180 degrees) and call that vertex A.
  2418. From that vertex, it can be proved geometrically that there
  2419. exists another vertex that is not connected by an edge to which
  2420. you can extend a line segment without intersecting any of the
  2421. polygon's edges. In short, from vertex A, find another vertex,
  2422. not immediately connected by an edge (there are two vertices
  2423. connected to A by an edge, don't pick any one of them). Call that
  2424. new vertex B. Make certain that AB does not intersect any of the
  2425. other edges. If it does, pick another B. Once you're settled on
  2426. your choice of B, split your polygon in two smaller polygons by
  2427. inserting edge AB. Repeat the operation with the two smaller
  2428. polygons until you either end up with triangles or end up with
  2429. convex polygons which you can split as above. It can again be
  2430. proved (though a little more difficultly) that you will end up
  2431. with n-2 triangles if you had a n sided polygon.
  2432.  
  2433. Here's a tip on how to find a locally concave vertex.
  2434.  
  2435.  
  2436. Figure 13
  2437.  
  2438. [The arrows point towards the ®in¯ side]
  2439.  
  2440. The two possible cases are shown in figure 13, either it is or it
  2441. is not convex. In 1, it is convex, as in 2 it is concave. To
  2442. determine if it is or not convex, take the line that passes
  2443. through a and u, above. Now, if v stands on the ®in¯ side of the
  2444. au line, it is convex. Otherwise, it is concave.
  2445.  
  2446.  
  2447.  
  2448.  
  2449.  
  2450.      __________________
  2451.  
  2452.  
  2453.  
  2454.  
  2455.  
  2456. 14   Gouraud shading
  2457.  
  2458.  
  2459.  
  2460. First, let us discuss a simple way for shading polygons. Imagine
  2461. we have a light source that's infinitely far. Imagine it's at
  2462. z=positive infinity, x=0, y=0. If it's not, you can rotate
  2463. everything so that it is (in practice, you'll only need to rotate
  2464. normals). Now, the z component of the polygons normals give us a
  2465. clue as to the angle between the polygon and the light rays. The
  2466. z component goes from -1, facing away from the light source
  2467. (should be darkest) to 0, facing perpendicular to the light
  2468. source (should be in half light/penumbra), to +1, facing the
  2469. light source (should be the brightest). From that, you can shade
  2470. it linearly or any way you want.
  2471.  
  2472. With this model, a polygon is uniformly shaded. Approximating
  2473. round objects through polygons will always look rough.
  2474.  
  2475. Gouraud shading is what we call an interpolated shading. It is
  2476. not physically correct, but it looks good. What it does is this:
  2477. it makes the shade of a polygon change smoothly to match the
  2478. shade of the adjacent polygon. This makes the polyhedra look
  2479. smooth and curved. However, it does not change the actual edges
  2480. to curves, thus the silhouette will remain unsmoothed. You can
  2481. help that by allowing curved edges if you feel like it.
  2482.  
  2483. First, we will interpolate a vertex normal for all vertexes. That
  2484. is, we will give each vertex a ®normal¯ (note that a point does
  2485. not have a normal, so you could call it pseudo-normal). To
  2486. interpolate that vertex ®normal¯, averaging all connected
  2487. polygons normals would be a way. Now, find the ®shade¯ for each
  2488. vertex.
  2489.  
  2490.  
  2491. Figure 14
  2492.  
  2493. Now, say point a is at (ax,ay) and point b is at (bx,by). We're
  2494. scan-converting the polygon, and the gray-and-red line represents
  2495. the current sanline, y0. Point P is the pixel we are currently
  2496. drawing, located at (x0,y0). Points m and n define the span we're
  2497. currently drawing. Now, we will interpolate the color for point m
  2498. using a and b, and the color for n using a and c, then
  2499. interpolate the color for P using the color for m and n. Our
  2500. interpolation will be linear. Say color for point a is Ca, color
  2501. for point b is Cb, for point c it is Cc, Cm for m, Cn for n and
  2502. CP for P.
  2503.  
  2504. We will say that color at point m, Cm, is as follows:
  2505.  
  2506. Cm=(y0-ay)/(by-ay) x (Cb-Ca)+Ca
  2507.  
  2508. That is, is point m is halfway between a and b, we'll use half ca
  2509. and half cb. If it's two-third the way towards b, we'll use 1/3Ca
  2510. and 2/3Cb. You get the picture. Same for n:
  2511.  
  2512. Cn=(y0-ay)/(cy-ay) x (Cc-Ca)+Ca.
  2513.  
  2514. Then we do the same for CP.
  2515.  
  2516. CP=(x0-mx)/(nx-mx) x (Cn-Cm)+Cn.
  2517.  
  2518. If you think a little, these calculations are exactly the same as
  2519. Bresenham's line drawing algorithm, seen previously in chapter
  2520. 2.1. It is thus possible to do them incrementally. Example:
  2521.  
  2522. Say we start with the topmost scanline. Color for point m is at
  2523. first Cb. Then, it will change linearly. When point m reaches
  2524. point a, it will be color Ca. Now, say dy=by-ay, dC=Cb-Ca, and
  2525. u=y0-ay.
  2526.  
  2527. Cm=dC/dy x u+Ca.
  2528.  
  2529. Then, when y0 increases by 1, u increases by one and we get
  2530.  
  2531. Cm'=dC/dy x (u+1)+Ca=dC/dy x u+Ca + dC/dy
  2532. Cm'=Cm+dC/dy
  2533.  
  2534. Same as Bresenham. Thus, when going from one scanline to the
  2535. next, we simply need to add dC/dy to Cm, no multiplications or
  2536. divisions are involved. The same goes for Cn. CP is done
  2537. incrementally from pixel to pixel.
  2538.  
  2539.  
  2540.  
  2541.  
  2542.  
  2543.  
  2544.      ____________________
  2545.  
  2546.  
  2547.  
  2548.  
  2549.  
  2550.  
  2551.  
  2552. 15   Clipping polygons to planes
  2553.  
  2554.  
  2555.  
  2556. Eventually, you might need to clip your polygon, minimally to the
  2557. z>0 volume. Several approaches can be used. We will discuss here
  2558. the Sutherlan-Hodgman algorithm. But first, let us speak of
  2559. trivial rejection/acceptation.
  2560.  
  2561. If a polygon does not intersect the clipping plane, it can be
  2562. either trivially accepted or rejected. For example, if every
  2563. single point in a polygon are in z>0 and we are clipping with
  2564. z>0, then the whole polygon can be accepted. If every single
  2565. point is in z<=0, the whole polygon can be trivially rejected.
  2566. The real problem comes with cases where some points are in z>0
  2567. and some are in z<=0. Another nifty thing you can do is pre-
  2568. calculate the bounding sphere of a poly with its center on a
  2569. given point in the poly. Let O be the sphere's center and R it's
  2570. radius. If Oz-R>0, then the poly can be trivially accepted even
  2571. faster (no need to check every single point). If Oz+R<=0, the
  2572. poly can be trivially refused. You can extend this to polyhedras.
  2573. You could also check wether the intersection of the sphere and
  2574. the polygon plane is in z>0 (or z<=0), which might be slightly
  2575. better than checking for the whole sphere.
  2576.  
  2577. Here comes the Sutherland-Hodgman algorithm. Start with a point
  2578. that you know is going to be accepted. Now, move clockwise (or
  2579. counter-clockwise) around the poly, accepting all edges that
  2580. should be trivially accepted. Now, when an edge crosses from
  2581. acceptance to rejection (AtoR), find the intersection with the
  2582. clipping plane and replace the offending edge by a shortened one
  2583. that can be trivially accepted. Then, keep moving until you find
  2584. an edge that crosses from rejection to acceptance (RtoA), clip
  2585. the edge and keep the acceptable half. Add an edge between the
  2586. two edges AtoR and RtoA. Keep going until you are done.
  2587.  
  2588.  
  2589. Figure 15
  2590.  
  2591. Figure 15 illustrates the process of clipping the abcde polygon
  2592. to the clipping plane. Of note is this: when performing this kind
  2593. of clipping on convex polygons, the results are clear.
  2594. Furthermore, a convex polygon always produce a convex polygon
  2595. when clipped. However, concave polygons introduce the issue of
  2596. degenerate edges and whether they should be there in a correctly
  2597. clipped polygon. Figure 16 shows such a case of degenerate edge
  2598. generation.
  2599.  
  2600.  
  2601. Figure 16
  2602.  
  2603. In figure 16, the polygon to the left is clipped to the plane
  2604. shown, the result is on the right. The bold edge is double. That
  2605. is, two edges pass there. This is what we call a degenerate edge.
  2606. Degenerate edges don't matter if what interests us is the area of
  2607. the polygon, or if they happen to fall outside of the view
  2608. volume. What interests us is indeed the area of the polygon, but
  2609. due to roundoff error, we could end up with the faint trace of an
  2610. edge on screen. On could eliminate these degenerate edges pretty
  2611. easily.
  2612.  
  2613. To do so, do not add an edge between the points where you clip at
  2614. first (edge xy in figure 15). Instead, once you know all clipping
  2615. points (x and y in the above example), sort them in increasing or
  2616. decreasing order according to one of the coordinate (e.g. you
  2617. could sort the clipping point in increasing x). Then, between
  2618. first and second point, add an edge, between third and fourth,
  2619. add an edge, between fifth and sixth, add an edge and so on. This
  2620. is based on the same idea that we used for our polygon drawing
  2621. algorithm in an earlier chapter. That is, when you intersect an
  2622. edge, you either come out of the polygon or go in the polygon.
  2623. Then, between the first and second clipped point belongs an edge,
  2624. etc...
  2625.  
  2626. However, since we are clipping to z=0, the degenerate edge has to
  2627. be outside the view volume. (Division by a very small z, when
  2628. making projections, ensures that the projected edge is far from
  2629. the view window). Since we are clipping to z>0, let us pull out
  2630. our limit mathematics.
  2631.  
  2632. Let us first examine the case where the edge lies entirely in the
  2633. z=0 plane.
  2634.  
  2635. Let us assume we are displaying our polygon using a horizontal
  2636. scan-line algorithm. Now, if the both of the offending edge's
  2637. endpoints are at y>0, then the projected edge will end up far up
  2638. of the view window. Same goes if both endpoints are at y>0. If
  2639. they are on either side of y=0, then all projected points will
  2640. end up above or below the view window, except the exact point on
  2641. the edge where y=0. If that point's x<0, then x/z will end up far
  2642. to the left, and if x>0, the point will end up far to the right.
  2643. If x=0, then we have the case x=y=0 and z tends towards positive
  2644. zero (don't flame me for that choice of words). In the case x=0,
  2645. x/z will be zero (because 0/a for any nonzero a is 0) and so for
  2646. y. Thus, the projected edge should pass through the center of the
  2647. view window, and go to infinity in both directions. It's slope
  2648. should be the same as the one it has in the z=0 plane. Just to
  2649. make it simple, if we have the plane equation (as we should) in
  2650. the form Ax+By+Cz+D=0, we can find the projected slope by fixing
  2651. z=1: Ax+By+C+D=0, or y=-A/Bx-(C+D). Thus the slope of the
  2652. projected line is -A/B. If B is zero, we have a vertical line.
  2653.  
  2654. What we conclude is that if the edge's z coordinate is 0 and both
  2655. of its endpoints are on the same side of plane y=0, then the edge
  2656. will not intercept any horizontal scanline and can thus be fully
  2657. ignored. If the endpoints are on either side of y=0, then it will
  2658. have an intersection with all horizontal scanlines. The projected
  2659. edge will end up left if, when evaluated at y=0 and z=0 its x
  2660. coordinate is less than 0 (from the plane equation, x=-D/A). If
  2661. the x coordinate is positive, the edge ends up far to the right.
  2662. If x=y=0 when z=0, the edge will be projected to a line passing
  2663. through the center of the screen with a slope of -A/B.
  2664.  
  2665. In the case where one of the edge's endpoints is in the z=0
  2666. plane, that endpoint will be projected to infinity and the other
  2667. will not, we will end up with a half line in the general case,
  2668. sometimes a line segment.
  2669.  
  2670. Let us name the endpoint whose z coordinate is 0 P0. If P0's x<0,
  2671. then it will be projected to the left of the window. If P0's x>0,
  2672. it will be projected to the right. Likewise, if P0's y<0, it will
  2673. be projected upwards, if y>0 it will be projected downwards.
  2674.  
  2675. The slope will be (v0-v1)/(u0-u1) where (u0,v0) is projected P0
  2676. and (u1,v1) is projected P1. This can be written as
  2677. u0=P0x/p0z     u1=P1x/P1z     v0=P0y/P0z     v1=P1y/P1z
  2678.  
  2679. It is of note that u1 and v1 are both relatively small numbers
  2680. compared to u0 and v0. The slope is therefore:
  2681.  
  2682. m=(P0y/P0z-P1y/P1z)/(P0x/P0z-P1x/P1z)
  2683. m=(P0y/P0z)/(P0x/P0z-P1x/P1z)-(P1y/P1z)/(P0x/P0z-P1x/P1z)
  2684. m=(P0y/P0z)/([P0xP1z-P1xP0z]/P0zP1z)-0
  2685. m=(P0y/P0z)(P0zP1z/[P0xP1z-0])
  2686. m=P0yP1z/(P0xP1z)
  2687. m=P0y/P0x.
  2688.  
  2689. By simmetry we could have found the inverse slope p=P0x/P0y.
  2690. Thus, in the case where P0x=0, we are facing a vertical
  2691. projection. Else, the projection's slope is P0y/P0x. Anyway, the
  2692. line passes through (u1,v1).
  2693.  
  2694. In the case where P0=0, (u0,v0)=(0,0). Then the projected edge
  2695. remains a line segment.
  2696.  
  2697.  
  2698.  
  2699.  
  2700.  
  2701.      _________________________
  2702.  
  2703.  
  2704.  
  2705.  
  2706.  
  2707.  
  2708. 16   Interpolations and forward differencing
  2709.  
  2710.  
  2711.  
  2712.  
  2713. Oftentimes, us graphics programmer want to interpolate stuff,
  2714. especially when the calculations are very complex. For example,
  2715. the texture mapping equations involve at least two divides per
  2716. pixel, which can be just a bit too much for small machines. Most
  2717. of the time, we have a scanline v=k on screen that we are
  2718. displaying, and we want to display a span of pixels, e.g. a
  2719. scanline in a triangle or something. The calculations for these
  2720. pixels can sometimes be complicated. If, for example, we have to
  2721. draw pixels in the span from u=2 to u=16 (a 14 pixel wide span)
  2722. for a given scanline, we would appreciate reducing the per-pixel
  2723. calculations to the minimum.
  2724.  
  2725. Let's take for our example, the z value of a plane in a given
  2726. pixel. Let's say the plane equation is x+2y+z=1. We have already
  2727. seen that z can be calculated with z=D/(Au+Bv+C), or
  2728. z=1/(u+2v+1). Let us imagine that we are rendering a polygon, and
  2729. that the current scanline is v=1. Then, z=1/(u+3) for the current
  2730. scanline. If that a span goes from u=0 to u=20 (that is, on line
  2731. v=.5, the polygon covers pixels u=0 through u=20). We can see
  2732. form the equation for z that when u=0, z=1/3, and when u=20,
  2733. z=1/23.
  2734.  
  2735. Calculating the other values is a bit complex because of the
  2736. division. (Divisions on computers are typically slower than other
  2737. operations.) As of such, we could perhaps use an approximation of
  2738. z instead. The most naive one would be the following: just assume
  2739. z is really of the form z=mu+b and passes through the points u=0,
  2740. z=1/3 and u=20, z=1/23. Thus:
  2741.  
  2742. z=mu+b verifies
  2743. 1/3=m(0)+b and
  2744. 1/23=m(20)+b
  2745.  
  2746. thus
  2747.  
  2748. 1/3=b
  2749. and
  2750. 1/23=20m+b
  2751. or
  2752. 1/23=20m+1/3
  2753. 1/23-1/3=20m
  2754. m=1/460-1/60 or approximately -0.01449275362319.
  2755.  
  2756. Thus, instead of using z=1/(u+3), we could use z=-
  2757. 0.01449275362319u+1/3. In this particular case, this would be
  2758. somewhat accurate because m is small.
  2759.  
  2760. Example, we know from the real equation that z=1/(u+3), thus when
  2761. u=10, z=1/13, approximately 0.07692307692308. From the
  2762. interpolated equation, when u=10, we get z=-
  2763. 0.01449275362319*10+1/3, or approximately 0.1884057971014. The
  2764. absolute error is approximately 0.11, which may or may not be
  2765. large according to the units used for z. However, the relative
  2766. error is around 30%, which is quite high, and it is possible to
  2767. create cases where the absolute error is quite drastic.
  2768.  
  2769. To reduce the error, we could use a higher degree polynomial.
  2770. E.g. instead of using z=mx+b, we could for instance use
  2771. z=Ax^2+Bx+C, a second degree polynomial. We would expect this
  2772. polynomial to reduce the error. The only problem with this and
  2773. higher degree polynomials is to generate the coefficients (A, B
  2774. and C in this case) which will minimize the error. Even defining
  2775. what "minimizing the error" is can be troublesome, and depending
  2776. on the exact definition, we will find different coefficients.
  2777.  
  2778. A nice way of "minimizing the error" is using a Taylor series.
  2779. Since this is not a calculus document, I will not teach it in
  2780. detail, but merely remind you of the sum. It can be demonstrated
  2781. that all continuous functions can be expressed as a sum of
  2782. polynomials for a given range. We can translate that function so
  2783. as to center that range about any real, and such other things. In
  2784. brief, the Taylor series of f(x) around a is:
  2785.  
  2786. T=f(a)+f'(a)(x-a)/1!+f''(a)(x-a)^2/2!+f'''(a)(x-
  2787. a)^3/3!+f''''(a)(x-a)^4/4!+...
  2788.  
  2789. This may look a bit scary, so here is the form of the general
  2790. term:
  2791.  
  2792. Ti=fi(a)(x-a)^i/i!
  2793.  
  2794. and
  2795.  
  2796. T=T0+T1+T2+T3+...+Ti+...
  2797.  
  2798. Where fi(a) is the ith derivative of f evaluated at point a, and
  2799. i! is the factorial of i, or 1x2x3x4x....xi. As an example, the
  2800. factorial of 4 is 1x2x3x4=24. It is possible to study this serie
  2801. and determine for which values of x it converges and such things,
  2802. and it is very interesting to note that for values of x for which
  2803. it does not converge, it diverges badly usually.
  2804.  
  2805. Taylor series usually do a good job of converging relatively
  2806. quickly in general. For particular cases, one can usually find
  2807. better solutions than a Taylor series, but in the general case,
  2808. they are quite handy. Saying that they converge "relatively
  2809. quickly" means that you could evaluate, say, the first 10 terms
  2810. and be quite confident that the error is small. There are
  2811. exceptions and special cases, of course, but this is generally
  2812. true.
  2813.  
  2814. What interests us in Taylor series is that they give us a very
  2815. convenient way of generating a polynomial of any degree to
  2816. approximate any given function for a certain range. In
  2817. particular, the z=1/(mu+b) equation could be approximated with a
  2818. Taylor series, and it's radius of convergence determined, etc...
  2819. (Please look carefully at the taylor series and you will see that
  2820. it is indeed a polynomial).
  2821.  
  2822. Here are a few well known and very used Taylor series expansions:
  2823.  
  2824. exp(x)=1+x+x^2/2+x^3/3!+x^4/4!+x^5/5!+x^6/6!+....
  2825. sin(x)=x-x^3/3!+x^5/5!-x^7/7!+x^9/9!-...
  2826. cos(x)=1-x^2/2!+x^4/4!-x^6/6!+x^8/8!-...
  2827.  
  2828. Taylor series for multivariable functions also exist, but they
  2829. will not be discussed here. For a good description of these, you
  2830. can get a good advanced calculus book. My personnal reference is
  2831. Advanced engineering mathematics, seventh edition by Erwin
  2832. Kreysig, ISBN 0-471-55380-8. As for derivatives, limits et al,
  2833. grab an introductory level calculus book.
  2834.  
  2835. Now the only problem is evalutating a polynomial at any point. As
  2836. you know, evaluating Ax^2+Bx+C at point x0 requires that we
  2837. square x0, multiply it by A, add B times x0 and then finally add
  2838. C. If we're doing that once per pixel, we are losing a lot of
  2839. time. However, the same idea as in incremental calculations can
  2840. be recuperated and used for any degree of polynomial. Generally
  2841. speaking, we're going to try to find what happens to f(x) as we
  2842. increase x by one. Or, we're trying to find f(x+1)-f(x) to see
  2843. the difference between one pixel and the next. Then, all we need
  2844. to do is add that value as we move from one pixel to the next.
  2845. Let's do an example with f(x)=mx+b.
  2846.  
  2847. f(x+1)-f(x)=[m(x+1)+b]-[mx+b]
  2848. f(x+1)-f(x)=m
  2849.  
  2850. So, as we move from a pixel to the next, we just need to add m to
  2851. the previous value of f(x).
  2852.  
  2853. If we have a second order polynomial, the calculations are
  2854. similar:
  2855.  
  2856. f(x)=Ax^2+Bx+C
  2857.  
  2858. f(x+1)-f(x)=[A(x+1)^2+B(x+1)+C]-[Ax^2+Bx+C]
  2859.      =[A(x^2+2x+1)+Bx+B+C]-[Ax^2+Bx+C]
  2860.      =[Ax^2+(2A+B)x+A+B+C]-[Ax^2+Bx+C]
  2861.      =2Ax+A+B
  2862.  
  2863. Let's name that last equation g(x). So, as we move from x to x+1,
  2864. we just need to add g(x) to the value of f(x). However,
  2865. calculating g(x) involves two multiplications (one of which which
  2866. can be optimized out by using bit shifts) and at least an add.
  2867. But g(x) is a linear equation, so we can apply forward
  2868. differencing to it again and calculate:
  2869.  
  2870. g(x+1)-g(x)=[2A(x+1)+A+B]-[2AX+A+B]
  2871.      =2A
  2872.  
  2873. So what we can do as we move from x to x+1 is first add g(x) to
  2874. f(x) and then add 2A to g(x), only two adds per pixel.
  2875.  
  2876. This can be extended to any degree of polynomial. In particular,
  2877. NURBS (not described in this document) can be optimized this way.
  2878. (I do not intend to discuss NURBS, but this optimization is
  2879. considered less efficient that subdivision, but is worth
  2880. mentionning.)
  2881.  
  2882. So what is the use for all this? Well, you could use a Taylor
  2883. series to do texture mapping instead of two divisions per pixel.
  2884. This is a generalization of linear approximation for texture
  2885. mapping. You could also use it for evaluating the z value of a
  2886. polygon in any pixel, as we were doing in the introduction to
  2887. this chapter. This however might not be very useful since you
  2888. might not need the actual z value, and the 1/z value might
  2889. suffice which, as we have already seen, can be calculated
  2890. incrementally without approximation (e.g. no error except
  2891. roundoff). This would be useful in visible surface determination.
  2892.  
  2893.  
  2894.  
  2895.  
  2896.      _________________________
  2897.  
  2898.  
  2899.  
  2900.  
  2901.  
  2902.  
  2903. 17   Specular highlights and the Phong illumination model
  2904.  
  2905.  
  2906.  
  2907.  
  2908.  
  2909. Up to now, we have been using a very straight illumination model.
  2910. We have assumed that the "quantity of light at a given point" can
  2911. be calculated with some form of dot product of the light vector
  2912. with the normal vector. (Let me remind you that (a,b,c) dot
  2913. (d,e,f) is ad+be+cf.) You should normalize the light vector for
  2914. this, and the plane normal should be of unit length too. Let us
  2915. assume the plane normal is (A,B,C) and that it is already
  2916. normalized. Let us further assume that the light vector (vector
  2917. from light source to point that is lighted in 3d) is (P,Q,R).
  2918. Then, the normalized light vector is (P,Q,R) times
  2919. 1/sqrt(P*P+Q*Q+R*R). As you can see, normalizing the light vector
  2920. is very annoying. (See chapter 2 for a more lengthy discussion of
  2921. related topics).
  2922.  
  2923. Now, we have been imagining that no matter what angle you look at
  2924. the object from, the intensity of the light is the same. In
  2925. reality, that may not be the case. If the object is somewhat
  2926. shiny and a bright light shines on it and you are looking at the
  2927. object from an appropriate angle, there should be a very intense
  2928. spot. If you move your eye, the spot moves, and if you move your
  2929. eye enough, the spot disappears entirely. (Try it with an apple
  2930. in a room with a single, intense light source, no mirrors et al).
  2931. This means that the light is reflected more in one general
  2932. direction than in the others.
  2933.  
  2934. Let us look at a little drawing. Let N be the surface normal, L
  2935. the light vector, R the reflection vector and V the eye-to-object
  2936. vector.
  2937.  
  2938.  
  2939. Figure 17
  2940.  
  2941. In the above picture, we see clearly that R is L mirrored by N.
  2942. Please not that the angle between L and R is not 90 degrees as it
  2943. may appear, but rather twice the angle between L and N. It can be
  2944. demonstrated that:
  2945.  
  2946. R=2N(N dot L)-L
  2947.  
  2948. Or, in more detail, if R=(A,B,C), N=(D,E,F) and L=(G,H,I), then N
  2949. dot L is DG+EH+FI, and thus we have
  2950.  
  2951. A=2D(DG+EH+FI)-G
  2952. B=2E(DG+EH+FI)-H
  2953. C=2F(DG+EH+FI)-I
  2954.  
  2955. It is of note that, if the light is at infinity, for a flat
  2956. polygon, N dot L is constant. If the light is not at infinity or
  2957. if the surface is curved, then N dot L varies.
  2958.  
  2959. Now, if we do not take into account the angle b in figure 17, the
  2960. amount of light perceived should be something like
  2961. I=Kcos(a)
  2962.  
  2963. where K is some constant that depends on the material and a is
  2964. the angle shown in figure 17. If to that we want to add the
  2965. specular highlight, we should add a term that depend on angle b
  2966. of figure 17. I becomes
  2967.  
  2968. I=Kcos(a)+Ccos^n(b)
  2969.  
  2970. Where C is a constant that depends on the material. The constant
  2971. n is a value that will specify how sharp is the reflection. The
  2972. higher the value for n, the more localized the reflection will
  2973. be. Lower values of n can be used for matte surfaces, and higher
  2974. values can be used for more reflective surfaces. Most light
  2975. sources don't shine as bright from a distance (but some do shine
  2976. as bright at any small distance, such as the sun, but that light
  2977. can be considered to lay "infinitely" far). In that case, we
  2978. could want to divide the above intensity by some function of the
  2979. distance. We know from physics that the quantity of energy at a
  2980. distance r of a light source is inversely proportional to the
  2981. square of the radius. However, this may not look good in computer
  2982. graphics. In short, you might want to use the square of the
  2983. length of the light vector, or the length of the light vector, or
  2984. some other formula. Let us assume we are using the length of the
  2985. light vector. Then, the intensity becomes:
  2986.  
  2987. I=1/|L| * [Kcos(a)+Ccos^n(b)]
  2988.  
  2989. Furthermore, it is known that if t is the angle between the
  2990. vectors A and B, then A dot B is |A||B|cos(t). Then,
  2991.  
  2992. cos(t)=(A dot B)/(|A||B|)
  2993.  
  2994. If |A| and |B| are both one, we can ignore them. So, let L' be
  2995. normalized L, and R' be normalized R and V' be normalized V, and
  2996. of course N is of unit length, then the equation is:
  2997.  
  2998. I=D * (K[N dot L']+C[R' dot V'])
  2999.  
  3000. Where D=1/|L|. We could have used some other function of |V|.
  3001.  
  3002. To that equation above, you may want to add some ambiant light,
  3003. e.g. light that shines from everywhere at the same time, such as
  3004. when you are standing outside (you wouldn't see a very dark
  3005. corner in daylight, normally, so the light that is still present
  3006. in a "dark corner" is the ambiant light). If you have multiple
  3007. light sources, just calculate the intensity from each light
  3008. source and add them together.
  3009.  
  3010. This of course assumes that a is comprised between 0 and 90
  3011. degrees, otherwise the light is on the wrong side of the surface
  3012. and doesn't affect it at all. (In which case the only light
  3013. present would be the light of intensity A).
  3014.  
  3015.  
  3016.  
  3017.  
  3018.  
  3019.      _________________________
  3020.  
  3021.  
  3022.  
  3023.  
  3024.  
  3025.  
  3026. 18   Phong shading
  3027.  
  3028.  
  3029.  
  3030.  
  3031.  
  3032. Polygons are flat, and as such have a constant normal across
  3033. their surface. When we used pseudo-normals for gouraud shading.
  3034. This was because we were attempting to model a curved surface
  3035. with flat polygons. We can use the Phong illumination model with
  3036. flat polygons if we want. That is, forget about the pseudo-
  3037. normals, use the plane normal everywhere in the Phong
  3038. illumination equation. This will yield very nice looking flat
  3039. surfaces, very realistic. However, most of the time, we want to
  3040. model curved surfaces and the polygons are merely an
  3041. approximation to that curved surface. In a curved surface, the
  3042. normal would not be constant for a large area, it would change
  3043. smoothly to reflect the curvature. In our polygons, of course,
  3044. the normal does not change. However, if we really want to model
  3045. curved surfaces with polygons, we find that the sharp contrasts
  3046. between facets is visually disturbing and does not represent a
  3047. curved surface well enough. To this end, we made the intensity
  3048. change gradually with Gouraud shading. There is however a problem
  3049. with Gouraud shading.
  3050.  
  3051. It is possible to demonstrate that no light inside a Gouraud
  3052. shaded polygon can be of higher intensity than any of the
  3053. vertices. As of such, if a highlight were to fall inside the
  3054. polygon (as is most probably the case) and not on a vertex, we
  3055. would miss it perhaps entierly. To this end, we might want to
  3056. interpolate the polygon normal instead of intensity from the
  3057. vertices pseudo-normals. The idea is the same as with Gouraud
  3058. shading, except that instead of calculating a pseudo-normal at
  3059. the vertices, calculating the intensities at the vertices and
  3060. then interpolating the intensity linearly, we will calculate
  3061. pseudo-normals at the vertices, interpolate linearly the pseudo-
  3062. normals and then calculate the intensity. The intensity can be
  3063. calculated using any illumination model, and in particular the
  3064. Phong illumination model can be visually pleasent, though some
  3065. surfaces don't have any specular highlights. (In which case you
  3066. can remove the specular factor from the equation entirely).
  3067.  
  3068. The calculations for Phong shading are very expensive per pixel,
  3069. in fact too expensive even for dedicated hardware oftentimes. It
  3070. would be absurd to think that one could do real-time true Phong
  3071. shading on today's platforms. But in the end, what is true Phong
  3072. shading? Nothing but an approximation to what would happen if the
  3073. polygon mesh really were meant to represent a curved surface.
  3074. I.e. Phong shading is an approximation to an approximation. The
  3075. principal objective of Phong shading is to yield nonlinear
  3076. falloff and not miss any specular highlights. Who is there to say
  3077. that no other function will achieve that?
  3078.  
  3079. One of the solutions to this has been proposed in SIGGRAPH '86,
  3080. included with this document. What they do is pretty
  3081. straightforward (but probably required a lot of hard work to
  3082. achieve). They make a Taylor series out of Phong shading and use
  3083. only the first two terms of the series for the approximation.
  3084. Thus, once the initialization phase is completed, Phong shading
  3085. requires but two adds per pixel, a little more with specular
  3086. highlights. The main problem, though, is the initialization
  3087. phase, which includes many multiplications, divisions, and even a
  3088. square root. It will doubtlessly be faster than exact Phong
  3089. shading, but I am not so confident that it will be sufficiently
  3090. fast to become very popular. Using Gouraud shading and a very
  3091. large number of polygons can yield comparable results at a very
  3092. acceptable speed.
  3093.  
  3094. In future versions of this document, I hope to discuss other ways
  3095. of not missing the specular highlight. But since this is exam
  3096. week, I'll keep it to this for the moment being.
  3097.  
  3098.  
  3099.  
  3100.  
  3101.  
  3102.      _________________________
  3103.  
  3104.  
  3105.  
  3106.  
  3107. Version history
  3108.  
  3109.  
  3110. Original version (no version number): original draft, chapters 1
  3111. through 8.
  3112.  
  3113. Version 0.10 beta: added chapters 9 through 11, added version
  3114. history. Still no proofreading, spellchecking or whatever.
  3115.  
  3116. Version 0.11 beta: modified some chapters, noticed than (Q,P,R)
  3117. is the normal vector (chapter 5). A little proofreading was done.
  3118.  
  3119. Version 0.12 beta: I just noticed that this document needs a
  3120. revision badly. Still, I don't have time. I just added a few
  3121. things about the scalar multiplication of two vectors being the D
  3122. coefficient in the plane equation. Added chapter 12. Also
  3123. corrected a part about using arrays instead of lists. The
  3124. overhead is just as bad. Better to use lists. Ah, and I have to
  3125. remember to add a part on calculating the plane equation from
  3126. more than three points to reduce roundoff error.
  3127.  
  3128. Version 0.20 beta: Still need to add the part about calculating
  3129. A,B and C in plane eq. from many points. However, corrected
  3130. several of the mistakes I talked about in the preceding paragraph
  3131. (i.e. revision). Ameliorated the demonstration for 2d rotations.
  3132. Started work on 3d rotations (this had me thinking...)
  3133.  
  3134. Version 0.21 beta: converted to another word processor. Will now
  3135. save in RTF instead of WRI.
  3136.  
  3137. Version 0.22 beta: corrected a few goofs. 3d rotations still on
  3138. the workbench. Might add a part about subdividing a polygon in
  3139. triangles (a task which seems to fascinate people though I'm not
  3140. certain why). Will also add the very simple Gouraud shading
  3141. algorithm in the next version (and drop a line about bump
  3142. mapping). This thing needs at least a table of contents, geez...
  3143. I ran the grammar checker on this. Tell me if it left anything
  3144. out.
  3145.  
  3146. Version 0.23 beta: well, I made the version number at the start
  3147. of this doc correct! :-) Did chapter 13 (subdividing into
  3148. triangles) and chapter 14 (Gouraud shading).
  3149.  
  3150. Version 0.24 beta: removed all GIFs from file because of recent
  3151. CompuServe-Unisys thingy. Don't ask me for them, call CompuServe
  3152. or Unisys [sigh].
  3153.  
  3154. Version 0.30 beta: added the very small yet powerful technique
  3155. for texture mapping that eliminates the division per pixel. It
  3156. can also help for z-buffering. Corrected the copyright notice.
  3157.  
  3158. Version 0.31 beta: I, err, added yet another bit to texture
  3159. mapping [did this right after posting it to x2ftp.oulu, silly
  3160. me]. Corrected a typo in chapter 13: a n-sided polygon turns into
  3161. n-2 triangles, not n triangles. Was bored tonight, added
  3162. something to the logs chapter (I know, it's not that useful, but
  3163. hey, why would I remove it?) Added ®Canada¯ to my address below
  3164. (it seems I almost forgot internet is worldwide tee hee hee).
  3165. Addendum: I'm not certain that the GIF thing (see the paragraph
  3166. about v0.24beta above) applies to me, but I'm not taking any
  3167. chances.
  3168.  
  3169. Version 0.32 beta: added bits and pieces here and there. The WWW
  3170. version supposedly just became available today. Special thanks to
  3171. Lasse Rasinen for converting it. Special thanks also to Antti
  3172. Rasinen. I can't seem to be able to connect to the WWW server
  3173. though, I just hope it really works. Corrected a goof in the free
  3174. directional texture mapping section.
  3175.  
  3176. Version 0.40 beta: Kevin Hunter joined forces with me and made
  3177. that part in chapter two about changes in coordinates system,
  3178. finding the square root et al. Modified the thing about
  3179. triangulating a polygon. Added the Sutherland-Hodgman clipping
  3180. algorithm and related material (the whole chapter 15). This
  3181. document deserves a bibliography/suggested readings/whatever, but
  3182. I don't think I have the time for that. Might add a pointer to
  3183. the 3d books list if I can find where it is. Anyway, if you're
  3184. really interested and want to know more, I'm using Computer
  3185. Graphics, Principles and Practice by Foley, van Dam, Feiner and
  3186. Hughes, from Addison-Wesley ISBN 0-201-12110-7. There. It's not
  3187. in the right format, but it's there. MY E-MAIL ADDRESS CHANGED!
  3188. PLEASE USE THIS ONE FROM NOW ON!
  3189.  
  3190. Version 0.41 beta: added to chapter 3. Gave a more detailed
  3191. discussion of a scan-line algorithm. Put in a line about Paul
  3192. Nettle's sbuffering in the same chapter. Will now include a
  3193. "where can I get this doc from" in the readme, and I'll try to
  3194. put the last few version history entries in my announcements for
  3195. future versions of the doc.
  3196.  
  3197. Version 0.42 beta: removed the buggy PS file. Added an ASCII file
  3198. containing most of the pics of the document.
  3199.  
  3200. Version 0.50 beta: added chapters 16, 17 and 18 about Phong
  3201. shading and illumination model, interpolations forward
  3202. differencing and Taylor series. I am also including an extract
  3203. from SIGGRAPH '86 for fast Phong shading. Source code is coming
  3204. out good, so it just might be in the next version.
  3205.  
  3206.  
  3207.  
  3208.  
  3209.      _____________________________
  3210.  
  3211.  
  3212.  
  3213.  
  3214.  
  3215. About the author
  3216.  
  3217.  
  3218.  
  3219. Sŵbastien Loisel is a student in university. He is studying
  3220. computer sciences and mathematics and has been doing all sorts of
  3221. programs, both down-to-earth and hypothetical, theoritical
  3222. models. He's been thinking a lot about computer graphics lately
  3223. and so decided to write this because he wanted to get his ideas
  3224. straight. After a while, he decided to distribute it on Internet
  3225. (why the hell am I using third person?)
  3226.  
  3227. The author would be happy to hear your comments, suggestions,
  3228. critics, ideas or receive a postcard, money, new computer and/or
  3229. hardware or whatever (grin). If you do want to get in touch with
  3230. me, well here's my snail mail address:
  3231.  
  3232. Sŵbastien Loisel
  3233. 1 J.K. Laflamme
  3234. Lŵvis, Quŵbec
  3235. Canada
  3236. G6V 3R1
  3237.  
  3238. MY E-MAIL ADDRESS CHANGED! PLEASE USE THIS ONE FROM NOW ON!
  3239. loiselse@ift.ulaval.ca
  3240.  
  3241. Addendum: see enclosed file, README.3D.
  3242.  
  3243. I would like to thank Lasse Rasinen making the WWW version
  3244. available by converting this doc to the proper format. I would
  3245. also like to thank Antti Rasinen for the WWW version. Thanks,
  3246. guys.
  3247.  
  3248. I would like to thank Kevin Hunter for writing a big chunk of
  3249. chapter 2. Thank you Kevin. Kevin Hunter can be reached (at the
  3250. time of this writing) as HUNTER@symbol.com.
  3251.  
  3252.  
  3253.