# AI 꾸러미 — 뚜껑 없는 단면 (뒷면 셰이딩)
> 닫힌 입체를 양면으로 그리고 같은 자르기 평면 — 잘린 곳으로 보이는 뒷면을 셰이더가 「평평한 단면」으로 칠하고 평면 깊이를 써서, 뚜껑 모형 없이 쇠를 깎은 듯한 단면 · 칼을 움직여도 즉시 · 쐐기 모양 자르기도  
> 견본: https://ai-techstudio.web.app/#t/i85

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

## 주문서

### 만들어 줘: 뚜껑 없는 단면 (뒷면 셰이딩)

#### 1. 목표
입체도형 단면에 「뚜껑 없는 단면 (뒷면 셰이딩)」 기술을 깔끔한 기본 느낌으로 넣어 줘. — 닫힌 입체를 양면으로 그리고 같은 자르기 평면 — 잘린 곳으로 보이는 뒷면을 셰이더가 「평평한 단면」으로 칠하고 평면 깊이를 써서, 뚜껑 모형 없이 쇠를 깎은 듯한 단면 · 칼을 움직여도 즉시 · 쐐기 모양 자르기도

#### 2. 핵심 기술 용어
- 뚜껑 없는 단면 (뒷면 셰이딩) — 닫힌 입체를 양면으로 그리고 같은 자르기 평면 — 잘린 곳으로 보이는 뒷면을 셰이더가 「평평한 단면」으로 칠하고 평면 깊이를 써서, 뚜껑 모형 없이 쇠를 깎은 듯한 단면 · 칼을 움직여도 즉시 · 쐐기 모양 자르기도

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

#### 4. 조건
- 한 프레임 16ms 안 (폰 포함) — 무거운 계산은 처음에 한 번 또는 여러 프레임에 나눠서
- 새 재질 · 셰이더는 첫 화면 전에 미리 컴파일 (renderer.compileAsync)
- 화면 가장자리 흐림 · 비네트로 얼버무리지 않기
- 외부 라이브러리 추가 없이 three.js 와 three/examples 만

#### 5. 완성 기준 (이게 보이면 성공)
- 견본처럼 「닫힌 입체를 양면으로 그리고 같은 자르기 평면 — 잘린 곳으로 보이는 뒷면을 셰이더가 「평평한 단면」으로 칠하고 평면 깊이를 써서, 뚜껑 모형 없이 쇠를 깎은 듯한 단면 · 칼을 움직여도 즉시 · 쐐기 모양 자르기도」 이(가) 첫 화면에서 바로 보인다
- 켬/끔(또는 전/후) 비교 단추로 이 기술이 만드는 차이를 확인할 수 있다
- 중요한 값 1~3개를 슬라이더로 바꿔 볼 수 있다
- 콘솔 오류 0, 폰 폭(390px)에서도 동작한다

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

## 원리
- 닫힌 입체를 양면으로 그리고 같은 자르기 평면 — 잘린 곳으로 보이는 뒷면을 셰이더가 「평평한 단면」으로 칠하고 평면 깊이를 써서, 뚜껑 모형 없이 쇠를 깎은 듯한 단면 · 칼을 움직여도 즉시 · 쐐기 모양 자르기도


## 흔한 실수 · 확인 목록
- (아직 정리된 것이 없어요)

## 완성 기준 체크리스트
- [ ] 견본처럼 「닫힌 입체를 양면으로 그리고 같은 자르기 평면 — 잘린 곳으로 보이는 뒷면을 셰이더가 「평평한 단면」으로 칠하고 평면 깊이를 써서, 뚜껑 모형 없이 쇠를 깎은 듯한 단면 · 칼을 움직여도 즉시 · 쐐기 모양 자르기도」 이(가) 첫 화면에서 바로 보인다
- [ ] 켬/끔(또는 전/후) 비교 단추로 이 기술이 만드는 차이를 확인할 수 있다
- [ ] 중요한 값 1~3개를 슬라이더로 바꿔 볼 수 있다
- [ ] 콘솔 오류 0, 폰 폭(390px)에서도 동작한다

## 이 기술 정보
- id: `i85` · 분류: 기계 · 구조 › 구조 · 조립 · 3D · 난이도 보통 · 폰 부담 보통 (폰 주의)
- 라이브 견본 (브라우저에서 직접 조작): https://ai-techstudio.web.app/#t/i85
- 쓰면 좋을 때: 쓰면 좋을 곳 — 입체도형 단면 · 원뿔 곡선 · 엔진 · 펌프 속 보기

## 견본 실제 코드 (라이브 견본이 돌리는 코드 — three.js · TypeScript)
### i85 견본 항목 — `src/demos/demosAirsup.ts:640`
```ts
  i85: {
    kind: '3d',
    caption: '칼(자르기 평면)이 쓸고 지나가며 펌프 속이 열려요 — 뚜껑 모형 없이 뒷면을 「쇠 단면」으로 칠함 (일자 ↔ 쐐기)',
    make() {
      const { scene, pump, stand } = pumpRig();
      pump.cut.collect(pump.root);
      const cam = new THREE.PerspectiveCamera(32, 1.6, 0.05, 60);
      const target = new THREE.Vector3(0.04, 0.36, 0);
      const dir = new THREE.Vector3(0.3, 0.38, 1);
      const P = pipelines({ ao: true, msaa: 4, aoSamples: 8 });
      const hud = new Hud();
      const lMode = hud.label();
      const lNote = hud.label();
      const st = { auto: true, amount: 0.7, wedge: false };
      let time = 0;
      let shownWedge = false;
      return {
        scene,
        camera: cam,
        update(t, dt) {
          time = t;
          FLUID_TIME.value = t;
          let a = st.amount;
          let wedge = st.wedge;
          if (st.auto) {
            const C = 9;
            const u = (t % C) / C;
            a = u < 0.08 ? 0 : u < 0.4 ? ease((u - 0.08) / 0.32) : u < 0.76 ? 1 : u < 0.96 ? 1 - ease((u - 0.76) / 0.2) : 0;
            wedge = Math.floor(t / C) % 2 === 1;
          }
          if (wedge !== shownWedge) {
            setWedge(pump.cut, wedge ? Math.PI / 4 : 0);
            shownWedge = wedge;
          }
          stand.rotation.y = Math.sin(t * 0.3) * 0.3 - 0.1;
          pump.cut.amount = a;
          pump.cut.update();
          pump.update(dt, t, 0.3, 0, false, 0);
        },
        render(r, w, h) {
          guarded(r, () => {
            scene.environment = envFor(r);
            r.localClippingEnabled = true;
            fitCamera(cam, w / h, target, dir, 1.4, 0.95);
            const p = P.get(r, w, h);
            Object.assign(p.params, { bloom: 0.1, bloomThreshold: 0.8, vignette: 0.5, grain: 0.015, ca: 0.004 });
            r.setRenderTarget(null);
            r.setViewport(0, 0, w, h);
            r.setScissorTest(false);
            if (warmPipe(r, p, scene, cam)) p.render(scene, cam, null, time);
            hud.begin(w, h);
            lMode.set(shownWedge ? '쐐기 자르기 (¼ 열기)' : '일자 자르기 (반 열기)', '#bfe6ff');
            const m = hud.px * 0.6;
            hud.place(lMode, m, m, hud.px, 0, 0);
            if (w > 420) {
              lNote.set('뚜껑 모형 0개 · 잘린 곳의 뒷면 = 평평한 금속 단면', '#ffffff', 'rgba(8,14,30,0.5)');
              hud.place(lNote, w / 2, h - m, hud.px * 0.85, 0.5, 1);
              lNote.mesh.visible = true;
            } else lNote.mesh.visible = false;
            hud.render(r);
          });
        },
        controls: [
          { type: 'toggle', label: '자동으로 쓸기', value: true, on: (v) => (st.auto = v) },
          { type: 'range', label: '자른 정도 (자동 끄면)', min: 0, max: 1, step: 0.01, value: 0.7, on: (v) => { st.amount = v; st.auto = false; } },
          { type: 'toggle', label: '쐐기 모양 (자동 끄면)', value: false, on: (v) => { st.wedge = v; } },
        ] satisfies Control[],
        dispose() {
          P.dispose();
          hud.dispose();
          disposeTree(scene);
        },
      };
    },
  }
```

## 관련 기술
- 이 효과를 만든 기술: [단면 자르기 (스텐실 단면 · 원뿔 곡선)](https://ai-techstudio.web.app/ai/t/i35.md) `i35`
