实现完整的 Path Tracing 算法
需要修改这一个函数:
• castRay(const Ray ray, int depth)in Scene.cpp: 在其中实现 Path Tracing 算法
// Implementation of Path Tracing Vector3f Scene::castRay(const Ray &ray, int depth) const { // TO DO Implement Path Tracing Algorithm here // 本函数为发射一条光ray和其当前深度depth,求最终的着色点颜色值 // 所用函数介绍: // Intersection intersect(const Ray& ray) const; // 返回光源与BVH的交点 // void sampleLight(Intersection &pos, float &pdf); // 对所有光源按面积均匀采样一个点,并将pdf修改为采样的概率密度 // Vector3f Material::sample(const Vector3f &wi, const Vector3f &N); // 按照该材质的性质和给定入射方向wi与法向量N,用某种分布采样一个出射方向 // float Material::pdf(V3f wi, V3f &wo, V3f N) // 给定入射wi,出射wo,法向量N,计算采样得出的该方向上的概率密度 // Vector3f Material::eval(V3f wi, V3f &wo, V3f N) // 计算f_r值 // Scene.RussianRoulette // PRR概率 Intersection intersection = intersect(ray); // 首先获取着色点位置pos if(intersection.happened){ Vector3f L_dir(0.0), L_indir(0.0); Vector3f p = intersection.coords; Vector3f wo = ray.direction; // 观测点打向着色点的方向 Vector3f N = intersection.normal; // 法线即为交点法线 Material *m = intersection.m; // 首先是直接光照,从光源发出 // 采样光源获取x点 Intersection light_intersection; float pdf_light = 0.0; sampleLight(light_intersection, pdf_light); Vector3f x = light_intersection.coords; // 从p点向x点打一光线ps,方向为ws Vector3f ws = (x - p).normalized(); Ray ray_px(p, ws); //从p点加一段距离避免判断光线被自己挡住 Intersection intersect_px = intersect(ray_px); // 如果光线打到的点是光源则修改L_dir if(intersect_px.happened && intersect_px.m->hasEmission()){ Vector3f NN = intersect_px.normal; L_dir = light_intersection.emit * m->eval(wo, ws, N) * dotProduct(ws, N) * dotProduct(-ws, NN) / intersect_px.distance / pdf_light; } // 然后是间接光照,采用RR判断是否需要继续打光线,故不需要使用depth if(get_random_float() <= RussianRoulette){ // 从p点向外打一根光线,如果有交点设为q Vector3f wi = m->sample(wo, N).normalized(); Ray ray_pq(p, wi); Intersection intersect_q = intersect(ray_pq); // 如果打中且不是光源计算间接光照 if(intersect_q.happened && !intersect_q.m->hasEmission()){ L_indir = castRay(ray_pq, depth + 1) * m->eval(wo, wi, N) * dotProduct(wi, N) / m->pdf(wo, wi, N) / RussianRoulette; } } return m->getEmission() + L_dir + L_indir; } //如果没有交点,返回黑色 return Vector3f(0.0); }View Code
ps:需要注意的是,由于框架改变、坐标系改变、通过自发光消除黑边等原因我们需要修改其他的函数
void Scene::sampleLight(Intersection &pos, float &pdf) const in Scene.cppvoid Scene::sampleLight(Intersection &pos, float &pdf) const { float emit_area_sum = 0; for (uint32_t k = 0; k < objects.size(); ++k) { // 如果物体发光则为光源 if (objects[k]->hasEmit()){ // 发光面积累加上物体所有三角形面积 emit_area_sum += objects[k]->getArea(); } } float p = get_random_float() * emit_area_sum; //随机取一块面积 emit_area_sum = 0; for (uint32_t k = 0; k < objects.size(); ++k) { if (objects[k]->hasEmit()){ emit_area_sum += objects[k]->getArea(); // 如果当前所取面积和超过之前所取阈值 p, // 则调用MeshTriangle类的采样函数 // 传入交点位置pos,和pdf,修改pos的发光位置和pdf值 if (p <= emit_area_sum){ objects[k]->Sample(pos, pdf); break; } } } }View Code inline bool Bounds3::IntersectP(const Ray& ray, const Vector3f& invDir, const std::array<int, 3>& dirIsNeg) const in Bounds3.hpp
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), // 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 t_enter_xzy = (pMin - ray.origin) * invDir; Vector3f t_exit_xzy = (pMax - ray.origin) * invDir; // 如果光线在某一个轴上的分量是负数,则对应轴上的面越大越先进入 if(!dirIsNeg[0]) std::swap(t_enter_xzy.x, t_exit_xzy.x); if(!dirIsNeg[1]) std::swap(t_enter_xzy.y, t_exit_xzy.y); if(!dirIsNeg[2]) std::swap(t_enter_xzy.z, t_exit_xzy.z); float tenter = std::max(std::max(t_enter_xzy.x, t_enter_xzy.y), t_enter_xzy.z); float texit = std::min(std::min(t_exit_xzy.x, t_exit_xzy.y), t_exit_xzy.z); return tenter <= texit && texit >= 0; }View Code Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const in BVH.cpp
Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const { Intersection res; // TODO Traverse the BVH to find intersection // 如果光线与改包围盒无交点,直接返回false的res if(!node->bounds.IntersectP(ray, ray.direction_inv, std::array<int, 3> {ray.direction.x > 0, ray.direction.y > 0, ray.direction.z > 0})){ return res; } // 如果当前节点为叶子节点,遍历node中的每一个物体使之于光线求交 if(node->object){ return node->object->getIntersection(ray); } // 如果当前节点的盒子与光线相交则分别递归到左右 Intersection l = getIntersection(node->left, ray); Intersection r = getIntersection(node->right, ray); //如果有两个交点,返回较近的那个交点 return l.distance <= r.distance ? l : r; }View Code bool Material::hasEmission() in Material.hpp
bool Material::hasEmission() { // 根据Material初始化时传入的第二个Vector3f的值决定(非0即合法即为光源) if (m_emission.norm() > EPSILON) return true; else return false; }View Code inline Intersection Triangle::getIntersection(Ray ray) in Triangle.hpp
inline Intersection Triangle::getIntersection(Ray ray) { Intersection inter; if (dotProduct(ray.direction, normal) > 0) return inter; double u, v, t_tmp = 0; Vector3f pvec = crossProduct(ray.direction, e2); double det = dotProduct(e1, pvec); if (fabs(det) < EPSILON) return inter; double det_inv = 1. / det; Vector3f tvec = ray.origin - v0; u = dotProduct(tvec, pvec) * det_inv; if (u < 0 || u > 1) return inter; Vector3f qvec = crossProduct(tvec, e1); v = dotProduct(ray.direction, qvec) * det_inv; if (v < 0 || u + v > 1) return inter; t_tmp = dotProduct(e2, qvec) * det_inv; // TODO find ray triangle intersection // 若相交,则修改交点inter的数据 if (t_tmp < 0) return inter; inter.happened = true; inter.coords = ray(t_tmp); // 根据光照内置()运算获取交点坐标 inter.distance = dotProduct(t_tmp * ray.direction, t_tmp * ray.direction); // 距离平方 inter.m = m; inter.obj = this; inter.normal = normal; return inter; }View Code
多线程加速:
在void Renderer::Render(const Scene& scene) in Render.cpp中,通过#pragma omp parallel for预处理指令使循环获得多线程并行处理能力
// 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 = 4; 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)); #pragma omp parallel for 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); // 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); }View Code
标签:Intersection,const,Vector3f,float,return,games101,Homework7,ray From: https://www.cnblogs.com/wosun/p/18134852