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}