# AI 꾸러미 — 절차 마모 재질 (CG 티 없애기) — Procedural wear / weathering material
> 미리 구운 3D 잡음 텍스처 하나로 거칠기 얼룩 · 색 차이 · 그을음 줄 · 열 변색을 물체 좌표에 입혀, 플라스틱 같은 CG 금속을 실제 쓰던 금속처럼 만든다.  
> 견본: https://ai-techstudio.web.app/#t/i84

이 문서를 코드 도우미(Claude Code · Cursor · ChatGPT 등)에 그대로 주면 돼요. 「## 주문서」 가 할 일, 나머지는 참고 자료예요.

## 주문서

### 만들어 줘: 절차 마모 재질 (CG 티 없애기) — Procedural wear / weathering material

#### 1. 목표
로켓 엔진 노즐 · 기계 부품에 절차 마모 재질을 입혀 줘 — 3D 잡음 텍스처 하나로 거칠기 얼룩 · 색 차이 · 그을음 줄 · 열 변색. 느낌은 실제 쓰던 금속.

#### 2. 핵심 기술 용어
- **Procedural wear / weathering material** — 절차 마모 재질 — 코드로 만든 낡음
- **Baked 3D noise texture (Data3DTexture)** — 미리 구운 3D 잡음 — 셰이더는 읽기만
- **Roughness breakup** — 거칠기를 고르지 않게 흩뜨리기
- **Heat tint gradient** — 열로 변한 색 (파랑 → 보라 → 금색)

#### 3. 환경
- 플랫폼: three.js r186 (ES 모듈 · TypeScript, `import * as THREE from "three"`), WebGL2, 외부 라이브러리 추가 없이
- 화면: 3D · 브라우저 — PC 와 폰(가로 844×390 · 세로 390×844) 모두, 60fps 목표

#### 4. 조건
- 잡음은 반복문 GLSL 이 아니라 미리 구운 Data3DTexture(RedFormat · LinearFilter · RepeatWrapping) 로
- 좌표는 물체 좌표 — 물체가 돌아도 얼룩이 붙어 있게
- 세기 값(거칠기 얼룩 · 색 차이 · 그을음 · 열 변색)은 uniform 으로 — 재질 하나로 여러 값
- 반사할 환경(PMREM 스튜디오)이 있어야 거칠기 얼룩이 보인다
- 왼쪽 그냥 금속 · 오른쪽 마모 금속을 같은 모양으로 나란히

#### 5. 완성 기준 (이게 보이면 성공)
- 오른쪽 금속에는 반사가 고르지 않게 흐려진 얼룩 · 세로 그을음 줄이 보이고, 왼쪽은 고른 거울 같은 금속이다
- 노즐 끝에서 위로 파랑 → 보라 → 금색 열 변색이 보이고, 경계가 잡음으로 흔들린다
- 「열」 · 「마모」 값을 바꾸면 바로 반영된다
- 첫 화면이 멈추지 않는다 (셰이더 컴파일 1초 안쪽)

#### 6. 진행 방식
- 핵심 코드 위주로, 설명은 짧게. 내 프로젝트에 끼워 넣기 쉬운 함수 · 클래스로 나눠 줘.
- 처음 화면에 바로 결과가 보이게, 그리고 켬/끔(또는 전/후) 비교를 할 수 있게 만들어 줘.
- 그림 · 소리 · 모델 파일이 필요하면 코드로 만든 임시 대체물로 먼저 돌아가게 하고, 진짜 파일로 바꿀 자리를 표시해 줘.
- 마지막에 「확인 방법」(무엇을 보면 성공인지)과 「조절할 값」 목록을 짧게 정리해 줘.
- 답변과 코드 주석은 한국어로 해 줘.

## 원리
- 시작할 때 64³ 크기의 4겹 값 잡음을 Data3DTexture 로 한 번 굽는다 (반복되게, 선형 필터).
- 셰이더는 물체 좌표 × detail 로 그 텍스처를 두세 번 읽기만 한다 — 반복문 잡음이 없어 컴파일이 빠르다.
- 거칠기 ×(1 ± 얼룩), 색 ×(1 ± 색 차이)로 고르지 않게 흩뜨리고, 세로로 늘인 잡음으로 그을음 줄을 그린다.
- 열 변색: 물체 높이 y 를 0~1 로 바꿔 파랑 → 보라 → 금색으로 섞는다 (잡음으로 경계를 흔든다).
- onBeforeCompile 로 표준 재질에 끼워 넣어 빛 · 반사 · 그림자는 그대로다.

## 핵심 코드 — 구운 3D 잡음으로 거칠기 · 색 · 그을음 · 열 변색
(발췌: vendor/airsup/core/materials.ts surf() 의 onBeforeCompile 조각 (demos/demosAirsup.ts i84 가 씀))
```glsl
// JS: NOISE3D.value = makeNoise3D(64)  — 64³ 4겹 값 잡음 Data3DTexture (RedFormat, Linear, Repeat)
// uDetail = (detail, roughVar, colorVar, bump) · uTintRange = (y0, y1, strength)
uniform highp sampler3D uNoise3D;
uniform vec4 uDetail; uniform float uStreaks;
uniform vec3 uTintA; uniform vec3 uTintB; uniform vec3 uTintC; uniform vec3 uTintRange;
varying vec3 vObj;                                   // 물체 좌표 (정점에서 넘김)
float n3(vec3 p) { return texture(uNoise3D, p).r; }
float fbm2(vec3 p) { return n3(p) * 0.65 + n3(p * 2.7 + 0.31) * 0.35; }

// ① clipping_planes_fragment 뒤: 잡음 값 둘
vec3 dp = vObj * uDetail.x;
float surfN1 = fbm2(dp);
float surfN2 = n3(dp * 3.3 + vec3(7.3, 1.1, 3.7));
float st = n3(vec3(vObj.x * uDetail.x * 2.2, vObj.y * uDetail.x * 0.18, vObj.z * uDetail.x * 2.2)); // 세로로 늘인 잡음
surfN1 = mix(surfN1, st, 0.55);

// ② color_fragment 뒤: 색 차이 · 열 변색 · 그을음
diffuseColor.rgb *= 1.0 + (surfN1 - 0.5) * uDetail.z * 2.0;
float ty = clamp((vObj.y - uTintRange.x) / (uTintRange.y - uTintRange.x), 0.0, 1.0);
ty = clamp(ty + (surfN2 - 0.5) * 0.18, 0.0, 1.0);
vec3 tc = ty < 0.5 ? mix(uTintA, uTintB, ty * 2.0) : mix(uTintB, uTintC, ty * 2.0 - 1.0);
diffuseColor.rgb = mix(diffuseColor.rgb, tc, uTintRange.z);
diffuseColor.rgb *= 1.0 - uStreaks * smoothstep(0.45, 0.8, surfN1);

// ③ metalnessmap_fragment 뒤: 거칠기 얼룩
roughnessFactor = clamp(roughnessFactor * (1.0 + (surfN2 - 0.5) * uDetail.y * 2.0), 0.03, 1.0);
// 견본 노즐 값: detail 1.3 · roughVar 0.28 · colorVar 0.05 · streaks 0.28 · tint 0x34477e → 0x6b5a78 → 0xb58a4a, 세기 0.6
```

## 흔한 실수 · 확인 목록
- [ ] **반복문 fbm 잡음을 셰이더에서 여러 번 부르면 윈도 컴파일이 17~20초 멈춘다** — 보물 동굴 게임에서 실제로 겪었다. 잡음은 미리 구운 3D 텍스처로 읽기만 하고, 값은 uniform 으로 돌려 셰이더 종류를 하나로.
- [ ] **세계 좌표로 잡음을 읽으면 물체가 움직일 때 얼룩이 흐른다** — 정점에서 물체 좌표(vObj)를 넘긴다.
- [ ] **환경 반사가 없으면 거칠기 얼룩이 안 보인다** — 금속은 반사로 드러난다. PMREM 스튜디오 환경을 넣는다.
- [ ] **마모를 세게 하면 「더러운」 그림이 된다** — 견본 색 차이는 0.05 처럼 작게. 거칠기 얼룩이 먼저, 색 얼룩은 아주 조금.

## 완성 기준 체크리스트
- [ ] 오른쪽 금속에는 반사가 고르지 않게 흐려진 얼룩 · 세로 그을음 줄이 보이고, 왼쪽은 고른 거울 같은 금속이다
- [ ] 노즐 끝에서 위로 파랑 → 보라 → 금색 열 변색이 보이고, 경계가 잡음으로 흔들린다
- [ ] 「열」 · 「마모」 값을 바꾸면 바로 반영된다
- [ ] 첫 화면이 멈추지 않는다 (셰이더 컴파일 1초 안쪽)

## 이 기술 정보
- id: `i84` · 분류: 그래픽 · 셰이더 › 재질 · 그림체 · 3D · 난이도 보통 · 폰 부담 보통 (폰 주의) — 조각마다 3D 텍스처 3~4번 읽기 (반복문 잡음보다 훨씬 싸다). 시작할 때 64³ = 26만 칸 굽기 한 번.
- 라이브 견본 (브라우저에서 직접 조작): https://ai-techstudio.web.app/#t/i84
- 쓰면 좋을 때: 금속 · 기계가 「컴퓨터 그림」처럼 매끈해 보일 때 / 여러 소품에 같은 낡음을 일관되게 입히고 싶을 때 (값만 다르게)
- 쓰지 말 때: 아주 귀엽고 깨끗한 그림체 — 낡음이 지저분해 보인다 / 폰에서 수백 종 재질 — 재질마다 셰이더가 컴파일된다. 대신 값 몇 가지로 묶기

## 견본 실제 코드 (라이브 견본이 돌리는 코드 — three.js · TypeScript)
### wearMats — `src/demos/demosAirsup.ts:394`
```ts
function wearMats(cut: CutState | null = null): EngineMats & { nozzleU: Record<string, { value: unknown }> } {
  const cap = 0xb4b9bf;
  const nozzle = surf({
    name: 'nozzle', color: 0x5f6267, metalness: 1, roughness: 0.44, detail: 1.3, roughVar: 0.28, colorVar: 0.05, bump: 0.0002, streaks: 0.28,
    tint: { y0: GEO.yE, y1: GEO.yT, a: 0x34477e, b: 0x6b5a78, c: 0xb58a4a, strength: 0.6 },
    ribs: [220, 0.00035], cut, capColor: cap,
  });
  return {
    nozzle,
    nozzleU: nozzle.userData['u'] as Record<string, { value: unknown }>,
    cast: surf({ color: 0xa3a8ae, metalness: 1, roughness: 0.44, detail: 1.1, roughVar: 0.22, colorVar: 0.035, bump: 0.00012, cut, capColor: cap }),
    machined: surf({ color: 0xc4c8cd, metalness: 1, roughness: 0.3, detail: 3, roughVar: 0.15, colorVar: 0.02, anisotropy: 0.5, cut, capColor: 0xb9bdc3 }),
    dark: surf({ color: 0x34373c, metalness: 0.9, roughness: 0.4, detail: 9, roughVar: 0.3, cut, capColor: 0x8a8f96 }),
    printed: surf({ color: 0xa9adb3, metalness: 1, roughness: 0.5, detail: 1.2, roughVar: 0.22, colorVar: 0.035, layers: [2300, 0.00004], bump: 0.00015, cut, capColor: cap }),
  };
}
```

## 관련 기술
- 같은 방법으로 만든 효과 예시: [가장자리 닳음 (모서리 마스크)](https://ai-techstudio.web.app/ai/t/i472.md) `i472`
- 먼저 알면 좋은 기술: [재질에 셰이더 끼워 넣기](https://ai-techstudio.web.app/ai/t/u08.md) `u08` · [그린 요철 · 노멀 맵](https://ai-techstudio.web.app/ai/t/u10.md) `u10`
- 다음에 해 볼 기술: [삼면 투영 무늬 (triplanar)](https://ai-techstudio.web.app/ai/t/i229.md) `i229` · [GPU 경로 추적 (사진 같은 렌더 · 샘플 쌓기)](https://ai-techstudio.web.app/ai/t/i545.md) `i545`
- 참고 문서: [AirsupHQ/airsup-lab (원본 저장소, MIT)](https://github.com/AirsupHQ/airsup-lab)
