Spaces:
Sleeping
Sleeping
| # Three.js texture & PBR reference (notebooklm research, 2026-07-22) | |
| Doc-grounded (threejs.org MeshPhysicalMaterial / MeshStandardMaterial / Texture / CanvasTexture / | |
| DataTexture / manual Β§Textures). Drives `analyze_texture.py` (finish classification + recipe) and | |
| the emitted `makeProceduralTextureSet` / material presets in `generate_threejs_factory.py`. | |
| ## Colour space β get this right or everything looks wrong | |
| - **Albedo / `map` / `emissiveMap` / `sheenColorMap` β `THREE.SRGBColorSpace`.** | |
| - **Data maps (`roughnessMap`, `metalnessMap`, `normalMap`, `aoMap`, `bumpMap`, `displacementMap`, | |
| `thicknessMap`, `anisotropyMap`) β `THREE.NoColorSpace`** (linear data, never sRGB). | |
| - `CanvasTexture`: `flipY = true` (default), auto `needsUpdate` on construct. | |
| - `DataTexture`: `flipY = false` (default) β memory (0,0) is bottom-left. Set `needsUpdate = true`. | |
| - After changing `wrapS/wrapT` on an already-used texture you MUST set `needsUpdate = true`. | |
| - `aoMap`/`lightMap` read UV channel 1 β geometry needs a 2nd UV set, or set `texture.channel = 1`. | |
| - Tiling: `wrapS = wrapT = THREE.RepeatWrapping` + `repeat.set(u,v)`; set `anisotropy = | |
| renderer.capabilities.getMaxAnisotropy()` so oblique angles stay sharp. | |
| ## Per-finish material recipe (MeshPhysicalMaterial scalars) | |
| | finishClass | metalness | roughness | clearcoat | ccRoughness | transmission | ior | envMapIntensity | anisotropy | author maps | | |
| |---|---|---|---|---|---|---|---|---|---| | |
| | `gemstone` (translucent quartz/glass) | 0.0 | 0.05 | 1.0 | 0.0 | 1.0 | 1.54 | 1.0 | 0.0 | map(gradient), thicknessMap | | |
| | `gem-metal` (chromed doppler blade β our blend) | 0.75 | 0.14 | 0.6 | 0.06 | 0.0 | 1.5 | 1.3 | 0.0 | map(gradient+smoke), roughnessMap | | |
| | `painted-metal` (glossy paint) | 0.0 | 0.5 | 1.0 | 0.05 | 0.0 | 1.5 | 1.0 | 0.0 | map, roughnessMap, clearcoatRoughnessMap | | |
| | `worn-composite` (aged rubber/grip) | 0.0 | 0.9 | 0.0 | 0.0 | 0.0 | 1.5 | 0.5 | 0.0 | map(mottled), roughnessMap, bumpMap | | |
| | `brushed-steel` | 1.0 | 0.35 | 0.0 | 0.0 | 0.0 | 1.5 | 1.0 | 1.0 | anisotropyMap, roughnessMap, normalMap | | |
| > Note: pure `gemstone` (transmission 1.0) is see-through glass. A painted/anodised metallic blade | |
| > (CS Doppler) is opaque β use `gem-metal` (metalness + gradient map + clearcoat, no transmission). | |
| ## Procedural map generation (in code) | |
| - **CanvasTexture** β draw to an `HTMLCanvasElement`: linear/radial gradients across palette stops | |
| (gemstone/doppler), layered low-alpha blobs for smoke, per-pixel mottle noise for worn finishes, | |
| thin horizontal streaks for brushed metal. `new THREE.CanvasTexture(canvas)` then set `colorSpace`. | |
| - **DataTexture** β push computed noise/height into a `Uint8Array`, `new THREE.DataTexture(arr,w,h, | |
| THREE.RGBAFormat, THREE.UnsignedByteType)`, `colorSpace = NoColorSpace`, `needsUpdate = true`. | |
| - **Height β Normal (no native helper β compute it):** Sobel/neighbour-difference the height field β | |
| `R = (dx*strength + 1)*0.5*255`, `G = (dy*strength + 1)*0.5*255`, `B = 255` (up +Z), A = 255. | |
| - **Independent channels** (skill rule, confirmed): never alias one map into another β albedo, | |
| roughness, normal/height, AO are separate signals with separate frequency content. | |
| ## Analysis β which finishClass? | |
| `analyze_texture.py` classifies a reference crop by pure-stdlib stats: | |
| - saturation + hue spread β chromatic gemstone vs neutral metal/grey. | |
| - luminance gradient across the crop β `gradient` finish (doppler) vs flat. | |
| - local variance / mottle β `worn-composite`. | |
| - directional (horizontal) high-freq streaks β `brushed-steel`. | |
| - specular-highlight fraction (very bright pixels) β metalness proxy. | |
| Output: `{finishClass, palette:[stops], mottle, gradientAxis, metalnessHint}` β material recipe. | |