Texturas Procedurais em three.js
Origem: room-at-dawn (MIT), extraído de src/core/tex.js, rng.js, src/scene/materials.js e das funções de material em parts/.
No quarto original, não havia nenhum arquivo de imagem; todas as texturas eram geradas proceduralmente no carregamento.
Ideias centrais:
- Projete com unidades físicas. Alturas em milímetros, UV em metros.
repeat = 1 / tileEmMetros; mudar a resolução não altera a aparência. - Grande parte do "realismo" está na rugosidade, não na cor. Digitais, marcas de limpeza, poeira são quase invisíveis na cor; só aparecem quando a luz especular passa.
- Tudo deve ser repetível e reproduzível. Ruídos têm período; números aleatórios sempre usam
RNG(seed). O mesmo seed sempre produz o mesmo resultado (essencial para烘培 de iluminação e testes de screenshot).
Arquivos
| Caminho | Conteúdo |
|---|---|
scripts/tex.js | Básico: RNG, pnoise/pfbm (período diferente por eixo), worley, vn (ruído rápido com lookup table), field, normalBytes (em milímetros), dataTex/canvasTex/packTex/colorTex, avgColor, once (cache), rep (compartilhar imagem, só mudar repeat), seamScore, texData |
scripts/surfaces.js | wallNormal + wallPatch (papel de parede de laca com variação de gesso e tinta em escala de 3.2 m), lacquerNormal, smudgeRough, dustRough, floorMaps, woodMaps, oakVeneer |
scripts/textiles.js | weaveNormal, waffleNormal, terryNormal, ribNormal, knitNormal, feltNormal, ropeNormal, fabricMaterial (sheen com透光 contraluz) |
scripts/wear.js | wornMaps (uma superfície inteira usada: duas telas de cor e rugosidade), speckleTexture, scuffTexture, e pincéis básicos softBlob, strand, fingerprint, noiseTile, overlay |
scripts/print.js | word/phrase (texto falso feito com sílabas), PAL, packer, fitText, paperGrain, makeBook + bookAtlas + bookGeometry, pagesTexture, topoPoster, photoPrints |
example/ | Um canto de sala, cada superfície usa um material da biblioteca. ?cam= (room, desk, glint, fabric, floor, wall, books); ?flat=1 para alternar para cor sólida |
example/preview.jpg | Screenshot do exemplo |
references/recipes.md | Como cada material é feito: tamanhos, parâmetros e como adicionar novos |
references/verify-engines.md | Métodos de verificação, problemas encontrados, desempenho e portabilidade para Godot/Unreal |
Executar o Exemplo
cd <pasta_da_skill> && python3 -m http.server 5422 --bind 127.0.0.1
# Abra http://127.0.0.1:5422/example/ (window.__tex contém tempo de geração, seamScore e albedo médio de cada textura)
(<pasta_da_skill> é o diretório base exibido ao carregar esta skill; quando instalado como plugin, fica em ~/.claude/plugins/cache/)
Uso
import { rep } from './scripts/tex.js';
import { floorMaps, wallNormal, wallPatch, lacquerNormal } from './scripts/surfaces.js';
import { knitNormal, fabricMaterial } from './scripts/textiles.js';
import { wornMaps } from './scripts/wear.js';
const fl = floorMaps(); // UV em metros; repeat já configurado
floor.material = new THREE.MeshStandardMaterial({ map: fl.map, normalMap: fl.normalMap, roughnessMap: fl.roughnessMap, roughness: 1 });
wall.material = wallPatch(new THREE.MeshStandardMaterial({ color: 0xebebe6, roughness: 0.93, normalMap: wallNormal() }));
door.material = new THREE.MeshStandardMaterial({ color: 0xdddddd, roughness: 0.6, normalMap: rep(lacquerNormal(), 9), normalScale: new THREE.Vector2(0.18, 0.18) });
sweater.material = fabricMaterial(0x6f7f6a, { normal: knitNormal(), tileM: 0.1 });
const w = wornMaps({ sizeM: [1.2, 0.6], hands: [[0.55, 0.5]], rings: [[0.9, 0.4, 0.032, 0.6]], prints: 6 });
deskTop.material = new THREE.MeshStandardMaterial({ map: w.map, roughnessMap: w.roughnessMap, roughness: 1 });
- Materiais com
roughnessMapdevem terroughnessdefinido como 1. O three.js multiplica ambos. - A rugosidade em
packTexewornMapsestá no canal G; canvas em escala de cinza também pode ser usado diretamente. - Cada material retorna
avg(cor linear média) para uso como albedo de luz refletida emthreejs-baked-gi.
Regras Obrigatórias
- Não use
Math.random(). Cada textura deve ter um seed fixo. - O período do ruído deve ser um número inteiro e alinhado com a grade da textura (ex.:
pfbm(u*6, v*6, oct, seed, 6)). Caso contrário, haverá emendas. UseseamScorepara verificar. - Alturas em milímetros, use
normalBytes(H, w, h, mm_por_tile). Ao mudar a resolução, a intensidade do relevo não muda; algumas texturas não têm tiles quadrados (piso 1.1 × 1.9 mm), então os eixos devem ser fornecidos separadamente. - Desenhe rugosidade junto com a cor. Cada traço em
wornMapsé desenhado em ambas as telas simultaneamente, usando o mesmonew RNG(i), garantindo que as formas fiquem alinhadas. - Em superfícies escuras, reduza detritos e pontos brilhantes (no exemplo, a mesa escura foi de 9000 para 2500), caso contrário, parecerá granito.
- Texto e padrões desenhados no Canvas devem ser gerados pela própria biblioteca (
word(), formas geométricas); não use nomes reais de livros, marcas ou logotipos. - A maior textura (2048²) leva 0,1–0,35 segundos para ser criada. Use
oncepara cache erep()para compartilhar a mesma imagem. Não gere para cada objeto individualmente.
Benchmarks (RTX 4090, Chrome headless, cena de exemplo 1.1 s)
| Textura | Resolução | Tempo | SeamScore (x/y) |
|---|---|---|---|
floorMaps | 2048² × 3 texturas | 0.32 s | Cor 0.96/1.10, Normal 1.00/1.02, Rugosidade 0.89/1.05 |
woodMaps | 1024² × 3 | 0.20–0.23 s | Cor 0.93/1.12, Normal 1.30/1.06 |
wornMaps (1.2 × 0.6 m, 3400 px/m) | 4080×2040 + rugosidade com metade do tamanho | 0.13 s | Não repetível |
oakVeneer | 1024² × 3 | 0.10 s | 0.98–1.06 |
wallNormal | 1024² | 0.04 s | 1.00/1.07 |
lacquerNormal, smudgeRough | 512² | 0.02–0.04 s | 0.98–1.04 |
| 7 tipos de tecido | 128²–512² | 1–19 ms | 0.94–1.10 (y da corda de algodão é 2.03, é um sulco proposital, veja verify-engines.md) |
bookAtlas (12 livros) | 2048×1536 | 0.05–0.08 s | — |
SeamScore ≈ 1 significa que as emendas são imperceptíveis; > 2 requer inspeção. O cálculo adiciona 1 nível de cor ao numerador e denominador para evitar que direções com pouca variação (como veios retos de madeira) produzam valores grandes devido a ruído abaixo de 1 nível de cor.