skatemakerpro/docs/GRASS.md
type-two a5c8765776 GODVERSE grass system + test-ride grinds
js/grass.js: reusable single-file grass module for all our games — GPU-instanced
blades (33k+ in one draw call), vertex-shader wind hashed per blade, root->tip
fake-AO gradient x per-instance tint, alpha-test pixel style (nearest-filter
16x24 canvas cards, stepped sway), deterministic seed, player-push uniform.
docs/GRASS.md records the technique ladder (clump maps, shell texturing,
billboard LOD, MB atlas gen) for future tiers. Auto-grows on parkland + islands,
PARK panel style/density, included in exported levels via buildParkScene.

Test ride: grind physics — falling onto any flattened steel (rails, ledge caps,
pool coping) locks a slide along the segment, pop off with Space or ride off the
end; grass parts around the rider.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 20:39:45 +10:00

3.2 KiB
Raw Permalink Blame History

GODVERSE grass — the module and the tricks

js/grass.js is the canonical grass system for all our games. It's a system, not a mesh — that's why it lives as a code module with style presets, not as GLBs on the farm. Lift the single file into any three.js project:

import { makeGrass } from './grass.js';
const grass = makeGrass({ heightAt, areas, style: 'lush', density: 8 });
scene.add(grass);                      // self-ticking wind; add and forget
grass.userData.setPlayer(x, z);        // optional: grass parts around the player

Styles: lush (tapered instanced blades), dry (same, straw palette), pixel (crossed alpha-test cards, 16×24 nearest-filter canvas texture, banded palette, stepped sway — PS1/N64 vibe).

The tricks it uses (why it's fast)

  1. GPU instancing — one InstancedMesh = one draw call for up to 40k blades. Per-blade cost after spawn is zero on the CPU.
  2. Vertex-shader windsin(time + hash(blade origin)), bend scaled by uv.y² so roots stay planted, tips fly. Two frequencies layered so it doesn't look like a metronome. Pixel style floors the sway to 3 steps.
  3. Root→tip gradient with darkened base — geometry vertex colors (0.35 at the root → 1.15 at the tip) multiply with a per-instance tip tint. Reads as ambient occlusion without any lighting cost. The single biggest visual win.
  4. Per-instance jitter — scale, rotation, lean, hue offset. Uniformity is what makes cheap grass look cheap.
  5. alphaTest, never alpha-blend — no depth sorting, no overdraw melt.
  6. No per-blade shadows — the base darkening does that job.
  7. Deterministic seed (mulberry32) — same park, same lawn, every load.
  8. Player-push uniform — blades within 1.2m bend away from the rider. Sells physicality for one uniform update per frame.

The next tiers (when a game needs bigger fields)

  • Chunked frustum culling — split into 16m tiles, cull per-tile. Needed beyond ~100m fields; our parks don't need it yet.
  • Far-field billboard cards — crossed quads with a grass-clump texture past ~40m, instanced blades near. Classic LOD split.
  • Clump maps (Ghost of Tsushima GDC 2021, "Procedural Grass in Ghost of Tsushima") — sample a Voronoi clump texture: blades in a clump share lean/height, clump centers get taller blades. Breaks the "even lawn" look at scale.
  • Shell texturing (Acerola's "How Do Games Render So Much Grass?") — N stacked transparent shells; dot patterns at increasing height thresholds. Ultra cheap retro turf/fur; a natural fourth style for the pixel games.
  • Bezier blades + tessellation for hero close-ups (Sucker Punch use 3-4 verts per blade too — verts are not where the money is; shading is).
  • MODELBEAST atlas textures — flux_local can generate blade/clump atlases for the billboard tiers (grass blade atlas, alpha background, hand-painted style), then bg_remove_local for clean alpha.

In SKATEMAKER PRO

Grass auto-grows on the parkland ring outside the slab and on ISLAND pads, from park.grass ({style, density} — PARK panel when nothing is selected). Exported levels include it via buildParkScene; the editor rebuilds it on committed ground changes only (never mid-drag).