Spaces:
Sleeping
Sleeping
File size: 3,761 Bytes
37e3d5a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | # 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.
|