homework6: Acceleration structure

没有优化过的光线追踪在计算时会将光线和每个三角形求交,假设屏幕空间有pixels个像素,一共traingles个三角形,光线弹射bounces次,那么算法的复杂度为:#pixels ⨉ # traingles (⨉ #bounces),这个计算复杂度是很高的。

理论回顾

计算光线和长方体包围盒是否有交点是很简单的。当光线与某个三角形有焦点的时候,必然会跟包围它的长方体相较。

包围盒

  • 计算包围盒的坐标

包围盒的坐标取决于里面的三角形的坐标,取每个维度的最大最小值作为包围盒的左下和右上坐标

  • 判断光线是否与包围盒有交点
    分析方法:先研究2D坐标,再考虑3D空间里的事情

BASS audio library android使用 bastion soundtrack_图形渲染

如图所示:

光线进入区域的时间取决于最晚进入的时间,即 t_enter=max(tmin1,tmin2);

光线离开区域的时间取决于最早出去的时间,即 t_exit=max(tmax1,tmax2);
时间t可以用直线的向量式方程计算。

将2d的场景推广到3d,可得光线是否与包围盒有交点的判断方法如下:

BASS audio library android使用 bastion soundtrack_图形渲染_02

  • 选择与坐标对其的方法

BASS audio library android使用 bastion soundtrack_游戏程序_03

slab坐标对其的时候,由于slab所在平面是垂直于某个坐标轴的,因此可以直接用直线在坐标轴上的速度和坐标分量求出在该维度下的tmin和tmax。总共只需要求三次即可。

按空间划分

BASS audio library android使用 bastion soundtrack_游戏程序_04

  1. Oct-Tree的问题在于:这是一种对空间均匀划分的算法,当某个物体出现在区域交界处的时候,其会占据多个空间,当光线与这两个区域相交的时候都需要判断是否与这个物体相交,这样的重复计算时冗余的,或者说这种方法增加了某个空间类的物体数量。
  2. KD-Tree会基于区域内的物体分布来划分空间,解决了Oct-Tree中物体很容易占据多个空间的问题。但还是一定程度上存在一个物体对应对个空间的情况。
  3. BSP-Tree将空间划分的更加细致,能保证每个物体只会出现一个划分后的空间里。但是BSP-Tree的划分平面不是与坐标对其的,当光线与这种划分后的子空间求交时的计算更加复杂。因此这也不是一种高效的方法。

按物体划分

  • BVH

基于问题场景,解决好这个空间划分的方法需要满足以下两个需求:

1、一个物体只出现在某一个保存物体的子空间内;

2、划分后的子空间要是坐标对其的;

考虑到子空间之间的重叠对性能的影响相比按空间划分的方法小很多,因此可以让子空间之间存在一点重叠,于是有了针对包围盒划分的BVH算法,如下图:

BASS audio library android使用 bastion soundtrack_游戏程序_05

  • BVH的建立

BASS audio library android使用 bastion soundtrack_图形渲染_06

开发的时候可以用递归的方法建立BVH

  • 遍历BVH的方法

BASS audio library android使用 bastion soundtrack_游戏程序_07

基本上就是先序遍历

代码展示

函数Bounds3::IntersectP

inline bool Bounds3::IntersectP(const Ray& ray, const Vector3f& invDir,
                                const std::array<int, 3>& dirIsNeg) const
{
    float tEnter=FLT_MIN;
    float tExit=FLT_MAX;
    for (int i=0; i<3; i++)
    {
        float t_min=(pMin[i]-ray.origin[i])*invDir[i];
        float t_max=(pMax[i]-ray.origin[i])*invDir[i];
        if (dirIsNeg[i]==0)    std::swap(t_min, t_max); // note: here must be ==0, because dirIsNeg is actually int(x>0)
        tEnter=std::max(t_min, tEnter);
        tExit=std::min(t_max, tExit);
    }
    return tEnter<=tExit && tExit>=0;
}

用来判断光线是否和Bound相交

Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const
{
    //TODO Traverse the BVH to find intersection
Intersection inter;
    std::array<int,3> dirIsNeg;

    dirIsNeg[0]=int(ray.direction.x>0);
    dirIsNeg[1]=int(ray.direction.y>0);
    dirIsNeg[2]=int(ray.direction.z>0);

    if (!node->bounds.IntersectP(ray, ray.direction_inv, dirIsNeg)) {
        return inter;
    }
    // 叶子节点
    if(!node->left && !node->right){
        inter = node->object->getIntersection(ray);
        return inter;
    }

    auto inter1 = getIntersection(node->left, ray);
    auto inter2 = getIntersection(node->right, ray);

    return inter1.distance<inter2.distance?inter1:inter2;
}

BVH树遍历,找到叶子节点中与光线相交的物体,并返回坐标