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