0%

【Games 101】HomeWork 7:路径追踪

HomeWork 7:路径追踪

首先按照 pdf 里面的操作,将相关函数迁移过来;

IntersectP 函数最后判断的时候,需要=,不然可能会有问题:

1
2
3
4
5
6
inline bool Bounds3::IntersectP(const Ray& ray, const Vector3f& invDir, const std::array<int, 3>& dirIsNeg) const
{
...
// 注意这里需要 = ,不然好像会出问题
return t_exit >= 0.0f && t_enter <= t_exit;
}

然后就是本次作业的内容,本次作业要实现的是路径追踪函数;

这个作业是真的有点难… 主要是知识点都忘了…

建议回去看一遍视频… 然后对照课程中的伪代码一步一步比对实现

castRay 这个函数实现的就是路径追踪

具体细节看代码里面的注释!

知识点参考的是这一节课的内容:

【Games 101】Lec 16:光线追踪 4(蒙特卡罗积分,路径追踪)

Scene.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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/*
这个作业是真的有点难... 主要是知识点都忘了...
建议回去看一遍视频... 然后对照课程中的伪代码一步一步比对实现
castRay 这个函数实现的就是路径追踪
具体细节看代码里面的注释!
*/
Vector3f Scene::castRay(const Ray &ray, int depth) const
{
// 首先,获得光线第一个打到的点(下面叫做着色点吧)
Intersection p = intersect(ray);

// 没打到,直接返回
if(!p.happened) return Vector3f(0,0,0);

// 如果着色点自己是光源,则直接返回
if(p.m->hasEmission())
{
return p.m->getEmission();
}

/*
否则,如果该着色点是非光源,按照课程讲的,则分为两部分来求:
1. 光源对该着色点的贡献(直接对光源取样,不用RR)
2. 其他非光源对该着色点的贡献(递归求解,用RR)
*/

// 光线的方向
Vector3f wo = ray.direction;
// 这个就是第 1 项,光源对该着色点的贡献
Vector3f L_indir(0);
// 这个就是第 2 项,光源对该着色点的贡献
Vector3f L_dir(0);

/*----------------- 1. 光源对该着色点的贡献(直接对光源取样,不用RR)---------------*/

// 对光源进行取样,得到光源的 pdf 和 光源上的点 和 光源的面积密度pdf
Intersection inter_l; // 在光源上的交点
float pdf_light; // 光源的 pdf
sampleLight(inter_l, pdf_light);
Vector3f x = inter_l.coords;

// 获得着色点p到光源(采样点)的 方向 与 距离
float p2lightDist = (x - p.coords).norm();
Vector3f p2lightDir = (x - p.coords).normalized();

// 从p点向取样得到的光源方向射出一条光线wi
Ray wi(p.coords, p2lightDir);
// 从p点向光源方向射出一条光线得到交点 inter_tmp,为判断光源与着色点是否有物体做准备
Intersection inter_tmp = intersect(wi);

// 判断 着色点p 和 光源之间 中间没有物体阻隔
if ((inter_l.coords - inter_tmp.coords).norm() < EPSILON * EPSILON)
{
// 这里我照着伪代码把变量都写出来了,清楚一点
Vector3f L_i = inter_l.emit;
Vector3f f_r = p.m->eval(wo, wi.direction, p.normal);
// (着色点法向量)和(着色点-光源采样点 向量)之间的夹角
float cos0 = dotProduct(wi.direction, p.normal);
// (光源平面法向量)和(光源采样点-着色点 向量)之间的夹角
float cos0_ = dotProduct(-wi.direction, inter_l.normal);

L_dir = L_i * f_r * cos0 * cos0_ / (p2lightDist * p2lightDist) / pdf_light;
}

/*----------------- 2. 其他非光源对该着色点的贡献(递归求解,用RR)---------------*/

// 俄罗斯轮盘获得随机概率
float P_RR = RussianRoulette;
int seed = rand() % 10;
// 生存概率
if (seed * 1.0 / 10 > P_RR) return L_dir;

// 在 着色点p 取样一个新的随机方向
Vector3f wi_new = p.m->sample(wo, p.normal);

// 发射出去求与其他物体的交点
Ray r(p.coords, wi_new);
Intersection q = intersect(r);

// 如果q点的物体不是光源
if (q.happened && !q.obj->hasEmit())
{
Vector3f f_r = p.m->eval(wo, wi_new, p.normal);
float cos0 = dotProduct(wi_new, p.normal);
float pdf_hemi = p.m->pdf(wo,wi_new,p.normal);

L_indir = castRay(r, depth+1) * f_r * cos0 / pdf_hemi / P_RR;
}

return L_dir + L_indir;
}

路径追踪结果:

可能是给虚拟机分配的内存和CPU数量少了… 跑了40分钟,主机的CPU也才跑了20多而已…

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