• GAMES101-ASSIGNMENT7(作业7)


    总览

             在之前的练习中,我们实现了 Whitted-Style Ray Tracing 算法,并且用 BVH等加速结构对于求交过程进行了加速。在本次实验中,我们将在上一次实验的基础上实现完整的 Path Tracing 算法。至此,我们已经来到了光线追踪版块的最后一节内容。

     调通框架

     2.1修改的内容

    相比上一次实验,本次实验对框架的修改较大,主要在以下几方面:
    • 修改了 main.cpp,以适应本次实验的测试模型 CornellBox
    • 修改了 Render,以适应 CornellBox 并且支持 Path Tracing 需要的同一 Pixel多次 Sample
    • 修改了 Object,Sphere,Triangle,TriangleMesh,BVH,添加了 area 属性与Sample 方法,以实现对光源按面积采样,并在 Scene 中添加了采样光源的接口 sampleLight
    • 修改了 Material 并在其中实现了 sample, eval, pdf 三个方法用于 Path Tracing 变量的辅助计算

    2.2你需要迁移的内容

    你需要从上一次编程练习中直接拷贝以下函数到对应位置:
    • Triangle::getIntersection in Triangle.hpp: 将你的光线-三角形相交函数粘贴到此处,请直接将上次实验中实现的内容粘贴在此。
    • IntersectP(const Ray& ray, const Vector3f& invDir,const std::array& dirIsNeg) in the Bounds3.hpp: 这个函数的2作用是判断包围盒 BoundingBox 与光线是否相交,请直接将上次实验中实现的内容粘贴在此处,并且注意检查 t_enter = t_exit 的时候的判断是否正确。
    • getIntersection(BVHBuildNode* node, const Ray ray)in BVH.cpp: BVH查找过程,请直接将上次实验中实现的内容粘贴在此处.

    2.3编译运行
    基础代码只依赖于 CMake,下载基础代码后,执行下列命令,就可以编译这个项目:

    1. mkdir build
    2. cd ./ build
    3. cmake ..
    4. make

    在此之后,你就可以通过 ./Raytracing 来执行程序。请务必确保程序可以正常编译之后,再进入下一节的内容。

     3 开始实现

    3.1代码框架

    在本次实验中,你只需要修改这一个函数:

    • castRay(const Ray ray, int depth)in Scene.cpp: 在其中实现 Path Tracing 算法

    可能用到的函数有:

    • intersect(const Ray ray)in Scene.cpp: 求一条光线与场景的交点
    • sampleLight(Intersection pos, float pdf) in Scene.cpp: 在场景的所有光源上按面积 uniform 地 sample 一个点,并计算该 sample 的概率密度
    • sample(const Vector3f wi, const Vector3f N) in Material.cpp: 按照该材质的性质,给定入射方向与法向量,用某种分布采样一个出射方向
    • pdf(const Vector3f wi, const Vector3f wo, const Vector3f N) in Maerial.cpp: 给定一对入射、出射方向与法向量,计算 sample 方法得到该出射方向的概率密度
    • eval(const Vector3f wi, const Vector3f wo, const Vector3f N) in Material.cpp: 给定一对入射、出射方向与法向量,计算这种情况下的 f_r 值可能用到的变量有:
    • RussianRoulette in Scene.cpp: P_RR, Russian Roulette 的概率

     IntersectP(const Ray& ray, const Vector3f& invDir,const std::array& dirIsNeg) in the Bounds3.hpp: 这个函数的作用是判断包围盒 BoundingBox 与光线是否相交,请直接将上次实验中实现的内容粘贴在此处,并且注意检查 t_enter = t_exit 的时候的判断是否正确。

    1. inline bool Bounds3::IntersectP(const Ray& ray, const Vector3f& invDir,
    2. const std::array<int, 3>& dirIsNeg) const
    3. {
    4. Vector3f tmin = (pMin - ray.origin) * invDir;
    5. Vector3f tmax = (pMax - ray.origin) * invDir;
    6. if (dirIsNeg[0])
    7. std::swap(tmin.x, tmax.x);
    8. if (dirIsNeg[1])
    9. std::swap(tmin.y, tmax.y);
    10. if (dirIsNeg[2])
    11. std::swap(tmin.z, tmax.z);
    12. float texit = std::min(tmax.x, std::min(tmax.y, tmax.z));
    13. float tenter = std::max(tmin.x, std::max(tmin.y, tmin.z));
    14. return tenter <= texit&& texit >= 0;
    15. }

    1. BVHBuildNode* BVHAccel::recursiveBuild(std::vector objects)
    2. {
    3. BVHBuildNode* node = new BVHBuildNode();
    4. // Compute bounds of all primitives in BVH node
    5. Bounds3 bounds;
    6. for (int i = 0; i < objects.size(); ++i)
    7. bounds = Union(bounds, objects[i]->getBounds());
    8. if (objects.size() == 1) {
    9. // Create leaf _BVHBuildNode_
    10. node->bounds = objects[0]->getBounds();
    11. node->object = objects[0];
    12. node->left = nullptr;
    13. node->right = nullptr;
    14. node->area = objects[0]->getArea();
    15. return node;
    16. }
    17. else if (objects.size() == 2) {
    18. node->left = recursiveBuild(std::vector{objects[0]});
    19. node->right = recursiveBuild(std::vector{objects[1]});
    20. node->bounds = Union(node->left->bounds, node->right->bounds);
    21. node->area = node->left->area + node->right->area;
    22. return node;
    23. }
    24. else {
    25. Bounds3 centroidBounds;
    26. for (int i = 0; i < objects.size(); ++i)
    27. centroidBounds =
    28. Union(centroidBounds, objects[i]->getBounds().Centroid());
    29. int dim = centroidBounds.maxExtent();
    30. switch (dim) {
    31. case 0:
    32. std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
    33. return f1->getBounds().Centroid().x <
    34. f2->getBounds().Centroid().x;
    35. });
    36. break;
    37. case 1:
    38. std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
    39. return f1->getBounds().Centroid().y <
    40. f2->getBounds().Centroid().y;
    41. });
    42. break;
    43. case 2:
    44. std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
    45. return f1->getBounds().Centroid().z <
    46. f2->getBounds().Centroid().z;
    47. });
    48. break;
    49. }
    50. auto beginning = objects.begin();
    51. auto middling = objects.begin() + (objects.size() / 2);
    52. auto ending = objects.end();
    53. auto leftshapes = std::vector(beginning, middling);
    54. auto rightshapes = std::vector(middling, ending);
    55. assert(objects.size() == (leftshapes.size() + rightshapes.size()));
    56. node->left = recursiveBuild(leftshapes);
    57. node->right = recursiveBuild(rightshapes);
    58. node->bounds = Union(node->left->bounds, node->right->bounds);
    59. node->area = node->left->area + node->right->area;
    60. }
    61. return node;
    62. }

    getIntersection(BVHBuildNode* node, const Ray ray)in BVH.cpp: BVH查找过程,请直接将上次实验中实现的内容粘贴在此处.

    1. Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const
    2. {
    3. // TODO Traverse the BVH to find intersection
    4. Intersection inter;
    5. Vector3f invdir(1 / ray.direction.x , 1 / ray.direction.y , 1 / ray.direction.z);
    6. //判断射线的方向正负,如果负,为1;bounds3.hpp中会用到。
    7. std::array<int , 3> dirIsNeg;
    8. dirIsNeg[0] = ray.direction.x < 0;
    9. dirIsNeg[1] = ray.direction.y < 0;
    10. dirIsNeg[2] = ray.direction.z < 0;
    11. //没有交点
    12. if (!node->bounds.IntersectP(ray,invdir,dirIsNeg)){
    13. return inter;
    14. }
    15. //有交点,且该点为叶子节点,去和三角形求交
    16. if (node -> left ==nullptr && node -> right == nullptr){
    17. return node -> object -> getIntersection(ray);
    18. }
    19. //该点为中间节点,继续判断,并返回最近的包围盒交点
    20. Intersection hit1 = getIntersection(node->left , ray);
    21. Intersection hit2 = getIntersection(node->right , ray);
    22. return hit1.distance < hit2.distance ? hit1:hit2;
    23. }

     Triangle::getIntersection in Triangle.hpp: 将你的光线-三角形相交函数粘贴到此处,请直接将上次实验中实现的内容粘贴在此。

    1. inline Intersection Triangle::getIntersection(Ray ray)
    2. {
    3. Intersection inter;
    4. if (dotProduct(ray.direction, normal) > 0)
    5. return inter;
    6. double u, v, t_tmp = 0;
    7. Vector3f pvec = crossProduct(ray.direction, e2);
    8. double det = dotProduct(e1, pvec);
    9. if (fabs(det) < EPSILON)
    10. return inter;
    11. double det_inv = 1. / det;
    12. Vector3f tvec = ray.origin - v0;
    13. u = dotProduct(tvec, pvec) * det_inv;
    14. if (u < 0 || u > 1)
    15. return inter;
    16. Vector3f qvec = crossProduct(tvec, e1);
    17. v = dotProduct(ray.direction, qvec) * det_inv;
    18. if (v < 0 || u + v > 1)
    19. return inter;
    20. t_tmp = dotProduct(e2, qvec) * det_inv;
    21. if(t_tmp<0)
    22. return inter;
    23. // TODO find ray triangle intersection
    24. inter.normal = normal;
    25. inter.coords = ray(t_tmp);
    26. inter.distance = t_tmp;
    27. inter.happened = true;
    28. inter.m = m;
    29. inter.obj = this;
    30. return inter;
    31. }

    castRay(const Ray ray, int depth)in Scene.cpp: 在其中实现 Path Tracing 算法

    1. // Implementation of Path Tracing
    2. Vector3f Scene::castRay(const Ray &ray, int depth) const
    3. {
    4. // TO DO Implement Path Tracing Algorithm here
    5. //创建变量以储存直接和间接光照计算值
    6. Vector3f L_dir = {0,0,0} , L_indir = {0,0,0};
    7. //1.判断是否有交点:光线与场景中物体相交?
    8. Intersection intersection = Scene::intersect(ray); //求一条光线与场景的交点
    9. if (!intersection.happened) //没交点
    10. return {};
    11. //2.ray打到光源了:说明渲染方程只用算前面的自发光项,因此直接返回材质的自发光项
    12. if (intersection.m->hasEmission()){//一、交点是光源:
    13. // if (depth == 0)//第一次打到光
    14. return intersection.m->getEmission();
    15. // return {};//弹射打到光,直接返回0,0.0
    16. }
    17. //---------二、交点是物体:1)向光源采样计算direct----------
    18. Intersection lightpos;
    19. float lightpdf = 0.0f;
    20. //对场景中的光源进行采样,得到采样点light_pos和pdf_light
    21. sampleLight(lightpos,lightpdf) ;//获得对光源的采样,包括光源的位置和采样的pdf(在场景的所有光源上按面积 uniform 地 sample 一个点,并计算该 sample 的概率密度)
    22. //计算光源到物体点的距离
    23. Vector3f collisionlight = lightpos.coords - intersection.coords;
    24. float dis = dotProduct(collisionlight,collisionlight);
    25. //判断光源与物体间有无遮挡,从光源发射一条相同方向的光线打到场景上,比较最后两者的距离
    26. Vector3f collisionlightdir = collisionlight.normalized(); //光源到物体的光线方向
    27. Ray light_to_object_ray (intersection.coords,collisionlightdir); //光源到物体的光线
    28. Intersection light_to_anything_ray = Scene::intersect(light_to_object_ray);
    29. auto f_r = intersection.m -> eval(ray.direction,collisionlightdir,intersection.normal);//材质,课上说了,BRDF==材质,ws不参与计算
    30. //判断光源与物体间有无遮挡
    31. if (light_to_anything_ray.distance - collisionlight.norm() > -0.005){//无遮挡
    32. //渲染方程
    33. L_dir = lightpos.emit * f_r * dotProduct(collisionlightdir , intersection.normal) * dotProduct(-collisionlightdir,lightpos.normal) / dis / lightpdf;
    34. }
    35. //--------二、交点是物体:2)向其他物体采样递归计算indirect---------;
    36. //俄罗斯轮盘赌
    37. if (get_random_float() > RussianRoulette)//打到物体后对半圆随机采样使用RR算法
    38. return L_dir;
    39. //随机生成一个w0方向
    40. Vector3f w0 = intersection.m ->sample(ray.direction , intersection.normal).normalized();//这里的w0其实没参与计算,返回的是一个随机的方向
    41. Ray object_to_object_ray(intersection.coords , w0);
    42. Intersection islight = Scene::intersect(object_to_object_ray);
    43. if (islight.happened && !islight.m->hasEmission()){ //光线击中的不是点光源
    44. float pdf = intersection.m ->pdf(ray.direction,w0,intersection.normal);
    45. f_r = intersection.m->eval(ray.direction,w0,intersection.normal);//材质,课上说了,BRDF==材质,ws不参与计算
    46. //渲染方程
    47. L_indir = castRay(object_to_object_ray,depth+1)*f_r*dotProduct(w0 , intersection.normal) / pdf / RussianRoulette;
    48. }
    49. return L_dir + L_indir;
    50. }

    结果:

     

     PS:在windows上运行的时候需要修改global.cpp文件的以下函数,不然可能会跑不了

    inline float get_random_float()

    {

        static std::random_device dev;

        static std::mt19937 rng(dev());

        static std::uniform_real_distribution dist(0.f, 1.f); // distribution in range [0,1]

        return dist(rng);

    }

  • 相关阅读:
    vue中组件间的通信
    IntersectionObserver监听滚动事件
    半导体CIM系统中的EAP:提升制造效率的关键
    【C++】Qt的属性系统,简单示例
    【附源码】计算机毕业设计JAVA计算机系教师教研科研管理系统
    HTML学生个人网站作业设计 明星易烊千玺介绍(HTML+CSS) web前端开发技术 web课程设计 网页规划与设计
    大数据知识合集之数据分析模型
    Vant UI的Sidebar侧边导航组件单独设置滚动条
    重磅!Grafana 9 正式发布,更强大、更易用了!
    [数据结构与算法] 图解线性表
  • 原文地址:https://blog.csdn.net/qq_48626761/article/details/126853620