1.解决报错
导入代码以后会出现两个函数需要返回值,这里先随便构造一个对应的返回类型解决报错。然后解决fopen的不安全问题,解决办法跟上个作业一样。然后是修改c++语言标准。
之后还有这个错误,应该是模型路径的问题。
修改代码如下:
string obj_path = "\\..\\..\\models\\cornellbox\\";
char pathBuf[MAX_PATH];
char* p;
if (GetModuleFileNameA(NULL, pathBuf, 1000))
{
p = strrchr(pathBuf, '\\');
if (p)
{
*p = '\0';
string exe_path = pathBuf;
obj_path = exe_path + obj_path;
}
}
MeshTriangle floor(obj_path + "floor.obj", white);
MeshTriangle shortbox(obj_path + "shortbox.obj", white);
MeshTriangle tallbox(obj_path + "tallbox.obj", white);
MeshTriangle left(obj_path + "left.obj", red);
MeshTriangle right(obj_path + "right.obj", green);
MeshTriangle light_(obj_path + "light.obj", light);
之后代码就可以跑通了,并且生成了一张纯白色的图片
2.castRay(const Ray ray, int depth)
完成这个函数的时候需要先将上次作业的几个函数移植到这次作业里
有一个需要修改的地方是在判断inline bool Bounds3::IntersectP时,需要写成t_enter<=t_exit,对浮点数处理,否则会导致部分地方是黑的:
另外在渲染的时候最好使用多线程处理,会极大缩短渲染时间
启用OpenMP第一步,在项目设置中启用OpenMP。
第二步,在 Render 函数的最外层for循环前添加一行:
#pragma omp parallel for
修改代码如下
#pragma omp parallel for
for (int j = 0; j < scene.height; ++j)
{
for (int i = 0; i < scene.width; ++i)
{
// generate primary ray direction
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 = normalize(Vector3f(-x, y, 1));
for (int k = 0; k < spp; k++)
{
//framebuffer[m] += scene.castRay(Ray(eye_pos, dir), 0) / spp;
framebuffer[j * scene.width + i] += scene.castRay(Ray(eye_pos, dir), 0) / spp;
}
//m++;
}
//UpdateProgress(j / (float)scene.height);
UpdateProgress(m++ / (float)scene.height);
}
UpdateProgress(1.f);
然后castray函数主要参照作业里给的伪代码
// Implementation of Path Tracing
Vector3f Scene::castRay(const Ray& ray, int depth) const
{
// TO DO Implement Path Tracing Algorithm here
Vector3f L_dir;
Vector3f L_indir;
// 从像素发出的光线与物体的交点
Intersection obj_inter = intersect(ray);
if (!obj_inter.happened)
return L_dir;
// 打到光源
if (obj_inter.m->hasEmission())
return obj_inter.m->getEmission();
// 打到物体
Vector3f p = obj_inter.coords;//交点坐标
Material* m = obj_inter.m;//交点材质
Vector3f N = obj_inter.normal.normalized();//交点法线
Vector3f wo = ray.direction; // 像素到物体的向量
// 有交点,对光源采样
Intersection light_inter;
float pdf_L = 1.0; //可以不初始化
sampleLight(light_inter, pdf_L); // 得到光源位置和对光源采样的pdf
Vector3f x = light_inter.coords;
Vector3f ws = (x - p).normalized(); //物体到光源
Vector3f emit = light_inter.emit;
Vector3f NN = light_inter.normal.normalized();
float d = (x - p).norm();
// 再次从光源发出一条光线,判断是否能打到该物体,即中间是否有阻挡
Ray Obj2Light(p, ws);
float d2 = intersect(Obj2Light).distance;
// 是否阻挡,利用距离判断,需注意浮点数的处理
if (d2 - d > -0.001) {
Vector3f eval = m->eval(wo, ws, N); // wo不会用到
float cos_theta = dotProduct(N, ws);
float cos_theta_x = dotProduct(NN, -ws);//ws从物体指向光源,与NN的夹角大于180
L_dir = emit * eval * cos_theta * cos_theta_x / std::pow(d, 2) / pdf_L;
}
// L_indir
float P_RR = get_random_float();
if (P_RR < RussianRoulette) {
Vector3f wi = m->sample(wo, N).normalized();
Ray r(p, wi);
Intersection inter = intersect(r);
// 判断打到的物体是否会发光取决于m
if (inter.happened && !inter.m->hasEmission()) {
Vector3f eval = m->eval(wo, wi, N);
float pdf_O = m->pdf(wo, wi, N);//1/2PI
float cos_theta = dotProduct(wi, N);
L_indir = castRay(r, depth + 1) * eval * cos_theta / pdf_O / RussianRoulette;
}
}
//4->16min
return L_dir + L_indir;
}