perf(web): share card texture via shader UV transform

Move the card sprite repeat/offset out of per-card texture clones and
into a uniform injected into the material shader. Cards now share the
deck sheet (one GPU upload), the shader, and the geometry, with only
per-card material uniforms differing, so navigating a deck no longer
re-uploads the sheet on every step.
This commit is contained in:
2026-08-14 10:46:09 +08:00
parent c665212209
commit 002bcb324b
5 changed files with 173 additions and 48 deletions
+25 -1
View File
@@ -299,4 +299,28 @@ flips.
**Alternatives considered:** Reporting only a hit and silently dropping
misses. Rejected — a command that needs to react to a wrong tap has no way to
do so. World-space trigger points. Rejected — they break when the part moves.
do so. World-space trigger points. Rejected — they break when the part moves.
## D21 — Card sprite UVs live in the material shader, not the texture
**Decision:** A card's sprite cell is selected by a repeat/offset injected into
the material's shader (`cardMaterial.ts` extends `MeshStandardMaterial` via
`onBeforeCompile`) rather than by cloning the texture and setting its
`repeat`/`offset`.
**Context:** Cards in a deck share one sprite sheet (drei caches the texture by
URL), but each card samples a different cell. The previous approach cloned the
texture per card to set its UVs; each clone gets its own WebGL texture binding,
so navigating a deck re-uploaded the whole sheet on every step. Moving the
transform into a per-material uniform lets cards share the texture (one GPU
upload), the shader (identical injected source → one program), and the geometry,
with only the material uniforms differing.
We inject our own uniform rather than setting `texture.repeat`/`offset` because
three r185 derives map UVs from a `mapTransform` matrix refreshed from
`map.matrix` every frame, which would overwrite a per-material transform set on
the shared texture.
**Alternatives considered:** Cloning the texture per card (previous approach).
Rejected — re-uploads the sheet per card. A module-level cache of per-card
clones. Rejected — still one upload per unique card instead of one per sheet.