GAMES101作业(07)- 路径追踪

作业来自官网

知识点整理

总览

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

任务

需要迁移的代码

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

需要实现的函数

  • castRay(const Ray ray, int depth)in Scene.cpp: 在其中实现 Path Trac-
    ing

用到的函数

  • 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 Material.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 的概率

解答

castRay() in Scene.cpp

  1. 先通过光线投射算法得到的光线对场景的物体求交,获得下一个着色点p。
  2. 着色点是光源就直接返回光源的emission值
  3. 时刻注意是否相交,不相交直接返回0,可以减少重复计算。
  4. 调用方法后ray的属性是不一定填全的,有时候t就没有,调用前要看一下方法。
  5. 计算 cos ⁡ θ ′ \cos \theta ' cosθ时的wi应该是负的,好像作业文件里面的伪代码不太对?

注意 main.cpp调分辨率、Render.cpp调spp可以加快运行速度。

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();
    }
    Vector3f wo = ray.direction;
    Vector3f L_dir(0);

    //在光源上的交点
    Intersection inter_l;
    //光源的pdf
    float pdf;
    //对光源进行取样,得到光源的pdf和光源上的点和光源的面积密度pdf
    sampleLight(inter_l,pdf);
    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);
    //判断中间没有物体阻隔
    if ((inter_l.coords-inter_tmp.coords).norm()<EPSILON*EPSILON){
    
    
        // 这里作业说明好像不是很正确,算cos theta prime时的wi应该时负的。
        L_dir = inter_l.emit*p.m->eval(wo,wi.direction,p.normal)*dotProduct(wi.direction,p.normal)*dotProduct(-wi.direction,inter_l.normal)/(p2lightDist*p2lightDist)/pdf;
    }
    Vector3f L_indir=0.0f;
    //俄罗斯轮盘获得随机概率
    float P_RR = RussianRoulette;
    int seed = rand()%10;
    if (seed*1.0/10>P_RR)//生存概率
        return L_dir;
    //取样一个新的随机方向
    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())
        L_indir = castRay(r,depth+1)* p.m->eval(wo,wi_new,p.normal)*dotProduct(wi_new,p.normal)/p.m->pdf(wo,wi_new,p.normal)/P_RR;
    return L_dir + L_indir;
}

IntersectP in the Bounds3.hpp

在这里犯了错误,上次作业遗留的,注意这里等号的条件也要包含在内,否则场景会渲染不全

inline bool Bounds3::IntersectP(const Ray& ray, const Vector3f& invDir,
                                const std::array<int, 3>& dirIsNeg) const
{
    
    
    float tx_enter=invDir.x*(pMin.x-ray.origin.x);
    float tx_exit=invDir.x*(pMax.x-ray.origin.x);
    if(dirIsNeg[0])
        std::swap(tx_enter,tx_exit);
    float ty_enter=invDir.y*(pMin.y-ray.origin.y);
    float ty_exit=invDir.y*(pMax.y-ray.origin.y);
    if(dirIsNeg[1])
        std::swap(ty_enter,ty_exit);
    float tz_enter=invDir.z*(pMin.z-ray.origin.z);
    float tz_exit=invDir.z*(pMax.z-ray.origin.z);
    if(dirIsNeg[2])
        std::swap(tz_enter,tz_exit);
    return tx_exit>=0&&tx_enter<=tx_exit&&ty_exit>=0&&ty_enter<=ty_exit&&tz_exit>=0&&tz_enter<=tz_exit;
}

结果

比较曲折,最开始得到全红的图片,然后又有几次得到全黑的图片,调试后得到了一张没渲染全的图片(是求交没考虑等号造成的)。

在这里插入图片描述
修改后就渲染比较全了(SPP=4)

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43399489/article/details/121706120