GAMES101 作业7——光线追踪3(实现Path Tracing算法)

举报
lutianfei 发表于 2022/05/13 12:08:28 2022/05/13
【摘要】 作业描述在之前的练习中,我们实现了 Whitted-Style Ray Tracing 算法,并且用 BVH 等加速结构对于求交过程进行了加速。在本次实验中,我们将在上一次实验的基础上实现完整的 Path Tracing 算法。至此,我们已经来到了光线追踪版块的最后一节内容,实现光线追踪。你需要从上一次编程练习中直接拷贝以下函数到对应位置:Triangle::getIntersection...

作业描述

在之前的练习中,我们实现了 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 Tracing 算法

注意:本次作业需要对global.hpp中的get_random_float方法中的三个变量增加static约束,否则代码会在运行2%后异常退出。

inline float get_random_float()
{
    static std::random_device dev;
    static std::mt19937 rng(dev());
    static std::uniform_real_distribution<float> dist(0.f, 1.f); // distribution in range [1, 6]

    return dist(rng);
}

本次作业的代码:

  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 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 的概率

需要注意的几点:

  1. 我们将光照对物体表面某一点的贡献分为光源其他反射物,当光源直接能够打到物体上时,不需要再考虑其他反射物的贡献,因为这根光线直接击中了光源
  2. 需要判断每一根光线是否击中光源
  3. 没有击中光源才需要通过 Russian Roulette 来计算其他反射物的光照贡献(间接光照)
Vector3f Scene::castRay(const Ray &ray, int depth) const
{
    Intersection inter = intersect(ray);

    if (inter.happened)
    {
        // 如果射线第一次打到光源,直接返回
        if (inter.m->hasEmission())
        {
            if (depth == 0)
            {
                return inter.m->getEmission();
            }
            else return Vector3f(0, 0, 0);
        }

        Vector3f L_dir(0, 0, 0);
        Vector3f L_indir(0, 0, 0);

        // 随机 sample 灯光,用该 sample 的结果判断射线是否击中光源
        Intersection lightInter;
        float pdf_light = 0.0f;
        sampleLight(lightInter, pdf_light);

        // 物体表面法线
        auto& N = inter.normal;
        // 灯光表面法线
        auto& NN = lightInter.normal;

        auto& objPos = inter.coords;
        auto& lightPos = lightInter.coords;

        auto diff = lightPos - objPos;
        auto lightDir = diff.normalized();
        float lightDistance = diff.x * diff.x + diff.y * diff.y + diff.z * diff.z;

        Ray light(objPos, lightDir);
        Intersection light2obj = intersect(light);

        // 如果反射击中光源
        if (light2obj.happened && (light2obj.coords - lightPos).norm() < 1e-2)
        {
            Vector3f f_r = inter.m->eval(ray.direction, lightDir, N);
            L_dir = lightInter.emit * f_r * dotProduct(lightDir, N) * dotProduct(-lightDir, NN) / lightDistance / pdf_light;
        }

        if (get_random_float() < RussianRoulette)
        {
            Vector3f nextDir = inter.m->sample(ray.direction, N).normalized();

            Ray nextRay(objPos, nextDir);
            Intersection nextInter = intersect(nextRay);
            if (nextInter.happened && !nextInter.m->hasEmission())
            {
                float pdf = inter.m->pdf(ray.direction, nextDir, N);
                Vector3f f_r = inter.m->eval(ray.direction, nextDir, N);
                L_indir = castRay(nextRay, depth + 1) * f_r * dotProduct(nextDir, N) / pdf / RussianRoulette;
            }
        }

        return L_dir + L_indir;
    }

    return Vector3f(0, 0, 0);
}

当设置SPP为60时结果如下图所示:

image.png

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。