Scanline Rendering
When most modern discussions of computer graphics mention terms like rasterization or ray tracing, one technique often goes quietly overlooked: scanline rendering. Despite its venerable age, this method remains relevant in certain niches—embedded systems, retro-style graphics, low-power hardware, and stylized engines.
In this article, we’ll explore how scanline rendering works by processing one horizontal line (a scanline) at a time. You’ll learn its historical use in early 3D engines, how it differs from ray tracing and rasterization, and why it still matters today. You’ll also see pseudocode and a conceptual diagram to help you understand the process visually and algorithmically.
What is Scanline Rendering?
Scanline rendering is a method of generating a 3D image (or 2D polygon scene) by processing one horizontal row of pixels at a time, rather than processing every polygon or every pixel individually. Each horizontal row of pixels is known as a scanline.
The Basic Idea
A scanline renderer works by sweeping across the image from top to bottom. For each scanline, it determines which polygons intersect that line, calculates where those polygons start and end along the scanline, and fills in the visible spans of pixels.
Instead of testing every pixel against every polygon, scanline rendering limits calculations to the relevant polygons on that line. This makes it extremely efficient, especially when memory or processing power is limited.
How the Scanline Rendering Process Works
Step 1: Project the Geometry
The first step is to project all 3D geometry (like triangles or polygons) onto the 2D screen. This converts the 3D coordinates (x, y, z) into 2D screen coordinates (x, y), with depth information preserved for visibility.
Step 2: Build an Edge Table
Once projected, the renderer builds an edge table—a list of all polygon edges, sorted by their minimum y-coordinate (the top of the edge). Each entry stores:
- The maximum y (where the edge ends)
- The current x coordinate (at the current scanline)
- The slope of the edge (Δx/Δy)
- The polygon ID
Step 3: Initialize the Active Edge List (AEL)
The Active Edge List (AEL) keeps track of all edges that currently intersect the scanline being processed. It updates dynamically as the scanline moves downward across the image.
Step 4: Process Each Scanline
For every scanline (from top to bottom):
- Add new edges that begin at this scanline to the AEL.
- Remove edges that end at this scanline.
- Sort active edges by their current x-coordinate.
- Fill in the pixels between pairs of edges—these are the spans that lie inside polygons.
- Update each edge’s x value for the next scanline (by adding its slope).
Because each scanline shares much of the same geometry with its neighbors, this approach takes advantage of horizontal coherence, meaning very little new computation is needed from one line to the next.
Pseudocode for a Simple Scanline Renderer
// List of polygons with vertices in screen coordinates
EdgeTable = []
// Build edge table
for each polygon p:
for each edge e of p:
if e.y1 == e.y2: continue // skip horizontal edges
ymin = min(e.y1, e.y2)
ymax = max(e.y1, e.y2)
x = (if e.y1 < e.y2 then e.x1 else e.x2)
invSlope = (e.x2 - e.x1) / (e.y2 - e.y1)
insert into EdgeTable[ymin] {ymax, x, invSlope, polygonID = p.id}
// Initialize Active Edge List
ActiveEdges = []
// For each scanline
for y = globalYMin to globalYMax:
// Add new edges
for each edgeEntry in EdgeTable[y]:
ActiveEdges.append(edgeEntry)
// Remove finished edges
ActiveEdges = ActiveEdges.filter(edge => edge.ymax > y)
// Sort by current x
sort ActiveEdges by edge.x ascending
// Fill spans between pairs of edges
for i = 0 to ActiveEdges.length-1 step 2:
e1 = ActiveEdges[i]
e2 = ActiveEdges[i+1]
xStart = ceil(e1.x)
xEnd = floor(e2.x)
for x = xStart to xEnd:
z = interpolateDepth(e1, e2, x)
if z < depthBuffer[x][y]:
depthBuffer[x][y] = z
frameBuffer[x][y] = shadePixel(polygonOf(e1), x, y)
// Update x for each edge
for each edge in ActiveEdges:
edge.x += edge.invSlope
Diagram of the Scanline Process
y = y0 ──────────────────────────────── ← topmost scanline
| edges A & B intersect here, fill between them
y = y0+1 ────────────────────────────────
| edges move slightly, fill updated spans
y = y0+2 ────────────────────────────────
| new edges begin, others end
...
y = yMax ──────────────────────────────── ← bottom scanline
Each scanline progresses downward, intersecting polygon edges. The renderer fills horizontal spans between pairs of edges, line by line, until the entire frame is complete.
Historical Use in Early 3D Engines
Scanline rendering dates back to the earliest days of computer graphics, long before modern GPUs or full Z-buffers existed.
Why It Was Popular
In early systems, memory was extremely limited, and per-pixel depth calculations were expensive. The scanline approach required only a small amount of memory for the current line and the edges active within it.
Key advantages in early hardware included:
- Low memory usage: Only active edges and spans had to be stored.
- Faster arithmetic: Incremental updates (adding slopes) were much cheaper than recalculating intersections every time.
- Natural alignment with CRT displays: Old monitors drew images line by line, matching the scanline renderer’s workflow perfectly.
Classic Usage
Many early software renderers and 3D engines in the 1980s and 1990s used scanline methods. These engines often combined scanline rendering with BSP trees (Binary Space Partitioning) to sort polygons front-to-back efficiently before filling spans.
Games and simulators with static scenery or low polygon counts relied heavily on scanline rendering for its speed and simplicity.
How Scanline Rendering Differs from Rasterization and Ray Tracing
Scanline vs Rasterization
Modern rasterization converts triangles into pixels using a fragment pipeline—each triangle is processed individually, and every pixel (fragment) gets tested for coverage and depth.
Scanline rendering, on the other hand, works line by line, filling spans across the image rather than handling one triangle at a time.
Main differences:
| Feature | Scanline Rendering | Rasterization |
|---|---|---|
| Processing Unit | Horizontal lines | Individual triangles/fragments |
| Depth Handling | Span or edge-based | Per-pixel Z-buffer |
| Memory Use | Low | Higher (full Z-buffer) |
| Parallelism | Sequential | Highly parallel (GPU optimized) |
| Efficiency | Great for small systems | Best for high-performance GPUs |
Scanline vs Ray Tracing
Ray tracing takes an entirely different approach. It casts rays from the camera through each pixel into the scene, checking which objects those rays hit. It can simulate reflections, shadows, and refractions, producing photo-realistic results—but at a much higher computational cost.
Key differences:
| Feature | Scanline Rendering | Ray Tracing |
|---|---|---|
| Approach | Line-by-line, edge-based | Pixel-by-pixel, ray-based |
| Performance | Very fast on simple scenes | Computationally expensive |
| Realism | Basic shading | Highly realistic lighting |
| Memory Use | Very low | Moderate to high |
| Best for | Embedded, stylized, retro engines | Film, photorealism, advanced graphics |
In short, scanline rendering focuses on efficiency and predictability, while ray tracing emphasizes accuracy and realism.
Why Scanline Rendering Is Still Relevant Today
Embedded and Low-Power Systems
Many embedded devices and small processors don’t have the resources for full rasterization pipelines. For them, scanline rendering provides a fast, low-memory solution that still produces solid results.
By processing one line at a time and storing minimal data, it can operate efficiently even on systems without dedicated graphics hardware.
Retro and Stylized Graphics
Scanline rendering is perfect for engines or games that want to recreate the look of classic 3D software renderers. Developers of stylized games often embrace its deterministic, line-by-line aesthetic.
Bandwidth and Memory Efficiency
The scanline method only requires small, per-line working memory. This minimizes memory bandwidth usage and can lead to predictable, low-latency performance.
Educational Value
Because it clearly demonstrates concepts like polygon filling, edge tables, and hidden surface removal, scanline rendering remains one of the best algorithms for learning the fundamentals of computer graphics.
Hybrid Systems
Even in modern rendering, some systems adopt hybrid scanline techniques—for example, tile-based rendering architectures that process the image in strips or tiles (essentially mini-scanlines). These methods still exploit the same locality and coherence advantages that made scanline rendering successful decades ago.
Limitations of Scanline Rendering
While efficient, scanline rendering isn’t ideal for every scenario.
Poor Scalability
As polygon counts increase, maintaining the active edge lists and sorting them each scanline becomes costly. Full Z-buffer rasterization scales much better with complex scenes.
Transparency and Special Effects
Handling transparency, reflections, or advanced lighting effects is complicated in a scanline pipeline, as it relies on per-span depth handling rather than per-pixel control.
Sequential Nature
Because scanline rendering depends on ordered, line-by-line processing, it doesn’t take advantage of the massive parallelism in modern GPUs.
Complexity with Dynamic Scenes
Scenes with heavy motion or overlapping geometry can make managing the active edge lists and spans more difficult and less predictable.
Despite these drawbacks, for controlled environments with limited geometry, scanline rendering can outperform more generalized methods.
Implementation Tips and Optimizations
If you want to build a scanline renderer from scratch or optimize one for a project, here are key strategies to keep it fast and efficient:
Edge Bucketing
Pre-group edges by their starting y-coordinate so you can quickly access which edges start on each scanline.
Maintain a Sorted Active Edge List
Keep your AEL sorted by x-position. Since edges move slowly between lines, incremental re-sorting each scanline is fast.
Fill Spans Instead of Pixels
Rather than checking each pixel individually, fill entire spans (between two edges). This dramatically reduces loop overhead.
Incremental Interpolation
Update attributes (like depth, color, texture coordinates) incrementally between scanlines using precomputed deltas instead of recalculating from scratch.
Combine with Simple Depth Buffering
If multiple polygons overlap, a small per-scanline or per-span depth buffer can resolve visibility efficiently without needing a full-frame Z-buffer.
Use Flat or Gouraud Shading
Simple shading models work best. Flat shading or Gouraud shading (interpolated vertex colors) keep computations light and maintain the scanline style.
Example Use-Case: Embedded 3D Engine
Imagine designing a small 3D engine for an embedded device or retro-style game console. The processor is limited, and memory is scarce. Using scanline rendering makes perfect sense.
The Workflow
- Transform and project all vertices to screen space.
- Build an edge table with polygons sorted by starting y.
- For each scanline:
- Activate new edges, remove completed ones.
- Sort active edges by x position.
- Pair edges to form spans, fill the spans with color or texture.
- Update edge intersections for the next scanline.
- Output the finished scanline to the display buffer.
The Benefits
- Predictable memory use: Only a few lists and buffers are needed per line.
- Consistent performance: Each scanline takes a known amount of time.
- Retro aesthetic: Perfect for old-school or stylized visuals.
Conclusion
Scanline rendering is one of the foundational techniques in computer graphics. It processes each horizontal line of the image in turn, using edge and span coherence to efficiently determine visible surfaces.
While newer methods like rasterization and ray tracing dominate modern graphics, scanline rendering remains a brilliant example of algorithmic elegance—simple, memory-efficient, and still perfectly suited for certain real-world applications.
For embedded devices, stylized games, or anyone learning the fundamentals of 3D rendering, scanline rendering is not just a historical curiosity—it’s a timeless tool for efficient and elegant image generation.