games101:七,加速光线追踪(AABB、BVH、SAH)+ 作业6

在光线追踪中,我们不仅要计算多次反射折射,还要计算每个光线的每个接触点与模型的每个三角形是否相交和相交点,计算量巨大,因此学习本节“加速光线追踪”算法来加速这个过程。

一,轴对齐包围盒(Axis-Aligned Bounding Box)-AABB

本质是将空间根据轴分为多个立方体区域,先判断光线进不进改立方体,在判断里边的模型,简单有效。

将每个立方体看做3对面或者3对轴坐标,直接卡位计算光线是否进入立方体,以二维为例:
在这里插入图片描述
t e n t e r = m a x ( t m i n ) t e x i t = m i n ( t m a x ) } = > { t e n t e r < t e x i t t e x i t > 0 \left.\begin{matrix}t_{enter} = max(t_{min})\\t_{exit} = min(t_{max}) \end{matrix}\right\}=>\left\{\begin{matrix}t_{enter} < t_{exit}\\t_{exit} > 0 \end{matrix}\right. tenter=max(tmin)texit=min(tmax)}=>{tenter<texittexit>0

  • 回顾一下光线与显示曲面求交的方法:(左式光线右式三角形平面)
    -

二,Bounding Volume Hierarchy-BVH

但如果场景中全都是细小模型,则aabb方法加速效果不明显,这时需要对aabb算法加速。一般有对空间划分和对象划分的加速,其中均匀空间划分(Uniform Spatial Partitions)和KD-Tree空间划分两种算法都是针对空间划分的优化,但有可能一个对象被两个包围盒都包含,因此目前不常用。

另一种以对象作为划分依据的典型算法就是BVH,用树形结构将对象分为各个部分。
在这里插入图片描述

  • 划分过程:

    1. 先计算场景中所有物体的总体包围盒,并计算包围盒在xyz轴中最长边的轴,假设为x。
    2. 计算所有物体的重心或包围盒中心,以x轴坐标排序,并按照顺序将物体分为2部分,作为左右叶子。
    3. 对左右叶子重复进行12步骤,直到当前包围盒中物体足够少为止,再把最后的物体信息储存在叶子结点上。(中间结点不储存物体对象信息)
  • 划分后光线求交点伪代码:
    在这里插入图片描述

三,Surface Area Heuristic-SAH

  • BVH算法将空间以对象为依据层层划分为对称结构,但实际中对称的划分有可能并不是最快的空间划分方式,还要考虑对象位置分布等情况。因此SAH就是求每种划分的运算时间以得到本理论的最好划分方法的算法。
  • 老师给的自学地址:http://15462.courses.cs.cmu.edu/fall2015/lecture/acceleration/slide_024

在BVH中划分过程中,取某个轴将各个物体按坐标排序后,理论上有N-1个分法。假设是S0空间被分为了S1和S2空间,SAH理论认为:

  1. 已经击中S0的情况下击中S1的概率 = S1表面积/S0表面积
  2. 该划分下的运算时间公式如下图,其中 C t r a v C_{trav} Ctrav为遍历空间二叉树box运算时间,计算比大小时可以去掉; C i s e c t C_{isect} Cisect为每个物体求交时间,可以设为常量计算,如1;N为物体个数。

因此我们遍历计算 以每个轴为基准划分–每个划分法(n-1个)下所有的运行时间,找出最小值对应的划分方法,作为最后的空间划分方法。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

作业6

  • Triangle::getIntersection in Triangle.hpp----注意看老师写的光线与显示曲面求交解法,比我自己写的漂亮多了,学习下。
inline Intersection Triangle::getIntersection(Ray ray)
{
    Intersection inter;

    if (dotProduct(ray.direction, normal) > 0)
        return inter;
    double u, v, t_tmp = 0;
    Vector3f pvec = crossProduct(ray.direction, e2);//S1
    double det = dotProduct(e1, pvec);
    if (fabs(det) < EPSILON)
        return inter;

    double det_inv = 1. / det;
    Vector3f tvec = ray.origin - v0;//S
    u = dotProduct(tvec, pvec) * det_inv;
    if (u < 0 || u > 1)
        return inter;
    Vector3f qvec = crossProduct(tvec, e1);//S2
    v = dotProduct(ray.direction, qvec) * det_inv;
    if (v < 0 || u + v > 1)
        return inter;
    t_tmp = dotProduct(e2, qvec) * det_inv;

    // TODO find ray triangle intersection
    if (t_tmp < 0)
        return inter;
    
    inter.distance = t_tmp;
    inter.happened = true;
    inter.m = m;
    inter.obj = this;
    inter.normal = normal;
    inter.coords = ray(t_tmp);

    return inter;
}
  • Bounds3::IntersectP in Bounds3.hpp–AABB相交判断,注意光线方向不同取值不同
inline bool Bounds3::IntersectP(const Ray& ray, const Vector3f& invDir,
                                const std::array<int, 3>& dirIsNeg) const
{
    Vector3f dirIsNegVec = Vector3f(dirIsNeg[0],dirIsNeg[1],dirIsNeg[2]);
    Vector3f One(1.0,1.0,1.0);
    
    Vector3f t_min, t_max;
    t_min = ((pMin*dirIsNegVec+pMax*(One-dirIsNegVec)) - ray.origin)*invDir;
    t_max = ((pMax*dirIsNegVec+pMin*(One-dirIsNegVec)) - ray.origin)*invDir;
    
    float t_enter,t_exit;
    t_enter = fmax(fmax(t_min.x,t_min.y),t_min.z);
    t_exit = fmin(fmin(t_max.x,t_max.y),t_max.z);
    
    return  (t_enter < t_exit && t_exit >= 0);
    
}
  • BVHAccel::getIntersection in BVH.cpp–划分树节点后光线求交点
Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const
{
    Intersection isect;
    
    Vector3f invDir(1.0/ray.direction.x,1.0/ray.direction.y,1.0/ray.direction.z);
    std::array<int, 3> dirIsNeg = {int(ray.direction.x>0),int(ray.direction.y>0),int(ray.direction.z>0)};
    
    if(!node->bounds.IntersectP(ray,invDir,dirIsNeg))
        return isect;
    
    if(node->left == nullptr && node->right == nullptr){
        return node->object->getIntersection(ray);
    }
    
    Intersection hitleft = getIntersection(node->left,ray);
    Intersection hitright = getIntersection(node->right,ray);

    return hitleft.distance < hitright.distance ? hitleft : hitright;
}
  • BVHAccel::recursiveBuildSAH in BVH.cpp–SAH划分else部分,其他和BVH一样
else {
        Bounds3 centroidBounds;
        for (int i = 0; i < objects.size(); ++i)
            centroidBounds = Union(centroidBounds, objects[i]->getBounds().Centroid());
        
        
        double time_svh = 10000.0;
        int index = 0;
        int axis = 0;
        
        double S0 = bounds.SurfaceArea();
        for (int i = 0; i < 3; i++)
        {
            switch (i) {
            case 0:
                std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
                    return f1->getBounds().Centroid().x <
                           f2->getBounds().Centroid().x;
                });
                break;
            case 1:
                std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
                    return f1->getBounds().Centroid().y <
                           f2->getBounds().Centroid().y;
                });
                break;
            case 2:
                std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
                    return f1->getBounds().Centroid().z <
                           f2->getBounds().Centroid().z;
                });
                break;
            }
            for(int k = 1; k < objects.size(); k++)
            {
                auto beginning = objects.begin();
                auto middling = objects.begin() + k;
                auto ending = objects.end();

                auto leftshapes = std::vector<Object*>(beginning, middling);
                auto rightshapes = std::vector<Object*>(middling, ending);
                assert(objects.size() == (leftshapes.size() + rightshapes.size()));
                
                Bounds3 bound1,bound2;
                for (int i = 0; i < leftshapes.size(); ++i)
                    bound1 = Union(bound1, leftshapes[i]->getBounds());
                for (int i = 0; i < rightshapes.size(); ++i)
                    bound2 = Union(bound2, rightshapes[i]->getBounds());
                double S1 = bound1.SurfaceArea();
                double S2 = bound2.SurfaceArea();
                
                double time = S1/S0 * leftshapes.size() * 1.0 + S2/S0 * rightshapes.size() * 1.0;
                if(time < time_svh)
                {
                    time_svh = time;
                    index = k;
                    axis = i;
                }
            }
        }
        
        switch (axis) {
        case 0:
            std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
                return f1->getBounds().Centroid().x <
                       f2->getBounds().Centroid().x;
            });
            break;
        case 1:
            std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
                return f1->getBounds().Centroid().y <
                       f2->getBounds().Centroid().y;
            });
            break;
        case 2:
            std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
                return f1->getBounds().Centroid().z <
                       f2->getBounds().Centroid().z;
            });
            break;
        }
        
        auto beginning = objects.begin();
        auto middling = objects.begin() + index;
        auto ending = objects.end();

        auto leftshapes = std::vector<Object*>(beginning, middling);
        auto rightshapes = std::vector<Object*>(middling, ending);
        
        assert(objects.size() == (leftshapes.size() + rightshapes.size()));

        node->left = recursiveBuildSAH(leftshapes);
        node->right = recursiveBuildSAH(rightshapes);

        node->bounds = Union(node->left->bounds, node->right->bounds);
    }
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值