GAMES101——作业7 路径追踪 (含提高:多线程,微平面理论)

news2024/12/26 22:20:18

任务

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

实现

        根据作业文档里的伪代码,可以对着写出代码。需要注意的一点是,如果纯按照这里的伪代码来写,最后的结果光源的位置是纯黑的,需要再加一段判断射线是否击中光源的代码。

Vector3f Scene::castRay(const Ray &ray, int depth) const
{
    Vector3f L_dir;
    Vector3f L_indir;

    //  视线
    Intersection obj_inter = intersect(ray);
    if (!obj_inter.happened)
        return L_dir;

    // 打到光源,直接返回光源的光照。
    if (obj_inter.m->hasEmission())
        return obj_inter.m->getEmission();

    Vector3f p = obj_inter.coords;              // 交点坐标
    Material *m = obj_inter.m;                  // 交点材质
    Vector3f N = obj_inter.normal.normalized(); // 交点法线
    Vector3f wo = ray.direction;                // 射线方向

    // 有交点,对光源采样
    Intersection light_inter;
    float pdf_L;
    sampleLight(light_inter, pdf_L); // 得到光源位置和对光源采样的pdf

    Vector3f x = light_inter.coords;
    Vector3f ws = (x - p).normalized(); // 物体到光源

    Vector3f emit = light_inter.emit;
    Vector3f NN = light_inter.normal.normalized();
    float d = (x - p).norm();

    // 判断光源和物体之间是否被其他物体遮挡
    Ray Obj2Light(p, ws);
    float d2 = intersect(Obj2Light).distance;
    // 根据距离判断是否遮挡
    if (d2 - d > -0.001)
    {
        Vector3f eval = m->eval(wo, ws, N);
        float cos_theta = dotProduct(N, ws);
        float cos_theta_x = dotProduct(NN, -ws);
        L_dir = emit * eval * cos_theta * cos_theta_x / std::pow(d, 2) / pdf_L;
    }

    //  计算间接光照
    float P_RR = get_random_float();
    if (P_RR < RussianRoulette)
    {
        Vector3f wi = m->sample(wo, N).normalized();
        Ray r(p, wi);
        Intersection inter = intersect(r);
        // 判断计算间接光照时是否打到了光源
        if (inter.happened && !inter.m->hasEmission())
        {
            Vector3f eval = m->eval(wo, wi, N);
            float pdf_O = m->pdf(wo, wi, N); // 1/2PI

            float cos_theta = dotProduct(wi, N);
            L_indir = castRay(r, depth + 1) * eval * cos_theta / pdf_O / RussianRoulette;
        }
    }

    return L_dir + L_indir;
}
结果

spp = 256

提高

        多线程

        这里直接简单的采用c++自带的thread来实现多线程

    int thread_num = 24;
    std::thread renders[thread_num];
    
    int rate = 0;

    std::cout << "SPP: " << spp << "\n";

    auto render_row = [&](uint32_t start_height,uint32_t end_height){
        for (uint32_t j = start_height; j < end_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));
                for (int k = 0; k < spp; k++){
                    framebuffer[j * scene.width + i] += scene.castRay(Ray(eye_pos, dir), 0) / spp;  
                }
            }
            rate++;
            UpdateProgress(rate / (float)scene.height);
        }
    };

    for(int k =0;k<thread_num;k++){
        uint32_t start = k * scene.height / thread_num;
        uint32_t end = (k == thread_num - 1) ? scene.height : (k+1)*scene.height / thread_num;
        renders[k] = std::thread(render_row,start,end);
    }
    for(int k=0;k<thread_num;k++){
        renders[k].join();
    }

以长方体和正方体的场景为例,设置spp为1,不采用多线程时,消耗了14秒的时间,当采用多线程时(这里采用24线程),所消耗的时间仅为3秒。

微平面模型

        ​​​​​

微平面(microfacet)理论假设物体表面由不同法向量的微小平面组成。

它引入了三个函数:DGF

  • D:是法线分布函数,它解释了在观看者角度反射光的微平面的比例。法线分布函数描述了在这个表面周围的法线分布情况,当输入向量h时,如果微平面中有35%与向量h取向一致,则法线分布函数就会返回0.35

  • G:是几何衰减函数,它解释了微平面彼此之间的阴影和遮挡。

  • F:是菲涅尔函数,它解释了菲涅耳效应,该效应使得与表面成较高的入射角的光线会以更高的镜面反射率进行反射。

        昨天晚上看完教程后手写推了一下该BRDF,微平面理论cook-torrance BRDF的推导

        菲涅尔函数的精确计算与近似,这里直接采用了作业框架自带的函数来求精确的菲涅尔函数的值。

        

        

        几何衰减函数

        

        法线分布函数,这里采用了各向同性的GGX法线分布函数

        

        代码实现

        菲涅尔函数(直接使用作业框架自带的菲涅尔函数)

    void fresnel(const Vector3f &I, const Vector3f &N, const float &ior, float &kr) const
    {
        float cosi = clamp(-1, 1, dotProduct(I, N));
        float etai = 1, etat = ior;
        if (cosi > 0) {  std::swap(etai, etat); }
        // Compute sini using Snell's law
        float sint = etai / etat * sqrtf(std::max(0.f, 1 - cosi * cosi));
        // Total internal reflection
        if (sint >= 1) {
            kr = 1;
        }
        else {
            float cost = sqrtf(std::max(0.f, 1 - sint * sint));
            cosi = fabsf(cosi);
            float Rs = ((etat * cosi) - (etai * cost)) / ((etat * cosi) + (etai * cost));
            float Rp = ((etai * cosi) - (etat * cost)) / ((etai * cosi) + (etat * cost));
            kr = (Rs * Rs + Rp * Rp) / 2;
        }
        // As a consequence of the conservation of energy, transmittance is given by:
        // kt = 1 - kr;
    }

        几何函数,将公式独立成GeometrySchlickGGX,然后在GeometrySmith中调用两次,这样能求得入射光和出射光各自的遮蔽情况。

    float GeometrySchlickGGX(float NdotV, float k)
    {
        float nom = NdotV;
        float denom = NdotV * (1.0 - k) + k;
        return nom / denom;
    }

    float GeometrySmith(Vector3f N, Vector3f V, Vector3f L, float roughness)
    {
        float r = (roughness + 1.0);
        float k = (r * r) / 8.0;
        float NdotV = std::max(dotProduct(N, V), 0.0f);
        float NdotL = std::max(dotProduct(N, L), 0.0f);
        float ggx2 = GeometrySchlickGGX(NdotV, k);
        float ggx1 = GeometrySchlickGGX(NdotL, k);

        return ggx1 * ggx2;
    }

        法线分布函数

   float DistributionGGX(Vector3f N, Vector3f H, float roughness)
    {
        float a = roughness * roughness;
        float a2 = a * a;
        float NdotH = std::max(dotProduct(N, H), 0.0f);
        float NdotH2 = NdotH * NdotH;

        float nom = a2;
        float denom = (NdotH2 * (a2 - 1.0) + 1.0);
        denom = M_PI * denom * denom;

        return nom / std::max(denom, 0.0000001f); 
    }

        对材质的sample,pdf和eval方法都作相应的更改,以及添加一个新的材质项MIRCO

enum MaterialType { DIFFUSE , MIRCO};
Vector3f Material::sample(const Vector3f &wi, const Vector3f &N){
    switch(m_type){
        case DIFFUSE:
        {
            // uniform sample on the hemisphere
            float x_1 = get_random_float(), x_2 = get_random_float();
            float z = std::fabs(1.0f - 2.0f * x_1);
            float r = std::sqrt(1.0f - z * z), phi = 2 * M_PI * x_2;
            Vector3f localRay(r*std::cos(phi), r*std::sin(phi), z);
            return toWorld(localRay, N);
            
            break;
        }
        case MIRCO:
        {
            float x_1 = get_random_float(), x_2 = get_random_float();
            float z = std::fabs(1.0f - 2.0f * x_1);
            float r = std::sqrt(1.0f - z * z), phi = 2 * M_PI * x_2;
            Vector3f localRay(r*std::cos(phi), r*std::sin(phi), z);
            return toWorld(localRay, N);
            
            break;
        }
    }
}
float Material::pdf(const Vector3f &wi, const Vector3f &wo, const Vector3f &N){
    switch(m_type){
        case DIFFUSE:
        {
            // uniform sample probability 1 / (2 * PI)
            if (dotProduct(wo, N) > 0.0f)
                return 0.5f / M_PI;
            else
                return 0.0f;
            break;
        }

        case MIRCO:
        {   
             if (dotProduct(wo, N) > 0.0f)
                return 0.5f / M_PI;
            else
                return 0.0f;
            break;
        }
    }
}
Vector3f Material::eval(const Vector3f &wi, const Vector3f &wo, const Vector3f &N)
{
    switch (m_type)
    {
    case DIFFUSE:
    {
        // calculate the contribution of diffuse   model
        float cosalpha = dotProduct(N, wo);
        if (cosalpha > 0.0f)
        {
            Vector3f diffuse = Kd / M_PI;
            return diffuse;
        }
        else
            return Vector3f(0.0f);
        break;
    }

    case MIRCO:
    {
        float cosalpha = dotProduct(N, wo);
        if (cosalpha > 0.0f)
        {

            Vector3f V = -wi;
            Vector3f L = wo;
            Vector3f H = normalize(V + L);

            float D = DistributionGGX(N, H, roughness);

            float G = GeometrySmith(N, V, L, roughness);

            float F;
            fresnel(wi, N, ior, F);

            Vector3f nominator = D * G * F;
            float denominator = 4 * std::max(dotProduct(N, V), 0.0f) * std::max(dotProduct(N, L), 0.0f);
            Vector3f specular = nominator / std::max(denominator, 0.001f);

            // 根据能量守恒,反射以外的能量被吸收发生漫反射
            float ks_ = F;
            float kd_ = 1.0f - ks_;

            Vector3f diffuse = 1.0f / M_PI;

            return Ks * specular + kd_ * Kd * diffuse;
        }
        else
            return Vector3f(0.0f);
        break;
    }
    }
}

        在main里面创建材质

 Material* mirco = new Material(MIRCO,Vector3f(0.0f));
    mirco->Kd = Vector3f(0.6);
    mirco->Ks = Vector3f(0.7);
    mirco->ior = 30;
    mirco->roughness = 0.9;

        引入兔子模型

        直接引入兔子模型会发生错误,因为兔子过小,参考论坛和他人的博客,需要对初始化模型的函数进行小小的修改。

   MeshTriangle(const std::string& filename, Material *mt = new Material(), 
        Vector3f Trans = Vector3f(0.0,0.0,0.0), Vector3f Scale = Vector3f(1.0,1.0,1.0))
    {
        objl::Loader loader;
        loader.LoadFile(filename);
        area = 0;
        m = mt;
        assert(loader.LoadedMeshes.size() == 1);
        auto mesh = loader.LoadedMeshes[0];
 
        Vector3f min_vert = Vector3f{std::numeric_limits<float>::infinity(),
                                     std::numeric_limits<float>::infinity(),
                                     std::numeric_limits<float>::infinity()};
        Vector3f max_vert = Vector3f{-std::numeric_limits<float>::infinity(),
                                     -std::numeric_limits<float>::infinity(),
                                     -std::numeric_limits<float>::infinity()};
        for (int i = 0; i < mesh.Vertices.size(); i += 3) {
            std::array<Vector3f, 3> face_vertices;
 
            for (int j = 0; j < 3; j++) {
                auto vert = Vector3f(mesh.Vertices[i + j].Position.X,
                                     mesh.Vertices[i + j].Position.Y,
                                     mesh.Vertices[i + j].Position.Z);
                vert = Scale*vert+Trans;
                face_vertices[j] = vert;
 
                min_vert = Vector3f(std::min(min_vert.x, vert.x),
                                    std::min(min_vert.y, vert.y),
                                    std::min(min_vert.z, vert.z));
                max_vert = Vector3f(std::max(max_vert.x, vert.x),
                                    std::max(max_vert.y, vert.y),
                                    std::max(max_vert.z, vert.z));
            }
 
            triangles.emplace_back(face_vertices[0], face_vertices[1],
                                   face_vertices[2], mt);
        }
 
        bounding_box = Bounds3(min_vert, max_vert);
 
        std::vector<Object*> ptrs;
        for (auto& tri : triangles){
            ptrs.push_back(&tri);
            area += tri.area;
        }
        bvh = new BVHAccel(ptrs);
    }

        在main里使用兔子模型,并赋予自定义的材质

    MeshTriangle bunny("../models/bunny/bunny.obj",mirco,Vector3f(200,0,350),Vector3f(2000,2000,2000));
    scene.Add(&bunny);
结果

以下是修改粗糙度roughness后产生的两种不同的结果

spp = 8                                                                spp =8

kd = 0.6                                                                kd = 0.6

ks = 0.7                                                                ks = 0.7

roughness = 0.1                                                   roughness = 0.9

ior = 30                                                                ior = 30

另外的将sample和pdf更改成镜面反射采样后的结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2054808.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用 CWE API 减轻软件产品中的安全风险

1. CWE REST API 推出的目的 8 月 8 号&#xff0c;CWE™ 计划推出了“CWE REST API”。 CWE™计划由美国网络安全与基础设施安全局(Cybersecurity & Infrastructure Security Agency(CISA))资助的国土安全系统工程与发展研究所(Homeland Security Systems Engineering a…

AutoSAR SecOC小小科普

目录 1.为什么要提SecOC 2.SecOC基本原理 2.1 参与到MAC计算的数据有哪些 2.2 新鲜度值如何管理 3.SecOC与各模块关联关系 1.为什么要提SecOC 在车载网络技术里&#xff0c;大家基本都是从CAN开始入门。在CAN DBC里&#xff0c;我们总能看到有些报文除了自带有效payload外…

AWS 消息队列服务 SQS

AWS 消息队列服务 SQS 引言什么是 SQSSQS 访问策略 Access Policy示例&#xff1a;如何为 DataLake Subscription 配置 SQS 引言 应用系统需要处理海量数据&#xff0c;数据发送方和数据消费方是通过什么方式来无缝集成消费数据的&#xff0c;AWS 提供 SQS 消息队列服务来解决…

42000 Star图标工具Mermaid!

Mermaid&#xff1a;用文本构建图表世界 - 精选真开源&#xff0c;释放新价值。 概览 Mermaid.js 是一个创新的开源工具&#xff0c;专为简化图表创建流程而设计。它通过一种简洁的文本描述语言&#xff0c;使得用户能够快速地生成流程图、序列图、甘特图等图表&#xff0c;而…

【威锋网-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

JavaEE过滤器的创建与使用过滤器的使用场景

过滤器 Filter也称之为过滤器&#xff0c;过滤器是javaEE规范肿定义的一种技术,可以让请求到达目标servlet之前,先进入到过滤器中,在过滤器中统一进行一些拦截处理,当处理完成后,可以继续向后执行,到达目标servlet,如果配置了多个过滤器,也可以进入下一个过滤器 创建过滤器 创…

一键更换Linux优质的软件源和docker源 —— 筑梦之路

一个非常牛逼的开源项目&#xff1a;https://github.com/SuperManito/LinuxMirrors.git LinuxMirrors 使换源更简单 - LinuxMirrors 支持的操作系统 系统名称适配版本Debian8.0 ~ 13Ubuntu14.04 ~ 24Kali Linux2.0 ~ 2024Linux Mint19.0 ~ 21 / LMDE 6DeepinallArmbianallP…

Redis7基础篇(一)

redis十大数据类型 目录 redis十大数据类型 redis键key 数据类型命令 redis字符串string 分布式锁 ​编辑 ​编辑​编辑应用场景 ​编辑​编辑 reids列表list 应用场景 redis哈希hash 应用场景 redis集合set 应用场景 redis有序集合zset&#xff08;sorted set集…

【设计模式】观察者模式和订阅发布模式

观察者模式 观察者模式包含观察目标和观察者两类对象。一个目标可以有任意数目的与之相依赖的观察者。一旦观察目标的状态发生改变&#xff0c;所有的观察者都将得到通知。 当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新&#xff0c;解决…

《机器学习》逻辑回归 梯度下降、混淆矩阵、随机种子、正则化惩罚 No.6

一、混淆矩阵 1、什么是混淆矩阵&#xff1f; 混淆矩阵是用于评估分类模型在不同类别上的预测准确性的工具。它提供了模型预测结果与真实结果之间的对应关系&#xff0c;帮助我们分析和理解模型的分类性能。 假设&#xff0c;要对15个人预测是否患病&#xff0c;使用1表示患病…

生产环境中MapReduce的最佳实践

目录 MapReduce跑的慢的原因 MapReduce常用调优参数 1. MapTask相关参数 2. ReduceTask相关参数 3. 总体调优参数 4. 其他重要参数 调优策略 MapReduce数据倾斜问题 1. 数据预处理 2. 自定义Partitioner 3. 调整Reduce任务数 4. 小文件问题处理 5. 二次排序 6. 使用…

【python与java的区别-03(集合、字典)】

一、Set python: 集合&#xff08;set&#xff09;是一个无序的不重复元素序列。 集合中的元素不会重复&#xff0c;并且可以进行交集、并集、差集等常见的集合操作。 可以使用大括号 { } 创建集合&#xff0c;元素之间用逗号 , 分隔&#xff0c; 或者也可以使用 set() 函数…

【解释器模式】设计模式系列:构建动态语言解释器与复杂表达式处理(深入理解并实现)

文章目录 深入理解并实现解释器模式1. 引言1.1 解释器模式的定义1.2 模式的主要优点和缺点1.3 适用场景1.4 实际应用案例简介 2. 解释器模式的基本概念2.1 模式的核心思想2.2 模式的角色2.3 模式的动态行为分析 3. 解释器模式的工作原理3.1 如何构建表达式树3.2 如何通过递归遍…

可达鸭举牌网页版本在线生成源码html5

源码介绍 可达鸭举牌网页版本&#xff0c;在线生成源码&#xff0c;点击分享即可制作DIY自己的举牌文字网页&#xff0c;需要GIF动图的自行用GIF图片录制工具录制下来。 PS:上传到服务器运行或者本地nginx运行&#xff0c;不要双击index.html&#xff0c;如果本地双击HTML&…

【3】AT32F437 OpenHarmony轻量系统第一个程序:点灯

在搭建好AT32F437 OpenHarmony 轻量系统之后&#xff0c;当然要尝试点一下灯了。 编写点灯程序 笔者在适配OpenHarmony轻量系统的时候&#xff0c;只对源码的device和vendor目录进行了修改&#xff0c;AT32的app目录笔者放置在了vendor/tree/master/artery/AT-START-F437/app…

什么是网络安全?网络安全防范技术包括哪些?

一、引言 在当今数字化的时代&#xff0c;网络已经成为人们生活和工作中不可或缺的一部分。然而&#xff0c;随着网络的普及和应用的广泛&#xff0c;网络安全问题也日益凸显。从个人隐私泄露到企业关键信息被盗&#xff0c;从网络欺诈到大规模的网络攻击&#xff0c;网络安全…

在国产芯片上实现YOLOv5/v8图像AI识别-【2.5】yolov8使用C++部署在RK3588更多内容见视频

本专栏主要是提供一种国产化图像识别的解决方案&#xff0c;专栏中实现了YOLOv5/v8在国产化芯片上的使用部署&#xff0c;并可以实现网页端实时查看。根据自己的具体需求可以直接产品化部署使用。 B站配套视频&#xff1a;https://www.bilibili.com/video/BV1or421T74f 背景…

ubuntu 安装两个nginx实例时的坑,非默认nginx实例配置修改总也不生效的问题

一、问题 由于工作需求xx云服务器上安装了两个nginx实例&#xff0c;突然有一天需要在非默认nginx上增加一个子站点&#xff0c;根据网上教程和原来的记录修改vi nginx.conf 后保存载总也不生效&#xff1f; 怎么破&#xff1f; 二、过程记录 假如&#xff1a;非默认nginx安装在…

HanLP分词的使用与注意事项

1 概述 HanLP是一个自然语言处理工具包&#xff0c;它提供的主要功能如下&#xff1a; 分词转化为拼音繁转简、简转繁提取关键词提取短语提取词语自动摘要依存文法分析 下面将介绍其分词功能的使用。 2 依赖 下面是依赖的jar包。 <dependency><groupId>com.ha…

使用SSH协议远程连接Ubuntu

1.切换到root用户 sudo -i 2.安装openssh-server apt update apt install openssh-server 3.启动ssh服务 service ssh start 4.查看ssh状态 &#xff08;q键: 退出&#xff09; service ssh status 5.检查ssh服务是否启动成功 ps -e | grep ssh 6.开机自启动 systemctl enable …