[part 1] c++로 Raytracing 만들기 세팅 방법

김핑궁·2023년 8월 10일

RayTracing

목록 보기
1/1
post-thumbnail

해당 포스팅은 "RayTracingInOneWeek"을 보고 학습한 내용입니다.

MAC OS M1 버전입니당.
윈도우는 저도 몰라요 ^_^

0. 개요


광선 추적 기법(RayTracing) 이란??

컴퓨터에서 물체를 렌더링할 때, 빛을 구현하는 방법에는 여러 가지 방법이 있다.
그 중에서 Ray Tracing은 "광원(Ray)"으로부터 시작하여 물체에 반사되어 "스크린"에 도달하는 빛을 역추적(Trace)하여 계산하는 방식이다.
즉, 계산량이 엄청 엄청 많다...
(참고로 엔비디아의 유명한 그래픽 카드인 "RTX XXXX" 에서 RT가 raytracing의 약자라고 한다. 첨 알았당..)

어쨌든 이 RayTracing 기법을 c++로 구현을 해보려고 한다.!

1. 이미지 PPM을 만들어 보자!

1.1 ppm 이미지 형식


ppm이란 ? "portable pixmap"의 약자이다.
3개의 Header 정보와 pixel 정보를 입력하면 이미지를 생성해준다!

ppm은 3개의 header 정보를 가진다.

  1. P3: 매직 넘버 (standard 값으로 P3는 ASCII 코드 값을 의미한다.)
  2. 256 256: 이미지의 가로, 세로 사이즈
  3. 255: pixel의 max값 (몇bit 사이즈를 만들것인지 정해준다.)

pixel 값은 R,G,B 값 (0~255)을 정해주면 된다.

따라서 이 ppm을 만드는 코드를 작성해보자.

그러나! 예제를 보니 cmake로 빌드 파일을 만들어 작업하셨다고 한다.
똑같이 따라해보자..!

먼저, homebrew를 통해 cmake를 설치해준다.

> brew install cmake

설치가 완료되면, 설치가 잘 되었는지 version을 확인해준다.

> cmake --version

이제, 본격적으로 코드를 작성해보자.
vscode를 사용하여 c++ 파일을 작성하였다.
(포스팅한 교수님은 확장자를 cc를 사용하셨지만, 찾아보니 Unix/Linux 에선 cc 확장자를 사용하는게 좋고, Xcode나 윈도우는 cpp 파일을 작성하는게 좋다고 한다. 그래서 걍 익숙한 cpp로 만들었다)

#include <iostream>
using namespace std;

int main() {

    // Image
    
    int image_width = 256;
    int image_height = 256;

    // Render

    cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";

    for (int j = 0; j < image_height; ++j) {
        for (int i = 0; i < image_width; ++i) {
            auto r = double(i) / (image_width-1);
            auto g = double(j) / (image_height-1);
            auto b = 0;

            int ir = static_cast<int>(255.999 * r);
            int ig = static_cast<int>(255.999 * g);
            int ib = static_cast<int>(255.999 * b);

            cout << ir << ' ' << ig << ' ' << ib << '\n';
        }
    }
}

ppm 작성 코드는 몇가지 주의점이 있다.

  1. 픽셀은 행으로 기록된다.
  2. 진행 방향은 왼쪽 -> 오른쪽 / 위 -> 아래 순이다.
  3. R/G/B 는 내부적으로 0~1 사이 값으로 기록된다.
  4. 예를 들어, 0 0 0 은 검은색, 1 1 1 은 흰색이다.

이 코드로 한번 실행을 시켜보자.

위와 같이 출력되면 된거다.

이 코드를 ppm 이미지 파일로 만들어 보자.

1.2 이미지 파일 생성


c++ 파일과 같은 경로에 CMakeLists.txt 파일을 만들어 줬다.
그리고 build 폴더도 만들어 줬다.

CMakeLists.txt 파일은 다음과 같이 작성해주었다.

CMAKE_MINIMUM_REQUIRED(VERSION 3.27)

project(project01)

add_executable(project01 project01.cpp)

위 버전은 설치된 버전에 맞게 입력해주면 된다.

그리고 ppm 확장자를 이미지로 만들어 주기 위해서
vscode에서 ppm viewer 를 설치해 주었다.

본격적으로 실행하여 ppm 파일을 만들어보자.

💬 먼저, cmake를 사용하여 빌드 파일을 만들어 주었다.
동일 디렉토리에 만들면, 복잡하기 때문에 build 폴더에 cmake를 실행해주었다.

 > cd build
 > cmake ..
 > make

warning 문구는 c++11 버전 문제이므로 일단은 넘어가주었다..ㅎㅎ
어쨌든 build 폴더에 Makefile 이 있으면 성공이다.

💬 여기서 다시 상위 폴더로 넘어가서
build 폴더에 있는 실행 파일을 ppm 이미지 파일로 만들어 주었다.

> cd ..
> ./build/[실행파일명] > proj.ppm

💬 그러면 해당 디렉토리에 ppm 파일이 생기고..!
클릭하면 이미지 파일을 확인할 수 있다.

짠, 이게 그래픽의 "hello world" 이자 시발점 이다.

1.3 Progress Indicator 추가


이제, 출력하는 데 필요한 Progress Indicator를 추가해보자.

Progress Indicator는

  1. 렌더링의 진행 상황을 추적하고
  2. 무한 루프 등의 문제로 중단된 상황을 식별할 수 있다.

지금은 표준 입력 스트림인 (std::cout)을 사용하고 있지만,
그대로 두고, 로깅 출력 스트림 (std::clog) 를 사용하고자 한다.

clog는 기본적으로 cout과 동일하게 콘솔에 출력하지만, 로그를 구분해준다.
그래서 for loop 안에 남은 scanlines를 확인할 수 있는 코드를 넣어줬다.

for (int j = 0; j < image_height; ++j) {

    //로깅 출력 스트림
    clog << "\nScanlines remaining: " << (image_height -j) << ' ' << flush;

    for (int i = 0; i < image_width; ++i) {
        auto r = double(i) / (image_width-1);
        auto g = double(j) / (image_height-1);
        auto b = 0;

        int ir = static_cast<int>(255.999 * r);
        int ig = static_cast<int>(255.999 * g);
        int ib = static_cast<int>(255.999 * b);
        
        cout << ir << ' ' << ig << ' ' << ib << '\n';
    }
}

clog << "\rDone.                 \n";

실행 결과는 다음과 같다.

지금은 실행되는 count가 작아서
호다닥 실행되느라 진행률이 잘 안보이지만, 나중에 Raytracer를
확장해 나아감에 따라 천천히 업데이트 되는 것을 볼 수 있다고 한다!

profile
J가 되고 싶은 극 P입니다

1개의 댓글

comment-user-thumbnail
2023년 8월 10일

글 잘 봤습니다.

답글 달기