GAMES101:作业6
附其他所有作业超链接如下:
Games101 作业0:作业0
Games101 作业1:作业1
Games101 作业2:作业2
Games101 作业3:作业3
Games101 作业4:作业4
Games101 作业5:作业5
Games101 作业6:作业6
Games101 作业7:作业7
完整代码获取途径:
https://github.com/liupeining/Games_101_homework
接下来是实现加速的光线追踪,本练习要求实现 Ray-Bounding Volume 求交与 BVH 查找;后文中笔者也实现了SAH(Surface Area Heuristic),并逐段代码讲解了SAH的实现过程。
首先,你需要从上一次编程练习中引用以下函数:
• Render() in Renderer.cpp: 将你的光线生成过程粘贴到此处,并且按照新框架更新相应调用的格式。
for (uint32_t j = 0; j < scene.height; ++j) {
for (uint32_t i = 0; i < scene.width; ++i) {
//这里的过程实际上相当于把屏幕空间先缩小到左下角为0,0的2*2格子里,然后移动到0,0处。
//和框架规定有关,作业一直有上下颠倒的问题,这里面可以在y填一个负号来解决。
//最后乘的系数是生成图片的大小。
float x = (2 * (i + 0.5) / scene.width - 1) * scale * imageAspectRatio; //正常需要减eye_pos的,但这里eye_pos是000向量,就略去不写了。
float y = -(2 * (j + 0.5) / scene.height - 1) * scale;
Vector3f dir = normalize(Vector3f(x, y, -1)); //归一化可以看这个类的声明
Ray ray(eye_pos,dir);
framebuffer[m++] = scene.castRay(ray,0);
}
UpdateProgress(j / (float)scene.height);
}
UpdateProgress(1.f);
这里需要根据框架改动两行:
Ray ray(eye_pos,dir);
framebuffer[m++] = scene.castRay(ray,0);
剩下的不需要变化。
• Triangle::getIntersection in Triangle.hpp: 将你的光线-三角形相交函数粘贴到此处,并且按照新框架更新相应相交信息的格式。
需要注意的地方已经注释在下方的程序中。
/*inline 函数仅仅是一个对编译器的建议,所以最后能否真正内联,看编译器的意思,
它如果认为函数不复杂,能在调用点展开,就会真正内联,并不是说声明了内联就会内联,声明内联只是一个建议而已。
为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了 inline 修饰符,表示为内联函数。
栈空间就是指放置程序的局部数据(也就是函数内数据)的内存空间。
*/
inline Intersection Triangle::getIntersection(Ray ray)
{
Intersection inter;
//如果结果大于0,那么这两个向量的夹角小于90度;
//这里normal是从三角指向眼的,因此若同方向则不可能穿过三角
if (dotProduct(ray.direction, normal) > 0)
return inter; // happen = false;
/*
Intersection的定义
Intersection(){
happened=false;
coords=Vector3f();
normal=Vector3f();
distance= std::numeric_limits<double>::max();
obj =nullptr;
m=nullptr;
}
*/
double u, v, t_tmp = 0;
Vector3f pvec = crossProduct(ray.direction, e2); //S1
double det = dotProduct(e1, pvec); //S1*E1(顺便说一句,能推出S1*E1=-D*N)
if (fabs(det) < EPSILON) //const float EPSILON = 0.00001; 分母特别小将导致t特别大,相当于很远很远,看不见
return inter;
double det_inv = 1. / det; // 1/S1*E1
Vector3f tvec = ray.origin - v0; //S
u = dotProduct(tvec, pvec) * det_inv; //b1
if (u < 0 || u > 1)
return inter;
Vector3f qvec = crossProduct(tvec, e1); //S2
v = dotProduct(ray.direction, qvec) * det_inv; //b2
if (v < 0 || u + v > 1)
return inter;
t_tmp = dotProduct(e2, qvec) * det_inv; //t
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;
}
在本次编程练习中,你需要实现以下函数:
• IntersectP(const Ray& ray, const Vector3f& invDir, const std::array<int, 3>& dirIsNeg) in the Bounds3.hpp: 这个函数的作用是判断包围盒 BoundingBox 与光线是否相交,你需要按照课程介绍的算法实现求交过程。
需要注意的内容及公式均已注释在代码内。
inline bool Bounds3::IntersectP(const Ray& ray, const Vector3f& invDir,
const std::array<int, 3>& dirIsNeg) const
{
//invDir = 1 / D; t = (Px - Ox) / dx
float t_Min_x = (pMin.x - ray.origin.x)*invDir[0];
float t_Min_y = (pMin.y - ray.origin.y)*invDir[1];
float t_Min_z = (pMin.z - ray.origin.z)*invDir[2];
float t_Max_x = (pMax.x - ray.origin.x)*invDir[0];
float t_Max_y = (pMax.y - ray.origin.y)*invDir[1];
float t_Max_z = (pMax.z - ray.origin.z)*invDir[2];
//如果发现射线的方向是反的,调换t_min和t_max的位置。
if(dirIsNeg[0])
{
float t = t_Min_x;
t_Min_x = t_Max_x;
t_Max_x = t;
}
if(dirIsNeg[1])