Company
교육 철학

Add CUDA-Ray클래스 개선하기

df9278f252c398b6bed912992b81ce4182a01ed0
commit

개요

Ray Tracing in One Weekend 3장(Ray)에서 사용하는 Ray 클래스를 CUDA 디바이스 코드에서 생성/사용할 수 있도록 포팅하고, 렌더 커널에서 레이를 생성해 하늘색(white → sky-blue) 배경 그라디언트를 렌더링하도록 변경한 작업을 정리한다.
이번 단계의 핵심은 아래 두 가지다.
Ray의 멤버 함수가 커널에서 호출 가능하도록 __device__(또는 필요 시 __host__ __device__) 지정자 적용
커널에서 픽셀별로 Ray를 만들고 RayColor()방향 기반 배경색을 계산

변경 파일 목록

파일
변경 유형
설명
Ray.h
수정
Ray 생성자/접근자에 CUDA 함수 지정자 추가
수정
Ray 기반 하늘 배경 렌더링, 카메라(뷰포트) 파라미터 전달

1. Ray.h: CUDA 디바이스에서 사용 가능하게 만들기

1.1 변경 목적

Ray는 렌더 커널에서 픽셀마다 생성되며, origin, direction, 그리고 at(t) 같은 접근/계산 함수들이 디바이스 코드에서 호출된다.
따라서 최소한 __device__ 지정자가 필요하다.
Vec3는 CPU 코드(호스트)와 GPU 코드(디바이스)에서 모두 쓰이므로 보통 __host__ __device__가 적합하다.
Ray는 현재 단계에서는 커널 내부에서만 쓰도록 설계했기 때문에 __device__만으로도 충분하다.

1.2 주요 변경 사항(C++/CUDA C++)

// Ray.h (예시) #pragma once #include "Vec3.h" class Ray { public: __device__ Ray() {} __device__ Ray(const Point3& origin, const Vec3& direction) : orig(origin), dir(direction) {} __device__ const Point3& origin() const { return orig; } __device__ const Vec3& direction() const { return dir; } // P(t) = A + tB __device__ Point3 at(double t) const { return orig + t * dir; } private: Point3 orig; Vec3 dir; };
C++
복사

1.3 부연 설명: __device__만 붙이면 생길 수 있는 포인트

만약 호스트 코드에서도 Ray를 생성하거나(예: CPU 레퍼런스 구현, 디버그 출력) 단위 테스트를 하려면,
__device__ 대신 __host__ __device__로 바꾸는 편이 유지보수에 유리하다.
다만 이번 단계처럼 “GPU 전용 구조로 단순하게 진행”하면 __device__만으로 컴파일/의도가 명확하다.

2. kernel.cu: Ray 기반 하늘 배경 렌더링

2.1 변경 목적

2장에서는 단순히 (i/maxX, j/maxY, 0.2) 형태의 수학적 그라디언트를 출력했다.
3장에서는 픽셀마다 레이를 실제로 생성하고, 레이 방향에 따라 하늘색 배경을 만들어 “레이 트레이서다운 구조”로 넘어간다.

2.2 주요 변경 사항

레이 방향 벡터를 단위화한 뒤, Y 성분을 이용해 흰색과 하늘색을 선형 보간한다.
// kernel.cu (예시) __device__ Color RayColor(const Ray& r) { Vec3 unitDirection = UnitVector(r.direction()); double t = 0.5 * (unitDirection.y() + 1.0); // 아래(흰색) -> 위(하늘색) 보간 return (1.0 - t) * Color(1.0, 1.0, 1.0) + t * Color(0.5, 0.7, 1.0); }
C++
복사
t = 0이면 흰색(레이가 아래를 향하는 방향)
t = 1이면 하늘색(레이가 위를 향하는 방향)
현재 단계에서는 “카메라” 클래스를 따로 만들기 전이므로, 뷰포트 구성 요소를 커널 파라미터로 직접 전달한다.
__global__ void render(Color* frameBuffer, int maxX, int maxY, Vec3 lowerLeftCorner, Vec3 horizontal, Vec3 vertical, Vec3 origin) { int i = blockIdx.x * blockDim.x + threadIdx.x; int j = blockIdx.y * blockDim.y + threadIdx.y; if (i >= maxX || j >= maxY) return; int pixelIndex = j * maxX + i; double u = double(i) / double(maxX); double v = double(j) / double(maxY); // 주의: direction은 "화면상의 점 - origin" 형태가 보통 더 명확하다. Vec3 direction = lowerLeftCorner + u * horizontal + v * vertical - origin; Ray r(origin, direction); frameBuffer[pixelIndex] = RayColor(r); }
C++
복사
원본 요약 글에서는 Ray r(origin, lowerLeftCorner + u*horizontal + v*vertical); 형태로 되어 있었는데,
일반적으로는 direction = (화면 점) - origin 형태로 쓰는 것이 의도가 더 명확하다.
현재 origin이 (0,0,0)이라면 두 표현이 결과적으로 같을 수 있지만, origin이 바뀌면 차이가 생긴다.

3. 카메라(뷰포트) 설정값

파라미터
설명
origin
(0, 0, 0)
카메라 위치
lowerLeftCorner
(-2, -1, -1)
뷰포트 좌하단
horizontal
(4, 0, 0)
뷰포트 가로 방향
vertical
(0, 2, 0)
뷰포트 세로 방향

렌더링 결과(현재 단계)

해상도: 1440 × 720
출력: 흰색(하단) → 하늘색(상단) 그라디언트 배경
출력 파일: output.ppm