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




