목차: Series 1: Index and Overview (Link)
이전 글: Series 12: Defocus Blur (Link)
먼저 Random 하게 많은 Sphere(구)를 그려서 이번 글에 사용할 표지 이미지를 만들어본다. 아래 코드 1은 Random 하게 많은 구를 그리기 위한 Main 코드이다.
코드 1: 많은 수의 구를 그리기 위한 Main 코드
#include <iostream> #include <fstream> #include "mkray.h" #include "mkhitablelist.h" #include "float.h" #include "mksphere.h" #include "mkcamera.h" #include "mkmaterial.h" using namespace std; vec3 color(const ray &r, hitable *world, int depth){ hitRecord rec; if(world->hit(r, 0.001, MAXFLOAT, rec)){ ray scattered; vec3 attenuation; if(depth < 50 && rec.matPtr->scatter(r, rec, attenuation, scattered)){ return attenuation * color(scattered, world, depth + 1); } else{ return vec3(0, 0, 0); } } else{ vec3 unitDirection = unitVector(r.direction()); float t = 0.5 * (unitDirection.y() + 1.0); return (1.0 - t)*vec3(1.0, 1.0, 1.0) + t * vec3(0.5, 0.7, 1.0); } } //MK: 코드 1-1 여러개의 구를 그리기 위해서 Random하게 구를 생성하는 함수 코드 hitable *randomScene(){ int n = 500; hitable **list = new hitable * [n + 1]; list[0] = new sphere(vec3(0, -1000, 0), 1000, new lambertian(vec3(0.5, 0.5, 0.5))); int i = 1; for(int a = -11; a < 11; a++){ for(int b= -11; b < 11; b++){ float chooseMat = drand48(); vec3 center(a + 0.9 * drand48(), 0.2, b + 0.9 * drand48()); if((center - vec3(4, 0.2, 0)).length() > 0.9){ if(chooseMat < 0.8){ list[i++] = new sphere(center, 0.2, new lambertian(vec3(drand48() * drand48(), drand48() * drand48(), drand48() * drand48()))); } else if(chooseMat < 0.95){ list[i++] = new sphere(center, 0.2, new metal(vec3(0.5*(1 + drand48()), 0.5*(1+drand48()), 0.5 *(1 + drand48())), 0.5 * drand48())); } else{ list[i++] = new sphere(center, 0.2, new dielectric(1.5)); } } } } list[i++] = new sphere(vec3(0, 1, 0), 1.0, new dielectric(1.5)); list[i++] = new sphere(vec3(-4, 1, 0), 1.0, new lambertian(vec3(0.4, 0.2, 0.1))); list[i++] = new sphere(vec3(4, 1, 0), 1.0, new metal(vec3(0.7, 0.6, 0.5), 0.0)); return new hitableList(list, i); } int main(){ int nx = 1200; int ny = 800; int ns = 10; string fileName = "Ch12_5.ppm"; ofstream writeFile(fileName.data()); if(writeFile.is_open()){ writeFile.flush(); writeFile << "P3\n" << nx << " " << ny << "\n255\n"; //MK: 코드 1-2 Random 하게 많은 구를 생성 hitable *world = randomScene(); vec3 lookfrom(13, 2, 3); vec3 lookat(0, 0, 0); float diskToFocus = (lookfrom - lookat).length(); float aperture = 0.1; camera cam(lookfrom, lookat, vec3(0, 1, 0), 20, float(nx)/float(ny), aperture, diskToFocus); for(int j = ny - 1; j >= 0; j--){ for(int i = 0; i < nx; i++){ vec3 col(0.0, 0.0, 0.0); for(int s = 0; s < ns; s++){ float u = float(i + drand48()) / float(nx); float v = float(j + drand48()) / float(ny); ray r = cam.getRay(u, v); col += color(r, world, 0); } col /= float(ns); col = vec3( sqrt(col[0]), sqrt(col[1]), sqrt(col[2]) ); int ir = int(255.99 * col[0]); int ig = int(255.99 * col[1]); int ib = int(255.99 * col[2]); writeFile << ir << " " << ig << " " << ib << "\n"; } } writeFile.close(); } return 0; }
위 코드를 실행하면 아래 그림 1과 같은 결과 이미지를 확인 할 수 있다 (뭔가 모르게 멋진 그림이다).
그림 1: 여러개의 구를 그린 결과 이미지
그림 1에서 Glass 구는 공중에 떠 있는 것처럼 보인다. 해당 부분은 버그가 아니라고 한다. 실제로 우리가 Glass 형태의 구를 본 적이 없어서 그렇다고 한다. 추가로 큰 Glass 구 아래에 있는 다른 구들은 Glass 구가 Ray를 굴절시키므으로 많은 Light를 받게 된다 (다른 큰 구는 Light를 차단함).
출처 1에서 제공하는 모든 코드를 작성하였다. 해당 글을 기반으로 추가로 할 수 있는 작업을 정리하였다.
- Lights: 특정 물체(Object)가 빛을 방출하는 등의 추가적인 구현
- Biasing Scattered Rays: 산란하는 Ray를 구현
- Triangle: 다양한 모델(Triangle로 구성된)을 그릴 수 있도록 구현
- Surface Textures: Wall Paper와 같은 그림을 물체에 추가할 수 있도록 구현
- Solid Textures: (추가 설명이 없음)
- Volumes and Media: Density에 따라 Ray의 Hit 여부를 결정하는 코드 구현 (해당 부분은 정확히 어떤 의미인지 잘 모르겠음)
- Parallelism: 병렬화 (출처 2에 CUDA를 사용한 병렬화 코드 설명이 있다)
MK: 드디어 Weekend 시리지를 마무리했다. 책 제목에 Weekend가 있어서 쉽게 마무리 할 수 있을 거로 생각했는데 거의 5개월이 넘게 걸린 것 같다. Weekend 다음 시리즈가 있음으로 다음 부분도 추가로 번역을 하면서 공부를 할 계획이다. 추가로 출처 2에 제공되는 CUDA 병렬화도 번역해볼 예정이다.
출처
- http://www.realtimerendering.com/raytracing/Ray%20Tracing%20in%20a%20Weekend.pdf
- https://devblogs.nvidia.com/accelerated-ray-tracing-cuda/
- http://www.jiniya.net/tt/528