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 주요 변경 사항

(1) RayColor() 함수 추가
레이 방향 벡터를 단위화한 뒤, 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이면 하늘색(레이가 위를 향하는 방향)
(2) 커널에서 카메라(뷰포트) 파라미터로 Ray 생성
현재 단계에서는 “카메라” 클래스를 따로 만들기 전이므로, 뷰포트 구성 요소를 커널 파라미터로 직접 전달한다.
__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