Programming Assignment 2

the vc++ project         just the assn2.cpp

I implemented a triangle scan conversion routine and Z-buffer hidden surface algorithm.  I started
with Bishop's skeleton glut code and used the frame buffer from that, with an additional array pz
(pixel z) for the z buffer.

My approach was to use a bounding box.  Upon entry into the function, a bounding box is found.
A line has the form y - kx + (k*x1 - y1) = 0, where x1 and y1 are coordinates of a point on the
line.  The last term is a constant, so I calculate k and the last term b for the three edges. Then, for
each scanline in the box, several things happen:

    Vectors of x y and z are calculated for interpolation later on.
    I initialize the edge equation above (left hand side), and it evaluates to positive or negative
        based on which side of the edge the point being evaluated is on.  I found that if the traversal
        of the edge decreases in x, then by negating the function and subsequent iterations, I get the
        property that if a point is within the triangle, then the three edge functions have the same sign
        at that point.
    I find the z and x being interpolated along on this row.  I do this by picking the two edges such
        that the row is in their range.  Then I interpolate along those edges to get two z's and two x's.

Then, for each pixel:

    Increment the edge functions accordingly.
    Check if any of the edge functions evaluated to zero.  If so, set them to be one of the non-zero
        evaluations.  All three will never be zero.

Finally, if the three evaluations have the same sign, compare the interpolated z to the current z in
the pz array.  If the triangle is to be drawn at this pixel, interpolate it's color and update the frame
buffer.
 

Optimizing?

My code was written with large triangles in mind.  My edge equation incrementing and prep-
rocessing for each row only matter if there are a good number of pixels on the row to check.  In
order to optimize for much smaller triangles, I would probably cut out most of the code.  I would
start by again finding the bounding box.  If we're only evaluating at pixel centers, I may then check
first to see if the max z of the triangle is greater than any z currently in the box of pixels.  For each
pixel that looks like it could get written to, I would then do the edge equation evaluations and if
necessary the interpolations for the pixel.  The comparison with all the pixels in the bounding box
is not efficient for large triangles.  But if a triangle is only a few pixels or less, we could probably
save some time by not having to calculate and evaluate edge equations.  However, even in the
large triangle case, it could be helpful to delay the edge equation incrementing until we find a good
z, but then we would have to keep track of how much to increment by once we got to one.  (It's
just that in my code, the cases being accounted for regarding the different traversals of edges
looks like a lot of comparisons per pixel.  To be able to stop short of those could be nice, part-
icularly if there are a lot of overlapping triangles in the image).

The only coherence I take advantage of (I think) is the incrementing rather than reevaluating the
edge formulas.  I guess by coding so straight-forwardly, I've made an algorithm that would be
easier to implement on hardware than others that optimize more for the different cases.

Model for Time Behavior

My model was same one discussed in class, namely:

    Ttotal = Tsetup + Nlines * Tline + Npixels * Tpixel

I ran the program on hundreds of randomly generated triangles, and output a file containing
basically a hundreds by 3 matrix.  The knowns are Ttotal, Nlines, and Npixels.  To solve the
system I used the method we dicussed in class for non-square matricies:
    Ax = B
    A'Ax = A'B
    x = inv(A'A)A'B
I took my space delimited text file and imported into matlab to do the math, basically repeating
the above sequence:

>> A = output(1:500,2:3);
>> A(1:500,3) = ones(500,1);
>> B = output(1:500,1);
>> AT = A';
>> C = AT*A;
>> D = AT*B;
>> x = inv(C)*D

x =

  1.0e-003 *

    0.0970
    0.0018
   -0.8924

>> for i = 1:500
approx_r(i) = output(i,2)*x(1) + output(i,3)*x(2) + x(3);
end
>> i = 1:500;
>> resi = output(1:500,1) - approx_r(1,1:500)';
>> plot(i,resi);                                                      (see below for plot)
>> r = sum(resi.*resi)

r =

    0.0968

>> cond(A)

ans =

  1.4930e+004

The residue is nice and small, but the variables got solved so that the equation is:

    Ttotal  =  (-8.92 E -4) + (9.7 E -5)*Nlines + (1.77 E -6)*Npixels

Why there is a negative Tsetup I don't know.  Maybe the fact that the condition number
(sensitivity to error) of the system matrix A is about 15K! And when we multiply it by
it's transpose, we get like the square of the sensitivity.  But the results of the solution are
good (averaging about 6% difference between the fitting curve and the data).  So maybe
the model is off a bit.  It seems intuitive though.  We would get more accuracy on the
Tline and Tpixel if we estimated the Tsetup.  I could compute that along with Nlines and
Npixels and Ttotal for a large number of triangles, and find it's average and distribution
to get some sort of reasonable constant Tsetup.  However, here's a fit on 50 triangles for
example (the projected total time in green, verses the actual total time in blue. The results
are pretty good:

Some Examples: