# AI 꾸러미 — 잔상 · 움직임 흐림 (Afterimage) — Afterimage (frame feedback / ghosting)
> 지난 화면을 조금씩 옅게 남겨 지금 화면과 겹쳐, 빠르게 미끄러지는 별 · 도는 공 · 달리는 차 뒤에 꼬리가 생겨 속도감이 난다.  
> 견본: https://ai-techstudio.web.app/#t/i05

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

## 주문서

### 만들어 줘: 잔상 · 움직임 흐림 (Afterimage) — Afterimage (frame feedback / ghosting)

#### 1. 목표
미끄러지는 별 · 도는 공 · 달리는 차에 잔상(Afterimage) 후처리를 넣어 줘 — 빠른 것 뒤에 꼬리가 남아 속도감이 나게. 분위기는 신나는 아케이드.

#### 2. 핵심 기술 용어
- **Afterimage (frame feedback / ghosting)** — 잔상 — 이전 화면을 옅게 남기기
- **AfterimagePass (damp)** — three.js 잔상 후처리 — damp 가 클수록 꼬리가 길다
- **Motion trail** — 움직임 꼬리

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

#### 4. 조건
- AfterimagePass(damp) 를 RenderPass 뒤에, 끝에 OutputPass
- damp 는 0.5~0.97 사이로 (1 이면 영원히 안 지워진다)
- 연출 순간에만 enabled = true, 끝나면 끄기
- 왼쪽 그냥 · 오른쪽 잔상 비교, 「꼬리 길이」 슬라이더

#### 5. 완성 기준 (이게 보이면 성공)
- 오른쪽에서 왕복하는 별 · 원을 도는 공 · 달리는 차 뒤에 꼬리가 남고, 왼쪽은 없다
- 「꼬리 길이」를 0.97 로 올리면 꼬리가 길게, 0.5 로 내리면 거의 사라진다
- 멈춘 바닥 · 배경은 양쪽이 똑같다

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

## 원리
- 그림 두 장을 번갈아 쓴다: 「지난 결과」와 「지금 장면」.
- 새 결과 = max(지금, 지난 결과 × damp) — damp(0.88)가 1 에 가까울수록 이전 모습이 오래 남는다.
- 그 결과를 다음 프레임의 「지난 결과」로 넘기므로 지난 자리들이 점점 옅어지는 꼬리가 된다.
- 움직이지 않는 것은 같은 자리에 겹쳐 그대로 보인다.

## 핵심 코드 — 잔상 후처리
(발췌: demos/demosLight.ts i05 make() 를 정리)
```ts
import { AfterimagePass } from 'three/examples/jsm/postprocessing/AfterimagePass.js';

const comp = new EffectComposer(renderer);
comp.addPass(new RenderPass(scene, cam));
const after = new AfterimagePass(0.88);      // damp — 클수록 꼬리가 길다
comp.addPass(after);
comp.addPass(new OutputPass());

let damp = 0.88;
function frame(dt: number) {
  (after.uniforms as Record<string, { value: number }>).damp.value = damp;
  comp.render(dt);
}
// 연출 순간에만: after.enabled = true; ... 끝나면 after.enabled = false;
```

## 흔한 실수 · 확인 목록
- [ ] **damp 를 1 에 가깝게 두면 화면에 얼룩이 영원히 남는다** — 0.97 아래로. 연출이 끝나면 패스를 꺼서 지운다.
- [ ] **카메라가 움직이면 화면 전체가 번진다** — 잔상은 화면 단위라 배경까지 꼬리가 생긴다. 카메라가 멈춘 순간에만 쓴다.
- [ ] **밝은 배경에서 꼬리가 거의 안 보인다** — 이전 화면과 max 로 섞기 때문 — 어두운 배경 · 빛나는 물체일 때 잘 보인다.

## 완성 기준 체크리스트
- [ ] 오른쪽에서 왕복하는 별 · 원을 도는 공 · 달리는 차 뒤에 꼬리가 남고, 왼쪽은 없다
- [ ] 「꼬리 길이」를 0.97 로 올리면 꼬리가 길게, 0.5 로 내리면 거의 사라진다
- [ ] 멈춘 바닥 · 배경은 양쪽이 똑같다

## 이 기술 정보
- id: `i05` · 분류: 그래픽 · 셰이더 › 후처리 · 3D · 난이도 쉬움 · 폰 부담 가벼움 (폰 OK) — 화면 크기 그림 두 장을 번갈아 쓰고 섞기 한 번. 가볍다.
- 라이브 견본 (브라우저에서 직접 조작): https://ai-techstudio.web.app/#t/i05
- 쓰면 좋을 때: 필살 투구 · 탈출 · 미끄러짐처럼 짧은 순간 속도감을 줄 때 / 어두운 배경에서 빛나는 것이 빠르게 움직일 때
- 쓰지 말 때: 평소 놀이 화면에 늘 켜 두기 — 화면이 번져 답답하다. 순간에만 켠다 / 밝은 배경 — max 섞기라 밝은 꼬리만 남아 얼룩진다

## 견본 실제 코드 (라이브 견본이 돌리는 코드 — three.js · TypeScript)
### i05 견본 항목 — `src/demos/demosLight.ts:1926`
```ts
  i05: {
    kind: '3d',
    caption: '왼쪽 그냥 · 오른쪽 Afterimage — 빠르게 미끄러지는 별 · 도는 공 뒤에 꼬리가 남아 속도감',
    make() {
      const scene = new THREE.Scene();
      scene.background = gradTex('#1a2560', '#070a1e');
      basicLights(scene, 0.7, 1.6, V3(-2, 4, 3));
      const ice = mesh(new THREE.BoxGeometry(5, 0.1, 2.6), std(0x9fd6ff, 0.25, 0.1), 0, -0.05, 0);
      scene.add(ice);
      const star = new THREE.Mesh(starGeo(0.34, 0.16, 0.16), new THREE.MeshStandardMaterial({ color: 0xffe066, emissive: 0xffb000, emissiveIntensity: 0.8 }));
      star.position.y = 0.4;
      const ball = mesh(new THREE.SphereGeometry(0.2, 24, 16), new THREE.MeshStandardMaterial({ color: 0xff6bb5, emissive: 0xff2d8a, emissiveIntensity: 0.6 }));
      const car = new THREE.Group();
      car.add(mesh(new RoundedBoxGeometry(0.6, 0.25, 0.35, 2, 0.06), std(0x38e1ff, 0.4), 0, 0.2, 0));
      car.add(mesh(new RoundedBoxGeometry(0.3, 0.18, 0.3, 2, 0.05), std(0xffffff, 0.4), -0.04, 0.4, 0));
      scene.add(star, ball, car);
      const cam = new THREE.PerspectiveCamera(36, 1, 0.1, 30);
      let damp = 0.88;
      const kit = postKit((r) => {
        const after = new AfterimagePass(damp);
        return { comp: splitChain(r, scene, cam, [after]), after };
      });
      const hud = new Hud();
      hud.vline(0.5);
      hud.label('그냥', 0.25, 0.95, 0.5, 1);
      hud.label('잔상', 0.75, 0.95, 0.5, 1, 'on');
      let dt = 0.016;
      return {
        scene,
        camera: cam,
        update(t, d) {
          dt = d;
          const p = (t * 0.55) % 2;
          const e = p < 1 ? p : 2 - p;
          const ease = e * e * (3 - 2 * e);
          star.position.x = -2 + ease * 4;
          star.rotation.z = -star.position.x * 2.2;
          ball.position.set(Math.cos(t * 5) * 1.1, 0.75 + Math.sin(t * 5) * 0.2, Math.sin(t * 5) * 0.6 - 0.3);
          car.position.set(Math.sin(t * 2.6) * 2.0, 0, 0.85);
          car.rotation.y = Math.cos(t * 2.6) > 0 ? 0 : Math.PI;
        },
        render(r, w, h) {
          fit(cam, w / h, V3(0, 0.4, 0), 2.2, V3(0, 0.55, 1));
          const k = kit.get(r, w, h);
          (k.after.uniforms as Record<string, { value: number }>)['damp']!.value = damp;
          k.comp.render(dt);
          hud.draw(r, w, h);
        },
        controls: [{ type: 'range', label: '꼬리 길이', min: 0.5, max: 0.97, step: 0.01, value: 0.88, on: (v) => (damp = v) }],
        dispose() {
          kit.dispose();
          hud.dispose();
          disposeTree(scene);
        },
      };
    },
  }
```

## 관련 기술
- 먼저 알면 좋은 기술: [후처리 사슬 (EffectComposer → OutputPass)](https://ai-techstudio.web.app/ai/t/u21.md) `u21`
- 다음에 해 볼 기술: [색 보정 LUT · 색수차 · 필름 결](https://ai-techstudio.web.app/ai/t/i07.md) `i07`
- 참고 문서: [three.js 예제 — webgl_postprocessing_afterimage](https://threejs.org/examples/#webgl_postprocessing_afterimage)
