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 antialias the 014 line at the same time. 015<p> 016 This is a fairly simplistic antialiasing 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 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 // Rasterize this line segment. 090 // In the following loop, as x moves across the logical 091 // horizontal pixels, we will compute a y value for each x. 092 double y = y0; 093 for (int x = (int)x0; x < (int)x1; x++, y += m) 094 { 095 // Interpolate this pixel's color between the two endpoint's colors. 096 float r = (float)Math.abs(r0 + slopeR*(x - x0)); 097 float g = (float)Math.abs(g0 + slopeG*(x - x0)); 098 float b = (float)Math.abs(b0 + slopeB*(x - x0)); 099 // We need the Math.abs() because otherwise, we sometimes get -0.0. 100 101 // Check if y is on the bottom or top edge of the pixel coordinate space. 102 if ( y <= 1 || h <= y ) 103 { 104 // Set this pixel in the framebuffer. 105 fb.setPixelVP(x - 1, h - (int)Math.round(y), new Color(r, g, b)); 106 107 // log interesting information to standard output 108 if (debug) logPixel(w, h, x, y, r, g, b); 109 } 110 else // y must be between two logical vertical pixel coordinates 111 { 112 // Let y_low and y_hi be the logical vertical pixel 113 // coordinates that bracket around y. 114 int y_low = (int)y; // the integer part of y 115 int y_hi = y_low + 1; 116 // Let weight be the fractional part of y. We will use 117 // weight to determine how much emphasis to place on 118 // each of the two pixels that bracket y. 119 float weight = (float)(y - y_low); 120 121 // Interpolate colors for the low and high pixels. 122 // The smaller weight is, the closer y is to the lower 123 // pixel, so we give the lower pixel more emphasis when 124 // weight is small. 125 float r_low = (float)((1 - weight) * r + weight * (bg.getRed()/255.0)); 126 float g_low = (float)((1 - weight) * g + weight * (bg.getGreen()/255.0)); 127 float b_low = (float)((1 - weight) * b + weight * (bg.getBlue()/255.0)); 128 float r_hi = (float)(weight * r + (1 - weight) * (bg.getRed()/255.0)); 129 float g_hi = (float)(weight * g + (1 - weight) * (bg.getGreen()/255.0)); 130 float b_hi = (float)(weight * b + (1 - weight) * (bg.getBlue()/255.0)); 131 132 // Set this (antialiased) pixel in the framebuffer. 133 fb.setPixelVP(x - 1, h - y_low, new Color(r_low, g_low, b_low)); 134 fb.setPixelVP(x - 1, h - y_hi, new Color(r_hi, g_hi, b_hi)); 135 136 // log interesting information to standard output 137 if (debug) logPixel(w, h, x, y, r_low, g_low, b_low, r_hi, g_hi, b_hi); 138 } 139 // Advance (x,y) to the next pixel (delta_x is 1, so delta_y is m). 140 } 141 // Set the pixel for the (x1,y1) endpoint 142 // (we do this separately to avoid roundoff errors). 143 fb.setPixelVP((int)x1 - 1, h - (int)y1, new Color(r1, g1, b1)); 144 } 145 else // abs(slope) > 1 146 { 147 if (y1 < y0) // swap (x0, y0) with (x1, y1) 148 { 149 double tempX = x0; 150 double tempY = y0; 151 x0 = x1; 152 y0 = y1; 153 x1 = tempX; 154 y1 = tempY; 155 // swap the colors too 156 float tempR = r0; 157 float tempG = g0; 158 float tempB = b0; 159 r0 = r1; 160 g0 = g1; 161 b0 = b1; 162 r1 = tempR; 163 g1 = tempG; 164 b1 = tempB; 165 } 166 167 // Compute this line segment's slope. 168 double m = (x1 - x0) / (y1 - y0); 169 double slopeR = (r1 - r0) / (y1 - y0); 170 double slopeG = (g1 - g0) / (y1 - y0); 171 double slopeB = (b1 - b0) / (y1 - y0); 172 173 // Rasterize this line segment. 174 // In the following loop, as y moves across the logical 175 // vertical pixels, we will compute a x value for each y. 176 double x = x0; 177 for (int y = (int)y0; y < (int)y1; x += m, y++) 178 { 179 // Interpolate this pixel's color between the two endpoint's colors. 180 float r = (float)Math.abs(r0 + slopeR*(y - y0)); 181 float g = (float)Math.abs(g0 + slopeG*(y - y0)); 182 float b = (float)Math.abs(b0 + slopeB*(y - y0)); 183 // We need the Math.abs() because otherwise, we sometimes get -0.0. 184 185 // Check if x is on the left or right edge of the pixel coordinate space. 186 if ( x <= 1 || w <= x ) 187 { 188 // Set this pixel in the framebuffer. 189 fb.setPixelVP((int)Math.round(x) - 1, h - y, new Color(r, g, b)); 190 191 // log interesting information to standard output 192 if (debug) logPixel(w, h, x, y, r, g, b); 193 } 194 else // x must be between two logical horizontal pixel coordinates 195 { 196 // x_low and x_hi will be pixel coordinates that bracket around x 197 int x_low = (int)x; // the integer part of x 198 int x_hi = x_low + 1; 199 200 float weight = (float)(x - x_low); // the fractional part of x 201 202 // Interpolate colors for the low and high pixels. 203 float r_low = (float)((1 - weight) * r + weight * (bg.getRed()/255.0)); 204 float g_low = (float)((1 - weight) * g + weight * (bg.getGreen()/255.0)); 205 float b_low = (float)((1 - weight) * b + weight * (bg.getBlue()/255.0)); 206 float r_hi = (float)(weight * r + (1 - weight) * (bg.getRed()/255.0)); 207 float g_hi = (float)(weight * g + (1 - weight) * (bg.getGreen()/255.0)); 208 float b_hi = (float)(weight * b + (1 - weight) * (bg.getBlue()/255.0)); 209 210 // Set this (antialiased) pixel in the framebuffer. 211 fb.setPixelVP(x_low - 1, h - y, new Color(r_low, g_low, b_low)); 212 fb.setPixelVP( x_hi - 1, h - y, new Color(r_hi, g_hi, b_hi)); 213 214 // log interesting information to standard output 215 if (debug) logPixel(w, h, x, y, r_low, g_low, b_low, r_hi, g_hi, b_hi); 216 } 217 // Advance (x,y) to the next pixel (delta_y is 1, so delta_x is m). 218 } 219 // Set the pixel for the (x1,y1) endpoint 220 // (we do this separately to avoid roundoff errors). 221 fb.setPixelVP((int)x1 - 1, h - (int)y1, new Color(r1, g1, b1)); 222 } 223 } 224 225 226 private static void logPixel(int w, int h, int x, double y, 227 float r, float g, float b) 228 { 229 System.out.printf( 230 "[w=%d, h=%d] x=%d, y=%f, r=%f, g=%f, b=%f\n", w, h, x, y, r, g, b); 231 } 232 233 private static void logPixel(int w, int h, double x, int y, 234 float r, float g, float b) 235 { 236 System.out.printf( 237 "[w=%d, h=%d] x=%f, y=%d, r=%f, g=%f, b=%f\n", w, h, x, y, r, g, b); 238 } 239 240 private static void logPixel(int w, int h, int x, double y, 241 float r1, float g1, float b1, 242 float r2, float g2, float b2) 243 { 244 System.out.printf( 245 "[w=%d, h=%d] x=%d, y=%f, r=%f, g=%f, b=%f\n", w, h, x, y, r1, g1, b1); 246 System.out.printf( 247 " x=%d, y=%f, r=%f, g=%f, b=%f\n", x, y, r2, g2, b2); 248 } 249 250 private static void logPixel(int w, int h, double x, int y, 251 float r1, float g1, float b1, 252 float r2, float g2, float b2) 253 { 254 System.out.printf( 255 "[w=%d, h=%d] x=%f, y=%d, r=%f, g=%f, b=%f\n", w, h, x, y, r1, g1, b1); 256 System.out.printf( 257 " x=%f, y=%d, r=%f, g=%f, b=%f\n", x, y, r2, g2, b2); 258 } 259}