需要加速结构来加速光线与场景的交点,本次练习中,重点关注物体划分算法Bounding Volume Hierarchy (BVH)。本练习要求实现Ray-Bounding Volume求交与BVH查找。
需要从上一次编程练习中引用以下函数:
在本次编程练习中,你需要实现以下函数:
修改了代码框架中的如下内容:
for (uint32_t j = 0; j < scene.height; ++j) {
for (uint32_t i = 0; i < scene.width; ++i) {
// generate primary ray direction
//计算栅格单元[i, j]中心对应的坐标[x, y]
float x = (2 * (i + 0.5) / (float)scene.width - 1) *
imageAspectRatio * scale;
float y = (1 - 2 * (j + 0.5) / (float)scene.height) * scale;
// TODO: Find the x and y positions of the current pixel to get the
// direction
// vector that passes through it.
// Also, don't forget to multiply both of them with the variable
// *scale*, and x (horizontal) variable with the *imageAspectRatio*
// Don't forget to normalize this direction!
//假设观察点和屏幕距离为1,观察点位于原点,向z轴负方向观察
//所以从观察点到栅格中心点的光线z = -1
Vector3f dir = normalize(Vector3f(x, y, -1));
//观察点的起始位置在eye_pos,但是观察点和屏幕相对位置不变,观察朝向不变
//所以,光线方向不变
Ray ray(eye_pos, dir);
framebuffer[m++] = scene.castRay(ray, 0);
}
UpdateProgress(j / (float)scene.height);
}
inline Intersection Triangle::getIntersection(Ray ray)
{
Intersection inter;
if (dotProduct(ray.direction, normal) > 0)
return inter;
double u, v, t_tmp = 0;//对应b1,b2,t
Vector3f pvec = crossProduct(ray.direction, e2);
double det = dotProduct(e1, pvec);
if (fabs(det) < EPSILON)
return inter;
double det_inv = 1. / det;
Vector3f tvec = ray.origin - v0;
u = dotProduct(tvec, pvec) * det_inv;
if (u < 0 || u > 1)
return inter;
Vector3f qvec = crossProduct(tvec, e1);
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.coords = Vector3f(ray.origin + t_tmp * ray.direction);
inter.normal = normal;
inter.obj = this;
return inter;
}
这个函数的作用是判断包围盒BoundingBox与光线是否相交。
对面
垂直于x轴:t * 光线x轴分量 = 包围盒上一点的x分量 - 光源起始点x分量
对面
是无限大的两个平面,光线暂时看作直线,所以一定有交点;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
double tx_min = (pMin.x - ray.origin.x) * invDir.x;
double tx_max = (pMax.x - ray.origin.x) * invDir.x;
//注意下pMin.x是指包围盒左边的面
// 假如光线是反向(从右往左),那么光线离包围盒pMin.x距离(tx_min)远
if (!dirIsNeg[0])
std::swap(tx_min, tx_max);
double ty_min = (pMin.y - ray.origin.y) * invDir.y;
double ty_max = (pMax.y - ray.origin.y) * invDir.y;
if (!dirIsNeg[1])
std::swap(ty_min, ty_max);
double tz_min = (pMin.z - ray.origin.z) * invDir.z;
double tz_max = (pMax.z - ray.origin.z) * invDir.z;
if (!dirIsNeg[2])
std::swap(tz_min, tz_max);
double t_enter = std::max(tx_min, std::max(ty_min, tz_min));
double t_exit = std::min(tx_max, std::min(ty_max, tz_max));
return t_enter < t_exit && t_exit >= 0;
}
Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const
{
// TODO Traverse the BVH to find intersection
Intersection isect;
// 光线没有碰到包围盒
if(!node->bounds.IntersectP(ray, ray.direction_inv, std::array<int, 3>{ray.direction.x>0, ray.direction.y>0, ray.direction.z>0}) )
return isect;
// 叶节点
if(node->left == nullptr && node->right == nullptr)
return node->object->getIntersection(ray);
// 中间节点
Intersection isect_left, isect_right;
isect_left = getIntersection(node->left, ray);
isect_right = getIntersection(node->right, ray);
//返回最近的交点
return isect_left.distance <= isect_right.distance ? isect_left : isect_right;
}
mkdir build
cd ./build
cmake ..
make
./RayTracing