【GAMES101】作业6——加速结构(包围盒,BVH)

作业内容:

1.复制上次作业的Render()、Triangle::getIntersection,并根据新框架调整格式;

2.实现IntersectP即判断包围盒BoundingBox是否与光线相交;

3.实现getIntersection判断BVH加速求教交的过程,递归调用;

作业实现:

1.Render()

框架里面定义了ray结构,包含了origin(起点),direction(射线方向),t(转移时间),(Destionation=origin+t*direction)

本次框架中的castRay为场景类castRay的方法:Vector3f castRay(const Ray &ray, int depth) const,需要ray作为输入参数,因此需要将相机视野用ray定义后传入castRay中。

Vector3f dir = Vector3f(x, y, -1); // Don't forget to normalize this direction!
dir = normalize(dir);
Ray ray(eye_pos, dir);
framebuffer[m++] = scene.castRay(ray,0);

2.Triangle::getIntersection

射线与三角形求交公式如上次作业一样,注意本次的交点是以Intersection形式返回的,其包括交点的坐标(coords)、法线(normal)、距离(distance)、相交的物体(object)、材质(m)等信息。

 

 

 

 

 

 

 

 

 

复制代码
inline Intersection Triangle::getIntersection(Ray ray)
{
    Intersection inter;//初始值是相交没有发生
    //先判断不相交的情况
    //normal为三角形指向眼睛的方向 若与射线方向交加小于90则看不到
    if (dotProduct(ray.direction, normal) > 0)
        return inter;
    
double u, v, t_tmp = 0;//b1,b2,t Vector3f pvec = crossProduct(ray.direction, e2);//prev为公式的S1 double det = dotProduct(e1, pvec);//E1*S1 if (fabs(det) < EPSILON)//防止分母过小 return inter; double det_inv = 1. / det; Vector3f tvec = ray.origin - v0; //tvec为公式的S u = dotProduct(tvec, pvec) * det_inv;//b1 if (u < 0 || u > 1)//重心坐标 u(b1)范围【0,1】 return inter; Vector3f qvec = crossProduct(tvec, e1);//qvec为公式的S2 v = dotProduct(ray.direction, qvec) * det_inv;//b2 if (v < 0 || u + v > 1)//u+v,v(b2)范围【0,1】 return inter; t_tmp = dotProduct(e2, qvec) * det_inv;//计算O+tD中t的值 if (t_tmp < 0) return inter;//t<0返回 //返回true的情况 即给intersection(inter)赋值 // TODO find ray triangle intersection /*inter的结构 Intersection(){ happened=false; coords=Vector3f(); normal=Vector3f(); distance= std::numeric_limits<double>::max(); obj =nullptr; m=nullptr; } */ inter.happened = true; inter.coords = ray(t_tmp);//origin+direction*t 交点坐标 inter.normal = normal; inter.distance = t_tmp; inter.obj = this; inter.m = m; return inter; }
复制代码

3.IntersectP判断AABB盒是否与射线相交

 

课程里面的方式为二维情况:将x0,x1,y0,y1代入射线,算出tminx,tminy,tmaxx,tmaxy分别是射线在x,y方向进入的时间与出去的时间,显然的是,只有射线在x,y方向上都进入了包围盒,才算整个进入了包围盒,在x,y方向都出包围盒,才算整个都出了包围盒。故进入包围盒的时间tenter=max(tminx,tminy),出去的时间texit=max(tmaxx,tmaxy),当tenter<texit且texit>=0说明包围盒相交(不需要tenter>0因为可能射线的原点在包围盒内)。需要注意的是如果射线在某个轴为负数,则算出来的tmin<tmax,但负方向进入包围盒的t是大于出包围盒的t的,所以负方向需要将tmin,tmax互换,将方法延伸至三维即可。

复制代码
inline bool Bounds3::IntersectP(const Ray& ray, const Vector3f& invDir,
                                const std::array<int, 3>& dirIsNeg) const
{
    // invDir: ray direction(x,y,z), invDir=(1.0/x,1.0/y,1.0/z), use this because Multiply is faster that Division
    // dirIsNeg: ray direction(x,y,z), dirIsNeg=[int(x>0),int(y>0),int(z>0)], use this to simplify your logic
    // TODO test if ray bound intersects
    //pMin左下的点 pMax右上的点
    //计算射线到进入时间tmin和出去时间tmax t=1/d(x-o)
    float t_min_x = invDir[0] * (pMin.x - ray.origin.x);
    float t_min_y = invDir[1] * (pMin.y - ray.origin.y);
    float t_min_z = invDir[2] * (pMin.z - ray.origin.z);
    float t_max_x = invDir[0] * (pMax.x - ray.origin.x);
    float t_max_y = invDir[1] * (pMax.y - ray.origin.y);
    float t_max_z = invDir[2] * (pMax.z - ray.origin.z);
    //注意如果射线是反的则需要交换tMin和tMax的位置
    if (dirIsNeg[0]) {
        float tmp = t_min_x;
        t_min_x = t_max_x;
        t_max_x = tmp;
    }
    if (dirIsNeg[1]) {
        float tmp = t_min_y;
        t_min_y = t_max_y;
        t_max_y = tmp;
    }
    if (dirIsNeg[2]) {
        float tmp = t_min_z;
        t_min_z = t_max_z;
        t_max_z = tmp;
    }
    //tenter=max(tminx,tminy,tminz) texit=min(tmaxx,tmaxy,taxz)
    float t_min = std::max(t_min_x, std::max(t_min_y, t_min_z));
    float t_max = std::min(t_max_x, std::min(t_max_y, t_max_z));
    if (t_min < t_max && t_max >= 0) {
        return true;
    }
    return false;
}
复制代码

4.getIntersection 判断BVH加速求教交

 

如果没有和包围盒相交则必然不会和里面物体相交故返回;

如果和叶子节点相交则返回和其里面的物体的交点;

如果和非叶子节点相交则返回左右字节中交点更近的一个。

复制代码
Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const
{
    // TODO Traverse the BVH to find intersection
    Intersection inter;
    //准备IntersectP函数需要的参数
    Vector3f invdir(1 / ray.direction.x, 1 / ray.direction.y, 1 / ray.direction.z);
    std::array<int, 3>dirIsNeg;
    dirIsNeg[0] = ray.direction.x < 0;
    dirIsNeg[1] = ray.direction.y < 0;
    dirIsNeg[2] = ray.direction.z < 0;
    //没有交点
    if (!node->bounds.IntersectP(ray, invdir, dirIsNeg)) {
        return inter;
    }
    //叶子
    if (node->left == nullptr && node->right == nullptr) {
        return node->object->getIntersection(ray);
    }
    //左右
    Intersection h1 = getIntersection(node->left, ray);
    Intersection h2 = getIntersection(node->right, ray);
    return h1.distance < h2.distance ? h1 : h2;    
}
复制代码

结果:

 

posted @   一只雷史莱姆  阅读(331)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示