Moving Camera Code Into Its Own Class
계속 진행하기 전에, 이제 카메라와 장면 렌더링 코드를 하나의 새로운 클래스인 camera 클래스로 통합하기에 좋은 시점입니다. 카메라 클래스는 두 가지 중요한 작업을 담당하게 됩니다:
1.
월드로 광선을 생성하고 발사합니다.
2.
이러한 광선의 결과를 사용하여 렌더링된 이미지를 구성합니다.
이 리팩토링에서는 ray_color() 함수를 메인 프로그램의 이미지, 카메라, 렌더링 섹션과 함께 수집할 것입니다. 새로운 카메라 클래스는 두 개의 public 메서드인 initialize()와 render(), 그리고 두 개의 private 헬퍼 메서드인 get_ray()와 ray_color()를 포함하게 됩니다.
궁극적으로 카메라는 우리가 생각할 수 있는 가장 단순한 사용 패턴을 따를 것입니다: 인자 없이 기본 생성되고, 소유하는 코드가 간단한 할당을 통해 카메라의 public 변수를 수정한 다음, 마지막으로 initialize() 함수 호출을 통해 모든 것이 초기화됩니다. 이 패턴은 소유자가 수많은 매개변수를 가진 생성자를 호출하거나 여러 setter 메서드를 정의하고 호출하는 대신 선택된 것입니다. 대신, 소유하는 코드는 명시적으로 관심 있는 것만 설정하면 됩니다. 마지막으로, 소유하는 코드가 initialize()를 호출하게 하거나, render() 시작 시 카메라가 이 함수를 자동으로 호출하게 할 수 있습니다. 우리는 두 번째 접근 방식을 사용할 것입니다.
main이 카메라를 생성하고 기본값을 설정한 후, render() 메서드를 호출할 것입니다. render() 메서드는 렌더링을 위해 카메라를 준비한 다음 렌더링 루프를 실행합니다.
다음은 우리의 새로운 카메라 클래스입니다.
#pragma once
#ifndef CAMERA_H
#define CAMERA_H
#include "Hittable.h"
class Camera
{
public:
double aspectRatio = 1.0; // Ratio of image width over height
int imageWidth = 100; // Rendered image width in pixel count
void Render(const Hittable& world)
{
Initialize();
std::cout << "P3\n" << imageWidth << ' ' << mImageHeight << "\n255\n";
for (int scanlineIndex = 0; scanlineIndex < mImageHeight; scanlineIndex++)
{
std::clog
<< "\rScanlines remaining: "
<< (mImageHeight - scanlineIndex)
<< ' '
<< std::flush;
for (int pixelIndex = 0; pixelIndex < imageWidth; pixelIndex++)
{
auto pixelCenter =
mPixel00Location
+ (pixelIndex * mPixelDeltaU)
+ (scanlineIndex * mPixelDeltaV);
auto rayDirection = pixelCenter - mCenter;
Ray ray(mCenter, rayDirection);
Color pixelColor = RayColor(ray, world);
WriteColor(std::cout, pixelColor);
}
}
std::clog << "\rDone. \n";
}
private:
void Initialize()
{
mImageHeight = static_cast<int>(imageWidth / aspectRatio);
mImageHeight = (mImageHeight < 1) ? 1 : mImageHeight;
mCenter = Point3(0.0, 0.0, 0.0);
// Determine viewport dimensions
auto focalLength = 1.0;
auto viewportHeight = 2.0;
auto viewportWidth = viewportHeight * (static_cast<double>(imageWidth) / mImageHeight);
// Calculate the vectors across the horizontal and down the vertical viewport edges
auto viewportU = Vec3(viewportWidth, 0.0, 0.0);
auto viewportV = Vec3(0.0, -viewportHeight, 0.0);
// Calculate the horizontal and vertical delta vectors from pixel to pixel
mPixelDeltaU = viewportU / imageWidth;
mPixelDeltaV = viewportV / mImageHeight;
// Calculate the location of the upper left pixel
auto viewportUpperLeft =
mCenter
- Vec3(0.0, 0.0, focalLength)
- viewportU / 2.0
- viewportV / 2.0;
mPixel00Location = viewportUpperLeft + 0.5 * (mPixelDeltaU + mPixelDeltaV);
}
Color RayColor(const Ray& ray, const Hittable& world) const
{
HitRecord hitRecord;
if (world.Hit(ray, Interval(0.0, Infinity), hitRecord))
{
return 0.5 * (hitRecord.Normal + Color(1.0, 1.0, 1.0));
}
Vec3 unitDirection = UnitVector(ray.Direction());
auto a = 0.5 * (unitDirection.Y() + 1.0);
return (1.0 - a) * Color(1.0, 1.0, 1.0)
+ a * Color(0.5, 0.7, 1.0);
}
private:
int mImageHeight = 0; // Rendered image height
Point3 mCenter; // Camera center
Point3 mPixel00Location; // Location of pixel 0, 0
Vec3 mPixelDeltaU; // Offset to pixel to the right
Vec3 mPixelDeltaV; // Offset to pixel below
};
#endif
C++
복사
*리스팅 39: [camera.h] 작동하는 camera 클래스*
그리고 다음은 훨씬 간결해진 main입니다:
#include "rtweekend.h"
#include "camera.h"
#include "hittable.h"
#include "hittable_list.h"
#include "sphere.h"
Color RayColor(const Ray& ray, const Hittable& world)
{
HitRecord hitRecord;
if (world.Hit(ray, Interval(0.0, Infinity), hitRecord))
{
return 0.5 * (hitRecord.Normal + Color(1.0, 1.0, 1.0));
}
Vector3 unitDirection = UnitVector(ray.Direction());
auto a = 0.5 * (unitDirection.Y() + 1.0);
return (1.0 - a) * Color(1.0, 1.0, 1.0) + a * Color (0.5, 0.7, 1.0);
}
int main()
{
HittableList world;
world.Add(std::make_shared<Sphere>(Point3(0.0, 0.0, -1.0), 0.5));
world.Add(std::make_shared<Sphere>(Point3(0.0, -100.5, -1.0), 100.0));
Camera camera;
camera.aspectRatio = 16.0 / 9.0;
camera.imageWidth = 400;
camera.Render(world);
}
C++
복사
새로 리팩토링된 이 프로그램을 실행하면 이전과 동일한 렌더링된 이미지를 얻을 수 있습니다.


