1. Rasterization

Scanline/Barycentric Triangle Rasterization

midgraphics-internalsrasterization

A rasterizer's core question, asked once per pixel per triangle: "is this pixel inside the triangle?" Software rasterizers answer it with BARYCENTRIC COORDINATES -- express a point p as a weighted combination of the triangle's 3 vertices, p = w0*v0 + w1*v1 + w2*v2 (with w0 + w1 + w2 == 1). p is inside the triangle exactly when every weight is >= 0 -- a negative weight means p has escaped past the edge opposite that vertex. These same weights are also exactly what real rasterizers reuse afterward to interpolate per-vertex color/depth/UV smoothly across the triangle's interior (a later exercise's DepthBuffer assumes a depth value arrived at a pixel via interpolation like this).

A real rasterizer never tests every pixel on screen -- only the triangle's screen-space bounding box, clipped to the framebuffer.

Implement:

  • barycentric_coordinates(px, py, v0, v1, v2) -- return (w0, w1, w2) such that (px, py) == w0*v0 + w1*v1 + w2*v2, or None if the triangle is degenerate (its 3 vertices are collinear -- zero area, no interior).
  • point_in_triangle(px, py, v0, v1, v2) -- True iff every weight is >= 0 (inside, or exactly on an edge/vertex). A degenerate triangle contains no points.
  • rasterize_triangle(v0, v1, v2, width, height) -- every (x, y) integer pixel whose center (sampled at the integer coordinate itself) is covered, restricted to the triangle's bounding box AND clipped to [0, width) x [0, height).

Example

barycentric_coordinates(0, 0, (0, 0), (4, 0), (0, 4))   # -> (1.0, 0.0, 0.0) -- exactly vertex v0
point_in_triangle(1, 1, (0, 0), (4, 0), (0, 4))          # -> True
point_in_triangle(3, 3, (0, 0), (4, 0), (0, 4))          # -> False -- outside the hypotenuse
rasterize_triangle((0, 0), (4, 0), (0, 4), width=10, height=10)
# -> every (x, y) with x, y >= 0 and x + y <= 4

Constraints

  • The formula must work regardless of the triangle's winding order (CW or CCW) -- both must classify the same points as inside.
  • Points exactly on an edge or vertex count as inside (>= 0, not > 0).
  • rasterize_triangle must clip to the framebuffer bounds -- never return a pixel outside [0, width) x [0, height).

Real-world grounding

  • ssloy/tinyrenderer's software rendering pipeline computes exactly this: barycentric coordinates over a triangle's screen-space bounding box, used both for the inside test and to interpolate depth (see the next exercise) and shading: our_gl.cpp (see the rasterize function).

Real rasterizers additionally handle sub-pixel precision, a documented top-left fill rule (so a pixel shared by two adjacent triangles is drawn exactly once, never twice or zero times), and perspective-correct interpolation via w -- this exercise implements the barycentric inside-test and bounding-box scan those refinements build on top of.

hints
triangle_rasterizer.pysign in to run
Loading...
test result

This is a preview. Sign in to run this exercise against the real tests.