我们来整理一下项目的代码
目录
----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>(1, 1, 1);
} 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>(1, 1);
} 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 = 0; i < size; ++i)
p[i] = vec2<T>( -1 + 2 * lvgm::rand01(), -1 + 2 * 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 = 0; i < size; ++i)
p[i] = vec3<T>(-1 + 2 * lvgm::rand01(), -1 + 2 * lvgm::rand01(), -1 + 2 * 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 = 0; i < size; ++i)
p[i] = -1 + 2 * lvgm::rand01();
return p;
}
//@brief: make list permute randomly
template<typename T>
void permute_method(T * p, size_t n)
{
for (int i = n - 1; i; i--)
{
size_t tar = int(lvgm::rand01() * (i + 1));
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 = 0; i < 256; ++i) p[i] = i;
permute_method(p, 256);
return p;
}
}
先说最外面的
基本和上次的没什么区别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;
}
}
/// 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 = 0.)
:_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
/// 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 / 2)
, _time1(t1)
, _time2(t2)
{
rtvar theta = vfov * π / 180;
rtvar half_height = tan(theta / 2) * 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 = 2 * half_width * _u;
_vertical = 2 * 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
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"
/// 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 = 0;
/*
@brief: get the box of Geometry
*/
virtual aabb getbox()const = 0;
};
}
/// 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 = 0; i < 3; ++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 < 0.)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
/// 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() < 0.)
return -1;
else
return 1;
}
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() < 0.)
return -1;
else
return 1;
}
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() < 0.)
return -1;
else
return 1;
}
inline bvh_node::bvh_node(intersect** world, const int n, const rtvar time1, const rtvar time2)
{
int axis = static_cast<int>(3 * lvgm::rand01());
if (axis == 0)
qsort(world, n, sizeof(intersect*), _x_cmp);
else if (axis == 1)
qsort(world, n, sizeof(intersect*), _y_cmp);
else
qsort(world, n, sizeof(intersect*), _z_cmp);
if (n == 1)
_left = _right = world[0];
else if (n == 2)
_left = world[0],
_right = world[1];
else
_left = new bvh_node(world, n / 2, time1, time2),
_right = new bvh_node(world + n / 2, n - n / 2, 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
/// 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 = 0; 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
/// 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 > 0)
{
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 = 1 - (φ + π) / (2 * π);
v = (θ + π / 2) / π;
}
} // rt namespace
/// 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 > 0)
{
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
/// 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 < 0)
info1._t = 0;
float distance_inside_boundary = (info2._t - info1._t)*sight.direction().normal();
float hit_distance = -(1 / _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
/// 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
/// 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
/// 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 = (π / 180.) * angle;
_sinθ = sin(radians);
_cosθ = cos(radians);
rtvec min(rtInf(), rtInf(), rtInf());
rtvec max = -min;
for (int i = 0; i < 2; ++i)
for (int j = 0; j < 2; ++j)
for (int k = 0; k < 2; ++k)
{
rtvar x = i * _box.max().x() + (1 - i)*_box.min().x();
rtvar y = j * _box.max().y() + (1 - j)*_box.min().y();
rtvar z = k * _box.max().z() + (1 - k)*_box.min().z();
rtvar newx = _cosθ * x + _sinθ * z;
rtvar newz = -_sinθ * x + _cosθ * z;
rtvec tester(newx, y, newz);
for (int c = 0; c < 3; ++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[0] = _cosθ * sight.origin()[0] - _sinθ * sight.origin()[2];
eye[2] = _sinθ * sight.origin()[0] + _cosθ * sight.origin()[2];
direction[0] = _cosθ * sight.direction()[0] - _sinθ * sight.direction()[2];
direction[2] = _sinθ * sight.direction()[0] + _cosθ * sight.direction()[2];
ray rotatedRay(eye, direction, sight.time());
if (_item->hit(rotatedRay, t_min, t_max, info))
{
rtvec p = info._p;
rtvec n = info._n;
p[0] = _cosθ * info._p[0] + _sinθ * info._p[2];
p[2] = -_sinθ * info._p[0] + _cosθ * info._p[2];
n[0] = _cosθ * info._n[0] + _sinθ * info._n[2];
n[2] = -_sinθ * info._n[0] + _cosθ * info._n[2];
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 = (π / 180.) * angle;
_sinθ = sin(radians);
_cosθ = cos(radians);
rtvec min(rtInf(), rtInf(), rtInf());
rtvec max = -min;
for (int i = 0; i < 2; ++i)
for (int j = 0; j < 2; ++j)
for (int k = 0; k < 2; ++k)
{
rtvar x = i * _box.max().x() + (1 - i)*_box.min().x();
rtvar y = j * _box.max().y() + (1 - j)*_box.min().y();
rtvar z = k * _box.max().z() + (1 - k)*_box.min().z();
rtvar newy = _cosθ * x - _sinθ * z;
rtvar newz = _sinθ * x + _cosθ * z;
rtvec tester(x, newy, newz);
for (int c = 0; c < 3; ++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[1] = _cosθ * sight.origin()[1] + _sinθ * sight.origin()[2];
eye[2] = -_sinθ * sight.origin()[1] + _cosθ * sight.origin()[2];
direction[1] = _cosθ * sight.direction()[1] + _sinθ * sight.direction()[2];
direction[2] = -_sinθ * sight.direction()[1] + _cosθ * sight.direction()[2];
ray rotatedRay(eye, direction, sight.time());
if (_item->hit(rotatedRay, t_min, t_max, info))
{
rtvec p = info._p;
rtvec n = info._n;
p[1] = _cosθ * info._p[1] - _sinθ * info._p[2];
p[2] = _sinθ * info._p[1] + _cosθ * info._p[2];
n[1] = _cosθ * info._n[1] - _sinθ * info._n[2];
n[2] = _sinθ * info._n[1] + _cosθ * info._n[2];
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 = (π / 180.) * angle;
_sinθ = sin(radians);
_cosθ = cos(radians);
rtvec min(rtInf(), rtInf(), rtInf());
rtvec max = -min;
for (int i = 0; i < 2; ++i)
for (int j = 0; j < 2; ++j)
for (int k = 0; k < 2; ++k)
{
rtvar x = i * _box.max().x() + (1 - i)*_box.min().x();
rtvar y = j * _box.max().y() + (1 - j)*_box.min().y();
rtvar z = k * _box.max().z() + (1 - k)*_box.min().z();
rtvar newx = _cosθ * x - _sinθ * y;
rtvar newy = _sinθ * x + _cosθ * y;
rtvec tester(newx, newy, z);
for (int c = 0; c < 3; ++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[0] = _cosθ * sight.origin()[0] + _sinθ * sight.origin()[1];
eye[1] = -_sinθ * sight.origin()[0] + _cosθ * sight.origin()[1];
direction[0] = _cosθ * sight.direction()[0] + _sinθ * sight.direction()[1];
direction[1] = -_sinθ * sight.direction()[0] + _cosθ * sight.direction()[1];
ray rotatedRay(eye, direction, sight.time());
if (_item->hit(rotatedRay, t_min, t_max, info))
{
rtvec p = info._p;
rtvec n = info._n;
p[0] = _cosθ * info._p[0] - _sinθ * info._p[1];
p[1] = _sinθ * info._p[0] + _cosθ * info._p[1];
n[0] = _cosθ * info._n[0] - _sinθ * info._n[1];
n[1] = _sinθ * info._n[0] + _cosθ * info._n[1];
info._p = p;
info._n = n;
return true;
}
return false;
}
aabb rotate_z::getbox()const
{
return _box;
}
} // rt namespace
鉴于这个系统的变换均针对运算期的每个点做变换,具体的几何体不变,所以,造成一个问题就是不方便获取变换后的法线等信息,也不利于重置纹理材质等,就长方体而言,比如我想改变某个面的纹理或者材质,就很困难,所以新加了很多接口,但是,这些接口都是统一的,所以又重新整了一个基类,提高代码重用性和可维护性
但我只改了反转向量,没有对旋转做适应的变动
/// 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 = 0;
virtual aabb getbox()const = 0;
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
/// 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 = 0; i < _size; ++i)
{
if (_list[i]->hit(sight, t_min, far, t_rec))
{
hitSomething = true;
far = t_rec._t; //将上一次的最近撞击点作为视线可达最远处
info = t_rec;
}
}
return hitSomething;
}
}
/// 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(0., 0., 1.))
{
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(0, 0, 1);
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(0., 1., 0.))
{
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(0, 1, 0);
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(1., 0., 0.))
{
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(1, 0, 0);
return true;
}
aabb yz_rect::getbox()const
{
return aabb(rtvec(_other - 0.0001, _u1, _v1), rtvec(_other + 0.0001, _u2, _v2));
}
}// rt namespace
/// 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[0].Getnormal(); }
// the normal of xy_rect, the normal's direction is -
inline const rtvec& xy_nflip() const { return _list[1].Getnormal(); }
inline const rtvec& xz_n() const { return _list[2].Getnormal(); }
inline const rtvec& xz_nflip() const { return _list[3].Getnormal(); }
inline const rtvec& yz_n() const { return _list[4].Getnormal(); }
inline const rtvec& yz_nflip() const { return _list[5].Getnormal(); }
inline void set_xy_material(material* m) { _list[0].setmaterial(m); }
inline void set_xy_flipmaterial(material* m) { _list[1].setmaterial(m); }
inline void set_xz_material(material* m) { _list[2].setmaterial(m); }
inline void set_xz_flipmaterial(material* m) { _list[3].setmaterial(m); }
inline void set_yz_material(material* m) { _list[4].setmaterial(m); }
inline void set_yz_flipmaterial(material* m) { _list[5].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*[6];
list[0] = new xy_rect(_min.x(), _max.x(), _min.y(), _max.y(), _max.z(), mat);
list[1] = new xy_rect(_min.x(), _max.x(), _min.y(), _max.y(), _min.z(), mat, true);
list[2] = new xz_rect(_min.x(), _max.x(), _min.z(), _max.z(), _max.y(), mat);
list[3] = new xz_rect(_min.x(), _max.x(), _min.z(), _max.z(), _min.y(), mat, true);
list[4] = new yz_rect(_min.y(), _max.y(), _min.z(), _max.z(), _max.x(), mat);
list[5] = new yz_rect(_min.y(), _max.y(), _min.z(), _max.z(), _min.x(), mat, true);
_list = new rectangles(list, 6);
}
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
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"
/// 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 = 0;
/*
@brief: 自发光
@param: 纹理所需信息
@retur: 纹理像素值
*/
virtual rtvec emitted(rtvar u, rtvar v, const rtvec& p)const { return rtvec(); }
};
}
/// 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
/// 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 = 0.);
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 < 1 && f >= 0)_fuzz = f;
else _fuzz = 1;
}
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) != 0;
}
} // rt namespace
/// 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(1., 1., 1.);
if (dot(InRay.direction(), info._n) > 0)
{
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 = (1. - _RI) / (1. + _RI);
r0 *= r0;
return r0 + (1 - r0)*pow((1 - cosine), 5);
}
} // rt namespace
/// 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
/// 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
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"
/// 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 = 0;
};
} // rt namespace
/// 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
/// 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(10 * p.x() + 2) * sin(10 * p.y() + 2) * sin(10 * p.z() + 2);
if (sines < 0)
return _odd->value(u, v, p);
else
return _even->value(u, v, p);
}
} // rt namespace
/// 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 = (1 - v)*_sizeY - 0.001;
if (i < 0)i = 0;
if (j < 0)j = 0;
if (i > _sizeX - 1)i = _sizeX - 1;
if (j > _sizeY - 1)j = _sizeY - 1;
rtvar r = int(_image[3 * i + 3 * _sizeX*j]) / 255.0;
rtvar g = int(_image[3 * i + 3 * _sizeX*j + 1]) / 255.0;
rtvar b = int(_image[3 * i + 3 * _sizeX*j + 2]) / 255.0;
return rtvec(r, g, b);
}
} // rt namespace
/// 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[2][2][2], 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(256);
int * Perlin::_perm_x = lvgm::generate_random0n(256);
int * Perlin::_perm_y = lvgm::generate_random0n(256);
int * Perlin::_perm_z = lvgm::generate_random0n(256);
rtvar Perlin::turb(const rtvec& p, int depth = 7) const
{
rtvar accumulate = 0;
rtvec t = p;
rtvar weight = 1.0;
for (int i = 0; i < depth; i++)
{
accumulate += weight*noise(t);
weight *= 0.5;
t *= 2;
}
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[2][2][2];
for (int a = 0; a < 2; ++a)
for (int b = 0; b < 2; ++b)
for (int c = 0; c < 2; ++c)
{
list[a][b][c] = _randomvalue[_perm_x[(i + a) & 255] ^ _perm_y[(j + b) & 255] ^ _perm_z[(k + c) & 255]];
#ifdef listtest
if (list[a][b][c].x() < 0)stds cout << "list.x < 0 " << stds endl;
if (list[a][b][c].y() < 0)stds cout << "list.y < 0 " << stds endl;
if (list[a][b][c].z() < 0)stds cout << "list.z < 0 " << stds endl;
#endif
}
return perlin_interp(list, u, v, w);
}
rtvar Perlin::perlin_interp(rtvec list[2][2][2], rtvar u, rtvar v, rtvar w)
{
#ifdef uvwtest
if (u < 0)stds cout << "u < 0 " << stds endl;
if (v < 0)stds cout << "v < 0 " << stds endl;
if (w < 0)stds cout << "w < 0 " << stds endl;
if (u > 1)stds cout << "u > 1 " << stds endl;
if (v > 1)stds cout << "v > 1 " << stds endl;
if (w > 1)stds cout << "w > 1 " << stds endl;
#endif
rtvar uu = u*u*(3 - 2 * u);
rtvar vv = u*v*(3 - 2 * v);
rtvar ww = u*w*(3 - 2 * w);
rtvar accumulate = 0;
for (int i = 0; i < 2; ++i)
for (int j = 0; j < 2; ++j)
for (int k = 0; k < 2; ++k)
{
rtvec weight(u - i, v - j, w - k);
accumulate +=
(i*uu + (1 - i) * (1 - uu))*
(j*vv + (1 - j) * (1 - vv))*
(k*ww + (1 - k) * (1 - ww))*
lvgm::dot(list[i][j][k], weight);
#ifdef accumulatetest
if (accumulate < 0)stds cout << "accumulate < 0 " << stds endl;
#endif
}
return accumulate;
}
} // rt namespace
/// 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(1., 1., 1.) * 0.5 * (1 + sin(_scale * p.z() + 10 * _noise.noise(p)));
}
} // rt namespace
如果有什么问题可以留言
来源:oschina
链接:https://my.oschina.net/u/4413093/blog/3662222