GAMES101 HW7
这里回顾GAMES101 HW7,这次作业的内容是路径追踪。
课程主页:
课程作业:
课程视频:
参考资料:
- https://games-cn.org/forums/topic/dalaoqingbangbangmangzuoyeqihuanzailijie-shadep-wo-hanshu/
- https://games-cn.org/forums/topic/games101-zuoye7-raokengyinlu-windows/
- https://zhuanlan.zhihu.com/p/350405670
- https://blog.csdn.net/qq_36242312/article/details/116307626
- https://games-cn.org/forums/topic/qiuzhuguanyuzuoye7deduoxianchengjiasu/
- https://blog.csdn.net/hejinjing_tom_com/article/details/124608702
- https://www.cnblogs.com/haippy/p/3236136.html
- https://blog.csdn.net/sevenjoin/article/details/82187127
一些问题
首先回顾这次作业的问题。
问题1:depth参数的作用。
直接光照只要计算一次,depth是为了保证这点。
问题2:ndefined reference to pthread_create
。
在CMakeLists.txt中给编译选项添加-pthread。
问题3:结果全黑。
IntersectP中需要加eps,新代码如下:
inline bool Bounds3::IntersectP(const Ray& ray, const Vector3f& invDir,
const std::array<int, 3>& dirIsNeg) const
{
float eps = 1e-4;
// 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
Vector3f origin = ray.origin;
Vector3f t_enter = (pMin - origin) * invDir;
Vector3f t_exit = (pMax - origin) * invDir;
// 根据方向调整enter和exit
if (dirIsNeg[0]) {
float d = t_enter.x;
t_enter.x = t_exit.x;
t_exit.x = d;
}
if (dirIsNeg[1]) {
float d = t_enter.y;
t_enter.y = t_exit.y;
t_exit.y = d;
}
if (dirIsNeg[2]) {
float d = t_enter.z;
t_enter.z = t_exit.z;
t_exit.z = d;
}
float t_min = std::max(t_enter.x, std::max(t_enter.y, t_enter.z));
float t_max = std::min(t_exit.x, std::min(t_exit.x, t_exit.z));
// add
t_max += eps;
return (t_min < t_max && t_max >= 0);
}
问题4:结果偏黑。
分母pdf_light不要加eps。
castRay
// Implementation of Path Tracing
Vector3f Scene::castRay(const Ray &ray, int depth) const
{
// TO DO Implement Path Tracing Algorithm here
// 光线和场景的交点
Intersection t1 = intersect(ray);
// 没有交点
if (!t1.happened) {
return Vector3f();
}
// 如果物体发光
if (t1.emit.norm() > 0) {
// 物体发光部分只统计一次
if (depth == 0) {
return t1.emit;
} else {
return Vector3f();
}
}
// 交点位置
Vector3f p = t1.coords;
// 场景法线
Vector3f N = (t1.normal).normalized();
// 入射方向
Vector3f wo = (-ray.direction).normalized();
// 材质
Material *m1 = t1.m;
// samplelight
Intersection inter;
float pdf_light;
sampleLight(inter, pdf_light);
// 光源位置
Vector3f x = inter.coords;
// 出射方向
Vector3f ws = (x - p).normalized();
// 光源法线
Vector3f NN = (inter.normal).normalized();
// Radiance Li
Vector3f emit = inter.emit;
// 距离
float dist = (x - p).norm();
// shoot a ray from p to x
Ray px = Ray(p, ws);
Intersection px_inter = intersect(px);
// 交点位置
Vector3f x_inter = px_inter.coords;
// If the ray is not blocked in the middle
Vector3f L_dir = Vector3f();
if (px_inter.happened && ((x_inter - x).norm() < 1e-3)) {
// 不要使用pdf_light + eps, 否则会全黑
L_dir = emit * m1->eval(wo, ws, N) * dotProduct(ws, N) * dotProduct(-ws, NN) / (dist * dist) / pdf_light;
}
Vector3f L_indir = Vector3f();
// Test Russian Roulette with probability RussianRoulette
if (get_random_float() < RussianRoulette) {
Vector3f wi = m1->sample(wo, N).normalized();
// Trace a ray r(p, wi)
Ray ray_new(p, wi);
// If ray r hit a non -emitting object at q
// 光线和场景的交点
Intersection t2 = intersect(ray_new);
Material *m2 = t1.m;
float pdf = m1->pdf(wo, wi, N);
L_indir = castRay(ray_new, depth + 1) * m2->eval(wo, wi, N) * dotProduct(wi, N) / pdf / RussianRoulette;
}
return L_dir + L_indir;
}
多进程版本
// The main render function. This where we iterate over all pixels in the image,
// generate primary rays and cast these rays into the scene. The content of the
// framebuffer is saved to a file.
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(278, 273, -800);
int m = 0;
// change the spp value to change sample ammount
int spp = 16;
std::cout << "SPP: " << spp << "\n";
// for (uint32_t j = 0; j < scene.height; ++j) {
// for (uint32_t 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;
// }
// m++;
// }
// UpdateProgress(j / (float)scene.height);
// }
// UpdateProgress(1.f);
// 整体进度
int progress = 0;
auto f = [&] (int x_start, int x_end, int y_start, int y_end) {
for (uint32_t j = y_start; j < y_end; ++j) {
int m = j * scene.width + x_start;
for (uint32_t i = x_start; i < x_end; ++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;
}
m++;
progress++;
}
std::lock_guard<std::mutex> lock(io_mutex);
UpdateProgress(progress / (float)scene.height / scene.width);
}
};
int hx = 5;
int hy = 5;
int hx_d = scene.height / hx;
int hy_d = scene.width / hy;
std::thread thread[hx][hy];
for (int i = 0; i < hx; i++) {
for (int j = 0; j < hy; j++) {
int x_start = i * hx_d;
int x_end = std::min(x_start + hx_d, scene.height);
int y_start = j * hy_d;
int y_end = std::min(y_start + hy_d, scene.width);
thread[i][j] = std::thread(f, x_start, x_end, y_start, y_end);
}
}
for (int i = 0; i < hx; i++) {
for (int j = 0; j < hy; j++) {
thread[i][j].join();
}
}
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 * std::pow(clamp(0, 1, framebuffer[i].x), 0.6f));
color[1] = (unsigned char)(255 * std::pow(clamp(0, 1, framebuffer[i].y), 0.6f));
color[2] = (unsigned char)(255 * std::pow(clamp(0, 1, framebuffer[i].z), 0.6f));
fwrite(color, 1, 3, fp);
}
fclose(fp);
}
效果
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Doraemonzzz!
评论
ValineLivere