此类是一个全景摄像机视角,书上介绍了详细原理。直接给实现代码。
类声明:
#pragma once #ifndef __SPHERICAL_HEADER__ #define __SPHERICAL_HEADER__ #include "camera.h" class Spherical :public Camera { public: Spherical(); ~Spherical(); Spherical(const Spherical& sp); void set_fov(const ldouble hfov, const ldouble vfov); void set_angle(const ldouble deg); Vector3 ray_direction(const Point3& pp, const integer hres, const integer vres, const ldouble s) const; virtual Camera* clone() const; virtual void render_scene(World& w); Spherical& operator=(const Spherical& sp); private: ldouble lambda_max, psi_max; }; #endif
类实现:
#include "pch.h" #include "spherical.h" #include "../utilities/world.h" #include "../utilities/viewplane.h" #include "../samplers/sampler.h" #include "../tracers/tracer.h" Spherical::Spherical() :Camera(), lambda_max(180), psi_max(180) {} Spherical::~Spherical() {} Spherical::Spherical(const Spherical& sp) : Camera(sp), lambda_max(sp.lambda_max), psi_max(sp.psi_max) {} void Spherical::set_fov(const ldouble hfov, const ldouble vfov) { lambda_max = hfov / 2; psi_max = vfov / 2; } void Spherical::set_angle(const ldouble deg) { ldouble rad = radian(deg); up = Point3(std::cos(rad) * up.x - std::sin(rad) * up.y, std::sin(rad) * up.x + std::cos(rad) * up.y, up.z); } Vector3 Spherical::ray_direction(const Point3& pp, const integer hres, const integer vres, const ldouble s) const { Point3 pn(2.0 / (s * hres) * pp.x, 2.0 / (s * vres) * pp.y, 0); ldouble lambda = pn.x * radian(lambda_max), psi = pn.y * radian(psi_max); ldouble phi = M_PI - lambda, theta = 0.5 * M_PI - psi; ldouble sin_phi = std::sin(phi), cos_phi = std::cos(phi), sin_theta = std::sin(theta), cos_theta = std::cos(theta); Vector3 dir = sin_theta * sin_phi * u + cos_theta * v + sin_theta * cos_phi * w; return dir; } Camera* Spherical::clone() const { return new Spherical(*this); } void Spherical::render_scene(World& w) { Ray ray; ViewPlane vp(w.vp); integer depth = 0; Point3 sp, pp; w.open_window(vp.hres, vp.vres); ray.o = eye; vp.s = 1 / vp.s; for (integer r = vp.vres - 1; r >= 0; r--)//render from left-corner to right-corner for (integer c = 0; c < vp.hres; c++) { RGBColor color; for (integer p = 0; p < vp.nsamples; p++) { sp = vp.sampler->sample_unit_square(); pp.x = (c - 0.5 * vp.hres + sp.x) * vp.s; pp.y = (r - 0.5 * vp.vres + sp.y) * vp.s; ray.d = ray_direction(pp, vp.hres, vp.vres, vp.s); color += w.tracer_ptr->trace_ray(ray); } color /= vp.nsamples; color *= exposure_time; w.display_pixel(r, c, color); } } Spherical& Spherical::operator=(const Spherical& sp) { if (this == &sp) return*this; Camera::operator=(sp); lambda_max = sp.lambda_max; psi_max = sp.psi_max; return *this; }
测试效果图(等之后加了材质后,再看看效果):