Depth sorting
rectangular polygons
model axes
graphics rendering
computational geometry

Depth sorting rectangular polygons, all parts of model axes facing

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

If all of your rectangular polygons face the camera and each rectangle lies on a single depth plane, depth sorting is much simpler than the general polygon-ordering problem. In that restricted case, a single depth key per rectangle is often enough; once rectangles span conflicting depths or intersect visually, a Z-buffer becomes the safer and more general solution.

Why the Restricted Case Is Easier

General depth sorting is hard because two polygons can overlap on screen while crossing each other in 3D. Then neither polygon is globally "in front" of the other.

Your case is easier when all of these are true:

  • each polygon is rectangular
  • all polygons face the viewer
  • each rectangle can be represented by one constant or near-constant depth value
  • there is no cyclic overlap relationship

Under those assumptions, painter-style ordering works well:

  1. transform rectangles into camera space
  2. compute one depth value per rectangle
  3. sort from farthest to nearest
  4. draw in that order

This is valid because each rectangle behaves like one front-facing layer rather than a shape with complicated depth variation.

Compute the Depth in Camera Space

The safest place to sort is camera space, not model space. A rectangle's z value in world or model coordinates only matters after the camera transform is applied.

A simple Python example:

python
1rectangles = [
2    {"name": "back", "z_camera": 12.0},
3    {"name": "middle", "z_camera": 7.0},
4    {"name": "front", "z_camera": 2.0},
5]
6
7ordered = sorted(rectangles, key=lambda r: r["z_camera"], reverse=True)
8print([r["name"] for r in ordered])

If the geometry guarantee really holds, this ordering is enough.

In a real renderer, z_camera might come from the transformed center of the rectangle or from the rectangle's plane distance in view space.

Which Depth Key Should You Use

When rectangles are truly parallel to the view plane, several keys are equivalent in practice:

  • center depth
  • average vertex depth
  • any vertex depth, if all vertices share the same depth

A slightly more explicit example uses four vertices:

python
1def average_depth(vertices):
2    return sum(v[2] for v in vertices) / len(vertices)
3
4rect = {
5    "name": "panel",
6    "vertices": [
7        (-1, -1, 5),
8        ( 1, -1, 5),
9        ( 1,  1, 5),
10        (-1,  1, 5),
11    ]
12}
13
14print(average_depth(rect["vertices"]))

If every vertex has the same camera-space depth, then polygon sorting is stable and straightforward.

When the Trick Stops Working

A single depth value per polygon fails when any of these happen:

  • one rectangle partially passes behind another
  • rectangles intersect in 3D
  • perspective makes one polygon cover a meaningful depth range
  • two polygons form a cyclic order in screen overlap

At that point, painter's algorithm becomes unreliable because there is no single correct whole-polygon order.

That is why modern graphics pipelines use a depth buffer. A depth buffer resolves visibility per pixel rather than assuming one polygon order works everywhere.

A Tiny Z-Buffer Example

You do not need a full renderer to see the idea. A depth buffer stores the nearest depth seen so far for each pixel.

python
1width, height = 4, 4
2zbuffer = [[float("inf")] * width for _ in range(height)]
3framebuffer = [["."] * width for _ in range(height)]
4
5
6def draw_pixel(x, y, z, color):
7    if z < zbuffer[y][x]:
8        zbuffer[y][x] = z
9        framebuffer[y][x] = color
10
11
12draw_pixel(1, 1, 5.0, "B")
13draw_pixel(1, 1, 2.0, "F")
14
15print(framebuffer[1][1])

The nearer pixel wins even if whole-polygon ordering would have been ambiguous. That is the core reason Z-buffering is robust.

A Practical Rule of Thumb

Use polygon-level depth sorting when your scene guarantees make it valid. Use a depth buffer when the scene is even slightly more general than that.

For front-facing UI quads, sprites, or billboard-like rectangles, sorting by camera-space depth is often all you need. For arbitrary 3D model surfaces, it is not.

This matters because many rendering bugs come from applying a simple sort to geometry that no longer satisfies the assumptions behind the sort.

Common Pitfalls

The most common mistake is sorting by model-space depth instead of camera-space depth. The camera transform can change which object is in front.

Another issue is using centroid or average depth for geometry that spans a meaningful depth range. A single average value cannot represent per-pixel visibility correctly.

People also assume that because all polygons are rectangles, sorting must be easy. Rectangle shape is not the key issue; the important question is whether each rectangle has one valid global depth order relative to the others.

Finally, nearly equal depth values can cause unstable ordering or Z-fighting. If the geometry is intentionally layered, a small separation or explicit rendering order may still be necessary.

Summary

  • Depth sorting is simple only when each rectangle can be represented by one valid depth value.
  • Compute that depth in camera space, not model space.
  • Painter-style ordering works for front-facing layered rectangles with no conflicting overlap.
  • The method fails when rectangles intersect or span inconsistent depths.
  • A Z-buffer is the robust solution once visibility becomes a per-pixel problem.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms