From Ver(ces to Fragments: Rasteriza(on From Ver(ces to Fragments 3D vertices vertex shader rasterizer fragment shader final pixels 2D screen fragments l determine fragments to be covered l interpolate attributes per fragment 1
Rasteriza(on How to draw primitives? Convert from geometric definition to pixels Rasterization = selecting the pixels Will be done frequently Must be fast: use integer arithmetic use addition instead of multiplication This Lecture Line-drawing algorithm Naïve algorithm Bresenham algorithm 2
Scan Conver(ng 2D Line Segments Given: Segment endpoints (integers x1, y1; x2, y2) Identify: Set of pixels (x, y) to display for segment (x2, y2) (x1, y1) Line Rasteriza(on Requirements Transform continuous primitive into discrete samples Uniform thickness & brightness Continuous appearance No gaps Accuracy Speed (x2, y2) (x1, y1) 3
Simple Line Slope-intercept equation of a line: y = mx + h Given (x 1, y 1 ) and (x 2, y 2 ): m = y 2 y 1 x 2 x 1 h (x 2, y 2 ) h = y 1 mx 1 (x 1, y 1 ) Naïve line approach: - increment x, solve for y - known as DDA (Digital Differential Analyzer) DDA Rasteriza(on Algorithm Simply compute y as a function of x Move vertical scan line from x1 to x2 Determine y in terms of x Set pixel (x, Round (y)) (x2, y2) Round is expensive!!! m = dy dx = y 2 y 1 x 2 x 1 y y = y1+ m(x x1) (x1, y1) Two floating point operations per pixel!!! x 4
Efficiency Computing y value is expensive y = y1 + m( x x1) Observe: y += m at each x step (m = dy/dx) (x2, y2) y(x+1) y(x) (x1, y1) y(x+1) m y(x) x x+1 x x+1 DDA Line Rasteriza(on (x2, y2) y(x+1) y(x) (x1, y1) x x+1 y(x+1) m y(x) x x+1 Line(x1,y1,x2,y2) { float slope, y; } slope = (y2-y1)/(x2-x1); y = y1; for(x = x1; x <= x2; x++) { PlotPixel(x, Round(y)); y = y + slope; } One floating point operation per pixel!!! Round is expensive!!! 5
Example m = 4 2 6 0 = 1 3 x = 12 34 50 y = 7/3 8/3 310/3 11/3 2 (0,2) (6, 4) Does it Work? OK for lines with a slope of 1 or less NOT ok for lines with slope greater than 1: lines become more discontinuous in appearance must add more than 1 pixel per column to make it work. Solution? - use symmetry. 6
BeHer: Bresenham's Line Algorithm Select pixel vertically closest to line segment intuitive, efficient, pixel center always within 0.5 vertically Same result as the DDA approach Incremental Algorithm: - current value uses previous value - integer operations only Bresenham's Algorithm Observation: If we're at pixel P(x, y), the next pixel must be either E (x+1, y) or NE (x+1, y+1) P NE E 7
Bresenham Step Which pixel to choose: E or NE? Error associated with a pixel: N E Error pixel NE E Error pixel E Pick the pixel with error < ½ The sum of the 2 errors is Bresenham Step How to compute the error? Line defined as y = mx + h Vertical distance from line to pixel (x, y): e(x, y) = mx+h-y e < 0 negative if pixel above L e = 0 zero on L e > 0 positive below L L e is called the error function. 8
Bresenham's Algorithm How to compute the error? Error Function: e(x, y) = mx+h-y Initially e(x 1, y 1 ) = 0 On each iteration: update x: update e: if (e 0.5): if (e > 0.5): x' = x +1 e' = e + m y' = y (choose pixel E) y' = y +1 (choose pixel NE) e' = e - 1 e e Summary of Bresenham Initialize e = 0 for (x = x1; x x2; x++) plot (x,y) update x, y, e NE E Generalize to handle all eight octants using symmetry Still using floating point! e' = e + m Can we use only integer arithmetic? 9
Bresenham with no Floa(ng Point y2 y1 Error function e(x, y) = mx+h-y, m = = x2 x1 At selected pixel (x, y): e(x, y) <= ½ Simplify: 2 e(x, y) 1 <= 0 2mx + 2h 2y 1 <= 0 2x dy + 2h dx 2y dx dx <= 0 (Note that h dx = y 1 dx x 1 dy is an integer) Define F(x,y) = 2x dy + 2h dx 2y dx dx If F <= 0 stay horizontal If F > 0 move up NE E dy dx Bresenham with no Floa(ng Point Define F(x, y) = 2x dy + 2h dx 2y dx dx If F <= 0 stay horizontal If F > 0 move up NE E Incremental update for next pixel: If stay horizontal: x+=1, F += 2dy If move up: x+=1, y+=1, F += 2(dy dx) 10
Bresenham Line Pseudocode BresenhamLine(x1,y1,x2,y2) { int dx = x2 x1; int dy = y2 y1; int f = -dx; int y = y1; } for(x = x1; x <= x2; x++) { if(f <= 0) f += 2*dy; else { f += 2*(dy-dx); y++; } PlotPixel(x, y); } Algorithm = single instruction on graphics chips! 2D Scan Conversion Geometric primitive 2D: point, line, polygon, circle... 3D: point, line, polyhedron, sphere... Primitives are continuous; screen is discrete 11
Use Line Rasteriza(on Compute the boundary pixels Scan- line Rasteriza(on Compute the boundary pixels Fill the spans 12
Next: Anti-Aliasing Effects of An(- aliasing 13
Addi(onal Benefit of An(- aliasing Note: brightness can vary with slope What is the maximum variation? Compensate for this with antialiasing 2 * L L How do we remove aliasing? Solution 1: Super-Sampling Divide pixel into sub-pixels : 2x 2, 3x3, 4x4, etc. Pixel color is the average of its sub-pixel colors Pixels Pixel Subdivision (4x4) 14
Super- Sampling Bresenham at sub-pixel level Pixel Subdivision Each pixel can have a maximum of 4 colored sub-pixels 0.0 0.0 0.0 1.0 0.5 1.0 1.0 0.0 0.5 0.0 0.0 0.0 Fraction line color for the pixel Assign color 0 0 0 4 2 4 4 0 2 0 0 0 How many sub-pixels are colored? How do we remove aliasing? Super-sampling is expensive Render internally at higher resolution Invoke fragment shader once per sub-fragment Down-sample to fit the screen resolution Solution 2: Multi-Sampling Faster alternative to super-sampling Invoke fragment shader once per fragment 15
OpenGL glfwwindowhint(glfw_samples, 4);! glenable(gl_multisampling);! On most OpenGL drivers, multisampling is enabled by default so this call is then a bit redundant, but it's usually a good idea to enable it anyways 16