From Empty Grass to Living Circuits

Tracks don’t exist in a void. The space around the asphalt matters — it sells the scale, grounds the camera, and turns a flat 2D view into something that feels like a real place. Here’s how The Undercut fills that space with procedurally generated forests.


Why trees matter in a top-down racing game

At first glance, trees in a 2D top-down game seem like pure decoration. But they serve several critical roles:

  • Depth cues — overlapping canopies with shadows break the flatness of the bird’s-eye view and give the terrain a sense of elevation
  • Scale reference — a car passing next to a tree cluster instantly communicates speed and distance
  • Track framing — vegetation naturally guides the eye along the circuit layout, making corners and straights more readable
  • Atmosphere — a bare track on flat grass looks like a test environment, not a racing venue

The challenge: every circuit is procedurally generated, so the trees can’t be hand-placed. They need to emerge from the same data that builds the track itself.

Three layers of forest

The vegetation system builds each tree from three stacked layers, rendered top-down:

  1. Ground shadow — a large, soft, dark ellipse underneath the canopy. It anchors the tree to the terrain and provides the first sense of depth. A second, tighter shadow layer adds density variation.
  2. Canopy — the main tree body. Each canopy is a cluster of overlapping circles with slight color and size variation, giving an organic, irregular silhouette. Hue shifts within a controlled range keep the palette natural while avoiding monotony.
  3. Inner shadow — a subtle dark ring around the canopy’s interior edge. This simulates the self-shadowing you see when looking down into a tree crown, adding volume without any 3D geometry.

The Undercut - Procedural tree clusters with layered shadows around a race circuit

The combination reads as surprisingly three-dimensional from the top-down camera — canopies appear to float above the ground, casting real shadows beneath them.

Placement rules

Trees are scattered across the terrain using a density map derived from the track layout:

  • Exclusion zones around the track surface, kerbs, sand traps, and pit lane — no tree should block the racing action or clip into track geometry
  • Density falloff — more trees close to the track edges (framing the circuit), thinning out toward the map boundaries
  • Cluster formation — trees don’t spawn individually. A parent point spawns a small group of 3–8 trees with randomized offsets, creating natural-looking groves rather than a uniform grid
  • Size variation — each cluster mixes large canopy trees with smaller ones, mimicking a real forest edge where tall trees shelter younger growth

The placement algorithm runs once when the circuit is generated — under 50 milliseconds for a full forest.

Live tuning

Getting the look right required constant iteration. Rather than recompile and reload, every visual parameter is exposed in a real-time debug panel:

The Undercut - Tree rendering debug panel with shadow, canopy, and hue parameters

The panel exposes over 15 parameters:

  • Shadow Alpha / Scale / Diffuse — control the darkness, size, and softness of both shadow layers
  • Tree Factor — global density multiplier, controlling how many trees populate the scene
  • Shadow Offset — shifts the shadow relative to the canopy, simulating a sun angle
  • Inner Shadow Alpha / Scale — the self-shadowing ring inside each canopy
  • Mipmap Bias — texture sharpness at different zoom levels
  • Hue Min / Max — the range of green tones in the canopy, from dark forest to bright spring
  • Cumulative blending — shadow layers can blend additively for denser overlap areas

Every change updates instantly on the live track view. This feedback loop made it possible to dial in the exact look in minutes rather than hours — and to quickly adapt the palette for different circuit themes or seasons.

Keeping it fast

Performance matters — The Undercut targets 60 FPS even on modest hardware, and a full forest can mean hundreds of trees on screen.

Key optimizations:

  • Instanced rendering — all trees share the same base geometry, drawn in a single batch with per-instance transforms and color offsets
  • View frustum culling — trees outside the visible camera area are skipped entirely
  • LOD by zoom — at far zoom levels, shadow detail is simplified and small trees are dropped
  • Texture atlas — canopy variations are packed into a single texture sheet, avoiding per-tree draw calls

The result: a full forest renders in under 1 ms per frame, leaving the GPU budget free for track rendering, cars, and UI.

The result

What started as “add some green stuff around the track” became a system that genuinely transforms the feel of the game. Circuits that looked flat and sterile now feel embedded in a landscape. The shadows sell depth. The color variation sells life. And because it’s all procedural, every generated track gets a unique forest that fits its layout — no manual work required.

Every race, a new circuit. Every circuit, a new forest.