프로그램 생성 텍스처
출처: room-at-dawn (MIT), src/core/tex.js, rng.js, src/scene/materials.js, 그리고 parts/ 디렉토리의 각 부품 텍스처 함수에서 가져왔습니다.
원 프로젝트의 침실에는 단 한 장의 이미지 파일도 없으며, 모든 텍스처는 로드 시 프로그램으로 생성됩니다.
핵심 아이디어:
- 물리적 단위로 설계하세요. 높이는 mm 단위로, 텍스처 한 칸이 몇 미터를 나타내는지 명확히 하고, UV도 미터 단위로 사용합니다. 이렇게 하면
repeat = 1 / tileMeters가 되며, 해상도를 변경해도 외관이 변하지 않습니다. - "사실감"의 대부분은 거칠기에서 오지, 색상에서 오지 않습니다. 지문, 닦은 자국, 먼지는 색상으로는 거의 보이지 않으며, 하이라이트가 스치면서 나타납니다.
- 모든 것은 반복 가능하고 재현 가능해야 합니다. 노이즈에는 주기가 있으며, 난수는 항상
RNG(seed)를 따릅니다. 동일한 시드로 생성된 결과는 항상 동일합니다(라이트 베이킹, 스크린샷 회귀 테스트에 유용).
파일
| 경로 | 내용 |
|---|---|
scripts/tex.js | 기본: RNG, pnoise/pfbm (축별로 다른 주기 가능), worley, vn (Lookup table 기반 고속 노이즈), field, normalBytes (mm 단위), dataTex/canvasTex/packTex/colorTex, avgColor, once (캐싱), rep (이미지 공유, repeat만 변경), seamScore, texData |
scripts/surfaces.js | wallNormal + wallPatch (우드칩 벽지 + 3.2m 스케일의 회반죽 및 페인트 변화), lacquerNormal, smudgeRough, dustRough, floorMaps, woodMaps, oakVeneer |
scripts/textiles.js | weaveNormal, waffleNormal, terryNormal, ribNormal, knitNormal, feltNormal, ropeNormal, fabricMaterial (쉬인 및 역광 투과 포함) |
scripts/wear.js | wornMaps (사용된 표면 전체: 색상과 거칠기 두 장), speckleTexture, scuffTexture, 기본 브러시 softBlob, strand, fingerprint, noiseTile, overlay |
scripts/print.js | word/phrase (음절로 조합된 가짜 단어), PAL, packer, fitText, paperGrain, makeBook + bookAtlas + bookGeometry, pagesTexture, topoPoster, photoPrints |
example/ | 방의 한쪽 구석, 모든 표면이 라이브러리의 텍스처 중 하나를 사용합니다. ?cam= (room, desk, glint, fabric, floor, wall, books); ?flat=1은 단색 비교용 |
example/preview.jpg | 예제 스크린샷 |
references/recipes.md | 각 텍스처의 제작 방법, 크기, 매개변수 및 새 텍스처를 추가하는 방법 |
references/verify-engines.md | 검증 방법, 발생한 문제, 성능, Godot/Unreal로의 이식 |
예제 실행
cd <skill 폴더> && python3 -m http.server 5422 --bind 127.0.0.1
# http://127.0.0.1:5422/example/ 열기 (window.__tex에는 각 텍스처의 생성 시간, 이음새 점수, 평균 알베도가 있습니다)
(<skill 폴더>는 이 스킬 로드 시 표시되는 Base directory입니다. 플러그인으로 설치 시 ~/.claude/plugins/cache/에 있습니다)
사용법
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는 미터 단위; repeat는 이미 설정됨
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 });
roughnessMap이 있는 재질의 경우roughness는 1로 설정해야 합니다. three.js는 두 값을 곱합니다.packTex와wornMaps의 거칠기는 G 채널에 있습니다. 그레이스케일 canvas도 직접 사용할 수 있습니다.- 각 텍스처는
avg(선형 평균 색상)를 반환하며, 이는threejs-baked-gi에서 반사광의 알베도로 사용됩니다.
필수 규칙
Math.random()을 사용하지 마십시오. 모든 텍스처에는 고정된 시드가 있어야 합니다.- 노이즈의 주기는 반드시 정수여야 하며, 텍스처의 격자와 정렬되어야 합니다 (예:
pfbm(u*6, v*6, oct, seed, 6)). 정렬되지 않으면 이음새가 생깁니다.seamScore로 확인하세요. - 높이는 mm 단위로,
normalBytes(H, w, h, mmPerTile)을 사용하세요. 해상도를 변경해도 범프의 강도가 변하지 않습니다. 일부 텍스처는 정사각형 타일이 아닙니다 (예: 바닥 1.1 × 1.9 mm). 두 축을 분리하여 지정합니다. - 거칠기는 색상과 함께 그려야 합니다.
wornMaps의 각 획은 두 canvas에 동시에 그려지며, 동일한new RNG(i)를 사용하여 두 이미지의 모양이 정렬되도록 합니다. - 어두운 표면의 경우, 파편과 반짝임을 줄여야 합니다 (예제의 어두운 책상 표면은 9000에서 2500으로 감소). 그렇지 않으면 화강암처럼 보일 수 있습니다.
- Canvas로 그리는 텍스트와 패턴은 항상 직접 생성해야 합니다 (
word(), 기하 도형). 실제 책 제목, 브랜드 또는 상표를 넣지 마십시오. - 가장 큰 텍스처(2048²) 생성에는 0.1–0.35초가 소요됩니다. 동일한 텍스처는
once로 캐싱하고rep()으로 공유하십시오. 각 오브젝트에 대해 별도로 생성하지 마십시오.
실제 성능 (RTX 4090, Chrome headless, 예제 전체 1.1초)
| 텍스처 | 크기 | 시간 | 이음새 점수 (x/y) |
|---|---|---|---|
floorMaps | 2048² × 3장 | 0.32 s | 색상 0.96/1.10, 법선 1.00/1.02, 거칠기 0.89/1.05 |
woodMaps | 1024² × 3 | 0.20–0.23 s | 색상 0.93/1.12, 법선 1.30/1.06 |
wornMaps (1.2 × 0.6 m, 3400 px/m) | 4080×2040 + 절반 크기 거칠기 | 0.13 s | 반복 안 함 |
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종 | 128²–512² | 1–19 ms | 0.94–1.10 (면 로프의 y는 2.03, 의도된 홈, verify-engines.md 참조) |
bookAtlas (12권) | 2048×1536 | 0.05–0.08 s | — |
이음새 점수가 1에 가까우면 이음새가 보이지 않음을 의미하며, 2보다 크면 확인이 필요합니다. 계산 시 분자와 분모에 각각 1개의 색상 계조를 더하여, 나뭇결과 같이 거의 요철이 없는 방향에서 1개 계조 미만의 노이즈로 인해 큰 비율이 계산되는 것을 방지합니다.