---
id: ground-types/004
title: Client rendering, stage 2 — textured transitions, and the renderer check on web and phone
epic: ground-types
state: done
priority: 1
blocked_by: []
estimate: M
created: 2026-08-17
updated: 2026-08-18
claimed_by: null
claimed_at: null
delivers: []
review_artifact: land-of-lor/docs/handoff/ground-types.md
---

## What to do

The spec's "its own special task": the ground shows real (placeholder) textures with soft edges
between any two types, corners included, and the same picture on Chrome and on the phone.

- `game/world/client/ground.gdshader` — `shader_type spatial`. Inputs: a nearest-sampled RGBA8
  data texture the size of the grid (R = lower-trigram index, G = upper index, B = flags;
  decode with `int(round(c * 255.0))` — no integer samplers) and an **8×8 tile atlas** (one
  `Texture2D`, gutter, mipmaps off). Per fragment: the four nearest cell centres, bilinear
  weights sharpened with `smoothstep` (optional value-noise wobble), each cell's tile sampled at
  the same world UV so like types are seamless — four data reads, four atlas reads. Try
  `sampler2DArray` only if the atlas look is unacceptable.
- `client/ground_atlas.gd` — the 64 tiles generated procedurally once from the ground and
  variant definitions (plain, speckle, stripes, dots, flowers for grass, cracks for lava,
  tiles / bricks / paths for Heaven); if `res://client/ground/<lower>/<upper>.png` exists it wins.
- `area_view.gd`: swap the material behind a toggle; the vertex-colour material stays as the
  fallback / debug view.
- `game/world/project.godot`: add `renderer/rendering_method.mobile="gl_compatibility"` (the
  POCs have it, the game doesn't, so the APK is very likely running the Vulkan Mobile renderer
  today) and fix the stale `config/features` "Forward Plus" entry. Re-check the two UI canvas
  shaders on the device afterwards.
- Verify on desktop Compatibility, on the **web export in Chrome**, and on **an APK on Kris's
  phone**: no shader compile errors in the console / logcat; frame time on the 60×60 Badlands at
  maximum zoom-out. The phone check needs the laptop — everything else can run from the task
  runner.

## Definition of done

Screenshots from all three, the numbers, and the atlas-vs-Texture2DArray decision written into
`docs/handoff/0NN-ground-rendering.md`. Committed and pushed.

## Where the work lands

`land-of-lor/game/world/client/`, `game/world/project.godot`, `docs/handoff/`.

## What was done (2026-08-17, laptop session)

- `client/ground.gdshader` (spatial; four-cell blend, `band` + value-noise `wobble`, cell
  data as a nearest RGBA8 texture decoded `floor(c*255+0.5)`, no integer samplers / texelFetch)
  and `client/ground_atlas.gd` (8×8 atlas of 32 px tiles drawn from each hexagram's colour
  and pattern — plain, speckle, streaks, dapple, cracks, pebbles, ripples, flowers, tiles;
  `res://client/ground/<lower>/<upper>.png` overrides a tile). `area_view.gd` swaps the
  material behind `USE_TEXTURED_GROUND`; stage 1 stays as fallback.
- `project.godot`: `renderer/rendering_method.mobile="gl_compatibility"`; features string
  fixed. **Decision:** atlas as primary; `Texture2DArray` not needed, not tried.
- Verified on desktop Compatibility (`_shots.gd`) and on the **exported web build in
  headless Chrome (WebGL2)** through the real signup → Start Game flow against a throwaway
  local director carrying the six ground files — no shader errors, textured home rendered.
  Screenshots + reasoning in `docs/handoff/ground-types.md` and `docs/handoff/ground-types/`.
- **Not done — needs Kris and a phone:** the on-device check and the 60×60 frame-time
  number. The `.mobile` override puts the phone on the backend the web check just passed;
  the APK itself is built in 008.