Company
교육 철학

카메라(Camera)

개요

Chapter 10에서는 카메라를 “고정된 위치”에서 벗어나, 임의의 위치(lookfrom)바라보는 지점(lookat) 을 지정해 자유롭게 배치할 수 있도록 확장한다.
또한 카메라의 기울기를 결정하는 월드 업 벡터(vup)수직 시야각(vfov) 을 통해 줌인/광각을 제어한다.
핵심 목표는 다음 3가지를 깔끔하게 정리하는 것이다.
카메라 로컬 좌표계(정규직교 기저) 만들기
vfov와 종횡비(aspect)로 뷰포트 크기 만들기
뷰포트 좌표 (s, t)에서 레이를 생성하기

카메라 파라미터

lookfrom: 카메라 위치 (눈의 위치)
lookat: 카메라가 바라보는 지점
vup: 월드 업 벡터. 보통 (0, 1, 0)
vfov: 수직 시야각(degrees). 작을수록 줌인, 클수록 광각
aspect: 종횡비 = imageWidth / imageHeight

카메라 좌표계 만들기 (정규직교 기저 u, v, w)

lookfromlookat을 이용해 카메라 기준 축을 만든다.
$mathbf{w}$: 카메라의 “뒤쪽” 방향 (시선 반대)
$mathbf{u}$: 카메라의 “오른쪽” 방향
$mathbf{v}$: 카메라의 “위쪽” 방향
w=unit(lookfromlookat)\mathbf{w} = \mathrm{unit}(\mathbf{lookfrom} - \mathbf{lookat})
u=unit(vup×w)\mathbf{u} = \mathrm{unit}(\mathbf{vup} \times \mathbf{w})
v=w×u\mathbf{v} = \mathbf{w} \times \mathbf{u}
이렇게 만들어진 $mathbf{u}, mathbf{v}, mathbf{w}$는 서로 직교하고 길이가 1인 기저가 된다.

뷰포트 크기 (vfov → height/width)

수직 시야각 vfov로 뷰포트의 높이를 결정한다.
θ=vfovπ/180\theta = \mathrm{vfov} \cdot \pi / 180
h=tan(θ/2)h = \tan(\theta/2)
여기서 $h$는 “뷰포트의 반높이(half height)”로 보면 된다.
halfHeight = hh
halfWidth = aspect · hh

뷰포트의 3개 벡터 (origin / horizontal / vertical)

카메라 위치를 원점(origin)으로 두면,
horizontal: 뷰포트 가로 방향 벡터
vertical: 뷰포트 세로 방향 벡터
lowerLeftCorner: 뷰포트의 좌하단 지점
을 만들 수 있다.
horizontal=2halfWidthu\mathbf{horizontal} = 2 \cdot \mathrm{halfWidth} \cdot \mathbf{u}
vertical=2halfHeightv\mathbf{vertical} = 2 \cdot \mathrm{halfHeight} \cdot \mathbf{v}
lowerLeft=originhalfWidthuhalfHeightvw\mathbf{lowerLeft} = \mathbf{origin} - \mathrm{halfWidth}\,\mathbf{u} - \mathrm{halfHeight}\,\mathbf{v} - \mathbf{w}
책 구현에서는 focal length를 1로 두는 셈이라, -w가 “카메라에서 뷰포트까지 거리” 역할을 한다.

그림으로 이해하기 (카메라/뷰포트/레이)

flowchart TB
	O["origin = lookfrom"] --> W["-w 방향으로 뷰포트"]
	W --> LL["lowerLeftCorner"]
	LL --> H["horizontal"]
	LL --> V["vertical"]
	O --> R["ray(s,t) = origin -> lowerLeft + s*horizontal + t*vertical"]
Mermaid
복사

C++ 코드 (Camera 핵심)

프로젝트에 따라 vec3 이름이나 ray 구현이 다르겠지만, 핵심은 동일하다.
class camera { public: camera( const point3& lookfrom, const point3& lookat, const vec3& vup, double vfov_degrees, double aspect ) { origin = lookfrom; auto theta = degrees_to_radians(vfov_degrees); auto halfHeight = tan(theta / 2); auto halfWidth = aspect * halfHeight; w = unit_vector(lookfrom - lookat); u = unit_vector(cross(vup, w)); v = cross(w, u); lowerLeftCorner = origin - halfWidth * u - halfHeight * v - w; horizontal = 2 * halfWidth * u; vertical = 2 * halfHeight * v; } ray get_ray(double s, double t) const { return ray(origin, lowerLeftCorner + s*horizontal + t*vertical - origin); } private: point3 origin; point3 lowerLeftCorner; vec3 horizontal; vec3 vertical; vec3 u, v, w; };
C++
복사

변경 파일 요약

기존 기본 생성자는 그대로 두고(하위 호환)
자유 시점 생성자 추가
lookfrom, lookat, vup, vfov, aspect
내부에 u, v, w 정규직교 기저 저장
GetRay(s, t)에서 뷰포트 좌표로 레이 생성
CreateWorld(imageWidth, imageHeight)처럼 해상도를 전달해
aspect = imageWidth / imageHeight를 GPU에서 계산 가능
카메라 생성 예시
lookfrom = (-2, 2, 1)
lookat = (0, 0, -1)
vup = (0, 1, 0)
vfov = 20°

GPU 구현 포인트

vfov → tan(theta/2)로 뷰포트 반높이를 만든 뒤, aspect로 반너비를 만든다.
cross(vup, w)cross(w, u)로 안정적으로 카메라 로컬 좌표계를 만든다.
CUDA에서 M_PI가 없을 수 있으니, pi = 3.1415926535897932385처럼 직접 정의하는 편이 안전하다.