Ray Tracing The Next Week 超详解 光线追踪2-9
Posted lv-anchoret
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Ray Tracing The Next Week 超详解 光线追踪2-9相关的知识,希望对你有一定的参考价值。
我们来整理一下项目的代码
目录
----include
--hit
--texture
--material
----RTdef.hpp
----ray.hpp
----camera.hpp
----main.cpp
3D泛型数学库中的randomfunc.hpp增加了新内容
#pragma once #include <lvgm ype_vec ype_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 "; throw "integer doesn‘t have a random number from 0 to 1 "; } 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 "; throw "integer doesn‘t have a random number from 0 to 1 "; } 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 "; throw "integer doesn‘t have a random number from 0 to 1 "; } 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 ype_vec ype_vec.h> //https://www.cnblogs.com/lv-anchoret/p/10163085.html #include <lvgmopticsfunc.hpp> //https://www.cnblogs.com/lv-anchoret/p/10241904.html #include <lvgm andfunc.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 ay tracing 1-2 ay tracing 1-2 ay.hpp" #include "E:OpenGL光线追踪code ay tracing 1-2 ay tracing 1-2includehitintersect.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
如果有什么问题可以留言
以上是关于Ray Tracing The Next Week 超详解 光线追踪2-9的主要内容,如果未能解决你的问题,请参考以下文章
Ray Tracing The Next Week 超详解 光线追踪2-4
Ray Tracing The Next Week 超详解 光线追踪2-3
Ray Tracing The Next Week 超详解 光线追踪2-7 任意长方体 && 场景案例
RAY TRACING THE REST OF YOUR LIFE 超详解 光线追踪 3-1