【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; }
结果:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了