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 and anti-alias the 014 line at the same time. 015<p> 016 This is a fairly simplistic anti-aliasing algorithm. 017*/ 018public class RasterizeAntialias 019{ 020 public static boolean debug = false; 021 022 /** 023 Rasterize a {@link LineSegment} into antialiased pixels in the 024 {@link FrameBuffer}'s viewport. 025 026 @param ls {@link LineSegment} to rasterize into the {@link FrameBuffer} 027 @param fb {@link FrameBuffer} to hold rasterized pixels 028 */ 029 public static void rasterize(LineSegment ls, FrameBuffer fb) 030 { 031 // Get the viewport's background color. 032 Color bg = fb.getBgColorVP(); 033 034 // Make local copies of several values. 035 int w = fb.getWidthVP(); 036 int h = fb.getHeightVP(); 037 038 Vertex v0 = ls.model.vertexList.get( ls.index[0] ); 039 Vertex v1 = ls.model.vertexList.get( ls.index[1] ); 040 041 // Get the colors from the two vertices. 042 float r0 = v0.r, g0 = v0.g, b0 = v0.b; 043 float r1 = v1.r, g1 = v1.g, b1 = v1.b; 044 045 // Round the line segment's two endpoints to the nearest 046 // logical pixel. This makes the algorithm a lot simpler, 047 // but it can cause a slight, but noticable, shift of the 048 // line segment. 049 double x0 = Math.round(v0.x), y0 = Math.round(v0.y); 050 double x1 = Math.round(v1.x), y1 = Math.round(v1.y); 051 052 // Rasterize a degenerate line segment (a line segment 053 // that projected onto a point) as a single pixel. 054 if ( (x0 == x1) && (y0 == y1) ) 055 { 056 // We don't know which endpoint of the line segment 057 // is in front, so just pick v0. 058 fb.setPixelVP((int)x0 - 1, h - (int)y0, new Color(r0, g0, b0)); 059 return; 060 } 061 062 if (Math.abs(y1 - y0) <= Math.abs(x1 - x0)) // if abs(slope) <= 1 063 { 064 if (x1 < x0) // swap (x0, y0) with (x1, y1) 065 { 066 double tempX = x0; 067 double tempY = y0; 068 x0 = x1; 069 y0 = y1; 070 x1 = tempX; 071 y1 = tempY; 072 // swap the colors too 073 float tempR = r0; 074 float tempG = g0; 075 float tempB = b0; 076 r0 = r1; 077 g0 = g1; 078 b0 = b1; 079 r1 = tempR; 080 g1 = tempG; 081 b1 = tempB; 082 } 083 084 // Compute this line segment's slope. 085 double m = (y1 - y0) / (x1 - x0); 086 double slopeR = (r1 - r0) / (x1 - x0); 087 double slopeG = (g1 - g0) / (x1 - x0); 088 double slopeB = (b1 - b0) / (x1 - x0); 089 090 // Rasterize this line segment. 091 // In the following loop, as x moves across the logical 092 // horizontal pixels, we will compute a y value for each x. 093 double y = y0; 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 // Check if y is on the bottom or top edge of the pixel coordinate space. 103 if ( y <= 1 || h <= y ) 104 { 105 // Set this pixel in the framebuffer. 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 else // y must be between two logical vertical pixel coordinates 112 { 113 // Let y_low and y_hi be the logical vertical pixel 114 // coordinates that bracket around y. 115 int y_low = (int)y; // the integer part of y 116 int y_hi = y_low + 1; 117 // Let weight be the fractional part of y. We will use 118 // weight to determine how much emphasis to place on 119 // each of the two pixels that bracket y. 120 float weight = (float)(y - y_low); 121 122 // Interpolate colors for the low and high pixels. 123 // The smaller weight is, the closer y is to the lower 124 // pixel, so we give the lower pixel more emphasis when 125 // weight is small. 126 float r_low = (float)((1 - weight) * r + weight * (bg.getRed()/255.0)); 127 float g_low = (float)((1 - weight) * g + weight * (bg.getGreen()/255.0)); 128 float b_low = (float)((1 - weight) * b + weight * (bg.getBlue()/255.0)); 129 float r_hi = (float)(weight * r + (1 - weight) * (bg.getRed()/255.0)); 130 float g_hi = (float)(weight * g + (1 - weight) * (bg.getGreen()/255.0)); 131 float b_hi = (float)(weight * b + (1 - weight) * (bg.getBlue()/255.0)); 132 133 // Set this (antialiased) pixel in the framebuffer. 134 fb.setPixelVP(x - 1, h - y_low, new Color(r_low, g_low, b_low)); 135 fb.setPixelVP(x - 1, h - y_hi, new Color(r_hi, g_hi, b_hi)); 136 137 // log interesting information to standard output 138 if (debug) logPixel(w, h, x, y, r_low, g_low, b_low, r_hi, g_hi, b_hi); 139 } 140 // Advance (x,y) to the next pixel (delta_x is 1, so delta_y is m). 141 } 142 // Set the pixel for the (x1,y1) endpoint 143 // (we do this separately to avoid roundoff errors). 144 fb.setPixelVP((int)x1 - 1, h - (int)y1, new Color(r1, g1, b1)); 145 } 146 else // abs(slope) > 1 147 { 148 if (y1 < y0) // swap (x0, y0) with (x1, y1) 149 { 150 double tempX = x0; 151 double tempY = y0; 152 x0 = x1; 153 y0 = y1; 154 x1 = tempX; 155 y1 = tempY; 156 // swap the colors too 157 float tempR = r0; 158 float tempG = g0; 159 float tempB = b0; 160 r0 = r1; 161 g0 = g1; 162 b0 = b1; 163 r1 = tempR; 164 g1 = tempG; 165 b1 = tempB; 166 } 167 168 // Compute this line segment's slope. 169 double m = (x1 - x0) / (y1 - y0); 170 double slopeR = (r1 - r0) / (y1 - y0); 171 double slopeG = (g1 - g0) / (y1 - y0); 172 double slopeB = (b1 - b0) / (y1 - y0); 173 174 // Rasterize this line segment. 175 // In the following loop, as y moves across the logical 176 // vertical pixels, we will compute a x value for each y. 177 double x = x0; 178 for (int y = (int)y0; y < (int)y1; x += m, y++) 179 { 180 // Interpolate this pixel's color between the two endpoint's colors. 181 float r = (float)Math.abs(r0 + slopeR*(y - y0)); 182 float g = (float)Math.abs(g0 + slopeG*(y - y0)); 183 float b = (float)Math.abs(b0 + slopeB*(y - y0)); 184 // We need the Math.abs() because otherwise, we sometimes get -0.0. 185 186 // Check if x is on the left or right edge of the pixel coordinate space. 187 if ( x <= 1 || w <= x ) 188 { 189 // Set this pixel in the framebuffer. 190 fb.setPixelVP((int)Math.round(x) - 1, h - y, new Color(r, g, b)); 191 192 // log interesting information to standard output 193 if (debug) logPixel(w, h, x, y, r, g, b); 194 } 195 else // x must be between two logical horizontal pixel coordinates 196 { 197 // x_low and x_hi will be pixel coordinates that bracket around x 198 int x_low = (int)x; // the integer part of x 199 int x_hi = x_low + 1; 200 201 float weight = (float)(x - x_low); // the fractional part of x 202 203 // Interpolate colors for the low and high pixels. 204 float r_low = (float)((1 - weight) * r + weight * (bg.getRed()/255.0)); 205 float g_low = (float)((1 - weight) * g + weight * (bg.getGreen()/255.0)); 206 float b_low = (float)((1 - weight) * b + weight * (bg.getBlue()/255.0)); 207 float r_hi = (float)(weight * r + (1 - weight) * (bg.getRed()/255.0)); 208 float g_hi = (float)(weight * g + (1 - weight) * (bg.getGreen()/255.0)); 209 float b_hi = (float)(weight * b + (1 - weight) * (bg.getBlue()/255.0)); 210 211 // Set this (antialiased) pixel in the framebuffer. 212 fb.setPixelVP(x_low - 1, h - y, new Color(r_low, g_low, b_low)); 213 fb.setPixelVP( x_hi - 1, h - y, new Color(r_hi, g_hi, b_hi)); 214 215 // log interesting information to standard output 216 if (debug) logPixel(w, h, x, y, r_low, g_low, b_low, r_hi, g_hi, b_hi); 217 } 218 // Advance (x,y) to the next pixel (delta_y is 1, so delta_x is m). 219 } 220 // Set the pixel for the (x1,y1) endpoint 221 // (we do this separately to avoid roundoff errors). 222 fb.setPixelVP((int)x1 - 1, h - (int)y1, new Color(r1, g1, b1)); 223 } 224 } 225 226 227 private static void logPixel(int w, int h, int x, double y, 228 float r, float g, float b) 229 { 230 System.out.printf( 231 "[w=%d, h=%d] x=%d, y=%f, r=%f, g=%f, b=%f\n", w, h, x, y, r, g, b); 232 } 233 234 private static void logPixel(int w, int h, double x, int y, 235 float r, float g, float b) 236 { 237 System.out.printf( 238 "[w=%d, h=%d] x=%f, y=%d, r=%f, g=%f, b=%f\n", w, h, x, y, r, g, b); 239 } 240 241 private static void logPixel(int w, int h, int x, double y, 242 float r1, float g1, float b1, 243 float r2, float g2, float b2) 244 { 245 System.out.printf( 246 "[w=%d, h=%d] x=%d, y=%f, r=%f, g=%f, b=%f\n", w, h, x, y, r1, g1, b1); 247 System.out.printf( 248 " x=%d, y=%f, r=%f, g=%f, b=%f\n", x, y, r2, g2, b2); 249 } 250 251 private static void logPixel(int w, int h, double x, int y, 252 float r1, float g1, float b1, 253 float r2, float g2, float b2) 254 { 255 System.out.printf( 256 "[w=%d, h=%d] x=%f, y=%d, r=%f, g=%f, b=%f\n", w, h, x, y, r1, g1, b1); 257 System.out.printf( 258 " x=%f, y=%d, r=%f, g=%f, b=%f\n", x, y, r2, g2, b2); 259 } 260}