0%

【Games 101】HomeWork 6:加速结构

HomeWork 6:加速结构

首先,是 Render()Triangle::getIntersection,照着上一节课一样,只是有些地方的形式需要改改;

然后就是此次作业的要求:

  • 理解,并使用加速结构 BVH,找到光线和场景中物体的交点
    • getIntersection(BVHBuildNode node, const Ray ray)*
  • 判断光线和包围盒是否相交,
    • IntersectP(const Ray& ray, const Vector3f& invDir, const std::array<int, 3>& dirIsNeg)

对于知识点参考这一节课:

【Games 101】Lec 14:光线追踪 2(加速光线追踪 和 辐射度量学)

Renderer.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 这个还是一样的,照着上一个作业的写就行(注意有些东西变了)
void Renderer::Render(const Scene& scene)
{
// 帧缓冲区,用于保存返回的颜色
std::vector<Vector3f> framebuffer(scene.width * scene.height);

float scale = tan(deg2rad(scene.fov * 0.5));
float imageAspectRatio = scene.width / (float)scene.height;

// 这个视点变了
Vector3f eye_pos(-1, 5, 10);
int m = 0;
for (uint32_t j = 0; j < scene.height; ++j)
{
for (uint32_t i = 0; i < scene.width; ++i)
{
float x = (2 * (i + 0.5) / (float)scene.width - 1) * imageAspectRatio * scale;
float y = (1 - 2 * (j + 0.5) / (float)scene.height) * scale;

Vector3f dir = Vector3f(x, y, -1); // Don't forget to normalize this direction!
dir = normalize(dir);

// 这里不是直接调用 castRay 了... 是先定义光线ray,然后在传到scene的castray方法中。
Ray ray(eye_pos,dir);
framebuffer[m++] = scene.castRay(ray,0);
}
UpdateProgress(j / (float)scene.height);
}
UpdateProgress(1.f);

// save framebuffer to file
FILE* fp = fopen("binary.ppm", "wb");
(void)fprintf(fp, "P6\n%d %d\n255\n", scene.width, scene.height);
for (auto i = 0; i < scene.height * scene.width; ++i) {
static unsigned char color[3];
color[0] = (unsigned char)(255 * clamp(0, 1, framebuffer[i].x));
color[1] = (unsigned char)(255 * clamp(0, 1, framebuffer[i].y));
color[2] = (unsigned char)(255 * clamp(0, 1, framebuffer[i].z));
fwrite(color, 1, 3, fp);
}
fclose(fp);
}

Triangle::getIntersection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
inline Intersection Triangle::getIntersection(Ray ray)
{
// 射线和三角形的交点(结构体,存了一些交点信息)
Intersection inter;

// 说明不是入射光线
if (dotProduct(ray.direction, normal) > 0) return inter;

double u, v, t_tmp = 0;

// 相当于 S1
Vector3f pvec = crossProduct(ray.direction, e2);
double det = dotProduct(e1, pvec);
if (fabs(det) < EPSILON) return inter;

// 相当于 1/dotProduct(S1,E1)
double det_inv = 1. / det;

// 相当于 S
Vector3f tvec = ray.origin - v0;
u = dotProduct(tvec, pvec) * det_inv;
if (u < 0 || u > 1) return inter;

// 相当于 S2
Vector3f qvec = crossProduct(tvec, e1);
v = dotProduct(ray.direction, qvec) * det_inv;
if (v < 0 || u + v > 1) return inter;

// 相当于 t
t_tmp = dotProduct(e2, qvec) * det_inv;
if (t_tmp <= 0) return inter;

// 下面完善一下交点信息
// 标记相交
inter.happened=true;

// 求一下坐标,Ray中重载了(), ray(t_tmp) = ray.origin + ray.direction * t_tmp
inter.coords = Vector3f(ray(t_tmp));
inter.distance = t_tmp;

// 交点是在这个三角形上,所以把这个三角形的材质、法线和指针传给交点。
inter.m = m;
inter.normal = normal;
inter.obj = this;

return inter;
}

BVH.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
BVH 加速结构,获取 光线 和 以node为根结点的子树 的交点
这个照着课上的为代码写就行了
好像一个节点最多包含一个物体
规范一点,注意指针的判空
*/
Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const
{
Intersection inter;

if(node == nullptr) return inter;

// 当前节点是叶子节点,直接返回和这个物体的交点就行
if(node->left == nullptr && node->right == nullptr)
{
if(node->object != nullptr)
{
inter = node->object->getIntersection(ray);
}
return 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);

Intersection inter1, inter2;

// 看看是否和左边的包围盒相交
if(node->left != nullptr && node->left->bounds.IntersectP(ray, ray.direction_inv, dirIsNeg))
{
inter1 = getIntersection(node->left,ray);
}

// 看看是否和右边的包围盒相交
if(node->right != nullptr && node->right->bounds.IntersectP(ray, ray.direction_inv, dirIsNeg))
{
inter2 = getIntersection(node->right,ray);
}

// 如果有两个交点,返回最近的那个
return inter1.distance < inter2.distance ? inter1 : inter2;
}

Bounds3.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 这个函数的作用是判断包围盒 BoundingBox 与光线是否相交
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),使用这个是因为乘法比除法快
// dirIsNeg: ray direction(x,y,z), dirIsNeg = [int(x>0),int(y>0),int(z>0)], 用这个来简化你的逻辑

float tx_min = (pMin.x - ray.origin.x) * invDir.x;
float tx_max = (pMax.x - ray.origin.x) * invDir.x;
if (dirIsNeg[0]) std::swap(tx_min, tx_max);

float ty_min = (pMin.y - ray.origin.y) * invDir.y;
float ty_max = (pMax.y - ray.origin.y) * invDir.y;
if (dirIsNeg[1]) std::swap(ty_min, ty_max);

float tz_min = (pMin.z - ray.origin.z) * invDir.z;
float tz_max = (pMax.z - ray.origin.z) * invDir.z;
if (dirIsNeg[2]) std::swap(tz_min, tz_max);

float t_enter = std::max(std::max(tx_min, ty_min), tz_min);
float t_exit = std::min(std::min(tx_max, ty_max), tz_max);

return t_exit > 0.0f && t_enter < t_exit;
}

结果:

欢迎关注我的其它发布渠道