首页 > 其他分享 >games101_Homework7

games101_Homework7

时间:2024-04-14 22:46:28浏览次数:31  
标签:Intersection const Vector3f float return games101 Homework7 ray

实现完整的 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.cpp
void 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

相关文章

  • games101_Homework6
    实现Ray-BoundingVolume求交与BVH查找在本次编程练习中,你需要实现以下函数:•IntersectP(constRay&ray,constVector3f&invDir,conststd::array<int,3="">&dirIsNeg)intheBounds3.hpp:这个函数的作用是判断包围盒BoundingBox与光线是否相交,你需要按照课程介......
  • Intel MacBook Pro+macOS 14配置Games101实验环境
    参考:求一个games101图形学课程的环境配置教程,最好能够简单易懂,CSDN教程根本看不懂什么意思?-不泊的回答-知乎https://www.zhihu.com/question/459126051/answer/3420947842macos现在怎么装homebrew?-MyloZ的回答-知乎https://www.zhihu.com/question/340411846/answe......
  • 04 games101-变换(模型、视图、投影)
    04变换(模型、视图、投影)MVP变换MVP变换用来描述视图变换的任务,即将虚拟世界中的三维物体映射(变换)到二维坐标中。MVP变换分为三步:●模型变换(modeltranformation):将模型空间转换到世界空间(找个好的地方,把所有人集合在一起,摆个pose)●摄像机变换(viewtranformation):将......
  • 06 games101-光栅化(深度测试与抗锯齿)
    06光栅化(深度测试与抗锯齿)从采样分析走样采样的对象:●在位置上采样——照片●在时间上采样——视频以下副标题均是在时域上分析。采样的瑕疵(Artifacts)Artifacts(Erros/Mistakes/Inaccuracies)●锯齿●摩尔纹●车轮效应●…走样的原因信号频率太快,采样太......
  • 05 games101-光栅化(三角形的离散化)
    05光栅化(三角形的离散化)三角形三角形的性质和优点:●最基础的多边形●其他图形可以拆解为三角形●三角形内一定是平面●内外的定义很明确●定义三个顶点后,三角形内可以插值光栅化(Rasterization)光栅化关键:判断一个像素和三角形的位置关系(像素中心点与三角形的位......
  • games101_Homework3
    摘要:在Raster部分实现数值插值,然后实现四种不同的像素着色器作业描述:作业1:修改函数rasterize_triangle(constTriangle&t)inrasterizer.cpp:在此处实现与作业2类似的插值算法,实现法向量、颜色、纹理颜色的插值。在rasterize_triangle函数中重复上次的包围盒进行点采样,......
  • games101_Homework4
    摘要:实现四个点的贝塞尔曲线作业描述:•bezier:该函数实现绘制Bézier曲线的功能。它使用一个控制点序列和一个OpenCV::Mat对象作为输入,没有返回值。它会使t在0到1的范围内进行迭代,并在每次迭代中使t增加一个微小值。对于每个需要计算的t,将调用另一个函数recursive......
  • GAMES101 Rasterization 光栅化
    向量点乘的作用计算两个方法方向夹角计算两个方向是否接近关于两个方向的计算向量叉乘\[\vec{a}\times\vec{b}=\begin{pmatrix}y_az_b-y_bz_a\\z_ax_b-x_az_b\\x_ay_b-y_ax_b\end{pmatrix}\]\[\veca\times\vecb=A^*b=\begin{pmatrix}0&-z_a&y_a\\z_a&0&-x_a\\-y_a&x......
  • games101_Homework1
    作业描述:本次作业的任务是填写一个旋转矩阵和一个透视投影矩阵。给定三维下三个点v0(2.0,0.0,−2.0),v1(0.0,2.0,−2.0),v2(−2.0,0.0,−2.0),你需要将这三个点的坐标变换为屏幕坐标,并在屏幕上绘制出对应的线框三角形(在代码框架中,我们已经提供了draw_triangl......
  • games101_Homework0
    作业描述:给定一个点P=(2,1),将该点绕原点先逆时针旋转45◦,再平移(1,2),计算出变换后点的坐标(要求用齐次坐标进行计算)。作业解答:#include<cmath>#include<eigen3/Eigen/Core>#include<eigen3/Eigen/Dense>#include<iostream>intmain(){//definepoin......