001/*
002
003*/
004
005package renderer.pipeline;
006import  renderer.scene.*;
007import  renderer.framebuffer.*;
008
009import java.awt.Color;
010
011/**
012   Rasterize a {@link LineSegment} into pixels in the
013   {@link FrameBuffer}'s viewport.
014<p>
015   This rasterization algorithm is based on
016<pre>
017     "Fundamentals of Computer Graphics", 3rd Edition,
018      by Peter Shirley, pages 163-165.
019</pre>
020*/
021public class Rasterize
022{
023   public static boolean debug = false;
024
025   /**
026      Rasterize a {@link LineSegment} into pixels in the
027      {@link FrameBuffer}'s viewport.
028
029      @param ls  {@link LineSegment} to rasterize into the {@link FrameBuffer}
030      @param fb  {@link FrameBuffer} to hold rasterized pixels
031   */
032   public static void rasterize(LineSegment ls, FrameBuffer fb)
033   {
034      int w = fb.getWidthVP();
035      int h = fb.getHeightVP();
036
037      Vertex v0 = ls.v[0];
038      Vertex v1 = ls.v[1];
039
040      // Get the colors from the two vertices.
041      float r0 = v0.r,  g0 = v0.g,  b0 = v0.b;
042      float r1 = v1.r,  g1 = v1.g,  b1 = v1.b;
043
044      // Round the line segment's two endpoints to the nearest
045      // logical pixel. This makes the algorithm a lot simpler,
046      // but it can cause a slight, but noticable, shift of the
047      // line segment.
048      double x0 = Math.round(v0.x),  y0 = Math.round(v0.y);
049      double x1 = Math.round(v1.x),  y1 = Math.round(v1.y);
050
051      // Rasterize a degenerate line segment (a line segment
052      // that projected onto a point) as a single pixel.
053      if ( (x0 == x1) && (y0 == y1) )
054      {
055         // We don't know which endpoint of the line segment
056         // is in front, so just pick v0.
057         fb.setPixelVP((int)x0 - 1, h - (int)y0, new Color(r0, g0, b0));
058         return;
059      }
060
061      if (Math.abs(y1 - y0) <= Math.abs(x1 - x0)) // if abs(slope) <= 1
062      {
063         if (x1 < x0) // swap (x0, y0) with (x1, y1)
064         {
065            double tempX = x0;
066            double tempY = y0;
067            x0 = x1;
068            y0 = y1;
069            x1 = tempX;
070            y1 = tempY;
071            // swap the colors too
072            float tempR = r0;
073            float tempG = g0;
074            float tempB = b0;
075            r0 = r1;
076            g0 = g1;
077            b0 = b1;
078            r1 = tempR;
079            g1 = tempG;
080            b1 = tempB;
081         }
082
083         // Compute this line segment's slope.
084         double      m = (y1 - y0) / (x1 - x0);
085         double slopeR = (r1 - r0) / (x1 - x0);
086         double slopeG = (g1 - g0) / (x1 - x0);
087         double slopeB = (b1 - b0) / (x1 - x0);
088
089         // In the following loop, as x moves across the logical
090         // horizontal pixels, we will compute a y value for each x.
091         double y = y0;
092
093         // Rasterize this line segment.
094         for (int x = (int)x0; x <= (int)x1; x++, y += m)
095         {
096            // Interpolate this pixel's color between the two endpoint's colors.
097            float r = (float)Math.abs(r0 + slopeR*(x - x0));
098            float g = (float)Math.abs(g0 + slopeG*(x - x0));
099            float b = (float)Math.abs(b0 + slopeB*(x - x0));
100            // We need the Math.abs() because otherwise, we sometimes get -0.0.
101
102            // The value of y will almost always be between
103            // two vertical pixel coordinates. By rounding off
104            // the value of y, we are choosing the nearest logical
105            // vertical pixel coordinate.
106            fb.setPixelVP(x - 1, h - (int)Math.round(y), new Color(r, g, b));
107
108            // log interesting information to standard output
109            if (debug) logPixel(w, h, x, y, r, g, b);
110
111            // Advance (x,y) to the next pixel. Since delta_x = 1, we need delta_y = m.
112         }
113      }
114      else  // abs(slope) > 1
115      {
116         if (y1 < y0) // swap (x0, y0) with (x1, y1)
117         {
118            double tempX = x0;
119            double tempY = y0;
120            x0 = x1;
121            y0 = y1;
122            x1 = tempX;
123            y1 = tempY;
124            // swap the colors too
125            float tempR = r0;
126            float tempG = g0;
127            float tempB = b0;
128            r0 = r1;
129            g0 = g1;
130            b0 = b1;
131            r1 = tempR;
132            g1 = tempG;
133            b1 = tempB;
134         }
135
136         // Compute this line segment's slope.
137         double      m = (x1 - x0) / (y1 - y0);
138         double slopeR = (r1 - r0) / (y1 - y0);
139         double slopeG = (g1 - g0) / (y1 - y0);
140         double slopeB = (b1 - b0) / (y1 - y0);
141
142         // In the following loop, as y moves across the logical
143         // vertical pixels, we will compute a x value for each y.
144         double x = x0;
145
146         // Rasterize this line segment.
147         for (int y = (int)y0; y <= (int)y1; x += m, y++)
148         {
149            // Interpolate this pixel's color between the two endpoint's colors.
150            float r = (float)Math.abs(r0 + slopeR*(y - y0));
151            float g = (float)Math.abs(g0 + slopeG*(y - y0));
152            float b = (float)Math.abs(b0 + slopeB*(y - y0));
153            // We need the Math.abs() because otherwise, we sometimes get -0.0.
154
155            // The value of x will almost always be between
156            // two horizontal pixel coordinates. By rounding off
157            // the value of x, we are choosing the nearest logical
158            // horizontal pixel coordinate.
159            fb.setPixelVP((int)Math.round(x) - 1, h - y, new Color(r, g, b));
160
161            // log interesting information to standard output
162            if (debug) logPixel(w, h, x, y, r, g, b);
163
164            // Advance (x,y) to the next pixel. Since delta_y = 1, we need delta_x = m.
165         }
166      }
167   }
168
169
170   private static void logPixel(int w, int h, int x, double y,
171                                float r, float g, float b)
172   {
173      System.out.printf(
174         "[w=%d, h=%d]  x=%d, y=%f, r=%f, g=%f, b=%f\n", w, h, x, y, r, g, b);
175   }
176
177   private static void logPixel(int w, int h, double x, int y,
178                                float r, float g, float b)
179   {
180      System.out.printf(
181         "[w=%d, h=%d]  x=%f, y=%d, r=%f, g=%f, b=%f\n", w, h, x, y, r, g, b);
182   }
183}