big
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
# Graphical Fidelity Direction
|
||||
|
||||
**Primary reference: [Sakura Crossing](https://github.com/Kenton-GMI/sakura-crossing)**
|
||||
(MIT) — a procedural Japanese suburb in three.js, rendered to look like a
|
||||
hand-painted 2D anime background. This is the target image for the project, with
|
||||
one deliberate departure: **the line work is slightly harder and thicker.**
|
||||
|
||||
Secondary reference: [The Long Silence](https://github.com/achimala/TheLongSilence).
|
||||
The transferable lesson there is not its realistic-space rendering. It is that
|
||||
one authored visual law is reused everywhere, expensive detail is paid for only
|
||||
where it can be seen, and every art decision is checked in repeatable captures.
|
||||
|
||||
Character-specific decisions are governed by
|
||||
[CHARACTER_ART_DIRECTION.md](CHARACTER_ART_DIRECTION.md): 90% urban anime
|
||||
character read, 10% selectively deployed utility technology.
|
||||
|
||||
## The cel law
|
||||
|
||||
Four layered techniques, ported from the reference. `scenes/maps/sakura_crossing`
|
||||
is the map built to demonstrate all four; `scenes/maps/sakura_palette.gd` is the
|
||||
palette port.
|
||||
|
||||
**1. Quantised toon lighting with a hue-shifted shadow** —
|
||||
`assets/shaders/sakura_cel.gdshader`. Direct light is snapped to one of 2–5 flat
|
||||
values from a hand-authored ramp, sampled at `dot(N,L) * 0.5 + 0.5`. Then:
|
||||
|
||||
```
|
||||
irradiance = band * mix(shadow_tint, vec3(1.0), band)
|
||||
```
|
||||
|
||||
The band drives its own tint, so full light is untinted and the darkest band is
|
||||
almost pure cool violet. **That hue shift in shadow is most of what separates
|
||||
"anime cel" from "low-poly 3D"** — a wall whose shade is merely 40% of its lit
|
||||
colour reads as an unlit polygon. High-key ramps (`RAMP_SOFT`, `RAMP_SOFT3`)
|
||||
exist for pale masses like blossom that must stay light on the shadow side.
|
||||
|
||||
Faceted shading is on by default and is not a shortcut: a quantised ramp over
|
||||
smooth normals puts a *curved* band boundary across a curved surface, which
|
||||
reads as an airbrushed gradient no matter how few steps it has.
|
||||
|
||||
**2. Screen-space ink from the SECOND difference of linear depth** —
|
||||
`assets/shaders/ink_edge.gdshader`. This replaced a conventional first-difference
|
||||
edge detect, and the distinction is the single most important one in the file. A
|
||||
first difference measures depth *slope*, which is large wherever a surface is
|
||||
oblique to the camera — so the road, every roof and every long wall carried ink,
|
||||
and the threshold had to be raised until it smeared. A second difference measures
|
||||
*curvature*, which is exactly zero across any planar surface however oblique. It
|
||||
therefore fires only on real silhouettes and real creases, and the threshold can
|
||||
be low enough to catch a distant railing.
|
||||
|
||||
Convex curvature inks strongly, concave faintly — the outside line of a shape is
|
||||
heavy, the contact line inside it is light, which is what an animator draws.
|
||||
|
||||
Two things to know when tuning it: `ink_hardness` also sets the **noise floor**,
|
||||
because a grazing ground plane quantises in the depth buffer into a staircase and
|
||||
a staircase has a large second difference at every step. Too soft and the road
|
||||
comes out finely hatched. And anything smaller than the line width — petals,
|
||||
distant wires — will be inked *solid*, so sub-pixel scatter belongs in the
|
||||
transparent pass where the ink cannot see it.
|
||||
|
||||
**3. Inverted-hull outlines** on hero props only — `toon_outline.gdshader`,
|
||||
applied via `LevelMaterials.outline()`.
|
||||
|
||||
**4. A four-light anime rig** — `LevelEnvironment._build_light_rig`. A warm
|
||||
quantised key (the only shadow caster), a **strong** cool bounce from the
|
||||
opposite quarter at roughly a third of the key, a weak violet up-light for
|
||||
undersides, and a violet-ground hemisphere carried by ambient. The bounce is the
|
||||
important one: it puts a distinct, cool, non-black value on every shadow plane,
|
||||
so a surface turning away from the sun turns *into another colour* rather than
|
||||
into darkness.
|
||||
|
||||
The colour grade is a **split-tone** — cool violet into the darks, warm paper
|
||||
white into the lights — and it lives at the end of the ink pass rather than in
|
||||
`Environment.adjustment`, so it can be a split rather than a single saturation
|
||||
number. Environment's adjustment stage is therefore disabled; enabling both
|
||||
grades the frame twice.
|
||||
|
||||
## Lettering and trim
|
||||
|
||||
The reference is emphatic that *"every sign, fascia, lantern and price strip is
|
||||
drawn at runtime — there is not one image asset in `src/`."* That is not a
|
||||
technical footnote. A Japanese street is **made of lettering**, and the first
|
||||
build of Sakura Crossing had correct forms, correct colour and no type on it
|
||||
anywhere; it read as an architectural massing study.
|
||||
|
||||
`scenes/maps/sakura_signage.gd` is the answer. Godot's equivalent of the
|
||||
reference's Canvas2D is **`TextMesh`** — real geometry from a font — which suits
|
||||
this renderer better than a drawn texture would, because the whole map is
|
||||
untextured flat colour and, more importantly, **geometry gets inked**. Every
|
||||
glyph picks up the screen-space outline exactly as the buildings do, which is
|
||||
what seats the lettering *in* the picture rather than on top of it.
|
||||
|
||||
Settings that matter, both measured:
|
||||
|
||||
- **`depth = 0`.** Extruded text is 4 500 verts for four kanji; flat is 2 100.
|
||||
The extrusion's side walls are most of the mesh and are never seen on a sign
|
||||
fixed to a wall. Flat glyphs sit a centimetre proud of their board instead.
|
||||
- **`curve_step = 8`.** The 0.5 default is 43 000 verts for three signs; 8 is
|
||||
13 700 and looks identical at any distance a sign is read from.
|
||||
|
||||
The font is a **system stack** — the same one the reference uses (`Yu Gothic`,
|
||||
`Meiryo`, `Hiragino Kaku Gothic ProN`, `MS Gothic`). Ships no font file; if none
|
||||
resolve, signs go blank and nothing crashes.
|
||||
|
||||
**Ink or no ink is an art decision, per object.** Raised lettering on a sign
|
||||
board wants the outline — it is an object standing off a wall. Paint on the
|
||||
ground does not: road markings were inked at first and came out *hollow*,
|
||||
because a stroke of 止まれ at a grazing angle is a few pixels wide, the ink fired
|
||||
on both edges and the two outlines met in the middle and ate the letter. Pass
|
||||
`inked: false` and the glyphs go to the transparent pass where the ink cannot
|
||||
see them. Same mechanism as the petals — **anything thinner than the line width
|
||||
gets inked solid.**
|
||||
|
||||
Trim is the other half. A cel image is judged on its *third* tier of structure,
|
||||
and the first build had almost none: no sills, no downpipes, no meters, no
|
||||
manholes, no tactile dots. All of it is render-only and most of it is a box —
|
||||
none of it needs to be clever, it needs to be **there**.
|
||||
|
||||
One layout lesson worth keeping: unit width is the character of a street. Eight
|
||||
shop units across this map made each one 19.5 m wide, which is a department
|
||||
store, not a shop — one awning and one noren stretched across sixty feet of
|
||||
frontage. Six-to-ten-metre units give twenty awning colours, twenty names and
|
||||
twenty doorways per side, and that rhythm is most of what a 商店街 *is*.
|
||||
|
||||
Cost: ~5 300 mesh instances, 6.85 ms/frame at 1080p in the heaviest view.
|
||||
|
||||
## Image contract
|
||||
|
||||
Every gameplay frame should have:
|
||||
|
||||
- A readable dark, mid and light band with less than 2% practical clipping.
|
||||
- One dominant warm/cool lighting relationship, not white ambient everywhere.
|
||||
- Dark contact at feet, props and building bases.
|
||||
- Large, medium and small visual structure, in that order.
|
||||
- Ink weight that follows importance: character silhouette, rigid accessory,
|
||||
architecture crease, then tertiary texture.
|
||||
- Quiet traversal space around loud signs, characters and combat VFX.
|
||||
- Surface detail that disappears cleanly before it becomes sub-pixel noise.
|
||||
|
||||
The current automatic guardrail is:
|
||||
|
||||
```powershell
|
||||
python tools/levels.py <capture-directory> --inset 8 --gate
|
||||
```
|
||||
|
||||
`debug/fidelity_probe.gd` is the deterministic look-development scene.
|
||||
`debug/visual_capture.gd` remains the real-game regression capture.
|
||||
|
||||
## Lighting and post
|
||||
|
||||
- Keep the sun as the form key and ambient as a low-energy colour bed.
|
||||
- Use tight SSAO only for contact. Broad grey AO fights flat cel bands.
|
||||
- Keep cast shadow and form shadow separate; cast shadows may be deeper and
|
||||
harder than a character or prop's terminator.
|
||||
- Let only authored emissives cross the glow threshold. White paint must never
|
||||
bloom.
|
||||
- Preserve a filmic shoulder and measure clipping instead of judging it from a
|
||||
single monitor.
|
||||
- The first-person weapon uses a camera-relative warm key and cool fill. Its
|
||||
graphic read must not change when the player turns in world space.
|
||||
|
||||
## Texturing and materials
|
||||
|
||||
`LevelMaterials.SURFACE_LAW` is the shared construction language:
|
||||
|
||||
- `wall`: storey-scale seams plus a restrained base grade.
|
||||
- `ground`: larger paving divisions with no vertical grade.
|
||||
- `panel`: tight machined divisions for metal and kiosks.
|
||||
- `trim`: grounding grade without a grid.
|
||||
|
||||
These are semantic materials, not colour presets. Do not apply one noise field
|
||||
to every asset. Foliage, vehicles and character silhouettes stay quiet unless
|
||||
their own authored texture calls for detail.
|
||||
|
||||
The next texture pass should add a small reusable decal sheet for:
|
||||
|
||||
- one or two edge chips;
|
||||
- drain and service markings;
|
||||
- poster tears and tape corners;
|
||||
- sparse hazard chevrons;
|
||||
- directional pavement paint.
|
||||
|
||||
Place decals by composition and gameplay route. Random coverage is not detail;
|
||||
it is visual noise.
|
||||
|
||||
## Modeling
|
||||
|
||||
Build and review assets in three reads:
|
||||
|
||||
1. Silhouette at combat distance.
|
||||
2. Primary planes and construction breaks at traversal distance.
|
||||
3. Fasteners, bevels and decals only at inspection distance.
|
||||
|
||||
For the city kit:
|
||||
|
||||
- Give rooflines and street corners deliberate silhouette interruptions.
|
||||
- Prefer modeled awnings, frames, vents and cable bundles over high-frequency
|
||||
material noise.
|
||||
- Reuse a small vocabulary of construction dimensions so adjacent modules feel
|
||||
manufactured together.
|
||||
- Merge static pieces by material only after their panel coordinates are stable;
|
||||
seams should continue across parts meant to read as one assembly.
|
||||
- Add LOD or visibility ranges before adding tertiary geometry across all 100
|
||||
city blocks.
|
||||
|
||||
## Character and weapon animation
|
||||
|
||||
Anime fidelity comes from pose design and timing more than interpolation count:
|
||||
|
||||
- Let the body lead every movement verb. Jump jets, grapple hardware and dash
|
||||
thrusters deploy only long enough to explain the motion, then return visual
|
||||
focus to the character.
|
||||
- Gameplay anticipation must be short but unmistakable.
|
||||
- Key combat silhouettes need a readable held pose before recovery.
|
||||
- Accelerate through impact, hold the strongest pose, then use a clean recovery
|
||||
arc instead of uniformly smooth motion.
|
||||
- Keep weapon, support hand and head intent aligned; the existing hold-profile
|
||||
checks should remain mandatory.
|
||||
- Judge locomotion from the gameplay camera, not only in an animation viewer.
|
||||
- Use `debug/anim_capture.gd`, `debug/fp_weapon_capture.gd` and the hold/pose
|
||||
checks whenever animation assets change.
|
||||
|
||||
Secondary cloth and hair should lag the torso and settle quickly enough not to
|
||||
obscure aim or team recognition. Their job is to sell acceleration, not to run
|
||||
an independent simulation showcase.
|
||||
|
||||
## VFX
|
||||
|
||||
- Reserve the brightest values for hits, muzzle flashes and abilities.
|
||||
- Shape effects as graphic wedges, rings and speed lines before adding soft
|
||||
particles.
|
||||
- Use colour to communicate gameplay class consistently.
|
||||
- Fade detail by projected size. A distant effect should become one clean spark,
|
||||
not many sub-pixel particles.
|
||||
- Keep screen-space distortion local and brief; the HUD and target silhouette
|
||||
must remain readable.
|
||||
|
||||
## Performance and review
|
||||
|
||||
Fidelity features should degrade by resolution or distance before being
|
||||
disabled outright. Panel seams and sky clouds are analytic and band-limited;
|
||||
future decals and tertiary geometry should use visibility ranges.
|
||||
|
||||
Review sets should include:
|
||||
|
||||
- deterministic fidelity probe, key and grazing angles;
|
||||
- Neon Alley street, spawn and aerial views;
|
||||
- one bright and one dark character skin;
|
||||
- each first-person weapon family;
|
||||
- run, slide, dash, reload and one ability impact;
|
||||
- a low render-scale capture to expose shimmer and sub-pixel detail.
|
||||
|
||||
Compare the set as a group. A change is not an improvement if one hero frame is
|
||||
better while combat readability or another map regresses.
|
||||
Reference in New Issue
Block a user