evomeme-dev 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/README.md +247 -0
  2. package/bin/evomeme +28 -0
  3. package/dist/canvas-public/app.js +5857 -0
  4. package/dist/canvas-public/cycle-detect.js +166 -0
  5. package/dist/canvas-public/index.html +395 -0
  6. package/dist/canvas-public/sequence-sheet.js +59 -0
  7. package/dist/canvas-public/smart-split.js +185 -0
  8. package/dist/canvas-public/styles.css +2276 -0
  9. package/dist/canvas-public/vendor/painterro.min.js +3 -0
  10. package/dist/index.js +25586 -0
  11. package/package.json +69 -0
  12. package/skills/evomeme-cloud-fs/SKILL.md +176 -0
  13. package/skills/evomeme-cloud-fs/agents/openai.yaml +7 -0
  14. package/skills/evomeme-cover/SKILL.md +132 -0
  15. package/skills/evomeme-cover/agents/openai.yaml +7 -0
  16. package/skills/evomeme-game-maker/SKILL.md +214 -0
  17. package/skills/evomeme-game-maker/agents/openai.yaml +4 -0
  18. package/skills/evomeme-login/SKILL.md +92 -0
  19. package/skills/evomeme-login/agents/openai.yaml +7 -0
  20. package/skills/evomeme-native-bridge-sdk/SKILL.md +286 -0
  21. package/skills/evomeme-native-bridge-sdk/agents/openai.yaml +7 -0
  22. package/skills/evomeme-tos-upload/SKILL.md +46 -0
  23. package/skills/evomeme-tos-upload/agents/openai.yaml +4 -0
  24. package/skills/game-art/SKILL.md +121 -0
  25. package/skills/game-art/agents/openai.yaml +7 -0
  26. package/skills/game-asset-image-generator/SKILL.md +104 -0
  27. package/skills/game-asset-image-generator/agents/openai.yaml +7 -0
  28. package/skills/game-asset-image-generator/references/background.md +32 -0
  29. package/skills/game-asset-image-generator/references/static-game-assets.md +38 -0
  30. package/skills/game-audio/SKILL.md +82 -0
  31. package/skills/game-audio/agents/openai.yaml +4 -0
  32. package/skills/game-audio/references/bgm-cdn.md +127 -0
  33. package/skills/game-audio/references/sfx-cdn.md +77 -0
  34. package/skills/game-demo/SKILL.md +103 -0
  35. package/skills/game-demo/agents/openai.yaml +7 -0
  36. package/skills/game-maker/SKILL.md +111 -0
  37. package/skills/game-maker/agents/openai.yaml +4 -0
  38. package/skills/game-test/SKILL.md +58 -0
  39. package/skills/game-test/agents/openai.yaml +7 -0
  40. package/skills/game-test/references/checklists/playtest-qa.md +12 -0
  41. package/skills/game-ui/SKILL.md +169 -0
  42. package/skills/game-ui/agents/openai.yaml +4 -0
  43. package/skills/game-ui/references/checklists/game-ui-quality.md +14 -0
  44. package/skills/game-ui/references/checklists/hud-readability.md +12 -0
  45. package/skills/game-ui/references/checklists/mobile-input.md +12 -0
  46. package/skills/game-ui/references/checklists/responsive-ui-fit.md +12 -0
  47. package/skills/game-ui/references/ui-patterns.md +140 -0
  48. package/skills/game-ui/references/ui-prompt-templates.md +63 -0
  49. package/skills/phaser-2d-game/SKILL.md +127 -0
  50. package/skills/phaser-2d-game/agents/openai.yaml +7 -0
  51. package/skills/phaser-2d-game/references/engine-performance.md +64 -0
  52. package/skills/phaser-2d-game/references/engine-physics.md +139 -0
  53. package/skills/phaser-2d-game/references/engine-runtime.md +129 -0
  54. package/skills/phaser-2d-game/references/engine-spritesheets.md +99 -0
  55. package/skills/phaser-2d-game/references/engine-tilemaps.md +80 -0
  56. package/skills/phaser-2d-game/references/file-structure.md +94 -0
  57. package/skills/phaser-2d-graphics-builder/SKILL.md +68 -0
  58. package/skills/phaser-2d-graphics-builder/agents/openai.yaml +7 -0
  59. package/skills/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-1.jpeg +0 -0
  60. package/skills/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-2.jpeg +0 -0
  61. package/skills/phaser-2d-graphics-builder/assets/scorecard-anchors/scene-3.jpg +0 -0
  62. package/skills/phaser-2d-graphics-builder/references/checklists/compose-quality.md +10 -0
  63. package/skills/phaser-2d-graphics-builder/references/checklists/performance-safe-visual-detail.md +9 -0
  64. package/skills/phaser-2d-graphics-builder/references/checklists/premium-game-quality-gate.md +15 -0
  65. package/skills/phaser-2d-graphics-builder/references/checklists/sprite-quality.md +10 -0
  66. package/skills/phaser-2d-graphics-builder/references/checklists/technical-art-quality.md +9 -0
  67. package/skills/phaser-2d-graphics-builder/references/checklists/visual-scorecard.md +9 -0
  68. package/skills/phaser-2d-graphics-builder/references/compose-recipes.md +71 -0
  69. package/skills/phaser-2d-graphics-builder/references/graphics-prompt-templates.md +33 -0
  70. package/skills/phaser-2d-graphics-builder/references/implementation-blueprint.md +82 -0
  71. package/skills/phaser-2d-graphics-builder/references/sprite-recipes.md +90 -0
  72. package/skills/phaser-2d-graphics-builder/references/technical-art.md +67 -0
  73. package/skills/phaser-2d-graphics-builder/references/vfx-recipes.md +67 -0
  74. package/skills/phaser-2d-graphics-builder/references/visual-scorecard.md +129 -0
  75. package/skills/threejs-3d-game/SKILL.md +113 -0
  76. package/skills/threejs-3d-game/agents/openai.yaml +7 -0
  77. package/skills/threejs-3d-game/references/engine-asset-pipeline.md +116 -0
  78. package/skills/threejs-3d-game/references/engine-audio.md +70 -0
  79. package/skills/threejs-3d-game/references/engine-input-camera.md +84 -0
  80. package/skills/threejs-3d-game/references/engine-performance.md +96 -0
  81. package/skills/threejs-3d-game/references/engine-physics.md +142 -0
  82. package/skills/threejs-3d-game/references/engine-runtime.md +199 -0
  83. package/skills/threejs-3d-game/references/file-structure.md +353 -0
  84. package/skills/threejs-aaa-graphics-builder/SKILL.md +62 -0
  85. package/skills/threejs-aaa-graphics-builder/agents/openai.yaml +7 -0
  86. package/skills/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-1.jpg +0 -0
  87. package/skills/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-2.jpg +0 -0
  88. package/skills/threejs-aaa-graphics-builder/assets/scorecard-anchors/scene-3.jpg +0 -0
  89. package/skills/threejs-aaa-graphics-builder/references/checklists/aaa-game-quality-gate.md +23 -0
  90. package/skills/threejs-aaa-graphics-builder/references/checklists/aaa-visual-scorecard.md +19 -0
  91. package/skills/threejs-aaa-graphics-builder/references/checklists/material-lighting-quality.md +14 -0
  92. package/skills/threejs-aaa-graphics-builder/references/checklists/performance-safe-visual-detail.md +14 -0
  93. package/skills/threejs-aaa-graphics-builder/references/checklists/procedural-model-quality.md +15 -0
  94. package/skills/threejs-aaa-graphics-builder/references/checklists/technical-art-quality.md +17 -0
  95. package/skills/threejs-aaa-graphics-builder/references/graphics-prompt-templates.md +236 -0
  96. package/skills/threejs-aaa-graphics-builder/references/implementation-blueprint.md +161 -0
  97. package/skills/threejs-aaa-graphics-builder/references/model-recipes.md +146 -0
  98. package/skills/threejs-aaa-graphics-builder/references/render-recipes.md +90 -0
  99. package/skills/threejs-aaa-graphics-builder/references/shader-cookbook.md +292 -0
  100. package/skills/threejs-aaa-graphics-builder/references/technical-art.md +167 -0
  101. package/skills/threejs-aaa-graphics-builder/references/visual-scorecard.md +147 -0
@@ -0,0 +1,292 @@
1
+ # Shader And Material Cookbook
2
+
3
+ Concrete material, shader, and post-processing recipes. Use with `technical-art.md` (budgets, when shader work is justified) and `render-recipes.md` (render pipeline).
4
+
5
+ Targets three.js `^0.184`; imports use the `three/addons/*` alias (maps to `examples/jsm`). Every entry lists **When**, **Cost** (draw calls / fill rate / compile), and **Read** (the rule: effects clarify gameplay, never hide missing geometry).
6
+
7
+ ## Prerequisite: Renderer And Environment Map
8
+
9
+ Metals and glossy dielectrics read as flat gray without an environment map to reflect. Set this up once before PBR materials.
10
+
11
+ ```ts
12
+ import * as THREE from 'three';
13
+ import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
14
+
15
+ renderer.outputColorSpace = THREE.SRGBColorSpace;
16
+ renderer.toneMapping = THREE.ACESFilmicToneMapping; // simpler tone mapping for bright arcade reads
17
+ renderer.toneMappingExposure = 1.0;
18
+ renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
19
+
20
+ // 5-line env map: neutral studio IBL, no HDR file needed.
21
+ const pmrem = new THREE.PMREMGenerator(renderer);
22
+ scene.environment = pmrem.fromScene(new RoomEnvironment(), 0.04).texture; // r184: RoomEnvironment() takes no args
23
+ scene.environmentIntensity = 1.0; // r184 global multiplier over per-material envMapIntensity
24
+ pmrem.dispose();
25
+ ```
26
+
27
+ Needed by any scene with metal, ceramic, glass, or clearcoat. Cost is one PMREM bake at startup, near-zero per frame; it lights support surfaces so hero emissive/trim still wins attention.
28
+
29
+ ## PBR Material Recipes
30
+
31
+ Copy the config; tune `color` to the palette. `envMapIntensity` values assume the env map above. Use `MeshStandardMaterial` unless a `MeshPhysicalMaterial`-only feature (clearcoat, transmission, sheen) is visible during play.
32
+
33
+ ```ts
34
+ // Painted metal (car body, ship hull panel) — dielectric paint over metal read via clearcoat.
35
+ new THREE.MeshPhysicalMaterial({ color: 0x1f6feb, metalness: 0.0, roughness: 0.5,
36
+ clearcoat: 0.9, clearcoatRoughness: 0.15, envMapIntensity: 1.0 });
37
+
38
+ // Bare brushed metal (raw steel, gun frame) — needs the env map to look metallic.
39
+ new THREE.MeshStandardMaterial({ color: 0xaeb4bd, metalness: 1.0, roughness: 0.4, envMapIntensity: 1.1 });
40
+
41
+ // Rubber / tire — near-black, no reflection, kills the env map.
42
+ new THREE.MeshStandardMaterial({ color: 0x0a0a0b, metalness: 0.0, roughness: 0.92, envMapIntensity: 0.35 });
43
+
44
+ // Matte plastic (housings, crates) — dielectric, mid roughness, muted reflection.
45
+ new THREE.MeshStandardMaterial({ color: 0xd23b3b, metalness: 0.0, roughness: 0.62, envMapIntensity: 0.6 });
46
+
47
+ // Glossy ceramic / clean hull — sharp reflection, add clearcoat for wet-look premium.
48
+ new THREE.MeshPhysicalMaterial({ color: 0xf5f5f5, metalness: 0.0, roughness: 0.12,
49
+ clearcoat: 1.0, clearcoatRoughness: 0.05, envMapIntensity: 1.0 });
50
+
51
+ // Emissive signal (beacon, pickup core) — dark base so only the glow reads; >1 intensity feeds bloom.
52
+ new THREE.MeshStandardMaterial({ color: 0x101010, emissive: 0x18e0ff, emissiveIntensity: 2.5,
53
+ metalness: 0.0, roughness: 0.4 });
54
+
55
+ // Cloth / fabric — high roughness + sheen for the soft edge highlight.
56
+ new THREE.MeshPhysicalMaterial({ color: 0x3a4a6b, metalness: 0.0, roughness: 0.9,
57
+ sheen: 1.0, sheenRoughness: 0.5, sheenColor: new THREE.Color(0x8899bb), envMapIntensity: 0.5 });
58
+ ```
59
+
60
+ **Read:** separate roles by roughness/metalness contrast (matte vs glossy, metal vs plastic), not hue alone.
61
+
62
+ ### Glass: real vs fake
63
+
64
+ ```ts
65
+ // REAL refractive glass — MeshPhysicalMaterial transmission.
66
+ new THREE.MeshPhysicalMaterial({ metalness: 0.0, roughness: 0.05, transmission: 1.0,
67
+ thickness: 0.5, ior: 1.5, envMapIntensity: 1.0 });
68
+ ```
69
+
70
+ - **When:** one or two hero surfaces (cockpit canopy, potion vial) at close range.
71
+ - **Cost:** high. Each transmissive material triggers an extra scene render into a transmission buffer every frame; fill-rate heavy and multiplies with resolution. Never use on repeated/instanced props.
72
+ - **Read:** refraction must not smear the hazard behind it into unreadability.
73
+
74
+ ```ts
75
+ // CHEAP fake glass — no transmission buffer. Use for repeated windows, visors, shields.
76
+ new THREE.MeshPhysicalMaterial({ color: 0x88ccff, metalness: 0.0, roughness: 0.1,
77
+ transparent: true, opacity: 0.25, clearcoat: 1.0, envMapIntensity: 1.5, depthWrite: false });
78
+ ```
79
+
80
+ - **Cost:** one transparent draw call, no extra render target. Add the fresnel rim below for a readable edge.
81
+
82
+ ## onBeforeCompile Patterns
83
+
84
+ Inject GLSL into stock materials to keep PBR lighting for free. Rules that make this safe with shared materials:
85
+
86
+ - **Cache key:** any material whose `onBeforeCompile` injects code MUST set `customProgramCacheKey` returning a string unique to that injection. Without it three can hand back a cached program compiled from a different (un-injected) material of the same type, silently dropping your code.
87
+ - **Sharing:** `onBeforeCompile` runs once per compiled program. Reuse one material instance across meshes and its uniforms update once for all. For per-object variation, use separate material instances (same cache key → program is still reused) or drive it from `instanceMatrix` / `instanceColor`.
88
+ - **Animating uniforms:** `onBeforeCompile` fires once, so stash the shader (`material.userData.shader = shader`) and write the uniform each frame: `if (m.userData.shader) m.userData.shader.uniforms.uTime.value = t;`. The snippets below use this pattern.
89
+
90
+ ### (a) Fresnel rim glow
91
+
92
+ `vNormal` and `vViewPosition` (both view space) exist in the Standard/Physical fragment shader; `saturate` is defined in `<common>`.
93
+
94
+ ```ts
95
+ material.onBeforeCompile = (shader) => {
96
+ shader.uniforms.uRimColor = { value: new THREE.Color(0x33ccff) };
97
+ shader.uniforms.uRimPower = { value: 3.0 };
98
+ shader.uniforms.uRimStrength = { value: 1.5 };
99
+ shader.fragmentShader =
100
+ 'uniform vec3 uRimColor;\nuniform float uRimPower;\nuniform float uRimStrength;\n' +
101
+ shader.fragmentShader.replace(
102
+ '#include <emissivemap_fragment>',
103
+ `#include <emissivemap_fragment>
104
+ float fres = pow(1.0 - saturate(dot(normalize(vNormal), normalize(vViewPosition))), uRimPower);
105
+ totalEmissiveRadiance += uRimColor * fres * uRimStrength;`
106
+ );
107
+ };
108
+ material.customProgramCacheKey = () => 'fresnel-rim';
109
+ ```
110
+
111
+ - **When:** shields, cloak/invuln states, silhouette separation from a busy background.
112
+ - **Cost:** a few ALU ops, no extra passes.
113
+ - **Read:** the rim marks a state change; keep base color readable when the rim is off.
114
+
115
+ ### (b) Scrolling emissive panels
116
+
117
+ Inject a private UV varying so it works without a map assigned.
118
+
119
+ ```ts
120
+ material.onBeforeCompile = (shader) => {
121
+ shader.uniforms.uTime = { value: 0 };
122
+ shader.uniforms.uPanelColor = { value: new THREE.Color(0x18e0ff) };
123
+ material.userData.shader = shader;
124
+ shader.vertexShader = 'varying vec2 vCookUv;\n' + shader.vertexShader.replace(
125
+ '#include <begin_vertex>', '#include <begin_vertex>\n vCookUv = uv;');
126
+ shader.fragmentShader =
127
+ 'uniform float uTime;\nuniform vec3 uPanelColor;\nvarying vec2 vCookUv;\n' +
128
+ shader.fragmentShader.replace(
129
+ '#include <emissivemap_fragment>',
130
+ `#include <emissivemap_fragment>
131
+ float scroll = fract(vCookUv.y * 6.0 - uTime * 0.5);
132
+ float band = smoothstep(0.46, 0.5, scroll) * smoothstep(0.54, 0.5, scroll);
133
+ totalEmissiveRadiance += uPanelColor * band * 2.0;`
134
+ );
135
+ };
136
+ material.customProgramCacheKey = () => 'scroll-emissive';
137
+ ```
138
+
139
+ - **When:** energy conduits, reactor walls, loading/charge bars, boost lanes.
140
+ - **Cost:** one `fract`/`smoothstep`, no textures.
141
+ - **Read:** scroll direction/speed should encode state (charging up, draining down).
142
+
143
+ ### (c) Wind sway (foliage / flags)
144
+
145
+ `transformed` is object space and displaced before `<project_vertex>` applies `instanceMatrix`, so read the instance translation column for per-instance phase. Assumes model origin at the base, up = +Y.
146
+
147
+ ```ts
148
+ material.onBeforeCompile = (shader) => {
149
+ shader.uniforms.uTime = { value: 0 };
150
+ material.userData.shader = shader;
151
+ shader.vertexShader = 'uniform float uTime;\n' + shader.vertexShader.replace(
152
+ '#include <begin_vertex>',
153
+ `#include <begin_vertex>
154
+ #ifdef USE_INSTANCING
155
+ float phase = instanceMatrix[3].x + instanceMatrix[3].z; // instance world offset
156
+ #else
157
+ float phase = 0.0;
158
+ #endif
159
+ float h = max(position.y, 0.0); // base stays planted, tips move most
160
+ transformed.x += sin(uTime * 1.5 + phase) * 0.08 * h;
161
+ transformed.z += cos(uTime * 1.1 + phase) * 0.05 * h;`
162
+ );
163
+ };
164
+ material.customProgramCacheKey = () => 'wind-sway';
165
+ ```
166
+
167
+ - **When:** grass cards, banners, antennae, kelp — background life, not gameplay geometry.
168
+ - **Cost:** two trig ops per vertex; free on an `InstancedMesh`.
169
+ - **Read:** sway is ambient motion; never move collidable/interactable geometry with it.
170
+
171
+ ### (d) Dissolve / spawn
172
+
173
+ Threshold-discard with a glowing edge. Inject a local-position varying and a hash; drive `uProgress` 0→1 to despawn, 1→0 to spawn.
174
+
175
+ ```ts
176
+ material.onBeforeCompile = (shader) => {
177
+ shader.uniforms.uProgress = { value: 0 };
178
+ shader.uniforms.uEdgeColor = { value: new THREE.Color(0xff6a00) };
179
+ material.userData.shader = shader;
180
+ shader.vertexShader = 'varying vec3 vDisPos;\n' + shader.vertexShader.replace(
181
+ '#include <begin_vertex>', '#include <begin_vertex>\n vDisPos = position;');
182
+ shader.fragmentShader =
183
+ `uniform float uProgress;\nuniform vec3 uEdgeColor;\nvarying vec3 vDisPos;
184
+ float hash13(vec3 p){ p = fract(p * 0.1031); p += dot(p, p.yzx + 33.33); return fract((p.x + p.y) * p.z); }\n` +
185
+ shader.fragmentShader.replace(
186
+ '#include <dithering_fragment>',
187
+ `float n = hash13(floor(vDisPos * 12.0));
188
+ if (n < uProgress) discard;
189
+ float edge = smoothstep(uProgress, uProgress + 0.08, n);
190
+ gl_FragColor.rgb += uEdgeColor * (1.0 - edge) * 3.0; // post-tonemap add feeds bloom
191
+ #include <dithering_fragment>`
192
+ );
193
+ };
194
+ material.customProgramCacheKey = () => 'dissolve';
195
+ ```
196
+
197
+ - **When:** enemy death, teleport-in, pickup spawn, object streaming.
198
+ - **Cost:** one hash + `discard` (discard disables early-Z; keep it to spawning objects, not the whole scene).
199
+ - **Read:** the edge color/direction telegraphs the event — spawn vs destroy must look different.
200
+
201
+ ## Gradient Sky Dome
202
+
203
+ Cheaper than a cubemap for stylized scenes: a `BackSide` sphere with a top/horizon lerp plus a sun disc and halo.
204
+
205
+ ```ts
206
+ const skyUniforms = {
207
+ uTop: { value: new THREE.Color(0x3a6fb0) },
208
+ uHorizon: { value: new THREE.Color(0xcfe4f5) },
209
+ uSunColor: { value: new THREE.Color(0xfff2cc) },
210
+ uSunDir: { value: new THREE.Vector3(0.4, 0.28, 0.6).normalize() },
211
+ };
212
+ const sky = new THREE.Mesh(
213
+ new THREE.SphereGeometry(500, 32, 16),
214
+ new THREE.ShaderMaterial({
215
+ side: THREE.BackSide, depthWrite: false, uniforms: skyUniforms,
216
+ vertexShader: `varying vec3 vDir;
217
+ void main(){ vDir = normalize(position); gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,
218
+ fragmentShader: `varying vec3 vDir;
219
+ uniform vec3 uTop, uHorizon, uSunColor, uSunDir;
220
+ void main(){
221
+ float h = clamp(vDir.y * 0.5 + 0.5, 0.0, 1.0);
222
+ vec3 col = mix(uHorizon, uTop, pow(h, 0.6));
223
+ float d = clamp(dot(normalize(vDir), normalize(uSunDir)), 0.0, 1.0);
224
+ col += uSunColor * (pow(d, 800.0) + pow(d, 8.0) * 0.25); // disc + halo
225
+ gl_FragColor = vec4(col, 1.0);
226
+ }`,
227
+ })
228
+ );
229
+ sky.frustumCulled = false;
230
+ scene.add(sky);
231
+ ```
232
+
233
+ - **When:** any stylized outdoor scene without a photographic backdrop.
234
+ - **Cost:** one draw call, no cubemap textures, no mips.
235
+ - **Read:** a raw `ShaderMaterial` bypasses tone mapping and sRGB conversion — author colors in display space; if the scene runs ACES, nudge them brighter. Keep horizon value distinct from hazards silhouetted against it.
236
+
237
+ ## Post-Processing Chain
238
+
239
+ Finishing pass only. Move tone mapping/sRGB to `OutputPass`; keep `renderer.toneMapping` set so it reads it.
240
+
241
+ ```ts
242
+ import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
243
+ import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
244
+ import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
245
+ import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
246
+ import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
247
+
248
+ const composer = new EffectComposer(renderer);
249
+ composer.addPass(new RenderPass(scene, camera));
250
+
251
+ // UnrealBloomPass(resolution, strength, radius, threshold)
252
+ const bloom = new UnrealBloomPass(new THREE.Vector2(innerWidth, innerHeight),
253
+ 0.45, // strength: 0.35-0.6
254
+ 0.3, // radius: 0.2-0.4
255
+ 0.85); // threshold: only pixels brighter than this bloom
256
+ composer.addPass(bloom);
257
+
258
+ const VignetteShader = {
259
+ uniforms: { tDiffuse: { value: null }, uStrength: { value: 0.85 }, uSize: { value: 0.72 } },
260
+ vertexShader: `varying vec2 vUv; void main(){ vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,
261
+ fragmentShader: `uniform sampler2D tDiffuse; uniform float uStrength, uSize; varying vec2 vUv;
262
+ void main(){
263
+ vec4 c = texture2D(tDiffuse, vUv);
264
+ float d = distance(vUv, vec2(0.5));
265
+ c.rgb *= mix(1.0, smoothstep(uSize, uSize - 0.45, d), uStrength);
266
+ gl_FragColor = c;
267
+ }`,
268
+ };
269
+ composer.addPass(new ShaderPass(VignetteShader));
270
+ composer.addPass(new OutputPass()); // ALWAYS last: tone mapping + sRGB
271
+
272
+ // In R3F projects: call composer.render() inside useFrame and disable the
273
+ // default R3F present path for that frame; do not also run a separate outer
274
+ // renderer.render() game loop. Imperative-only demos may replace renderer.render().
275
+ // In R3F projects: call composer.render() inside useFrame and disable the
276
+ // default R3F present path for that frame; do not also run a separate outer
277
+ // renderer.render() game loop. Imperative-only demos may replace renderer.render().
278
+ // loop: composer.render() instead of renderer.render()
279
+ // resize: composer.setSize(w, h); composer.setPixelRatio(Math.min(devicePixelRatio, 2));
280
+ ```
281
+
282
+ - **Bloom rule:** bloom sells authored emissive (threshold 0.85 keeps mid-bright materials out). It must never be the main source of detail — if a shape only reads because it glows, the geometry is missing.
283
+ - **Vignette cost:** one full-screen ShaderPass; keep `uStrength` subtle, never darken the play path.
284
+ - **Mobile:** the composer allocates full-resolution HDR targets, so cost scales with DPR². Cap DPR before adding passes. On low-end, skip the composer (call `renderer.render`) or run bloom-only via `composer.setPixelRatio(Math.min(devicePixelRatio, 1.25))`. Compare screenshots with post on/off and profile.
285
+
286
+ ## Cheap Tricks
287
+
288
+ - **Vertex-color AO** — bake occlusion into the mesh: `geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3))`, material `{ vertexColors: true }`, darken cavities/creases. **When:** static props/terrain. **Cost:** one attribute, zero draw calls. **Read:** ground shapes with contact darkness; don't tint gameplay-critical surfaces.
289
+ - **Polygon-offset decals** — coplanar decal mesh with `{ polygonOffset: true, polygonOffsetFactor: -1, polygonOffsetUnits: -1, transparent: true }` to kill z-fighting. **When:** panel lines, numbers, faction glyphs. **Cost:** +1 draw call each — instance repeats. **Read:** decals imply function/scale, not random surface noise.
290
+ - **Fake contact shadow** — flat `PlaneGeometry` with a radial-gradient `CanvasTexture`, `{ transparent: true, depthWrite: false }`, under the object; scale/fade alpha with height. **When:** hovering or moving props that don't warrant a shadow map. **Cost:** one draw call, no shadow pass. **Read:** anchors floating objects so position reads.
291
+ - **Emissive LOD signals** — swap `emissiveIntensity` or material by distance so far pickups/beacons still read, add geometry detail only up close. **When:** dense repeated signals. **Cost:** a material/uniform swap. **Read:** keep the signal color constant across LOD so identity survives the transition.
292
+ - **Matcap props** — `new THREE.MeshMatcapMaterial({ matcap })` bakes lighting into one texture; no lights or env needed. **When:** background/stylized props. **Cost:** the cheapest lit-looking material, one texture. **Read:** matcap ignores scene lights, so never use it where a dynamic light or state glow must show on the surface.
@@ -0,0 +1,167 @@
1
+ # Technical Art For Three.js Games
2
+
3
+ Use this reference before premium/AAA/showcase graphics work, shader/material/post-processing changes, VFX systems, generated/imported asset cleanup, LOD/instancing work, or any visual pass that could affect browser performance.
4
+
5
+ Technical art is the bridge between art direction and real-time constraints. The goal is not maximum detail; it is readable authored detail that survives active gameplay, mobile viewports, and WebGL budgets.
6
+
7
+ Research basis: Three.js exposes renderer diagnostics through `WebGLRenderer.info`; `InstancedMesh` is designed for many objects sharing geometry/materials; `LOD` switches objects by distance; `KTX2Loader` supports Basis Universal GPU texture workflows; MDN WebGL best practices emphasize eliminating errors, understanding system limits, eager deletion, batching draw calls, per-pixel VRAM budgets, smaller back buffers, mipmaps, compressed textures, and high-DPI discipline.
8
+
9
+ ## Technical Art Brief
10
+
11
+ Before implementation, write:
12
+
13
+ - Art direction in renderable terms: shapes, materials, lighting, VFX, camera, UI/world motifs.
14
+ - Hero surfaces: what must look authored at active-play distance.
15
+ - Support surfaces: what can be procedural, instanced, simplified, or culled.
16
+ - Material kit: named roles, not one-off colors.
17
+ - VFX language: event-driven effects and state readability.
18
+ - Lighting stack: key/fill/rim/practical/contact/depth.
19
+ - Render budget target: draw calls, triangles, textures, materials, DPR, shadow/post cost.
20
+ - Asset strategy: procedural / generated 2D / generated 3D / hybrid / imported.
21
+ - Mobile constraint: what changes first if performance or readability fails.
22
+
23
+ ## Render Budget Starting Points
24
+
25
+ These are starting contracts, not universal limits — measure on the target game, and document every deliberate overrun as a tradeoff. The canvas inspector (`pnpm exec node ../game-test/scripts/inspect-threejs-canvas.mjs` when available, or any project `inspect:canvas` script) compares live diagnostics against the same numbers and reports over-budget rows.
26
+
27
+ | Metric (worst active-play view) | Desktop tier | Mobile tier |
28
+ | --- | --- | --- |
29
+ | Draw calls (`info.render.calls`) | <= 300 | <= 150 |
30
+ | Triangles (`info.render.triangles`) | <= 750k | <= 300k |
31
+ | Geometries (`info.memory.geometries`) | <= 300 | <= 200 |
32
+ | Textures (`info.memory.textures`) | <= 60 | <= 40 |
33
+ | Texture memory (est.) | <= 256 MB | <= 128 MB |
34
+ | Shadow-casting lights | <= 2 | 1 |
35
+ | Shadow map size | <= 2048 | <= 1024 |
36
+ | DPR cap | 2 | 1.5-2 |
37
+ | Post passes (beyond render+output) | <= 2 | 0-1 |
38
+
39
+ How to spend within them:
40
+
41
+ - Draw calls: repeated world/detail pieces should be instanced or merged by material.
42
+ - Triangles: spend on silhouettes near the camera; reduce background detail through LOD, impostors, or simplified meshes.
43
+ - Materials: share material roles aggressively. Unique material count often grows faster than geometry count.
44
+ - Textures: keep opaque large images compressed or small; avoid unique 2K+ textures for tiny repeated props.
45
+ - Shadows: reserve real shadows for hero objects and grounding anchors; use blob/contact meshes for small repeated props (see `shader-cookbook.md` for the cheap contact-shadow recipe).
46
+ - Post: every pass must earn its cost and preserve gameplay clarity; concrete chain settings are in `shader-cookbook.md`.
47
+
48
+ Always report actual renderer diagnostics after the graphics pass: calls, triangles, geometries, textures, material count if available, post passes, shadow settings, DPR cap, and bottlenecks.
49
+
50
+ ## Material And Shader System
51
+
52
+ Use a material kit of named shared roles, not one-off colors. Reuse each role across every mesh that plays the same part:
53
+
54
+ - `bodyPrimary`: dominant player/world shell.
55
+ - `bodySecondary`: panel contrast.
56
+ - `trim`: rails, bevel highlights, borders, edge highlights.
57
+ - `hazard`: danger surfaces, damage cues, warning stripes.
58
+ - `reward`: collectible surfaces with readable value.
59
+ - `shieldBoost`: shield, boost, and status states.
60
+ - `glass`: cockpit, shield, lens, visor.
61
+ - `emissiveSignal`: authored glow strips, status lights, beacon cores.
62
+ - `groundContact`: dark matte surfaces and shadow receivers.
63
+ - `decalDark` and `decalLight`: panel lines, scratches, numbers, icons.
64
+ - UI/world signal colors shared between HUD and diegetic markers.
65
+
66
+ Use `MeshStandardMaterial` for most surfaces. Use `MeshPhysicalMaterial` selectively for cockpit glass, clearcoat panels, iridescent shields, or premium hero details. Share materials across repeated meshes.
67
+
68
+ Shader or `onBeforeCompile` work must have a reason:
69
+
70
+ - State readability: shield ripple, heat, cloak, damage pulse.
71
+ - Surface identity: water, forcefield, hologram, scanline, energy core.
72
+ - Performance: cheap procedural variation instead of many textures.
73
+ - Composition: separating player/threat/reward from background.
74
+
75
+ Reject shader work that only adds noise, bloom bait, or hidden cost without improving active-play decisions. When shader work is justified, use the proven recipes in `shader-cookbook.md` (material values, onBeforeCompile patterns, sky, post chain) instead of improvising GLSL.
76
+
77
+ ## VFX Readability
78
+
79
+ Every VFX effect must answer:
80
+
81
+ - What event or state triggers it?
82
+ - What does it tell the player?
83
+ - Does it point to player, threat, reward, objective, or impact?
84
+ - How long does it last?
85
+ - Is it pooled or cheap to recreate?
86
+ - Does it obscure collision, HUD, or the next decision?
87
+ - Is there a reduced-motion fallback for heavy shake/strobe?
88
+
89
+ Use event-driven VFX over permanent particle clutter:
90
+
91
+ - Pickup: ring contraction, shard burst, score trail, brief HUD echo.
92
+ - Hit/fail: impact ring, debris, damage flash, brief hit pause, camera impulse.
93
+ - Boost/speed: engine trail, lane streaks, FOV ease, side streaks, audio pitch.
94
+ - Near miss/combo: side spark, line snap, badge pulse, streak counter.
95
+ - Shield/invulnerable: refractive shell, rim pulse, absorbed-impact ripple, material swap.
96
+ - Spawn/despawn: anticipation pulse, telegraph, dissolve or scale snap.
97
+
98
+ Pool effects and reuse geometries/materials. Permanent particle fields must stay cheap and sparse.
99
+
100
+ ## Instancing, LOD, And Culling
101
+
102
+ Use instancing for many copies with the same geometry/material and different transforms: windows, bolts, lane markers, city lights, debris, foliage-like props, stars, crowd cards, track panels, repeated pickups, background modules.
103
+
104
+ Rules:
105
+
106
+ - Update `instanceMatrix.needsUpdate` and `instanceColor.needsUpdate` only after batched changes.
107
+ - Compute or update bounds for instanced groups when transforms change materially.
108
+ - Do not instance everything blindly. Different materials or constantly changing transforms can erase the win.
109
+ - Keep collision separate from instanced visual detail.
110
+
111
+ Use LOD when:
112
+
113
+ - A hero/background object spans large distance ranges.
114
+ - The silhouette matters near camera but not far away.
115
+ - Imported/generated models are heavier than needed for background use.
116
+
117
+ Rules:
118
+
119
+ - Add hysteresis or distance gaps to reduce visible popping.
120
+ - Use impostor cards or simplified silhouettes for far layers when appropriate.
121
+ - Verify LOD transitions during gameplay camera motion, not only static orbit.
122
+
123
+ ## Generated And Imported Asset Cleanup
124
+
125
+ For every imported GLB/FBX hero asset:
126
+
127
+ - Confirm scale, pivot, forward/up orientation, bounds, and active-play silhouette.
128
+ - Create a simple collision proxy independent from the visual mesh.
129
+ - Inspect file size, approximate triangles, mesh count, material count, texture count, and animation clips when available.
130
+ - Replace or simplify excessive materials and textures.
131
+ - Add LOD or simplified background variant when reused many times.
132
+ - Verify PBR material readability under the game's lighting, not only in a model viewer.
133
+ - Keep API keys and temporary URLs out of client code and checked-in files.
134
+
135
+ ## Decals, Trim, And Surface Detail
136
+
137
+ Prefer reusable surface systems:
138
+
139
+ - Canvas-generated trim sheets for panel lines, markings, arrows, numbers.
140
+ - Thin offset decal meshes for hazard marks, faction symbols, lane glyphs, scuffs.
141
+ - Shared small textures for noise/wear rather than unique full-size images.
142
+ - Procedural UV-independent detail for repeated hard-surface props.
143
+
144
+ Surface detail must reinforce scale, function, faction, route, or state. Do not add random lines everywhere.
145
+
146
+ ## Color And Readability
147
+
148
+ Readability beats palette consistency:
149
+
150
+ - Threats differ from rewards by shape and motion, not only hue.
151
+ - Interactables differ from background by silhouette/value/material.
152
+ - UI signal colors match world signal colors.
153
+ - Bloom/fog/darkness cannot be the primary separator.
154
+ - Colorblind-risk information has shape/icon/motion backup.
155
+
156
+ ## Technical Art Report
157
+
158
+ Report:
159
+
160
+ - Technical art brief.
161
+ - Material kit and shader/VFX decisions.
162
+ - Instancing/LOD/culling strategy.
163
+ - Render budget target and actual diagnostics.
164
+ - Imported/generated asset cleanup evidence.
165
+ - VFX readability checks.
166
+ - Mobile/DPR/post/shadow tradeoffs.
167
+ - Remaining visual performance risks.
@@ -0,0 +1,147 @@
1
+ # Visual Scorecard
2
+
3
+ Score active-play screenshots, not idle title screens or isolated showroom models. Use desktop and mobile screenshots when mobile is in scope.
4
+
5
+ Scores are self-assessed against the anchors below, so they drift optimistic. Two countermeasures are mandatory for premium/AAA/showcase claims: cite Measured Evidence for the categories it supports, and run the Fresh-Eyes Review before finalizing.
6
+
7
+ ## Calibration Anchors
8
+
9
+ Reference screenshots for score calibration are packaged in `threejs-aaa-graphics-builder/assets/scorecard-anchors/`:
10
+
11
+ - `scene-1.jpg` — score ~1: primitive player and pickups on a flat sparse arena, utility HUD.
12
+ - `scene-2.jpg` — score ~2: authored track kit, imported hero asset, designed genre HUD, intentional lighting.
13
+ - `scene-3.jpg` — score ~2.5-3: dense layered world in active play, readable hero silhouette, event VFX, cohesive HUD.
14
+
15
+ Before scoring World/environment, Hero/player, Materials/textures, or Lighting/render, view the anchors and compare: if your screenshot reads closer to `scene-1.jpg` than `scene-3.jpg` for that surface, the category is at most a 1-2 regardless of how much code was written.
16
+
17
+ ## Scoring Scale
18
+
19
+ - 0: Placeholder. Default primitives, sparse world, unreadable state, debug UI, or no evidence.
20
+ - 1: Basic styled. Playable and themed, but still obvious prototype assets, flat composition, repeated silhouettes, or generic UI.
21
+ - 2: Premium stylized. Authored silhouettes, material/detail systems, readable state, cohesive UI/world, measured performance.
22
+ - 3: Showcase. Strong art direction, memorable hero and world, dense authored detail, excellent readability, polished VFX/rendering, and diagnostics.
23
+
24
+ ## Categories
25
+
26
+ 1. Art direction.
27
+ - 0: No clear theme.
28
+ - 1: Theme is mostly colors/fog.
29
+ - 2: Theme affects forms, materials, UI, world, and feedback.
30
+ - 3: Distinct identity visible in every surface.
31
+ 2. Hero/player.
32
+ - 0: Default primitive stack.
33
+ - 1: Basic object with glow or simple attachments.
34
+ - 2: Authored silhouette, decals/trim, state cues, collision proxy.
35
+ - 3: Memorable model with layered construction and expressive feedback.
36
+ 3. Obstacles/enemies.
37
+ - 0: Cubes/cones/spheres.
38
+ - 1: Recolored repeated silhouette.
39
+ - 2: Three readable variants with telegraphs and material cues.
40
+ - 3: Varied family with animation, anticipation, and gameplay clarity.
41
+ 4. Rewards/interactables.
42
+ - 0: Plain sphere/ring/token.
43
+ - 1: Repeated object with simple glow.
44
+ - 2: Two authored forms with idle/collect states and UI feedback.
45
+ - 3: Desirable, animated, and clearly valued during motion.
46
+ 5. World/environment.
47
+ - 0: Flat plane, empty arena, box skyline.
48
+ - 1: Themed but sparse repeated blocks.
49
+ - 2: Layered prop kit with foreground/midground/background and scale cues.
50
+ - 3: Dense authored world that supports gameplay readability.
51
+ 6. Materials/textures.
52
+ - 0: Flat colors.
53
+ - 1: Basic roughness/metalness or emissive color.
54
+ - 2: Shared material roles, procedural decals, trim, panel lines, wear/noise.
55
+ - 3: Rich cohesive material language with measured texture/resource use.
56
+ 7. Lighting/render.
57
+ - 0: Default lights or unreadable darkness.
58
+ - 1: Fog/bloom used as main style.
59
+ - 2: Intentional tone mapping, exposure, key/fill/rim, contact, depth.
60
+ - 3: Cinematic but readable composition with disciplined post-processing.
61
+ 8. VFX/motion.
62
+ - 0: None or random particles.
63
+ - 1: Generic particles/trails.
64
+ - 2: Event-driven VFX for boost, pickup, hit, fail, combo, shield, or spawn.
65
+ - 3: High-impact effects that clarify gameplay and remain performant.
66
+ 9. UI/HUD.
67
+ - 0: Debug text or missing UI.
68
+ - 1: Generic stat-card dashboard.
69
+ - 2: Genre-specific HUD states, meters/icons, responsive text fit.
70
+ - 3: Cohesive game interface with strong hierarchy and polished transitions.
71
+ 10. Performance evidence.
72
+ - 0: No metrics after visual changes.
73
+ - 1: Informal "seems fine".
74
+ - 2: Renderer counts, build/browser QA, desktop/mobile screenshots, and technical-art budget notes.
75
+ - 3: Baseline/post metrics, bottleneck notes, budgets, optimized asset strategy, and VFX/readability tradeoffs.
76
+
77
+ ## Thresholds
78
+
79
+ Premium:
80
+
81
+ - Every category at least 2.
82
+ - Average at least 2.3.
83
+ - Desktop and mobile active-play screenshots captured when mobile is in scope.
84
+ - Renderer diagnostics reported after graphics changes.
85
+
86
+ Showcase:
87
+
88
+ - At least six categories score 3.
89
+ - No category below 2.
90
+ - Average at least 2.7.
91
+ - Performance evidence includes before/after or budget-aware notes.
92
+
93
+ ## Automatic Failures
94
+
95
+ Any of these prevents a premium/AAA/showcase claim:
96
+
97
+ - Active screenshot is primitive-dominant.
98
+ - Main world is mostly stretched boxes, flat planes, or a sparse arena.
99
+ - Hero asset is mostly default primitives plus glow.
100
+ - Obstacles or rewards are one repeated silhouette.
101
+ - HUD is mostly rectangular stat/debug cards.
102
+ - Fog, darkness, bloom, or particles hide missing authored geometry.
103
+ - UI overlaps the play path, clips text, or fails mobile safe areas.
104
+ - The game is not playable through real input.
105
+ - No active-play screenshot was captured.
106
+ - No renderer diagnostics were collected after major graphics work.
107
+ - No technical-art budget or imported/generated asset diagnostics were reported for premium graphics work.
108
+
109
+ ## Measured Evidence
110
+
111
+ Run the canvas inspector (`pnpm exec node` on `../game-test/scripts/inspect-threejs-canvas.mjs`, or a project `inspect:canvas` script) on desktop and mobile and cite its `metrics` and `renderBudget` blocks in the scorecard. The numbers are advisory signals, not gates, but they must be reported and low values must be explained rather than ignored:
112
+
113
+ - `colorEntropyBits` below ~3.0 or `dominantColorShare` above ~0.6 suggests a sparse, flat scene — supporting evidence against World/environment or Materials/textures scores above 2.
114
+ - `edgeDensity` below ~0.04 suggests primitive-dominant or empty framing — supporting evidence against World/environment and Hero/player scores above 2.
115
+ - `luminance.contrast` below ~60 suggests fog/darkness compression — supporting evidence against Lighting/render scores above 2.
116
+ - `renderBudget` rows over the tier budget require a documented tradeoff in the technical-art budget (see `technical-art.md`).
117
+ - Renderer diagnostics (calls, triangles, geometries, textures) back the Performance evidence category.
118
+
119
+ ## Fresh-Eyes Review
120
+
121
+ The builder must not be the only grader. For premium/AAA/showcase claims:
122
+
123
+ - If the runner supports subagents (Task tool or equivalent), spawn a reviewer with ONLY: the screenshots, this scorecard file, and the inspector metrics JSON. No build context, no prior scores. The reviewer must receive the COMPLETE capture set — every captured state, desktop and mobile — never a hand-picked subset; a curated selection can hide weak states or miss content the builder knows exists (capture states with the inspector's `--state` flag so nothing is gated behind live play). The reviewer fills the scorecard independently; reconcile by taking the lower score per category unless concrete evidence overturns it. Report both score sets.
124
+ - If subagents are unavailable, run an adversarial self-review before finalizing: for each category, write one sentence making the strongest case that the score is a 1, citing what is visible in the screenshot; only then assign the score. Include these sentences in the report.
125
+
126
+ ## Report Format
127
+
128
+ ```text
129
+ Visual scorecard:
130
+ - Art direction: before X / after Y - evidence:
131
+ - Hero/player: before X / after Y - evidence:
132
+ - Obstacles/enemies: before X / after Y - evidence:
133
+ - Rewards/interactables: before X / after Y - evidence:
134
+ - World/environment: before X / after Y - evidence:
135
+ - Materials/textures: before X / after Y - evidence:
136
+ - Lighting/render: before X / after Y - evidence:
137
+ - VFX/motion: before X / after Y - evidence:
138
+ - UI/HUD: before X / after Y - evidence:
139
+ - Performance evidence: before X / after Y - evidence:
140
+ Measured evidence: colorEntropyBits / edgeDensity / luminance.contrast /
141
+ dominantColorShare per viewport, renderer diagnostics, render budget rows
142
+ Fresh-eyes review: subagent scores or adversarial self-review notes
143
+ Average:
144
+ Automatic failures remaining:
145
+ ```
146
+
147
+ If any category remains below threshold, state the exact next pass instead of declaring completion.