first commit
This commit is contained in:
@@ -0,0 +1,625 @@
|
||||
프로젝트명
|
||||
Pet Glass / Pet Behind Glass
|
||||
|
||||
목표
|
||||
사용자가 반려동물 사진 또는 동영상을 업로드하면,
|
||||
Three.js 기반으로 "유리문 뒤에 반려동물이 있는 것처럼 보이는 시점 변화형 왜곡 효과"를 제공하는 웹 MVP를 구현한다.
|
||||
|
||||
중요
|
||||
단순 이미지 필터가 아니라,
|
||||
카메라/시점 이동에 따라 유리 굴절 결과가 동적으로 바뀌어야 한다.
|
||||
|
||||
기술 스택
|
||||
- Vite
|
||||
- TypeScript
|
||||
- Three.js
|
||||
- GLSL Shader
|
||||
- Vanilla TS 기반
|
||||
- React 사용하지 않음
|
||||
- 별도 백엔드 없음
|
||||
- 모든 처리는 브라우저 로컬에서 수행
|
||||
|
||||
핵심 컨셉
|
||||
|
||||
구조:
|
||||
|
||||
PerspectiveCamera
|
||||
↓
|
||||
Glass Plane
|
||||
↓
|
||||
Pet Plane
|
||||
|
||||
Pet Plane에는 사용자가 업로드한 이미지 또는 동영상을 Texture로 적용한다.
|
||||
|
||||
Glass Plane에는 ShaderMaterial을 적용하여
|
||||
유리 종류에 따른 굴절/왜곡을 표현한다.
|
||||
|
||||
카메라 또는 view offset이 움직이면
|
||||
굴절되는 이미지 위치도 같이 변화해야 한다.
|
||||
|
||||
|
||||
========================
|
||||
1. 프로젝트 초기 구성
|
||||
========================
|
||||
|
||||
Vite + vanilla-ts 기반으로 프로젝트 생성.
|
||||
|
||||
필수 패키지:
|
||||
- three
|
||||
|
||||
예상 구조:
|
||||
|
||||
src/
|
||||
├─ main.ts
|
||||
├─ style.css
|
||||
├─ scene/
|
||||
│ ├─ SceneManager.ts
|
||||
│ ├─ CameraController.ts
|
||||
│ └─ RendererManager.ts
|
||||
├─ objects/
|
||||
│ ├─ PetPlane.ts
|
||||
│ └─ GlassPlane.ts
|
||||
├─ shaders/
|
||||
│ ├─ common.vert
|
||||
│ ├─ mosaic.frag
|
||||
│ ├─ reeded.frag
|
||||
│ └─ frosted.frag
|
||||
├─ input/
|
||||
│ ├─ MediaLoader.ts
|
||||
│ ├─ PointerController.ts
|
||||
│ └─ GyroController.ts
|
||||
├─ types/
|
||||
│ └─ glass.ts
|
||||
└─ ui/
|
||||
└─ controls.ts
|
||||
|
||||
|
||||
========================
|
||||
2. 화면 구성
|
||||
========================
|
||||
|
||||
단일 페이지로 구현.
|
||||
|
||||
화면은 크게 두 영역:
|
||||
|
||||
1. 상단/사이드 컨트롤 영역
|
||||
2. Three.js Canvas 영역
|
||||
|
||||
초기 UI:
|
||||
|
||||
- 사진 업로드 버튼
|
||||
- 동영상 업로드 버튼
|
||||
- 유리 효과 선택
|
||||
- Mosaic
|
||||
- Reeded
|
||||
- Frosted
|
||||
- Distortion Strength 슬라이더
|
||||
- View Sensitivity 슬라이더
|
||||
- Reset View 버튼
|
||||
- Gyroscope 사용 버튼
|
||||
- 현재 효과명 표시
|
||||
|
||||
모바일 우선 반응형으로 구현.
|
||||
|
||||
|
||||
========================
|
||||
3. 미디어 업로드
|
||||
========================
|
||||
|
||||
이미지:
|
||||
- jpg
|
||||
- jpeg
|
||||
- png
|
||||
- webp
|
||||
|
||||
동영상:
|
||||
- mp4
|
||||
- webm
|
||||
- 브라우저에서 재생 가능한 파일
|
||||
|
||||
이미지 업로드 시:
|
||||
THREE.Texture 생성
|
||||
|
||||
동영상 업로드 시:
|
||||
HTMLVideoElement 생성 후
|
||||
THREE.VideoTexture 사용
|
||||
|
||||
새 파일을 업로드하면
|
||||
기존 Texture 및 Object URL을 적절히 dispose/revoke 처리할 것.
|
||||
|
||||
업로드한 미디어의 aspect ratio를 유지해야 한다.
|
||||
|
||||
예:
|
||||
세로 사진이면 세로 비율 유지
|
||||
가로 사진이면 가로 비율 유지
|
||||
|
||||
Canvas 안에서 최대한 크게 보이되 잘리지 않도록 contain 방식으로 배치.
|
||||
|
||||
|
||||
========================
|
||||
4. Three.js Scene 구성
|
||||
========================
|
||||
|
||||
PerspectiveCamera 사용.
|
||||
|
||||
기본 구성:
|
||||
|
||||
Scene
|
||||
- Camera
|
||||
- PetPlane
|
||||
- GlassPlane
|
||||
|
||||
PetPlane:
|
||||
- PlaneGeometry
|
||||
- 업로드된 Texture 적용
|
||||
- 기본 위치 z = 0
|
||||
|
||||
GlassPlane:
|
||||
- PlaneGeometry
|
||||
- PetPlane보다 카메라 방향으로 조금 앞
|
||||
- 예: z = 0.15 ~ 0.3
|
||||
- ShaderMaterial 사용
|
||||
|
||||
Camera:
|
||||
- 기본 정면 위치
|
||||
- lookAt(0, 0, 0)
|
||||
|
||||
예:
|
||||
camera.position.set(0, 0, 3)
|
||||
|
||||
|
||||
========================
|
||||
5. 시점 이동
|
||||
========================
|
||||
|
||||
마우스 또는 터치 drag로 시점을 이동할 수 있어야 한다.
|
||||
|
||||
중요:
|
||||
단순히 Canvas를 좌우 이동하는 것이 아니라
|
||||
실제 카메라 또는 shader의 view offset이 변해야 한다.
|
||||
|
||||
PointerController 구현.
|
||||
|
||||
입력값을 정규화:
|
||||
|
||||
x: -1 ~ 1
|
||||
y: -1 ~ 1
|
||||
|
||||
카메라 이동 예:
|
||||
|
||||
camera.position.x = normalizedX * maxOffsetX
|
||||
camera.position.y = normalizedY * maxOffsetY
|
||||
|
||||
항상:
|
||||
camera.lookAt(0, 0, 0)
|
||||
|
||||
단,
|
||||
카메라 이동만으로 효과가 약한 경우
|
||||
shader uniform으로도 view offset을 넘길 것.
|
||||
|
||||
예:
|
||||
|
||||
uViewOffset: vec2
|
||||
|
||||
normalized pointer 값을
|
||||
ShaderMaterial uniform으로 전달.
|
||||
|
||||
|
||||
========================
|
||||
6. Mosaic Glass 구현
|
||||
========================
|
||||
|
||||
가장 먼저 완성할 핵심 효과.
|
||||
|
||||
사각형 모자이크 유리 구현.
|
||||
|
||||
요구사항:
|
||||
|
||||
- 전체 UV를 NxN grid로 나눈다.
|
||||
- 각 셀은 작은 볼록/오목 렌즈처럼 동작한다.
|
||||
- 셀 내부 위치에 따라 source texture의 UV가 변형된다.
|
||||
- 카메라/view offset에 따라 sampling 위치가 바뀐다.
|
||||
- 셀마다 약간씩 다른 distortion을 넣을 수 있다.
|
||||
- 셀 경계가 살짝 보이면 좋다.
|
||||
|
||||
필수 uniform:
|
||||
|
||||
uTexture
|
||||
uGridSize
|
||||
uStrength
|
||||
uViewOffset
|
||||
uResolution
|
||||
uTime
|
||||
|
||||
개념:
|
||||
|
||||
vec2 scaled = vUv * uGridSize;
|
||||
vec2 cell = floor(scaled);
|
||||
vec2 local = fract(scaled);
|
||||
|
||||
local 좌표를 중심 기준:
|
||||
|
||||
vec2 centered = local - 0.5;
|
||||
|
||||
이를 이용해 렌즈 형태 distortion 적용.
|
||||
|
||||
그 후
|
||||
uViewOffset에 따라 UV sampling 위치를 추가로 이동.
|
||||
|
||||
중요:
|
||||
시점을 좌우로 움직였을 때
|
||||
강아지의 눈/코/얼굴 부분이 셀 사이에서 서로 다른 위치로 이동해
|
||||
실제 모자이크 유리를 옆에서 보는 느낌이 나야 한다.
|
||||
|
||||
단순 pixelation처럼 보이면 안 된다.
|
||||
|
||||
|
||||
========================
|
||||
7. Reeded Glass 구현
|
||||
========================
|
||||
|
||||
세로 골유리 효과.
|
||||
|
||||
유리를 세로 방향으로 반복되는 원통형 렌즈 배열처럼 표현.
|
||||
|
||||
특징:
|
||||
|
||||
- Y 방향 변화 거의 없음
|
||||
- X 방향 distortion 중심
|
||||
- 일정 간격의 vertical ridge
|
||||
- 카메라가 좌우 이동하면 ridge마다 보이는 이미지 위치가 달라짐
|
||||
|
||||
sin 또는 반복 함수 사용 가능.
|
||||
|
||||
예시 개념:
|
||||
|
||||
float ridge = sin(vUv.x * frequency);
|
||||
|
||||
uv.x += ridge * strength;
|
||||
|
||||
여기에
|
||||
uViewOffset.x를 반영하여
|
||||
시점 변화가 눈에 띄게 구현.
|
||||
|
||||
|
||||
========================
|
||||
8. Frosted Glass 구현
|
||||
========================
|
||||
|
||||
반투명 서리유리 느낌.
|
||||
|
||||
요구사항:
|
||||
|
||||
- 기본 blur
|
||||
- UV noise distortion
|
||||
- 이미지 형태는 대략 보이지만 디테일은 흐려짐
|
||||
- 시점 이동 시 noise sampling 또는 refraction 방향이 조금 변함
|
||||
|
||||
성능을 위해
|
||||
무거운 multi-pass blur는 MVP에서는 피할 수 있음.
|
||||
|
||||
간단한 multi-sample 방식 사용 가능.
|
||||
|
||||
예:
|
||||
주변 4~9개 texture sample을 평균.
|
||||
|
||||
|
||||
========================
|
||||
9. 효과 타입 관리
|
||||
========================
|
||||
|
||||
TypeScript 타입 정의:
|
||||
|
||||
type GlassType =
|
||||
| 'mosaic'
|
||||
| 'reeded'
|
||||
| 'frosted';
|
||||
|
||||
interface GlassSettings {
|
||||
type: GlassType;
|
||||
strength: number;
|
||||
gridSize: number;
|
||||
sensitivity: number;
|
||||
}
|
||||
|
||||
GlassPlane에서 현재 효과를 변경 가능하도록 구현.
|
||||
|
||||
방법은 자유:
|
||||
- material 교체
|
||||
또는
|
||||
- ShaderMaterial fragment shader 교체
|
||||
|
||||
가능하면 구조적으로 분리.
|
||||
|
||||
|
||||
========================
|
||||
10. Gyroscope
|
||||
========================
|
||||
|
||||
모바일 환경에서 DeviceOrientation 사용.
|
||||
|
||||
사용자가 "Gyroscope 사용" 버튼을 누른 뒤
|
||||
권한 요청.
|
||||
|
||||
iOS 등 권한 요청이 필요한 환경 고려.
|
||||
|
||||
기기의 좌우/상하 tilt 값을
|
||||
|
||||
-1 ~ 1
|
||||
|
||||
범위로 normalization하고
|
||||
PointerController와 동일한 view offset 시스템에 연결.
|
||||
|
||||
즉:
|
||||
|
||||
손가락 드래그
|
||||
또는
|
||||
휴대폰 기울이기
|
||||
|
||||
둘 다 같은 ViewController 상태를 변경하도록 만들 것.
|
||||
|
||||
기본값은 gyro OFF.
|
||||
|
||||
|
||||
========================
|
||||
11. 애니메이션 루프
|
||||
========================
|
||||
|
||||
requestAnimationFrame 사용.
|
||||
|
||||
매 프레임:
|
||||
|
||||
- input state 갱신
|
||||
- camera interpolation
|
||||
- shader uniform 갱신
|
||||
- video texture update
|
||||
- renderer.render()
|
||||
|
||||
카메라 움직임은 바로 jump시키지 말고
|
||||
lerp를 적용해서 부드럽게 움직일 것.
|
||||
|
||||
예:
|
||||
|
||||
currentX += (targetX - currentX) * 0.08
|
||||
|
||||
|
||||
========================
|
||||
12. 모바일 UX
|
||||
========================
|
||||
|
||||
모바일에서 가장 중요.
|
||||
|
||||
Canvas:
|
||||
- 터치 드래그 가능
|
||||
- 페이지 스크롤과 충돌하지 않도록 처리
|
||||
|
||||
화면 중앙에 처음 약 2초간 안내:
|
||||
|
||||
"화면을 좌우로 움직여보세요"
|
||||
|
||||
또는
|
||||
|
||||
"휴대폰을 기울여보세요"
|
||||
|
||||
미디어 업로드 전에는
|
||||
placeholder 화면 표시:
|
||||
|
||||
"반려동물 사진 또는 영상을 선택하세요"
|
||||
|
||||
|
||||
========================
|
||||
13. 기본 샘플
|
||||
========================
|
||||
|
||||
사용자가 아무 파일도 업로드하지 않아도 테스트 가능하도록
|
||||
assets/sample-pet.* 형태의 샘플 이미지를 둘 수 있는 구조를 만들어둘 것.
|
||||
|
||||
실제 파일이 없다면
|
||||
fallback placeholder로 구현해도 됨.
|
||||
|
||||
샘플을 코드에 강하게 의존시키지는 말 것.
|
||||
|
||||
|
||||
========================
|
||||
14. 성능 요구
|
||||
========================
|
||||
|
||||
모바일 웹 기준.
|
||||
|
||||
목표:
|
||||
60fps에 최대한 근접.
|
||||
|
||||
주의:
|
||||
|
||||
- 셀마다 실제 Mesh 생성 금지
|
||||
- Mosaic는 반드시 shader 기반
|
||||
- 수백 개 Plane 생성 금지
|
||||
- texture 재생성 최소화
|
||||
- resize 시 renderer.setSize 처리
|
||||
- devicePixelRatio 제한
|
||||
|
||||
예:
|
||||
|
||||
renderer.setPixelRatio(
|
||||
Math.min(window.devicePixelRatio, 2)
|
||||
);
|
||||
|
||||
|
||||
========================
|
||||
15. 리소스 정리
|
||||
========================
|
||||
|
||||
다음 경우 dispose 처리:
|
||||
|
||||
- 새 이미지 업로드
|
||||
- 새 동영상 업로드
|
||||
- material 교체
|
||||
- geometry 교체
|
||||
- component/scene 제거
|
||||
|
||||
Three.js:
|
||||
texture.dispose()
|
||||
material.dispose()
|
||||
geometry.dispose()
|
||||
|
||||
Object URL:
|
||||
URL.revokeObjectURL()
|
||||
|
||||
|
||||
========================
|
||||
16. UI 스타일
|
||||
========================
|
||||
|
||||
디자인은 MVP 수준으로 간단하게.
|
||||
|
||||
스타일 방향:
|
||||
- Dark UI
|
||||
- Canvas가 가장 큰 비중
|
||||
- 컨트롤은 반투명 panel
|
||||
- 모바일에서는 하단 control sheet처럼 배치 가능
|
||||
|
||||
과한 디자인 작업 하지 말 것.
|
||||
|
||||
기능 검증이 우선.
|
||||
|
||||
|
||||
========================
|
||||
17. 중요 구현 원칙
|
||||
========================
|
||||
|
||||
이 프로젝트의 핵심은 아래이다.
|
||||
|
||||
X:
|
||||
사진에 단순 pixelation 필터 적용
|
||||
|
||||
O:
|
||||
유리 plane과 반려동물 plane 사이에 공간이 있고,
|
||||
사용자의 시점 변화에 따라
|
||||
유리를 통해 보이는 반려동물의 위치와 형태가 달라지는 것.
|
||||
|
||||
따라서 Mosaic 효과 구현 시
|
||||
정면에서 좌우로 드래그했을 때
|
||||
결과 이미지가 확실히 변화해야 한다.
|
||||
|
||||
단순하게 전체 이미지를 translate하는 방식도 안 된다.
|
||||
|
||||
각 유리 cell/ridge마다
|
||||
서로 다른 refraction 느낌이 보여야 한다.
|
||||
|
||||
|
||||
========================
|
||||
18. 1차 완료 기준
|
||||
========================
|
||||
|
||||
아래 조건 만족 시 MVP 1차 완료.
|
||||
|
||||
1.
|
||||
npm install
|
||||
npm run dev
|
||||
로 실행 가능
|
||||
|
||||
2.
|
||||
사진 업로드 가능
|
||||
|
||||
3.
|
||||
동영상 업로드 가능
|
||||
|
||||
4.
|
||||
업로드한 미디어가 PetPlane에 표시됨
|
||||
|
||||
5.
|
||||
Mosaic Glass 적용 가능
|
||||
|
||||
6.
|
||||
마우스/터치 좌우 drag 시
|
||||
모자이크 굴절 결과가 동적으로 변화함
|
||||
|
||||
7.
|
||||
Reeded Glass 적용 가능
|
||||
|
||||
8.
|
||||
Frosted Glass 적용 가능
|
||||
|
||||
9.
|
||||
Strength 조절 가능
|
||||
|
||||
10.
|
||||
모바일 viewport 정상 대응
|
||||
|
||||
11.
|
||||
TypeScript 오류 없음
|
||||
|
||||
12.
|
||||
콘솔에 지속적인 에러 없음
|
||||
|
||||
|
||||
========================
|
||||
19. 구현 우선순위
|
||||
========================
|
||||
|
||||
아래 순서를 반드시 우선할 것.
|
||||
|
||||
1.
|
||||
Three.js Scene
|
||||
|
||||
2.
|
||||
이미지 Texture
|
||||
|
||||
3.
|
||||
PetPlane
|
||||
|
||||
4.
|
||||
GlassPlane
|
||||
|
||||
5.
|
||||
Pointer drag
|
||||
|
||||
6.
|
||||
Mosaic Shader
|
||||
|
||||
7.
|
||||
시점에 따른 refraction
|
||||
|
||||
8.
|
||||
VideoTexture
|
||||
|
||||
9.
|
||||
Reeded Shader
|
||||
|
||||
10.
|
||||
Frosted Shader
|
||||
|
||||
11.
|
||||
Gyroscope
|
||||
|
||||
12.
|
||||
UI polish
|
||||
|
||||
|
||||
========================
|
||||
20. 작업 방식
|
||||
========================
|
||||
|
||||
먼저 현재 프로젝트 파일 구조를 확인한다.
|
||||
|
||||
이미 코드가 있다면
|
||||
기존 구조를 최대한 유지하면서 구현한다.
|
||||
|
||||
필요 이상으로 추상화하지 않는다.
|
||||
|
||||
처음부터 완벽한 아키텍처를 만들기보다
|
||||
실제로 동작하는 shader prototype을 우선한다.
|
||||
|
||||
특히 Mosaic Shader를 가장 먼저 눈으로 확인 가능한 수준까지 구현할 것.
|
||||
|
||||
각 주요 단계 완료 후
|
||||
어떤 파일을 생성/수정했는지 간단히 정리한다.
|
||||
|
||||
작업 완료 후 아래도 보고한다.
|
||||
|
||||
- 생성/수정 파일 목록
|
||||
- 구현된 기능
|
||||
- 아직 구현하지 않은 기능
|
||||
- 실행 방법
|
||||
- 주요 shader 동작 원리
|
||||
- 다음 개선 포인트
|
||||
Reference in New Issue
Block a user