【Ray Tracing The Next Week 超详解】 光线追踪2-9

2023-06-20,,

我们来整理一下项目的代码

目录

----include

--hit

--texture

--material

----RTdef.hpp

----ray.hpp

----camera.hpp

----main.cpp

3D泛型数学库中的randomfunc.hpp增加了新内容

#pragma once

#include <lvgm\type_vec\type_vec.h>
#include <random> namespace lvgm
{ //@brief: create a random number that from 0 to 1 completely
template<typename T = lvgm::precision>
const T rand01()
{
if (typeid(T) == typeid(int))
{
std::cerr << "integer doesn't have a random number from 0 to 1\n";
throw "integer doesn't have a random number from 0 to 1\n";
} static std::mt19937 mt;
static std::uniform_real_distribution<T> r;
return r(mt);
} //@brief: find a random point in unit_sphere
template<typename T = lvgm::precision>
const lvgm::vec3<T> random_unit_sphere()
{
if (typeid(T) == typeid(int))
{
std::cerr << "integer doesn't have a random number from 0 to 1\n";
throw "integer doesn't have a random number from 0 to 1\n";
} lvgm::vec3<T> p;
do
{
p = 2.0*lvgm::vec3<T>(rand01(), rand01(), rand01()) - lvgm::vec3<T>(, , );
} while (dot(p, p) >= 1.0);
return p;
} //@brief: find a random point in unit_plane
template<typename T = lvgm::precision>
const lvgm::vec2<T> random_unit_plane()
{
if (typeid(T) == typeid(int))
{
std::cerr << "integer doesn't have a random number from 0 to 1\n";
throw "integer doesn't have a random number from 0 to 1\n";
} lvgm::vec2<T> p;
do
{
p = 2.0*lvgm::vec2<T>(rand01(), rand01()) - lvgm::vec2<T>(, );
} while (dot(p, p) >= 1.0);
return p;
} //@brief: generate a list of normalized vector(-1~1)
template<typename T = precision>
vec2<T> * random_normalized2D(const size_t size)
{
vec2<T> * p = new vec2<T>[size];
for (int i = ; i < size; ++i)
p[i] = vec2<T>( - + * lvgm::rand01(), - + * lvgm::rand01()).ret_unitization();
return p;
} //@brief: generate a list of normalized vector(-1~1)
template<typename T = precision>
vec3<T> * random_normalized3D(const size_t size)
{
vec3<T> * p = new vec3<T>[size];
for (int i = ; i < size; ++i)
p[i] = vec3<T>(- + * lvgm::rand01(), - + * lvgm::rand01(), - + * lvgm::rand01()).ret_unitization();
return p;
} //@brief: generate a list of normalized vector(-1~1)
template<typename T = precision>
T * random_normalized1D(const size_t size)
{
T * p = new T[size];
for (int i = ; i < size; ++i)
p[i] = - + * lvgm::rand01();
return p;
} //@brief: make list permute randomly
template<typename T>
void permute_method(T * p, size_t n)
{
for (int i = n - ; i; i--)
{
size_t tar = int(lvgm::rand01() * (i + ));
T t = p[i];
p[i] = p[tar];
p[tar] = t;
}
} //@brief: generate a list of random value(0~n)
template<typename T = int>
T * generate_random0n(size_t size)
{
int * p = new int[size];
for (int i = ; i < ; ++i) p[i] = i;
permute_method(p, );
return p;
} }

randomfunc.hpp

先说最外面的

基本和上次的没什么区别https://www.cnblogs.com/lv-anchoret/p/10243553.html

/// RTdef.hpp
// https://www.cnblogs.com/lv-anchoret/p/10243553.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1.1
// [brief ] the basic concept of rt
// ----------------------------------------------------- #pragma once #include <lvgm\type_vec\type_vec.h> //https://www.cnblogs.com/lv-anchoret/p/10163085.html
#include <lvgm\opticsfunc.hpp> //https://www.cnblogs.com/lv-anchoret/p/10241904.html
#include <lvgm\randfunc.hpp> //https://www.cnblogs.com/lv-anchoret/p/10241904.html
#include <algorithm> #define stds std:: namespace rt
{
using rtvar = lvgm::precision; using rtvec = lvgm::vec3<rtvar>; constexpr static rtvar rtInf() { return static_cast<rtvar>(0x3f3f3f3f); } //最大值 constexpr rtvar π = 3.1415926; template<typename T>
const T& rtmin(const T& a, const T& b)
{
return a < b ? a : b;
} template<typename T>
const T& rtmax(const T& a, const T& b)
{
return a > b ? a : b;
} }

RTdef.hpp

/// ray.hpp
// https://www.cnblogs.com/lv-anchoret/p/10182002.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2018.12
// [brief ] the ray-class for the ray-tracing project
// from the 《ray tracing in one week》
// ----------------------------------------------------- #pragma once #include "RTdef.hpp" namespace rt
{ class ray
{
public:
ray()
:_a{ rtvec() }
, _b{ rtvec() }
{ } ray(const rtvec& a, const rtvec& b, const rtvar time = .)
:_a(a)
, _b(b)
,_time(time)
{ } ray(const ray& r)
:_a(r._a)
,_b(r._b)
,_time(r._time)
{ } inline rtvec origin()const { return _a; } inline rtvec direction()const { return _b; } inline rtvar time()const { return _time; } inline rtvec go(const rtvar t)const { return _a + t * _b; } private:
rtvec _a; rtvec _b; rtvar _time; };
} // rt namespace

ray.hpp

/// camera.hpp
// https://www.cnblogs.com/lv-anchoret/p/10221058.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2018.12
// [brief ] the camera-class for the ray-tracing project
// from the 《ray tracing in one week》
// ----------------------------------------------------- #pragma once namespace rt
{ // the statement of camera class class camera
{
public: /*
@brief: the camera constructor
@param: lookfrom -> the position of eye
lookat -> the vector of sight's direction
vup -> the vertical up direction
vfov -> 相机在垂直方向上关于屏幕岔开的角度
aspect -> Screen aspect ratio
focus -> the focal length
t1 -> Shutter start time
t2 -> Shutter end time
*/
camera(rtvec lookfrom, rtvec lookat, rtvec vup, rtvar vfov, rtvar aspect, rtvar aperture, rtvar focus, rtvar t1, rtvar t2); inline const ray get_ray(const rtvar u, const rtvar v)const; inline const ray get_ray(const lvgm::vec2<rtvar>& para)const; public:
inline const rtvec& eye()const { return _eye; } inline const rtvec& start()const { return _start; } inline const rtvec& horizontal()const { return _horizontal; } inline const rtvec& vertical()const { return _vertical; } inline const rtvec& u()const { return _u; } inline const rtvec& v()const { return _v; } inline const rtvec& w()const { return _w; } inline const rtvar lens_r()const { return _lens_radius; } private:
rtvec _u; rtvec _v; rtvec _w; rtvec _eye; rtvec _start; //left-bottom rtvec _horizontal; rtvec _vertical; rtvar _lens_radius; //the radius of lens rtvar _time1; rtvar _time2; }; //the implementation of camera class inline camera::camera(rtvec lookfrom, rtvec lookat, rtvec vup, rtvar vfov, rtvar aspect, rtvar aperture, rtvar focus, rtvar t1, rtvar t2)
:_eye(lookfrom)
, _lens_radius(aperture / )
, _time1(t1)
, _time2(t2)
{
rtvar theta = vfov * π / ;
rtvar half_height = tan(theta / ) * focus;
rtvar half_width = aspect * half_height;
_w = (lookfrom - lookat).ret_unitization();
_u = cross(vup, _w).ret_unitization();
_v = cross(_w, _u); _start = _eye - half_width * _u - half_height * _v - focus * _w;//高和宽都乘了焦距,w也要乘,不然公式是错的
_horizontal = * half_width * _u;
_vertical = * half_height * _v;
} inline const ray camera::get_ray(const rtvar u, const rtvar v)const
{
rtvec rd = rtvec(_lens_radius * lvgm::random_unit_plane());
rtvec offset = _u * rd.x() + _v * rd.y();
rtvar time = _time1 + lvgm::rand01() * (_time2 - _time1);
return ray{ _eye + offset, _start + u*_horizontal + v*_vertical - (_eye + offset), time };
} inline const ray camera::get_ray(const lvgm::vec2<rtvar>& para)const
{
return get_ray(para.u(), para.v());
} } // rt namespace

camera.hpp

hit

里面包含的很多物体以及一些变换

/// RThit.hpp
// https://www.cnblogs.com/lv-anchoret/p/10190092.html // -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] some intersects
// intersections
// aabb
// bvh
// sphere
// moving_sphere
// rectangle
// rectangles
// box
// translate
// rotate
// ----------------------------------------------------- #pragma once #include "ray.hpp"
#include "aabb_box.hpp"
#include "intersect.hpp" #include "bvh.hpp"
#include "intersections.hpp" #include "sphere.hpp"
#include "moving_sphere.hpp"
#include "rectangleBasic.hpp"
#include "rectangles.hpp"
#include "rectangle.hpp"
#include "flip_normal.hpp"
#include "box.hpp"
#include "constant_medium.hpp" #include "translate.hpp"
#include "rotate.hpp"

RThit.hpp

/// intersect.hpp
//https://www.cnblogs.com/lv-anchoret/p/10190092.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2018.12
// [brief ] the intersect-class for the ray-tracing project
// from the 《ray tracing in one week》
// ----------------------------------------------------- #pragma once namespace rt
{
class material;
class aabb; // the infomation of intersection point struct hitInfo
{
lvgm::precision _t; //ray 中的系数t
rtvec _p; //相交点、撞击点
rtvec _n; //_p点的表面法线
material* _materialp; //材质
rtvar _u; //texture-u
rtvar _v; //texture-v
}; // the statement of intersect class class intersect
{
public:
intersect() { } /*
@brief: 撞击函数,求取撞击点相关记录信息
@param: sight->视线
系数t的上下界->筛选撞击点
info->返回撞击点信息
@retur: 是否存在合法撞击点
*/
virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const = ; /*
@brief: get the box of Geometry
*/
virtual aabb getbox()const = ; }; }

intersect.hpp

/// aabb.hpp
// https://www.cnblogs.com/lv-anchoret/p/10284085.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the aabb-class for the ray-tracing project
// from the 《ray tracing the next week》
// ----------------------------------------------------- #pragma once namespace rt
{ //the statement of aabb class class aabb
{
public:
aabb() { } /*
@brief: the box of Geometry
@param: min -> 盒子坐标最小端
max -> 盒子坐标最大端
*/
aabb(const rtvec& min, const rtvec& max); /*
@brief: 相交碰撞检测
@param: sight -> 视线
解的上下界
@retur: 是否碰撞相交
*/
inline bool hit(const ray& sight, rtvar tmin, rtvar tmax)const; /*
@brief: 两个包围盒的包围盒
*/
static aabb _surrounding_box(aabb box1, aabb box2); public: inline const rtvec& min()const { return _min; } inline const rtvec& max()const { return _max; } private:
rtvec _min; rtvec _max;
}; //the implementation of aabb class inline aabb::aabb(const rtvec& min, const rtvec& max)
:_min(min)
,_max(max)
{
} inline bool aabb::hit(const ray& sight, rtvar tmin, rtvar tmax)const
{
for (int i = ; i < ; ++i)
{
rtvar div = 1.0 / sight.direction()[i];
rtvar t1 = (_min[i] - sight.origin()[i]) / sight.direction()[i];
rtvar t2 = (_max[i] - sight.origin()[i]) / sight.direction()[i];
if (div < .)stds swap(t1, t2);
if (stds min(t2, tmax) <= stds max(t1, tmin))
return false;
}
return true;
} aabb aabb::_surrounding_box(aabb box1, aabb box2)
{
auto fmin = [](const rtvar a, const rtvar b) {return a < b ? a : b; };
auto fmax = [](const rtvar a, const rtvar b) {return a > b ? a : b; };
rtvec min{ fmin(box1.min().x(),box2.min().x()),
fmin(box1.min().y(),box2.min().y()),
fmin(box1.min().z(),box2.min().z()) };
rtvec max{ fmax(box1.max().x(),box2.max().x()),
fmax(box1.max().y(),box2.max().y()),
fmax(box1.max().z(),box2.max().z()) };
return aabb(min, max);
} } //rt namespace

aabb.hpp

/// bvh.hpp
// https://www.cnblogs.com/lv-anchoret/p/10284085.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the bvh-class for the ray-tracing project
// from the 《ray tracing the next week》
// ----------------------------------------------------- #pragma once namespace rt
{ // the statement of bvh_node class class bvh_node :public intersect
{
public:
bvh_node() { } /*
@brief: bvh树
@param: world -> all Geometry of the scene
n -> the number of the Geometry
time1 -> start time
time2 -> end time
*/
bvh_node(intersect** world, const int n, const rtvar time1, const rtvar time2); virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override; virtual aabb getbox()const override; private:
intersect* _left; intersect* _right; aabb _box; }; // the implementation of bvh_node class inline int _x_cmp(const void * lhs, const void * rhs)
{
intersect * lc = *(intersect**)lhs;
intersect * rc = *(intersect**)rhs;
aabb lbox = lc->getbox();
aabb rbox = rc->getbox(); if (lbox.min().x() - rbox.min().x() < .)
return -;
else
return ;
} inline int _y_cmp(const void * lhs, const void * rhs)
{
intersect * lc = *(intersect**)lhs;
intersect * rc = *(intersect**)rhs;
aabb lbox = lc->getbox();
aabb rbox = rc->getbox(); if (lbox.min().y() - rbox.min().y() < .)
return -;
else
return ;
} inline int _z_cmp(const void * lhs, const void * rhs)
{
intersect * lc = *(intersect**)lhs;
intersect * rc = *(intersect**)rhs;
aabb lbox = lc->getbox();
aabb rbox = rc->getbox();
if (lbox.min().z() - rbox.min().z() < .)
return -;
else
return ;
} inline bvh_node::bvh_node(intersect** world, const int n, const rtvar time1, const rtvar time2)
{
int axis = static_cast<int>( * lvgm::rand01());
if (axis == )
qsort(world, n, sizeof(intersect*), _x_cmp);
else if (axis == )
qsort(world, n, sizeof(intersect*), _y_cmp);
else
qsort(world, n, sizeof(intersect*), _z_cmp); if (n == )
_left = _right = world[];
else if (n == )
_left = world[],
_right = world[];
else
_left = new bvh_node(world, n / , time1, time2),
_right = new bvh_node(world + n / , n - n / , time1, time2); aabb lbox = _left->getbox();
aabb rbox = _right->getbox(); _box = aabb::_surrounding_box(lbox, rbox);
} aabb bvh_node::getbox()const
{
return _box;
} bool bvh_node::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
{
if (_box.hit(sight, t_min, t_max))
{
hitInfo linfo, rinfo;
bool lhit = _left->hit(sight, t_min, t_max, linfo);
bool rhit = _right->hit(sight, t_min, t_max, rinfo);
if (lhit && rhit)
{
if (linfo._t < rinfo._t)
info = linfo;
else
info = rinfo;
return true;
}
else if (lhit)
{
info = linfo;
return true;
}
else if (rhit)
{
info = rinfo;
return true;
}
else
return false;
}
else
return false;
} } //rt namespace

bvh.hpp

/// intersections.hpp
// https://www.cnblogs.com/lv-anchoret/p/10190092.html // -----------------------------------------------------
// [author] lv
// [begin ] 2018.12
// [brief ] the intersections-class for the ray-tracing project
// from the 《ray tracing in one week》
// ----------------------------------------------------- #pragma once namespace rt
{ // the statement of intersections class class intersections :public intersect
{
public:
intersections() { } /*
@brief: the list of scene's Geometry
@param: list -> all Geometry of the scene
n -> the number of the scene's Geometry
*/
intersections(intersect** list, size_t n) :_list(list), _size(n) { } virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override; virtual aabb getbox()const override { return aabb(); } public:
const size_t get_size()const { return _size; } private:
intersect** _list; size_t _size;
}; // the implementation of intersections class bool intersections::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
{
hitInfo t_rec;
bool hitSomething = false;
rtvar far = t_max; //刚开始可以看到无限远
for (int i = ; i < _size; ++i)
{
if (_list[i]->hit(sight, t_min, far, t_rec))
{
hitSomething = true;
far = t_rec._t; //将上一次的最近撞击点作为视线可达最远处
info = t_rec;
}
}
return hitSomething;
} } // rt namespace

intersections.hpp

/// sphere.hpp
// https://www.cnblogs.com/lv-anchoret/p/10190092.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the sphere-class for the ray-tracing project
// from the 《ray tracing in one week》
// -----------------------------------------------------
#pragma once namespace rt
{ class sphere :public intersect
{
public:
sphere() { } /*
@para1: 球心坐标
@para2: 球半径
@para3: 材质
*/
sphere(const rtvec& h, rtvar r, material* ma); ~sphere(); virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override; virtual aabb getbox()const override; /*
获取几何球体中p点对应的2D纹理坐标(u,v)
*/
static void get_sphere_uv(const rtvec& p, rtvar& u, rtvar& v); public: inline const rtvar r()const { return _radius; } inline const rtvec& heart()const { return _heart; } inline const material* get_material()const { return _materialp; } inline rtvar& r() { return _radius; } inline rtvec& heart() { return _heart; } inline void set_material(material* m) { _materialp = m; }
private:
rtvec _heart; rtvar _radius; material* _materialp;
}; inline sphere::sphere(const rtvec& h, rtvar r, material* ma)
:_heart(h)
, _radius(r)
, _materialp(ma)
{
} inline sphere::~sphere()
{
if (_materialp)
delete _materialp;
} bool sphere::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
{
rtvec trace = sight.origin() - _heart;
rtvar a = dot(sight.direction(), sight.direction());
rtvar b = 2.0 * dot(trace, sight.direction());
rtvar c = dot(trace, trace) - _radius * _radius;
rtvar delt = b*b - 4.0*a*c;
if (delt > )
{
info._materialp = _materialp;
rtvar x = (-b - sqrt(delt)) / (2.0*a);
if (x < t_max && x > t_min)
{
info._t = x;
info._p = sight.go(x);
info._n = (info._p - _heart) / _radius;
get_sphere_uv((info._p - _heart) / _radius, info._u, info._v); //将撞击点向量规格化
return true;
}
x = (-b + sqrt(delt)) / (2.0*a);
if (x < t_max && x > t_min)
{
info._t = x;
info._p = sight.go(x);
info._n = (info._p - _heart) / _radius;
get_sphere_uv((info._p - _heart) / _radius, info._u, info._v); //!!!
return true;
}
}
return false;
} aabb sphere::getbox()const
{
return aabb(_heart - rtvec(_radius, _radius, _radius), _heart + rtvec(_radius, _radius, _radius));
} void sphere::get_sphere_uv(const rtvec& p, rtvar& u, rtvar& v)
{
rtvar φ = atan2(p.z(), p.x());
rtvar θ = asin(p.y());
u = - (φ + π) / ( * π);
v = (θ + π / ) / π;
} } // rt namespace

sphere.hpp

/// moving_sphere.hpp
// https://www.cnblogs.com/lv-anchoret/p/10269488.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the moving_sphere-class for the ray-tracing project
// from the 《ray tracing in one week》
// ----------------------------------------------------- #pragma once namespace rt
{
//the statement of moving_shpere class class moving_sphere :public intersect
{
public:
moving_sphere() { } /*
@brief: 模拟运动模糊的几何体
@param: 起始位置的球心
结束位置的球心
开始时间
结束时间
球体半径
纹理
*/
moving_sphere(rtvec heart1, rtvec heart2, rtvar t1, rtvar t2, rtvar r, material* mp); virtual bool hit(const ray& r, rtvar tmin, rtvar tmax, hitInfo& info)const override; virtual aabb getbox()const override; inline rtvec heart(rtvar t)const; private:
rtvec _heart1;
rtvec _heart2;
rtvar _time1;
rtvar _time2;
rtvar _radius;
material* _materialp;
}; //the implementation of moving_sphere class inline moving_sphere::moving_sphere(rtvec heart1, rtvec heart2, rtvar t1, rtvar t2, rtvar r, material* mp)
:_heart1(heart1)
, _heart2(heart2)
, _time1(t1)
, _time2(t2)
, _radius(r)
, _materialp(mp)
{
} inline rtvec moving_sphere::heart(rtvar t)const
{
return _heart1 + ((t - _time1) / (_time2 - _time1)) * (_heart2 - _heart1);
} bool moving_sphere::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
{
rtvec trace = sight.origin() - heart(sight.time());
rtvar a = dot(sight.direction(), sight.direction());
rtvar b = 2.0 * dot(trace, sight.direction());
rtvar c = dot(trace, trace) - _radius * _radius;
rtvar delt = b*b - 4.0*a*c;
if (delt > )
{
info._materialp = _materialp;
rtvar x = (-b - sqrt(delt)) / (2.0*a);
if (x < t_max && x > t_min)
{
info._t = x;
info._p = sight.go(x);
info._n = (info._p - heart(sight.time())) / _radius;
return true;
}
x = (-b + sqrt(delt)) / (2.0*a);
if (x < t_max && x > t_min)
{
info._t = x;
info._p = sight.go(x);
info._n = (info._p - heart(sight.time())) / _radius;
return true;
}
}
return false;
} aabb moving_sphere::getbox()const
{
rtvec delt{ _radius, _radius, _radius };
return aabb::_surrounding_box(aabb(_heart1 - delt, _heart1 + delt), aabb(_heart2 - delt, _heart2 + delt));
} } // rt namespace

moving_sphere.hpp

/// constant_medium.hpp

// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the constant_dedium-class for the ray-tracing project
// from the 《ray tracing the next week》
// ----------------------------------------------------- #pragma once namespace rt
{ class constant_medium :public intersect
{
public:
/*
形体
颗粒浓度
纹理
*/
constant_medium(intersect* p, rtvar d, texture* tex); virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override; virtual aabb getbox()const override; public:
inline const rtvar get_density()const { return _density; } inline const material* get_material()const { return _materialp; } inline void set_density(const rtvar density) { _density = density; } inline void set_material(material* m) { _materialp = m; } private:
intersect* _item; rtvar _density; material* _materialp;
}; inline constant_medium::constant_medium(intersect* p, rtvar d, texture* tex)
:_item(p)
,_density(d)
,_materialp(new isotropic(tex))
{
} aabb constant_medium::getbox()const
{
return _item->getbox();
} bool constant_medium::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
{
hitInfo info1, info2;
if (_item->hit(sight, -rtInf(), rtInf(), info1)) {
if (_item->hit(sight, info1._t + 0.0001, rtInf(), info2)) {
if (info1._t < t_min)
info1._t = t_min;
if (info2._t > t_max)
info2._t = t_max;
if (info1._t >= info2._t)
return false;
if (info1._t < )
info1._t = ;
float distance_inside_boundary = (info2._t - info1._t)*sight.direction().normal();
float hit_distance = -( / _density)*log(lvgm::rand01());
if (hit_distance < distance_inside_boundary) {
info._t = info1._t + hit_distance / sight.direction().normal();
info._p = sight.go(info._t);
info._n = rtvec(lvgm::rand01(), lvgm::rand01(), lvgm::rand01()); // arbitrary
info._materialp = _materialp;
return true;
}
}
}
return false;
} } // rt namespace

constant_medium.hpp

/// flip_normal.hpp
// https://www.cnblogs.com/lv-anchoret/p/10307569.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the flip_normal-class for the ray-tracing project
// from the 《ray tracing the next week》
// ----------------------------------------------------- #pragma once namespace rt
{ class flip_normal: public intersect
{
public:
flip_normal(intersect * p) :_p(p) { } virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override
{
if (_p->hit(sight, t_min, t_max, info))
{
info._n = -info._n;
return true;
}
return false;
} virtual aabb getbox()const override
{
return _p->getbox();
} private:
intersect* _p;
}; } // rt namespace

flip_normal.hpp

/// translate.hpp
// https://www.cnblogs.com/lv-anchoret/p/10307569.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the translate-class for the ray-tracing project
// from the 《ray tracing the next week》
// ----------------------------------------------------- #pragma once namespace rt
{ class translate :public intersect
{
public:
translate(intersect* p, const rtvec& offset); virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override; virtual aabb getbox()const override; inline const rtvec get_offset()const { return _offset; } inline void set_offset(const rtvec& f) { _offset = f; } private:
intersect* _item; rtvec _offset; }; translate::translate(intersect* p, const rtvec& offset)
:_item(p)
, _offset(offset)
{
} bool translate::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
{
ray movedRay(sight.origin() - _offset, sight.direction(), sight.time());
if (_item->hit(movedRay, t_min, t_max, info))
{
info._p += _offset;
return true;
}
return false;
} aabb translate::getbox()const
{
aabb box = _item->getbox();
return aabb(box.min() + _offset, box.max() + _offset);
} }// rt namespace

translate.hpp

/// rotate.hpp
// https://www.cnblogs.com/lv-anchoret/p/10307569.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the translate-class for the ray-tracing project
// from the 《ray tracing the next week》
// ----------------------------------------------------- #pragma once namespace rt
{ // the statement of rotate_y class class rotate_y :public intersect
{
public:
rotate_y(intersect* p, rtvar angle); virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override; virtual aabb getbox()const override; private:
intersect* _item; rtvar _sinθ; rtvar _cosθ; aabb _box; }; // the statement of rotate_x class class rotate_x :public intersect
{
public:
rotate_x(intersect* p, rtvar angle); virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override; virtual aabb getbox()const override; private:
intersect* _item; rtvar _sinθ; rtvar _cosθ; aabb _box; }; // the statement of rotate_z class class rotate_z :public intersect
{
public:
rotate_z(intersect* p, rtvar angle); virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override; virtual aabb getbox()const override; private:
intersect* _item; rtvar _sinθ; rtvar _cosθ; aabb _box; }; // the implementation of rotate_y class rotate_y::rotate_y(intersect* p, rtvar angle)
:_item(p)
{
rtvar radians = (π / .) * angle;
_sinθ = sin(radians);
_cosθ = cos(radians);
rtvec min(rtInf(), rtInf(), rtInf());
rtvec max = -min;
for (int i = ; i < ; ++i)
for (int j = ; j < ; ++j)
for (int k = ; k < ; ++k)
{
rtvar x = i * _box.max().x() + ( - i)*_box.min().x();
rtvar y = j * _box.max().y() + ( - j)*_box.min().y();
rtvar z = k * _box.max().z() + ( - k)*_box.min().z();
rtvar newx = _cosθ * x + _sinθ * z;
rtvar newz = -_sinθ * x + _cosθ * z;
rtvec tester(newx, y, newz);
for (int c = ; c < ; ++c)
{
if (tester[c] > max[c])
max[c] = tester[c];
if (tester[c] < min[c])
min[c] = tester[c];
}
}
_box = aabb(min, max);
} bool rotate_y::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
{
rtvec eye = sight.origin();
rtvec direction = sight.direction();
eye[] = _cosθ * sight.origin()[] - _sinθ * sight.origin()[];
eye[] = _sinθ * sight.origin()[] + _cosθ * sight.origin()[];
direction[] = _cosθ * sight.direction()[] - _sinθ * sight.direction()[];
direction[] = _sinθ * sight.direction()[] + _cosθ * sight.direction()[];
ray rotatedRay(eye, direction, sight.time());
if (_item->hit(rotatedRay, t_min, t_max, info))
{
rtvec p = info._p;
rtvec n = info._n;
p[] = _cosθ * info._p[] + _sinθ * info._p[];
p[] = -_sinθ * info._p[] + _cosθ * info._p[];
n[] = _cosθ * info._n[] + _sinθ * info._n[];
n[] = -_sinθ * info._n[] + _cosθ * info._n[];
info._p = p;
info._n = n;
return true;
}
return false;
} aabb rotate_y::getbox()const
{
return _box;
} // the implementation of rotate_x class rotate_x::rotate_x(intersect* p, rtvar angle)
:_item(p)
{
rtvar radians = (π / .) * angle;
_sinθ = sin(radians);
_cosθ = cos(radians);
rtvec min(rtInf(), rtInf(), rtInf());
rtvec max = -min;
for (int i = ; i < ; ++i)
for (int j = ; j < ; ++j)
for (int k = ; k < ; ++k)
{
rtvar x = i * _box.max().x() + ( - i)*_box.min().x();
rtvar y = j * _box.max().y() + ( - j)*_box.min().y();
rtvar z = k * _box.max().z() + ( - k)*_box.min().z();
rtvar newy = _cosθ * x - _sinθ * z;
rtvar newz = _sinθ * x + _cosθ * z;
rtvec tester(x, newy, newz);
for (int c = ; c < ; ++c)
{
if (tester[c] > max[c])
max[c] = tester[c];
if (tester[c] < min[c])
min[c] = tester[c];
}
}
_box = aabb(min, max);
} bool rotate_x::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
{
rtvec eye = sight.origin();
rtvec direction = sight.direction();
eye[] = _cosθ * sight.origin()[] + _sinθ * sight.origin()[];
eye[] = -_sinθ * sight.origin()[] + _cosθ * sight.origin()[];
direction[] = _cosθ * sight.direction()[] + _sinθ * sight.direction()[];
direction[] = -_sinθ * sight.direction()[] + _cosθ * sight.direction()[];
ray rotatedRay(eye, direction, sight.time());
if (_item->hit(rotatedRay, t_min, t_max, info))
{
rtvec p = info._p;
rtvec n = info._n;
p[] = _cosθ * info._p[] - _sinθ * info._p[];
p[] = _sinθ * info._p[] + _cosθ * info._p[];
n[] = _cosθ * info._n[] - _sinθ * info._n[];
n[] = _sinθ * info._n[] + _cosθ * info._n[];
info._p = p;
info._n = n;
return true;
}
return false;
} aabb rotate_x::getbox()const
{
return _box;
} // the implementation of rotate_z class rotate_z::rotate_z(intersect* p, rtvar angle)
:_item(p)
{
rtvar radians = (π / .) * angle;
_sinθ = sin(radians);
_cosθ = cos(radians);
rtvec min(rtInf(), rtInf(), rtInf());
rtvec max = -min;
for (int i = ; i < ; ++i)
for (int j = ; j < ; ++j)
for (int k = ; k < ; ++k)
{
rtvar x = i * _box.max().x() + ( - i)*_box.min().x();
rtvar y = j * _box.max().y() + ( - j)*_box.min().y();
rtvar z = k * _box.max().z() + ( - k)*_box.min().z();
rtvar newx = _cosθ * x - _sinθ * y;
rtvar newy = _sinθ * x + _cosθ * y;
rtvec tester(newx, newy, z);
for (int c = ; c < ; ++c)
{
if (tester[c] > max[c])
max[c] = tester[c];
if (tester[c] < min[c])
min[c] = tester[c];
}
}
_box = aabb(min, max);
} bool rotate_z::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
{
rtvec eye = sight.origin();
rtvec direction = sight.direction();
eye[] = _cosθ * sight.origin()[] + _sinθ * sight.origin()[];
eye[] = -_sinθ * sight.origin()[] + _cosθ * sight.origin()[];
direction[] = _cosθ * sight.direction()[] + _sinθ * sight.direction()[];
direction[] = -_sinθ * sight.direction()[] + _cosθ * sight.direction()[];
ray rotatedRay(eye, direction, sight.time());
if (_item->hit(rotatedRay, t_min, t_max, info))
{
rtvec p = info._p;
rtvec n = info._n;
p[] = _cosθ * info._p[] - _sinθ * info._p[];
p[] = _sinθ * info._p[] + _cosθ * info._p[];
n[] = _cosθ * info._n[] - _sinθ * info._n[];
n[] = _sinθ * info._n[] + _cosθ * info._n[];
info._p = p;
info._n = n;
return true;
}
return false;
} aabb rotate_z::getbox()const
{
return _box;
} } // rt namespace

rotate.hpp

鉴于这个系统的变换均针对运算期的每个点做变换,具体的几何体不变,所以,造成一个问题就是不方便获取变换后的法线等信息,也不利于重置纹理材质等,就长方体而言,比如我想改变某个面的纹理或者材质,就很困难,所以新加了很多接口,但是,这些接口都是统一的,所以又重新整了一个基类,提高代码重用性和可维护性

但我只改了反转向量,没有对旋转做适应的变动

/// rectangleBasic.hpp

// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the rectangle-class for the ray-tracing project
// ----------------------------------------------------- #pragma once namespace rt
{ class rectangle :public intersect
{
public:
rectangle() { } rectangle(rtvar u1, rtvar u2, rtvar v1, rtvar v2, rtvar other, material* mat, rtvec nor)
:_u1(u1)
, _u2(u2)
, _v1(v1)
, _v2(v2)
, _materialp(mat)
, _normal(nor)
{
} virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override = ; virtual aabb getbox()const = ; public:
inline const rtvec Getnormal()const { return _normal; } inline const lvgm::vec2<lvgm::precision> Urange()const { return lvgm::vec2<lvgm::precision>(rtmin(_u1, _u2), rtmax(_u1, _u2)); } inline const lvgm::vec2<lvgm::precision> Vrange()const { return lvgm::vec2<lvgm::precision>(rtmin(_v1, _v2), rtmax(_v1, _v2)); } inline const material* Getmaterial()const { return _materialp; } inline void flipNormal() { _normal = -_normal; } inline void setmaterial(material* m) { _materialp = m; } inline void setnormal(const rtvec& n) { _normal = n; } protected:
material* _materialp; rtvar _u1, _u2; rtvar _v1, _v2; rtvar _other; rtvec _normal;
}; } // rt namespace

rectangleBasic.hpp

/// rectangles.hpp

// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the rectangles-class for the ray-tracing project
// ----------------------------------------------------- #pragma once namespace rt
{
class rectangles:public rectangle
{
public:
rectangles(rectangle** list, size_t n) :_list(list), _size(n) { } virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override; virtual aabb getbox()const override { return aabb(); } private:
rectangle** _list; size_t _size;
}; // the implementation of intersections class bool rectangles::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
{
hitInfo t_rec;
bool hitSomething = false;
rtvar far = t_max; //刚开始可以看到无限远
for (int i = ; i < _size; ++i)
{
if (_list[i]->hit(sight, t_min, far, t_rec))
{
hitSomething = true;
far = t_rec._t; //将上一次的最近撞击点作为视线可达最远处
info = t_rec;
}
}
return hitSomething;
} }

rectangles.hpp

/// rectangle.hpp

// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the rect-class for the ray-tracing project
// from the 《ray tracing the next week》
// ----------------------------------------------------- #pragma once namespace rt
{ //the statement of xy_rect class class xy_rect : public rectangle
{
public:
xy_rect() { } /*
@brief: the rectangle in the x-y plane
@param: the boundary of x axis
the boundary of y axis
the value of z axis
the material of rectangle
flip normal or not
*/
xy_rect(rtvar x1, rtvar x2, rtvar y1, rtvar y2, rtvar z0, material* mat, bool _flipNormal = false); virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override; virtual aabb getbox()const override;
}; //the statement of xz_rect class class xz_rect : public rectangle
{
public:
xz_rect() { } /*
@brief: the rectangle in the x-z plane
@param: the boundary of x axis
the boundary of z axis
the value of y axis
the material of rectangle
*/
xz_rect(rtvar x1, rtvar x2, rtvar z1, rtvar z2, rtvar y0, material* mat, bool _flipNormal = false); virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override; virtual aabb getbox()const override;
}; //the statement of yz_rect class class yz_rect : public rectangle
{
public:
yz_rect() { } /*
@brief: the rectangle in the y-z plane
@param: the boundary of y axis
the boundary of z axis
the value of x axis
the material of rectangle
*/
yz_rect(rtvar y1, rtvar y2, rtvar z1, rtvar z2, rtvar x0, material* mat, bool _flipNormal = false); virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override; virtual aabb getbox()const override;
}; // the implementation of xy_rect class inline xy_rect::xy_rect(rtvar x1, rtvar x2, rtvar y1, rtvar y2, rtvar z0, material* mat, bool _flipNormal)
: rectangle(x1, x2, y1, y2, z0, mat, rtvec(., ., .))
{
if (_flipNormal)flipNormal();
} bool xy_rect::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
{
rtvar t = (_other - sight.origin().z()) / sight.direction().z();
if (t < t_min || t > t_max)return false; rtvar x = sight.origin().x() + t*sight.direction().x();
rtvar y = sight.origin().y() + t*sight.direction().y();
if (x < _u1 || x > _u2 || y < _v1 || y > _v2)
return false; info._u = (x - _u1) / (_u2 - _u1);
info._v = (y - _v1) / (_v2 - _v1);
info._t = t;
info._materialp = _materialp;
info._p = sight.go(t);
info._n = rtvec(, , ); return true;
} aabb xy_rect::getbox()const
{
return aabb(rtvec(_u1, _v1, _other - 0.0001), rtvec(_u2, _v2, _other + 0.0001));
} // the implementation of xz_rect class inline xz_rect::xz_rect(rtvar x1, rtvar x2, rtvar z1, rtvar z2, rtvar y0, material* mat, bool _flipNormal)
: rectangle(x1, x2, z1, z2, y0, mat, rtvec(., ., .))
{
if (_flipNormal)flipNormal();
} bool xz_rect::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
{
rtvar t = (_other - sight.origin().y()) / sight.direction().y();
if (t < t_min || t > t_max)return false; rtvar x = sight.origin().x() + t*sight.direction().x();
rtvar z = sight.origin().z() + t*sight.direction().z();
if (x < _u1 || x > _u2 || z < _v1 || z > _v2)
return false; info._u = (x - _u1) / (_u2 - _u1);
info._v = (z - _v1) / (_v2 - _v1);
info._t = t;
info._materialp = _materialp;
info._p = sight.go(t);
info._n = rtvec(, , ); return true;
} aabb xz_rect::getbox()const
{
return aabb(rtvec(_u1, _other - 0.0001, _v1), rtvec(_u2, _other + 0.0001, _v2));
} // the implementation of yz_rect class inline yz_rect::yz_rect(rtvar y1, rtvar y2, rtvar z1, rtvar z2, rtvar x0, material* mat, bool _flipNormal)
: rectangle(y1, y2, z1, z2, x0, mat, rtvec(., ., .))
{
if (_flipNormal)flipNormal();
} bool yz_rect::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
{
rtvar t = (_other - sight.origin().x()) / sight.direction().x();
if (t < t_min || t > t_max)return false; rtvar y = sight.origin().y() + t*sight.direction().y();
rtvar z = sight.origin().z() + t*sight.direction().z();
if (y < _u1 || y > _u2 || z < _v1 || z > _v2)
return false; info._u = (y - _u1) / (_u2 - _u1);
info._v = (z - _v1) / (_v2 - _v1);
info._t = t;
info._materialp = _materialp;
info._p = sight.go(t);
info._n = rtvec(, , ); return true;
} aabb yz_rect::getbox()const
{
return aabb(rtvec(_other - 0.0001, _u1, _v1), rtvec(_other + 0.0001, _u2, _v2));
} }// rt namespace

rectangle.hpp

/// box.hpp
// https://www.cnblogs.com/lv-anchoret/p/10307569.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the box-class for the ray-tracing project
// from the 《ray tracing the next week》
// ----------------------------------------------------- #pragma once namespace rt
{ // the statement of box class class box: public intersect
{
public:
box() { } box(const rtvec& pointmin, const rtvec& pointmax, material * mat); virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override; virtual aabb getbox()const override; public:
// the normal of xy_rect, the normal's direction is +
inline const rtvec& xy_n() const { return _list[].Getnormal(); } // the normal of xy_rect, the normal's direction is -
inline const rtvec& xy_nflip() const { return _list[].Getnormal(); } inline const rtvec& xz_n() const { return _list[].Getnormal(); } inline const rtvec& xz_nflip() const { return _list[].Getnormal(); } inline const rtvec& yz_n() const { return _list[].Getnormal(); } inline const rtvec& yz_nflip() const { return _list[].Getnormal(); } inline void set_xy_material(material* m) { _list[].setmaterial(m); } inline void set_xy_flipmaterial(material* m) { _list[].setmaterial(m); } inline void set_xz_material(material* m) { _list[].setmaterial(m); } inline void set_xz_flipmaterial(material* m) { _list[].setmaterial(m); } inline void set_yz_material(material* m) { _list[].setmaterial(m); } inline void set_yz_flipmaterial(material* m) { _list[].setmaterial(m); } private:
rtvec _min; rtvec _max; rectangles* _list;
}; // the implementation of box class inline box::box(const rtvec& pointmin, const rtvec& pointmax, material * mat)
:_min(pointmin)
,_max(pointmax)
{
rectangle ** list = new rectangle*[];
list[] = new xy_rect(_min.x(), _max.x(), _min.y(), _max.y(), _max.z(), mat);
list[] = new xy_rect(_min.x(), _max.x(), _min.y(), _max.y(), _min.z(), mat, true);
list[] = new xz_rect(_min.x(), _max.x(), _min.z(), _max.z(), _max.y(), mat);
list[] = new xz_rect(_min.x(), _max.x(), _min.z(), _max.z(), _min.y(), mat, true);
list[] = new yz_rect(_min.y(), _max.y(), _min.z(), _max.z(), _max.x(), mat);
list[] = new yz_rect(_min.y(), _max.y(), _min.z(), _max.z(), _min.x(), mat, true);
_list = new rectangles(list, );
} bool box::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
{
return _list->hit(sight, t_min, t_max, info);
} aabb box::getbox()const
{
return aabb(_min, _max);
} } // rt namespace

box.hpp

material

/// RTmaterial.hpp

// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] some materials
// diffuse
// metal
// dielectric
// areaLight
// isotropic
// ----------------------------------------------------- #pragma once #include "E:\OpenGL\光线追踪\code\ray tracing 1-2\ray tracing 1-2\ray.hpp"
#include "E:\OpenGL\光线追踪\code\ray tracing 1-2\ray tracing 1-2\include\hit\intersect.hpp"
#include "material.hpp" #include "diffuse.hpp"
#include "metal.hpp"
#include "dielectric.hpp"
#include "light.hpp"
#include "isotropic.hpp"

RTmaterial.hpp

/// material.hpp

// -----------------------------------------------------
// [author] lv
// [begin ] 2018.12
// [brief ] the material-class for the ray-tracing project
// from the 《ray tracing in one week》
// ----------------------------------------------------- #pragma once namespace rt
{ // the statement of material class class material
{
public: /*
@brief: produce a scattered ray
@param: InRay -> Incident light
info -> the information of intersect-point(hit-point)
attenuation -> when scattered, how much the ray should be attenuated by tis reflectance R
scattered -> as we talk, it is a new sight; or
it is the scattered ray with the intersect-point
@retur: the function calculate a scattered ray or not
*/
virtual bool scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const = ; /*
@brief: 自发光
@param: 纹理所需信息
@retur: 纹理像素值
*/
virtual rtvec emitted(rtvar u, rtvar v, const rtvec& p)const { return rtvec(); } }; }

material.hpp

/// diffuse.hpp
// https://www.cnblogs.com/lv-anchoret/p/10198423.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2018.12
// [brief ] one of the materials
// ----------------------------------------------------- #pragma once namespace rt
{ class texture; // the statement of lambertian class class lambertian : public material
{
public:
lambertian(texture* _tex); virtual bool scatter(const ray& rIn, const hitInfo& info, rtvec& attenuation, ray& scattered)const override; public:
const texture* get_texture()const { return _albedo; } void set_texture(texture* tex) { _albedo = tex; } protected: texture* _albedo;
}; // the implementation of lambertian class inline lambertian::lambertian(texture* _tex)
:_albedo(_tex)
{
} bool lambertian::scatter(const ray& rIn, const hitInfo& info, rtvec& attenuation, ray& scattered)const
{
rtvec target = info._p + info._n + lvgm::random_unit_sphere();
scattered = ray{ info._p, target - info._p };
attenuation = _albedo->value(info._u, info._v, info._p);
return true;
} } // rt namespace

diffuse.hpp

/// metal.hpp
// https://www.cnblogs.com/lv-anchoret/p/10206773.html // -----------------------------------------------------
// [author] lv
// [begin ] 2018.12
// [brief ] one of the materials
// ----------------------------------------------------- #pragma once namespace rt
{ // the statement of metal class class metal :public material
{
public: /*
@param: f -> Fuzzy index
*/
metal(texture* a, const rtvar f = .); virtual bool scatter(const ray& rIn, const hitInfo& info, rtvec& attenuation, ray& scattered)const override; public:
const texture* get_texture()const { return _albedo; } const rtvar get_fuzz()const { return _fuzz; } void set_texture(texture* tex) { _albedo = tex; } void set_fuzz(const rtvar f) { _fuzz = f; } protected: texture* _albedo; rtvar _fuzz;
}; inline metal::metal(texture* a, const rtvar f )
:_albedo(a)
{
if (f < && f >= )_fuzz = f;
else _fuzz = ;
} bool metal::scatter(const ray& rIn, const hitInfo& info, rtvec& attenuation, ray& scattered)const
{
rtvec target = reflect(rIn.direction().ret_unitization(), info._n);
scattered = ray{ info._p, target + _fuzz * lvgm::random_unit_sphere() };
attenuation = _albedo->value(info._u, info._v, info._p);
return dot(scattered.direction(), info._n) != ;
} } // rt namespace

metal.hpp

/// dielectric.hpp
// https://www.cnblogs.com/lv-anchoret/p/10217719.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] one of the materials
// ----------------------------------------------------- #pragma once namespace rt
{ // the statement of dielectric class class dielectric :public material
{
public: /*
@param: refractive indices
*/
dielectric(const rtvar RI) :_RI(RI) { } virtual bool scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const override; public:
const rtvar get_refIndex()const { return _RI; } void set_refIndex(rtvar ri) { _RI = ri; } protected:
rtvar _RI; inline rtvar schlick(const rtvar cosine)const;
}; // the implementation of dielectric class bool dielectric::scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const
{
rtvec outward_normal;
rtvec refracted;
rtvec reflected = reflect(InRay.direction(), info._n);
rtvar eta;
rtvar reflect_prob;
rtvar cos;
attenuation = rtvec(., ., .); if (dot(InRay.direction(), info._n) > )
{
outward_normal = -info._n;
eta = _RI;
cos = _RI * dot(InRay.direction(), info._n) / InRay.direction().normal();
}
else
{
outward_normal = info._n;
eta = 1.0 / _RI;
cos = -dot(InRay.direction(), info._n) / InRay.direction().normal();
} if (refract(InRay.direction(), outward_normal, eta, refracted))
reflect_prob = schlick(cos); //如果有折射,计算反射系数
else
reflect_prob = 1.0; //如果没有折射,那么为全反射 if (lvgm::rand01() < reflect_prob)
scattered = ray(info._p, reflected);
else
scattered = ray(info._p, refracted); return true;
} inline rtvar dielectric::schlick(const rtvar cosine)const
{
rtvar r0 = (. - _RI) / (. + _RI);
r0 *= r0;
return r0 + ( - r0)*pow(( - cosine), );
} } // rt namespace

dielectric.hpp

/// light.hpp

// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the areaLight-class for the ray-tracing project
// from the 《ray tracing the next week》
// ----------------------------------------------------- #pragma once namespace rt
{ //the statement of areaLight class class areaLight :public material
{
public:
areaLight() { } areaLight(texture* mat) :_emit(mat) { } virtual bool scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const { return false; } virtual rtvec emitted(rtvar u, rtvar v, const rtvec& p)const { return _emit->value(u, v, p); } public:
const texture* get_texture()const { return _emit; } void set_texture(texture* tex) { _emit = tex; } private:
texture* _emit;
}; } // rt namespace

light.hpp

/// isotropic.hpp

// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the isotropic-class for the ray-tracing project
// from the 《ray tracing the next week》
// ----------------------------------------------------- #pragma once namespace rt
{ class isotropic :public material
{
public:
isotropic(texture* tex) :_albedo(tex) { } virtual bool scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const override
{
scattered = ray(info._p, lvgm::random_unit_sphere());
attenuation = _albedo->value(info._u, info._v, info._p);
return true;
} public:
const texture* get_texture()const { return _albedo; } void set_texture(texture* tex) { _albedo = tex; } private:
texture * _albedo;
}; } // rt namespace

isotropic.hpp

texture

/// RTtexture.hpp

// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] some textures
// checker
// constant
// Perlin noise
// image
// ----------------------------------------------------- #pragma once #include "RTdef.hpp" #include "texture.hpp" #include "checker_tex.hpp"
#include "constant_tex.hpp" #include "Perlin.hpp"
#include "noise_tex.hpp" #include "image_tex.hpp"

RTtexture.hpp

/// texture.hpp

// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the texture-class for the ray-tracing project
// from the 《ray tracing the next week》
// ----------------------------------------------------- #pragma once namespace rt
{ class texture
{
public: virtual rtvec value(rtvar u, rtvar v, const rtvec& p)const = ; }; } // rt namespace

texture.hpp

/// constant_tex.hpp
// https://www.cnblogs.com/lv-anchoret/p/10285473.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the constant_texture-class for the ray-tracing project
// from the 《ray tracing the next week》
// ----------------------------------------------------- #pragma once namespace rt
{ // the statement of constant_texture class class constant_texture :public texture
{
public: constant_texture() { } constant_texture(const rtvec& color); virtual rtvec value(rtvar u, rtvar v, const rtvec& p)const override; public: inline const rtvec& color()const { return _color; } inline void set_color(const rtvec& color) { _color = color; } private: rtvec _color; }; // the implementation of constant_texture class inline constant_texture::constant_texture(const rtvec& color)
:_color(color)
{
} rtvec constant_texture::value(rtvar u, rtvar v, const rtvec& p)const
{
return _color;
} } // rt namesapce

constant_texture .hpp

/// checker_tex.hpp
// https://www.cnblogs.com/lv-anchoret/p/10285473.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the checker_texture-class for the ray-tracing project
// from the 《ray tracing the next week》
// ----------------------------------------------------- #pragma once namespace rt
{ // the statement of checker_texture class class checker_texture :public texture
{
public:
checker_texture() { } /*
传入两个用于交错的纹理信息
*/
checker_texture(texture* t1, texture* t2); virtual rtvec value(rtvar u, rtvar v, const rtvec& p)const override; private: texture* _even; texture* _odd; }; // the implementation of checker_texture class inline checker_texture::checker_texture(texture * t1, texture * t2)
:_even(t1)
, _odd(t2)
{
} rtvec checker_texture::value(rtvar u, rtvar v, const rtvec& p)const
{
rtvar sines = sin( * p.x() + ) * sin( * p.y() + ) * sin( * p.z() + );
if (sines < )
return _odd->value(u, v, p);
else
return _even->value(u, v, p);
} } // rt namespace

checker_texture.hpp

/// image_tex.hpp
// https://www.cnblogs.com/lv-anchoret/p/10295137.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the image_texture-class for the ray-tracing project
// from the 《ray tracing the next week》
// ----------------------------------------------------- #pragma once namespace rt
{ // the statement of image_texture class class image_texture: public texture
{
public:
image_texture() { } /*
@param: image -> the rgb list of image texture
a b -> the size of image texture
*/
image_texture(unsigned char* image, size_t a, size_t b); virtual rtvec value(rtvar u, rtvar v, const rtvec& p)const override; public: inline unsigned char* image()const { return _image; } inline size_t sizeX()const { return _sizeX; } inline size_t sizeY()const { return _sizeY; } private:
unsigned char* _image; size_t _sizeX; size_t _sizeY;
}; //the implementation of image_texture class image_texture::image_texture(unsigned char* image, size_t a, size_t b)
:_image(image)
,_sizeX(a)
,_sizeY(b)
{
} rtvec image_texture::value(rtvar u, rtvar v, const rtvec& p)const
{
int i = u*_sizeX;
int j = ( - v)*_sizeY - 0.001;
if (i < )i = ;
if (j < )j = ;
if (i > _sizeX - )i = _sizeX - ;
if (j > _sizeY - )j = _sizeY - ;
rtvar r = int(_image[ * i + * _sizeX*j]) / 255.0;
rtvar g = int(_image[ * i + * _sizeX*j + ]) / 255.0;
rtvar b = int(_image[ * i + * _sizeX*j + ]) / 255.0;
return rtvec(r, g, b);
} } // rt namespace

image_texture.hpp

/// Perlin.hpp
// https://www.cnblogs.com/lv-anchoret/p/10291292.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the Perlin-class for the ray-tracing project
// from the 《ray tracing the next week》
// ----------------------------------------------------- #pragma once namespace rt
{ // the statement of Perlin class class Perlin
{
public: /*
产生噪声值
*/
inline rtvar noise(const rtvec& p)const; /*
@brief: 三线性插值
@param: list -> 辅助数组
u v w -> 0~1 的辅助系数
@retur: 插值结果值
*/
static rtvar perlin_interp(rtvec list[][][], rtvar u, rtvar v, rtvar w); /*
对噪声值进行采样
*/
inline rtvar turb(const rtvec& p, int depth) const; inline rtvec* randomvalue()const { return _randomvalue; } inline int* perm_x()const { return _perm_x; } inline int* perm_y()const { return _perm_y; } inline int* perm_z()const { return _perm_z; } private:
static rtvec * _randomvalue; static int * _perm_x; static int * _perm_y; static int * _perm_z;
}; // the implement of Perlin class rtvec * Perlin::_randomvalue = lvgm::random_normalized3D(); int * Perlin::_perm_x = lvgm::generate_random0n(); int * Perlin::_perm_y = lvgm::generate_random0n(); int * Perlin::_perm_z = lvgm::generate_random0n(); rtvar Perlin::turb(const rtvec& p, int depth = ) const
{
rtvar accumulate = ;
rtvec t = p;
rtvar weight = 1.0;
for (int i = ; i < depth; i++)
{
accumulate += weight*noise(t);
weight *= 0.5;
t *= ;
}
return abs(accumulate);
} inline rtvar Perlin::noise(const rtvec& p)const
{
int i = floor(p.x());
int j = floor(p.y());
int k = floor(p.z());
rtvar u = p.x() - i;
rtvar v = p.y() - j;
rtvar w = p.z() - k; rtvec list[][][];
for (int a = ; a < ; ++a)
for (int b = ; b < ; ++b)
for (int c = ; c < ; ++c)
{
list[a][b][c] = _randomvalue[_perm_x[(i + a) & ] ^ _perm_y[(j + b) & ] ^ _perm_z[(k + c) & ]];
#ifdef listtest
if (list[a][b][c].x() < )stds cout << "list.x < 0 " << stds endl;
if (list[a][b][c].y() < )stds cout << "list.y < 0 " << stds endl;
if (list[a][b][c].z() < )stds cout << "list.z < 0 " << stds endl;
#endif
}
return perlin_interp(list, u, v, w);
} rtvar Perlin::perlin_interp(rtvec list[][][], rtvar u, rtvar v, rtvar w)
{
#ifdef uvwtest
if (u < )stds cout << "u < 0 " << stds endl;
if (v < )stds cout << "v < 0 " << stds endl;
if (w < )stds cout << "w < 0 " << stds endl;
if (u > )stds cout << "u > 1 " << stds endl;
if (v > )stds cout << "v > 1 " << stds endl;
if (w > )stds cout << "w > 1 " << stds endl;
#endif
rtvar uu = u*u*( - * u);
rtvar vv = u*v*( - * v);
rtvar ww = u*w*( - * w); rtvar accumulate = ;
for (int i = ; i < ; ++i)
for (int j = ; j < ; ++j)
for (int k = ; k < ; ++k)
{
rtvec weight(u - i, v - j, w - k);
accumulate +=
(i*uu + ( - i) * ( - uu))*
(j*vv + ( - j) * ( - vv))*
(k*ww + ( - k) * ( - ww))*
lvgm::dot(list[i][j][k], weight);
#ifdef accumulatetest
if (accumulate < )stds cout << "accumulate < 0 " << stds endl;
#endif
}
return accumulate;
} } // rt namespace

Perlin.hpp

/// noise_tex.hpp
// https://www.cnblogs.com/lv-anchoret/p/10291292.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the noise_texture-class for the ray-tracing project
// from the 《ray tracing the next week》
// ----------------------------------------------------- #pragma once namespace rt
{ // the statement of noise_texture class class noise_texture :public texture
{
public:
noise_texture() { } noise_texture(const rtvar scale); virtual rtvec value(rtvar u, rtvar v, const rtvec& p)const override; inline const rtvar get_scale()const { return _scale; } inline void set_scale(const rtvar s) { _scale = s; } private:
Perlin _noise; rtvar _scale;
}; // the implementation of noise_texture class noise_texture::noise_texture(const rtvar scale)
:_scale(scale)
{
} rtvec noise_texture::value(rtvar u, rtvar v, const rtvec& p)const
{
return rtvec(., ., .) * 0.5 * ( + sin(_scale * p.z() + * _noise.noise(p)));
} } // rt namespace

noise_texture.hpp

如果有什么问题可以留言

【Ray Tracing The Next Week 超详解】 光线追踪2-9的相关教程结束。

《【Ray Tracing The Next Week 超详解】 光线追踪2-9.doc》

下载本文的Word格式文档,以方便收藏与打印。