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

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

目录

----include

--hit

--texture

--material

----RTdef.hpp

----ray.hpp

----camera.hpp

----main.cpp

先说最外面的

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

/// RTdef.hpp
// https://www.cnblogs.com/lv-anchoret/p/10243553.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1.1
// [brief ]        the basic concept of rt
// -----------------------------------------------------

#pragma once

#include <lvgm\type_vec\type_vec.h>        //https://www.cnblogs.com/lv-anchoret/p/10163085.html
#include <lvgm\opticsfunc.hpp>            //https://www.cnblogs.com/lv-anchoret/p/10241904.html
#include <lvgm\randfunc.hpp>            //https://www.cnblogs.com/lv-anchoret/p/10241904.html
#include <algorithm>

#define stds std::

namespace rt
{
    using rtvar = lvgm::precision;

    using rtvec = lvgm::vec3<rtvar>;

    constexpr static rtvar rtInf() { return static_cast<rtvar>(0x3f3f3f3f); }        //最大值

    constexpr rtvar π = 3.1415926;

    template<typename T>
    const T& rtmin(const T& a, const T& b)
    {
        return a < b ? a : b;
    }

    template<typename T>
    const T& rtmax(const T& a, const T& b)
    {
        return a > b ? a : b;
    }

}
RTdef.hpp
/// ray.hpp
// https://www.cnblogs.com/lv-anchoret/p/10182002.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2018.12
// [brief ]        the ray-class for the ray-tracing project
//                from the 《ray tracing in one week》
// -----------------------------------------------------

#pragma once

#include "RTdef.hpp"

namespace rt
{

class ray
    {
public:
    ray()
        :_a{ rtvec() }
        , _b{ rtvec() }
    {  }

    ray(const rtvec& a, const rtvec& b, const rtvar time = 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 
ray.hpp
/// camera.hpp
// https://www.cnblogs.com/lv-anchoret/p/10221058.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2018.12
// [brief ]        the camera-class for the ray-tracing project
//                from the 《ray tracing in one week》
// -----------------------------------------------------

#pragma once


namespace rt
{

// the statement of camera class

class camera
    {
public:

    /*
    @brief: the camera constructor
    @param: lookfrom -> the position of eye
            lookat -> the vector of sight's direction
            vup -> the vertical up direction
            vfov -> 相机在垂直方向上关于屏幕岔开的角度
            aspect -> Screen aspect ratio
            focus -> the focal length
            t1 -> Shutter start time
            t2 -> Shutter end time
    */
    camera(rtvec lookfrom, rtvec lookat, rtvec vup, rtvar vfov, rtvar aspect, rtvar aperture, rtvar focus, rtvar t1, rtvar t2);

    inline const ray get_ray(const rtvar u, const rtvar v)const;

    inline const ray get_ray(const lvgm::vec2<rtvar>& para)const;

public:
    inline const rtvec& eye()const { return _eye; }

    inline const rtvec& start()const { return _start; }

    inline const rtvec& horizontal()const { return _horizontal; }

    inline const rtvec& vertical()const { return _vertical; }

    inline const rtvec& u()const { return _u; }

    inline const rtvec& v()const { return _v; }

    inline const rtvec& w()const { return _w; }

    inline const rtvar lens_r()const { return _lens_radius; }

private:
    rtvec _u;

    rtvec _v;

    rtvec _w;

    rtvec _eye;

    rtvec _start;        //left-bottom

    rtvec _horizontal;

    rtvec _vertical;

    rtvar _lens_radius;  //the radius of lens

    rtvar _time1;

    rtvar _time2;

    };



//the implementation of camera class

inline camera::camera(rtvec lookfrom, rtvec lookat, rtvec vup, rtvar vfov, rtvar aspect, rtvar aperture, rtvar focus, rtvar t1, rtvar t2)
    :_eye(lookfrom)
    , _lens_radius(aperture / 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 
camera.hpp

 hit

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

/// RThit.hpp
//    https://www.cnblogs.com/lv-anchoret/p/10190092.html

// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        some intersects
//                intersections
//                aabb
//                bvh
//                sphere
//                moving_sphere
//                rectangle
//                rectangles
//                box
//                translate
//                rotate
// -----------------------------------------------------

#pragma once

#include "ray.hpp"
#include "aabb_box.hpp"
#include "intersect.hpp"

#include "bvh.hpp"
#include "intersections.hpp"

#include "sphere.hpp"
#include "moving_sphere.hpp"
#include "rectangleBasic.hpp"
#include "rectangles.hpp"
#include "rectangle.hpp"
#include "flip_normal.hpp"
#include "box.hpp"
#include "constant_medium.hpp"

#include "translate.hpp"
#include "rotate.hpp"
RThit.hpp
/// intersect.hpp
//https://www.cnblogs.com/lv-anchoret/p/10190092.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2018.12
// [brief ]        the intersect-class for the ray-tracing project
//                from the 《ray tracing in one week》
// -----------------------------------------------------

#pragma once


namespace rt
{
class material;
class aabb;


// the infomation of intersection point

struct hitInfo
    {
    lvgm::precision _t;        //ray 中的系数t
    rtvec _p;                //相交点、撞击点
    rtvec _n;                //_p点的表面法线
    material* _materialp;    //材质
    rtvar _u;                //texture-u
    rtvar _v;                //texture-v
    };


// the statement of intersect class

class intersect
    {
public:
    intersect() {  }

    /*
    @brief: 撞击函数,求取撞击点相关记录信息
    @param: sight->视线
    系数t的上下界->筛选撞击点
    info->返回撞击点信息
    @retur: 是否存在合法撞击点
    */
    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const = 0;

    /*
    @brief: get the box of Geometry
    */
    virtual aabb getbox()const = 0;

    };

}
intersect.hpp
/// aabb.hpp
// https://www.cnblogs.com/lv-anchoret/p/10284085.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the aabb-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------

#pragma once


namespace rt
{

//the statement of aabb class

class aabb
    {
public:
    aabb() {  }

    /*
    @brief: the box of Geometry
    @param: min -> 盒子坐标最小端
            max -> 盒子坐标最大端
    */
    aabb(const rtvec& min, const rtvec& max);

    /*
    @brief: 相交碰撞检测
    @param: sight -> 视线
            解的上下界
    @retur: 是否碰撞相交
    */
    inline bool hit(const ray& sight, rtvar tmin, rtvar tmax)const;

    /*
    @brief: 两个包围盒的包围盒
    */
    static aabb _surrounding_box(aabb box1, aabb box2);
    
public:

    inline const rtvec& min()const { return _min; }

    inline const rtvec& max()const { return _max; }

private:
    rtvec _min;

    rtvec _max;
    };


//the implementation of aabb class

inline aabb::aabb(const rtvec& min, const rtvec& max)
    :_min(min)
    ,_max(max)
    {
    }

inline bool aabb::hit(const ray& sight, rtvar tmin, rtvar tmax)const
    {
    for (int i = 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 
aabb.hpp
/// bvh.hpp
// https://www.cnblogs.com/lv-anchoret/p/10284085.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the bvh-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------

#pragma once


namespace rt
{

// the statement of bvh_node class

class bvh_node :public intersect
    {
public:
    bvh_node() {  }

    /*
    @brief: bvh树
    @param: world -> all Geometry of the scene
            n -> the number of the Geometry
            time1 -> start time
            time2 -> end time
    */
    bvh_node(intersect** world, const int n, const rtvar time1, const rtvar time2);


    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override;

    virtual aabb getbox()const override;

private:
    intersect* _left;

    intersect* _right;

    aabb _box;

    };


// the implementation of bvh_node class

inline    int _x_cmp(const void * lhs, const void * rhs)
    {
    intersect * lc = *(intersect**)lhs;
    intersect * rc = *(intersect**)rhs;
    aabb lbox = lc->getbox();
    aabb rbox = rc->getbox();

    if (lbox.min().x() - rbox.min().x() < 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
bvh.hpp
/// intersections.hpp
//    https://www.cnblogs.com/lv-anchoret/p/10190092.html

// -----------------------------------------------------
// [author]        lv
// [begin ]        2018.12
// [brief ]        the intersections-class for the ray-tracing project
//                from the 《ray tracing in one week》
// -----------------------------------------------------

#pragma once


namespace rt
{

// the statement of intersections class

class intersections :public intersect
    {
public:
    intersections() {  }


    /*
    @brief: the list of scene's Geometry
    @param: list -> all Geometry of the scene
            n -> the number of the scene's Geometry
    */    
    intersections(intersect** list, size_t n) :_list(list), _size(n) {  }
        
    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override;

    virtual aabb getbox()const override { return aabb(); }

public:
    const size_t get_size()const { return _size; }

private:
    intersect** _list;

    size_t _size;
    };



// the implementation of intersections class

bool intersections::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
    {
    hitInfo t_rec;
    bool hitSomething = false;
    rtvar far = t_max;            //刚开始可以看到无限远
    for (int i = 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 
intersections.hpp
/// sphere.hpp
//  https://www.cnblogs.com/lv-anchoret/p/10190092.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the sphere-class for the ray-tracing project
//                from the 《ray tracing in one week》
// -----------------------------------------------------
#pragma once

namespace rt
{

class sphere :public intersect
    {
public:
    sphere() {  }

        /*
        @para1: 球心坐标
        @para2: 球半径
        @para3: 材质
        */
    sphere(const rtvec& h, rtvar r, material* ma);

    ~sphere();
        
    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override;

    virtual aabb getbox()const override;

    /*
    获取几何球体中p点对应的2D纹理坐标(u,v)
    */
    static void get_sphere_uv(const rtvec& p, rtvar& u, rtvar& v);

public:

    inline const rtvar r()const            { return _radius;    }

    inline const rtvec& heart()const    { return _heart;    }

    inline const material* get_material()const { return _materialp; }

    inline rtvar& r()                    { return _radius;    }

    inline rtvec& heart()                { return _heart;    }

    inline void set_material(material* m) { _materialp = m; }
private:
    rtvec _heart;

    rtvar _radius;

    material* _materialp;
    };


inline sphere::sphere(const rtvec& h, rtvar r, material* ma) 
    :_heart(h)
    , _radius(r)
    , _materialp(ma)
    {
    }

inline sphere::~sphere() 
    {
    if (_materialp)
        delete _materialp; 
    }

bool sphere::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
    {
    rtvec trace = sight.origin() - _heart;
    rtvar a = dot(sight.direction(), sight.direction());
    rtvar b = 2.0 * dot(trace, sight.direction());
    rtvar c = dot(trace, trace) - _radius * _radius;
    rtvar delt = b*b - 4.0*a*c;
    if (delt > 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
sphere.hpp
/// moving_sphere.hpp
// https://www.cnblogs.com/lv-anchoret/p/10269488.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the moving_sphere-class for the ray-tracing project
//                from the 《ray tracing in one week》
// -----------------------------------------------------

#pragma once


namespace rt
{
//the statement of moving_shpere class

class moving_sphere :public intersect 
    {
public:
    moving_sphere() {  }

    /*
    @brief: 模拟运动模糊的几何体
    @param: 起始位置的球心
            结束位置的球心
            开始时间
            结束时间
            球体半径
            纹理
    */
    moving_sphere(rtvec heart1, rtvec heart2, rtvar t1, rtvar t2, rtvar r, material* mp);

    virtual bool hit(const ray& r, rtvar tmin, rtvar tmax, hitInfo& info)const override;

    virtual aabb getbox()const override;

    inline rtvec heart(rtvar t)const;
    
private:
    rtvec _heart1;
    rtvec _heart2;
    rtvar _time1;
    rtvar _time2;
    rtvar _radius;
    material* _materialp;
    };



//the implementation of moving_sphere class

inline moving_sphere::moving_sphere(rtvec heart1, rtvec heart2, rtvar t1, rtvar t2, rtvar r, material* mp)
    :_heart1(heart1)
    , _heart2(heart2)
    , _time1(t1)
    , _time2(t2)
    , _radius(r)
    , _materialp(mp)
    {
    }

inline rtvec moving_sphere::heart(rtvar t)const
    {
    return _heart1 + ((t - _time1) / (_time2 - _time1)) * (_heart2 - _heart1);
    }

bool moving_sphere::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
    {
    rtvec trace = sight.origin() - heart(sight.time());
    rtvar a = dot(sight.direction(), sight.direction());
    rtvar b = 2.0 * dot(trace, sight.direction());
    rtvar c = dot(trace, trace) - _radius * _radius;
    rtvar delt = b*b - 4.0*a*c;
    if (delt > 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
moving_sphere.hpp
/// constant_medium.hpp

// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the constant_dedium-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------


#pragma once

namespace rt
{

class constant_medium :public intersect
    {
public:
    /*
    形体
    颗粒浓度
    纹理
    */
    constant_medium(intersect* p, rtvar d, texture* tex);

    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override;

    virtual aabb getbox()const override;

public:
    inline const rtvar get_density()const { return _density; }

    inline const material* get_material()const { return _materialp; }

    inline void set_density(const rtvar density) { _density = density; }

    inline void set_material(material* m) { _materialp = m; }

private:
    intersect* _item;

    rtvar _density;

    material* _materialp;
    };



inline constant_medium::constant_medium(intersect* p, rtvar d, texture* tex)
    :_item(p)
    ,_density(d)
    ,_materialp(new isotropic(tex))
    {
    }

aabb constant_medium::getbox()const
    {
    return _item->getbox();
    }

bool constant_medium::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const 
    {
    hitInfo info1, info2;
    if (_item->hit(sight, -rtInf(), rtInf(), info1)) {
        if (_item->hit(sight, info1._t + 0.0001, rtInf(), info2)) {
            if (info1._t < t_min)
                info1._t = t_min;
            if (info2._t > t_max)
                info2._t = t_max;
            if (info1._t >= info2._t)
                return false;
            if (info1._t < 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
constant_medium.hpp
/// flip_normal.hpp
// https://www.cnblogs.com/lv-anchoret/p/10307569.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the flip_normal-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------

#pragma once


namespace rt
{
    
class flip_normal: public intersect
    {
public:
    flip_normal(intersect * p) :_p(p) {  }
    
    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override
        {
        if (_p->hit(sight, t_min, t_max, info))
            {
            info._n = -info._n;
            return true;
            }
        return false;
        }

    virtual aabb getbox()const override
        {
        return _p->getbox();
        }

private:
    intersect* _p;
    };

} // rt namespace 
flip_normal.hpp
/// translate.hpp
// https://www.cnblogs.com/lv-anchoret/p/10307569.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the translate-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------

#pragma once

namespace rt
{

class translate :public intersect
    {
public:
    translate(intersect* p, const rtvec& offset);

    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override;

    virtual aabb getbox()const override;

    inline const rtvec get_offset()const { return _offset; }

    inline void set_offset(const rtvec& f) { _offset = f; }

private:
    intersect* _item;

    rtvec _offset;

    };



translate::translate(intersect* p, const rtvec& offset)
    :_item(p)
    , _offset(offset)
    {
    }

bool translate::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
    {
    ray movedRay(sight.origin() - _offset, sight.direction(), sight.time());
    if (_item->hit(movedRay, t_min, t_max, info))
        {
        info._p += _offset;
        return true;
        }
    return false;
    }

aabb translate::getbox()const
    {
    aabb box = _item->getbox();
    return aabb(box.min() + _offset, box.max() + _offset);
    }

}// rt namespace
translate.hpp
/// rotate.hpp
// https://www.cnblogs.com/lv-anchoret/p/10307569.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the translate-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------

#pragma once



namespace rt
{

// the statement of rotate_y class

class rotate_y :public intersect
    {
public:
    rotate_y(intersect* p, rtvar angle);

    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override;

    virtual aabb getbox()const override;

private:
    intersect* _item;

    rtvar _sinθ;

    rtvar _cosθ;

    aabb _box;

    };

// the statement of rotate_x class

class rotate_x :public intersect
    {
public:
    rotate_x(intersect* p, rtvar angle);

    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override;

    virtual aabb getbox()const override;

private:
    intersect* _item;

    rtvar _sinθ;

    rtvar _cosθ;

    aabb _box;

    };

// the statement of rotate_z class

class rotate_z :public intersect
    {
public:
    rotate_z(intersect* p, rtvar angle);

    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override;

    virtual aabb getbox()const override;

private:
    intersect* _item;

    rtvar _sinθ;

    rtvar _cosθ;

    aabb _box;

    };


// the implementation of rotate_y class

rotate_y::rotate_y(intersect* p, rtvar angle)
    :_item(p)
    {
    rtvar radians = (π / 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
rotate.hpp

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

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

/// rectangleBasic.hpp

// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the rectangle-class for the ray-tracing project
// -----------------------------------------------------

#pragma once


namespace rt
{

class rectangle :public intersect
    {
public:
    rectangle() {  }

    rectangle(rtvar u1, rtvar u2, rtvar v1, rtvar v2, rtvar other, material* mat, rtvec nor)
        :_u1(u1)
        , _u2(u2)
        , _v1(v1)
        , _v2(v2)
        , _materialp(mat)
        , _normal(nor)
    {
    }


    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override = 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 
rectangleBasic.hpp
/// rectangles.hpp

// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the rectangles-class for the ray-tracing project
// -----------------------------------------------------

#pragma once


namespace rt
{
class rectangles:public rectangle
    {
public:
    rectangles(rectangle** list, size_t n) :_list(list), _size(n) {  }

    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override;

    virtual aabb getbox()const override { return aabb(); }

private:
    rectangle** _list;

    size_t _size;
    };



    // the implementation of intersections class

bool rectangles::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
    {
    hitInfo t_rec;
    bool hitSomething = false;
    rtvar far = t_max;            //刚开始可以看到无限远
    for (int i = 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;
    }

}
rectangles.hpp
/// rectangle.hpp

// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the rect-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------

#pragma once


namespace rt
{

//the statement of xy_rect class

class xy_rect : public rectangle 
    {
public:
    xy_rect() {  }

    /*
    @brief: the rectangle in the x-y plane
    @param: the boundary of x axis
            the boundary of y axis
            the value of z axis
            the material of rectangle
            flip normal or not
    */
    xy_rect(rtvar x1, rtvar x2, rtvar y1, rtvar y2, rtvar z0, material* mat, bool _flipNormal = false);

    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override;

    virtual aabb getbox()const override;
    };

//the statement of xz_rect class

class xz_rect : public rectangle 
    {
public:
    xz_rect() {  }

    /*
    @brief: the rectangle in the x-z plane
    @param: the boundary of x axis
            the boundary of z axis
            the value of y axis
            the material of rectangle
    */
    xz_rect(rtvar x1, rtvar x2, rtvar z1, rtvar z2, rtvar y0, material* mat, bool _flipNormal = false);

    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override;

    virtual aabb getbox()const override;
    };

//the statement of yz_rect class

class yz_rect : public rectangle 
    {
public:
    yz_rect() {  }

    /*
    @brief: the rectangle in the y-z plane
    @param: the boundary of y axis
            the boundary of z axis
            the value of x axis
            the material of rectangle
    */
    yz_rect(rtvar y1, rtvar y2, rtvar z1, rtvar z2, rtvar x0, material* mat, bool _flipNormal = false);

    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override;

    virtual aabb getbox()const override;
    };



// the implementation of xy_rect class

inline xy_rect::xy_rect(rtvar x1, rtvar x2, rtvar y1, rtvar y2, rtvar z0, material* mat, bool _flipNormal)
    : rectangle(x1, x2, y1, y2, z0, mat, rtvec(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 
rectangle.hpp
/// box.hpp
// https://www.cnblogs.com/lv-anchoret/p/10307569.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the box-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------

#pragma once


namespace rt
{

// the statement of box class

class box: public intersect
    {
public:
    box() {  }

    box(const rtvec& pointmin, const rtvec& pointmax, material * mat);

    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override;

    virtual aabb getbox()const override;

public:
    // the normal of xy_rect, the normal's direction is +
    inline const rtvec& xy_n()        const    { return _list[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 
box.hpp

 material

/// RTmaterial.hpp

// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        some materials
//                diffuse
//                metal
//                dielectric
//                areaLight
//                isotropic
// -----------------------------------------------------

#pragma once


#include "E:\OpenGL\光线追踪\code\ray tracing 1-2\ray tracing 1-2\ray.hpp"
#include "E:\OpenGL\光线追踪\code\ray tracing 1-2\ray tracing 1-2\include\hit\intersect.hpp"
#include "material.hpp"

#include "diffuse.hpp"
#include "metal.hpp"
#include "dielectric.hpp"
#include "light.hpp"
#include "isotropic.hpp"
RTmaterial.hpp
/// material.hpp

// -----------------------------------------------------
// [author]        lv
// [begin ]        2018.12
// [brief ]        the material-class for the ray-tracing project
//                from the 《ray tracing in one week》
// -----------------------------------------------------

#pragma once

namespace rt
{

// the statement of material class

class material
    {
public:

    /*
    @brief: produce a scattered ray
    @param: InRay -> Incident light
            info -> the information of intersect-point(hit-point)
            attenuation -> when scattered, how much the ray should be attenuated by tis reflectance R
            scattered -> as we talk, it is a new sight; or
                         it is the scattered ray with the intersect-point
    @retur: the function calculate a scattered ray or not
    */
    virtual bool scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const = 0;


    /*
    @brief: 自发光
    @param: 纹理所需信息
    @retur: 纹理像素值
    */
    virtual rtvec emitted(rtvar u, rtvar v, const rtvec& p)const { return rtvec(); }

    };

}
material.hpp
/// diffuse.hpp
// https://www.cnblogs.com/lv-anchoret/p/10198423.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2018.12
// [brief ]        one of the materials
// -----------------------------------------------------

#pragma once

namespace rt
{

class texture;

// the statement of lambertian class

class lambertian : public material
    {
public:
    lambertian(texture* _tex);

    virtual bool scatter(const ray& rIn, const hitInfo& info, rtvec& attenuation, ray& scattered)const override;

public:
    const texture* get_texture()const { return _albedo; }

    void set_texture(texture* tex) { _albedo = tex; }

protected:

    texture* _albedo;
    };



// the implementation of lambertian class

inline lambertian::lambertian(texture* _tex)
    :_albedo(_tex) 
    {
    }

bool lambertian::scatter(const ray& rIn, const hitInfo& info, rtvec& attenuation, ray& scattered)const
    {
    rtvec target = info._p + info._n + lvgm::random_unit_sphere();
    scattered = ray{ info._p, target - info._p };
    attenuation = _albedo->value(info._u, info._v, info._p);
    return true;
    }

} // rt namespace
diffuse.hpp
/// metal.hpp
// https://www.cnblogs.com/lv-anchoret/p/10206773.html

// -----------------------------------------------------
// [author]        lv
// [begin ]        2018.12
// [brief ]        one of the materials
// -----------------------------------------------------

#pragma once

namespace rt
{

// the statement of metal class

class metal :public material
    {
public:

    /*
    @param: f -> Fuzzy index
    */
    metal(texture* a, const rtvar f = 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
metal.hpp
/// dielectric.hpp
// https://www.cnblogs.com/lv-anchoret/p/10217719.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        one of the materials
// -----------------------------------------------------

#pragma once


namespace rt
{

// the statement of dielectric class

class dielectric :public material
    {
    public:

        /*
        @param: refractive indices
        */
        dielectric(const rtvar RI) :_RI(RI) {  }

        virtual bool scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const override;

    public:
        const rtvar get_refIndex()const { return _RI; }

        void set_refIndex(rtvar ri) { _RI = ri; }

    protected:
        rtvar _RI;

        inline rtvar schlick(const rtvar cosine)const;
    };



// the implementation of dielectric class

bool dielectric::scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const
    {
    rtvec outward_normal;
    rtvec refracted;
    rtvec reflected = reflect(InRay.direction(), info._n);
    rtvar eta;
    rtvar reflect_prob;
    rtvar cos;
    attenuation = rtvec(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 
dielectric.hpp
/// light.hpp

// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the areaLight-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------

#pragma once



namespace rt
{

//the statement of areaLight class

class areaLight :public material
    {
public:
    areaLight() {  }

    areaLight(texture* mat) :_emit(mat) {  }

    virtual bool scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const { return false; }

    virtual rtvec emitted(rtvar u, rtvar v, const rtvec& p)const { return _emit->value(u, v, p); }

public:
    const texture* get_texture()const { return _emit; }

    void set_texture(texture* tex) { _emit = tex; }

private:
    texture* _emit;
    };

} // rt namespace
light.hpp
/// isotropic.hpp

// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the isotropic-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------

#pragma once


namespace rt
{

    class isotropic :public material
    {
    public:
        isotropic(texture* tex) :_albedo(tex) {  }

        virtual bool scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const override
        {
            scattered = ray(info._p, lvgm::random_unit_sphere());
            attenuation = _albedo->value(info._u, info._v, info._p);
            return true;
        }

    public:
        const texture* get_texture()const { return _albedo; }

        void set_texture(texture* tex) { _albedo = tex; }

    private:
        texture * _albedo;
    };

} // rt namespace
isotropic.hpp

 texture

/// RTtexture.hpp

// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        some textures
//                checker
//                constant
//                Perlin noise
//                image
// -----------------------------------------------------

#pragma once


#include "RTdef.hpp"

#include "texture.hpp"

#include "checker_tex.hpp"
#include "constant_tex.hpp"

#include "Perlin.hpp"
#include "noise_tex.hpp"

#include "image_tex.hpp"
RTtexture.hpp
/// texture.hpp

// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the texture-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------

#pragma once


namespace rt
{

class texture
    {
public:
    
    virtual rtvec value(rtvar u, rtvar v, const rtvec& p)const = 0;
    
    };

} // rt namespace
texture.hpp
/// constant_tex.hpp
// https://www.cnblogs.com/lv-anchoret/p/10285473.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the constant_texture-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------

#pragma once


namespace rt
{

// the statement of constant_texture class
    
class constant_texture :public texture
    {
public:

    constant_texture() {  }

    constant_texture(const rtvec& color);

    virtual rtvec value(rtvar u, rtvar v, const rtvec& p)const override;

public:

    inline const rtvec& color()const { return _color; }

    inline void set_color(const rtvec& color) { _color = color; }

private:

    rtvec _color;

    };



// the implementation of constant_texture class

inline constant_texture::constant_texture(const rtvec& color)
    :_color(color)
    {
    }

rtvec constant_texture::value(rtvar u, rtvar v, const rtvec& p)const
    {
    return _color;
    }

} // rt namesapce 
constant_texture .hpp
/// checker_tex.hpp
// https://www.cnblogs.com/lv-anchoret/p/10285473.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the checker_texture-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------


#pragma once


namespace rt
{

// the statement of checker_texture class

class checker_texture :public texture
    {
    public:
        checker_texture() {  }

        /*
        传入两个用于交错的纹理信息
        */
        checker_texture(texture* t1, texture* t2);

        virtual rtvec value(rtvar u, rtvar v, const rtvec& p)const override;

    private:

        texture* _even;

        texture* _odd;

    };



// the implementation of checker_texture class

inline checker_texture::checker_texture(texture * t1, texture * t2)
        :_even(t1)
        , _odd(t2)
    {
    }

rtvec checker_texture::value(rtvar u, rtvar v, const rtvec& p)const
    {
    rtvar sines = sin(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 
checker_texture.hpp
/// image_tex.hpp
// https://www.cnblogs.com/lv-anchoret/p/10295137.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the image_texture-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------

#pragma once 

namespace rt
{

// the statement of image_texture class

class image_texture: public texture
    {
public:
    image_texture() {  }

    /*
    @param: image -> the rgb list of image texture 
            a  b  -> the size of image texture
    */
    image_texture(unsigned char* image, size_t a, size_t b);

    virtual rtvec value(rtvar u, rtvar v, const rtvec& p)const override;

public:

    inline unsigned char* image()const    { return _image; }

    inline size_t sizeX()const            { return _sizeX; }

    inline size_t sizeY()const            { return _sizeY; }

private:
    unsigned char* _image;

    size_t _sizeX;

    size_t _sizeY;
    };



//the implementation of image_texture class

image_texture::image_texture(unsigned char* image, size_t a, size_t b)
    :_image(image)
    ,_sizeX(a)
    ,_sizeY(b)
    {
    }

rtvec image_texture::value(rtvar u, rtvar v, const rtvec& p)const
    {
    int i = u*_sizeX;
    int j = (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 
image_texture.hpp
/// Perlin.hpp
// https://www.cnblogs.com/lv-anchoret/p/10291292.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the Perlin-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------

#pragma once


namespace rt
{

// the statement of Perlin class

class Perlin
    {
public:

    /*
    产生噪声值
    */
    inline rtvar noise(const rtvec& p)const;

    /*
    @brief: 三线性插值
    @param: list -> 辅助数组
    u v w -> 0~1 的辅助系数
    @retur: 插值结果值
    */
    static rtvar perlin_interp(rtvec list[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 
Perlin.hpp
/// noise_tex.hpp
// https://www.cnblogs.com/lv-anchoret/p/10291292.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the noise_texture-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------

#pragma once


namespace rt
{

// the statement of noise_texture class

class noise_texture :public texture
    {
public:
    noise_texture() {  }

    noise_texture(const rtvar scale);    

    virtual rtvec value(rtvar u, rtvar v, const rtvec& p)const override;

    inline const rtvar get_scale()const { return _scale; }

    inline void set_scale(const rtvar s) { _scale = s; }
    
private:
    Perlin _noise;

    rtvar _scale;
    };



// the implementation of noise_texture class

noise_texture::noise_texture(const rtvar scale)
    :_scale(scale)
    {
    }

rtvec noise_texture::value(rtvar u, rtvar v, const rtvec& p)const
    {
    return rtvec(1., 1., 1.) * 0.5 * (1 + sin(_scale * p.z() + 10 * _noise.noise(p)));
    }

} // rt namespace 
noise_texture.hpp

如果有什么问题可以留言

猜你喜欢

转载自www.cnblogs.com/lv-anchoret/p/10327509.html