
# 3D 투영 공식 시각화
## 1. 3D 포인트 투영 기본 공식
* **개념**: 3D 공간상의 점(X, Y, Z)을 2D 화면에 투영하는 간단한 수학 공식
* **계산**:
* 화면 상의 X 좌표: `X / Z`
* 화면 상의 Y 좌표: `Y / Z`
* **결과**: Z 값이 동일한 여러 점을 애니메이션하고 회전시키면 3D 장면처럼 보임
## 2. 웹 기술을 이용한 구현
### 2.1. HTML 구조
```html
```
### 2.2. JavaScript (index.js)
* **Canvas 접근**: ID를 사용하여 `canvas` 엘리먼트 직접 접근
* `let canvas = game;` (HTML 엘리먼트 ID가 유효한 JavaScript 변수명일 경우)
* **Canvas 크기 설정**:
* `canvas.width = 800;`
* `canvas.height = 800;`
* **2D 렌더링 컨텍스트 확보**:
* `let ctx = canvas.getContext('2d');`
* **기본 도형 그리기**:
* `ctx.fillStyle = 'green';` (채우기 색상 설정)
* `ctx.fillRect(0, 0, 100, 100);` (사각형 그리기)
* **배경 채우기**:
* `ctx.fillStyle = 'background';`
* `ctx.fillRect(0, 0, canvas.width, canvas.height);`
* **상수 정의**:
* `const background = '#222';`
* `const foreground = '#fff';`
* **화면 지우기 함수**:
```javascript
function clear() {
ctx.fillStyle = background;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
```
* **점(Point) 그리기 함수**:
```javascript
function point(x, y, size, color) {
ctx.fillStyle = color;
ctx.fillRect(x - size / 2, y - size / 2, size, size); // 중앙 정렬
}
```
* `s = 20;` (점의 크기)
### 2.3. 좌표계 변환
* **3D 디스플레이 좌표계**:
* `0,0`이 **중앙**
* X축: 왼쪽 `-1` ~ 오른쪽 `1`
* Y축: 위쪽 `1` ~ 아래쪽 `-1`
* **HTML Canvas 좌표계**:
* `0,0`이 **좌측 상단**
* X축: `0` ~ `width`
* Y축: `0` ~ `height`
* **좌표 변환 함수 (`screen`)**: 3D 좌표계를 Canvas 좌표계로 변환
```javascript
function screen(p) {
let x = p.x;
let y = p.y;
// x: [-1, 1] -> [0, 2] -> [0, 1] -> [0, width]
x = (x + 1) / 2 * canvas.width;
// y: [-1, 1] -> [0, 2] -> [0, 1] -> [0, height]
y = (y + 1) / 2 * canvas.height;
return { x, y };
}
```
* **Canvas 좌표계 변환 시 Y축 뒤집기**:
* `y = canvas.height - y;` 또는 `y = 1 - y;` (정규화된 Y 값 사용 시)
## 3. 3D 투영 구현
### 3.1. 투영 함수 (`project`)
* 3D 점을 2D 화면 좌표로 변환
```javascript
function project(p) {
const x = p.x / p.z;
const y = p.y / p.z;
return { x, y };
}
```
* **주의**: `z` 값이 0이면 **division by zero** 오류 발생. Z값은 0보다 커야 함.
### 3.2. 애니메이션
* **프레임 함수**:
```javascript
function frame(dt) {
// ... (점들의 Z 값 업데이트 등)
// ... (점 렌더링)
requestAnimationFrame(frame); // 다음 프레임 예약
}
```
* **Z축 이동**: `zOffset` 증가
* **델타 타임 (`dt`)**: `1000 / FPS` (FPS: 초당 프레임 수)
### 3.3. 회전 함수 (`rotate`)
* Y축 기준 회전 (XZ 평면 회전)
```javascript
function rotate(p, angle) {
const c = Math.cos(angle);
const s = Math.sin(angle);
const x = p.x * c - p.z * s;
const z = p.z * c + p.x * s;
return { x, y: p.y, z };
}
```
## 4. 큐브 렌더링
### 4.1. 버텍스 (Vertices)
* 3D 공간상의 점들의 배열 (`vs`)
### 4.2. 면 (Faces)
* 버텍스 인덱스를 이용해 면을 정의하는 배열 (`fs`)
* 각 면은 폴리곤을 형성하는 버텍스 인덱스의 배열
### 4.3. 선 그리기 함수 (`line`)
```javascript
function line(p1, p2) {
ctx.beginPath();
ctx.moveTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.stroke();
}
```
### 4.4. 렌더링 로직
1. **각 버텍스**에 대해:
* `translateZ` 함수를 사용하여 Z축 이동 적용
* `rotate` 함수를 사용하여 회전 적용
* `project` 함수를 사용하여 2D로 투영
* `screen` 함수를 사용하여 Canvas 좌표계로 변환
* `point` 함수로 화면에 그리기
2. **각 면**에 대해:
* 면을 구성하는 버텍스들을 순회하며 `line` 함수로 연결
### 4.5. 와이어프레임 큐브
* 버텍스 렌더링을 제거하고 면의 선만 그림
## 5. 투영 공식의 원리 (기하학적 해석)
* **유사 삼각형**:
* `Eye` (0,0)에서 `3D Point (X, Y, Z)`를 거쳐 `Screen (X', Y')`에 도달하는 광선
* `Eye`에서 `Screen`까지의 거리 (1)와 `Eye`에서 `3D Point`까지의 Z 거리 (`Z`)
* `Screen`상의 X' 좌표와 `3D Point`의 X 좌표
* 두 삼각형의 닮음비를 이용: `1 / Z = X' / X` => `X' = X / Z`
* 두 삼각형의 닮음비를 이용: `1 / Z = Y' / Y` => `Y' = Y / Z`
## 6. 활용
* OpenGL, WebGL, WebGPU 없이 2D HTML Canvas와 간단한 공식만으로 복잡한 3D 모델 렌더링 가능