In this chapter
In this chapter
Two versions of the same wall
PixelPunk has two kinds of masonry: patterns painted by shaders, and small pieces of real geometry. The shader paints the wall texture across the whole surface, and instanced bricks add depth around openings, at corners and along other edges that matter.
The two have to agree on the brick grid. If a generated brick sits across a mortar joint painted behind it, the wall stops making sense. It gets worse when a face is rotated, because a shortcut based on world axes no longer measures distance along the wall.
The material-coordinate pass gave both systems the same metric face coordinates. Distance along a wall is measured along its horizontal tangent, and the course and column sizes are mirrored in the shader include and in GDScript. Tests compare the two sets of numbers so they can’t drift apart without anyone noticing.
With the old surround routine put back for comparison, 161 of 387 tested bricks crossed a painted joint. After the fix, none did. That says a lot more than “the texture looks better”, because it names exactly which disagreement went away.
Dirt needs a reason to be there
Random noise is easy; wear that convinces needs context. Damp should rise from the ground or the waterline. A wall sheltered under an eave should weather differently from an exposed one. Dirt and moss need small, local masks instead of a grunge filter over everything.
So the weathering reads the geometry. When a roof, a neighbour or the terrain changes, the exposure data has to change with it. A stain left floating after a resize is just another broken join.
Wet paving is selective in the same way. Rain darkens the surface and adds some reflections, but it doesn’t turn every square metre into a mirror. The town should look like it is made of materials, and not as if someone varnished it.
The zoomed-out test
The material work came round again on 23 September. Walls, paving and meadow still didn’t read well enough from the distance people actually build at. A good close-up doesn’t help much if the same material turns into a flat fill at normal zoom.
The contrast pass widened the tone variation between blocks, darkened the mortar, picked out worn edges and stopped joints shrinking below a fraction of an art pixel. Plaster got broader tone fields and repairs. The meadow got several greens and small clumps of turf, with the detail fading out as each pixel covers more ground.
It stayed a shader-only change across four files, and the shared masonry grid didn’t move. That mattered, because richer shading shouldn’t undo the agreement between painted joints and generated bricks that had taken so much work to reach.


One rejected idea, one approximation
A trial of AgX tonemapping made the scene darker and muddier at its current exposures, so it wasn’t adopted. The failed attempt stays in the notes, which stops a later pass from treating it as an obvious idea nobody tried.
The miniature lens is a deliberate approximation. With the pitched orthographic camera, a blur that increases away from a horizontal focus band is enough to suggest a photographed model. It’s a two-pass screen-space blur with no depth information, and the player can turn it off.
The aim of all this material work is for a few pixels to read as stone, plaster, water or grass before the player has to think about it, and that comes down to choosing what to put in them.
From the project notebook
This chapter draws on the project’s records as they stood on 23 September 2026.
- docs/visual-handoff.md · Metric face coordinates, weathering, wet paving, tilt-shift and texture contrast
- shaders/masonry_grid.gdshaderinc
- artifacts/texture-proto/README.md
- artifacts/texture-proto/v2/bench-summary.txt

