Avatar
Quan Zhou
Innovation Enthusiast
I work across full-stack, AI, and Game technology.
© 2025 | poTAToworkshop

Simple city demo

Back
2026-1-5

1.Project Motivation

I wanted to build a demo tailored to a specific project and role—something that would continue to be useful for job hunting and even my longer-term career. The idea itself offered inspiration while keeping its scope and boundaries relatively clear, which made it exciting from the very beginning.

After discussing this with ChatGPT, it proposed a solution that I found genuinely interesting. The first step of the project was defined as: procedurally generating 10–50k buildings using GPU instancing.

2.The First Step

image.png

The initial step was straightforward: generate a large number of cubes with random heights and random footprints. These dense rectangular blocks have almost nothing to do with a real city, but the gap between this state and an actual city directly points to the direction of the next step.

3.Basical Road Network

image.png

Starting from the simplest idea is usually a good approach, so I began by generating a perfectly regular grid. The problem becomes clear immediately: the gap between this and real roads lies in how clean and regular it is. From there, the next intuitive idea was to introduce a main road and let secondary roads grow outward from it.

image.png

This produced a fishbone-like road structure, which feels even further away from real urban road networks. At least regular grids still plausibly appear in simulation games.

4.Tensor Fields and Streamlines

I spent some time researching procedural city generation approaches and eventually referenced this article by Martin Evans:

https://martindevans.me/game-development/2015/12/11/Procedural-Generation-For-Dummies-Roads/

The core idea is actually very clear:

  • Generate a tensor field on a plane
  • Randomly generate seed points
  • Let seeds trace along the tensor field to form streamlines
  • Each streamline becomes a road

image.png

Tensor field

image.png

The first streamline showed some strange curvature. In a constant tensor field, streamlines form a regular grid, so fundamentally the tensor field itself determines the road shape. By properly controlling the tensor field, these odd roads can be avoided.

image.png

Another issue is loops. Tensor fields easily produce circular paths, but due to accumulated numerical error, paths often fail to close naturally.

Here I used the RK4 sampling method from the original article, which produced much more stable results.

image.png

I then experimented with randomly generating more seeds.

image.png

Seeds can either be generated completely at random or spawned during tracing to grow in new directions. In a radial tensor field, this produced very convincing satellite-like road networks.

image.png

After repeatedly tuning the tensor field parameters, I arrived at a result I was happy with and laid down actual road surfaces.

5.Placing Buildings

The original article goes on to introduce more complex algorithms. Here, I chose a more direct approach: bake the road network into a texture and use it as a mask for building placement.

The problems with the current version are obvious:

  • Building distribution is too uniform
  • There is no sense of clustering
  • There are almost no “empty” areas

Overall, everything still feels too dense.

image.png

BuildingHeight.png

To break this uniformity, I used Substance Designer to generate additional mask textures to control building density and distribution.

image.png

After adding this layer, the city started to feel like it could breathe. With some further shadow adjustments, the result already looks decent.

The next directions for improvement are also clear:

  • Orient buildings toward roads
  • Introduce more variation in building scale
  • Control the number of buildings per unit area

At this point, the problem is no longer about individual “points,” but is strongly tied to block-level structure, which means more complex algorithms will be needed.