GAMES101-Assignment6

news2025/2/26 22:49:27

一、问题总览

需要加速结构来加速光线与场景的交点,本次练习中,重点关注物体划分算法Bounding Volume Hierarchy (BVH)。本练习要求实现Ray-Bounding Volume求交与BVH查找。
需要从上一次编程练习中引用以下函数:

  • Render() in Renderer.cpp: 将你的光线生成过程粘贴到此处,并且按照新框架更新相应调用的格式。
  • Triangle::getIntersection in Triangle.hpp: 将你的光线-三角形相交函数粘贴到此处,并且按照新框架更新相应相交信息的格式。

在本次编程练习中,你需要实现以下函数:

  • IntersectP(const Ray& ray, const Vector3f& invDir,
    const std::array<int, 3>& dirIsNeg) in the Bounds3.hpp: 这个函数的作用是判断包围盒BoundingBox与光线是否相交,需要按照课程介绍的算法实现求交过程。
  • getIntersection(BVHBuildNode* node, const Ray ray)in BVH.cpp: 建立BVH之后,我们可以用它加速求交过程。该过程递归进行,你将在其中调用你实现的Bounds3::IntersectP.

二、代码框架

修改了代码框架中的如下内容:

  • Material.hpp: 我们从将材质参数拆分到了一个单独的类中,现在每个物体实例都可以拥有自己的材质。
  • Intersection.hpp: 这个数据结构包含了相交相关的信息。
  • Ray.hpp: 光线类,包含一条光的源头、方向、传递时间t和范围range.
  • Bounds3.hpp: 包围盒类,每个包围盒可由pMin和pMax两点描述(请思考为什么)。Bounds3::Union函数的作用是将两个包围盒并成更大的包围盒。与材质一样,场景中的每个物体实例都有自己的包围盒。
  • BVH.hpp: BVH加速类。场景scene拥有一个BVHAccel实例。从根节点开始,我们可以递归地从物体列表构造场景的BVH.

三、参考答案

3.1 Render() in Renderer.cpp

  • 从观察点向屏幕栅格一次发出若干条光线
    • 栅格每个单元只有一条光线穿过
    • 将穿过栅格的每一条光线按列优先的放射保存在framebuffer中
  • scene.castRay实现了Whitted-syle光线追踪算法,需要将光线(方向和起始点)和深度(光线反射次数)作为参数
    for (uint32_t j = 0; j < scene.height; ++j) {
        for (uint32_t i = 0; i < scene.width; ++i) {
            // generate primary ray direction
            //计算栅格单元[i, j]中心对应的坐标[x, y]
            float x = (2 * (i + 0.5) / (float)scene.width - 1) *
                      imageAspectRatio * scale;
            float y = (1 - 2 * (j + 0.5) / (float)scene.height) * scale;
            // TODO: Find the x and y positions of the current pixel to get the
            // direction
            //  vector that passes through it.
            // Also, don't forget to multiply both of them with the variable
            // *scale*, and x (horizontal) variable with the *imageAspectRatio*

            // Don't forget to normalize this direction!
            //假设观察点和屏幕距离为1,观察点位于原点,向z轴负方向观察
            //所以从观察点到栅格中心点的光线z = -1
            Vector3f dir = normalize(Vector3f(x, y, -1));
            //观察点的起始位置在eye_pos,但是观察点和屏幕相对位置不变,观察朝向不变
            //所以,光线方向不变
            Ray ray(eye_pos, dir);
            framebuffer[m++] = scene.castRay(ray, 0);
        }
        UpdateProgress(j / (float)scene.height);
    }

3.2 Triangle::getIntersection 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;//对应b1,b2,t
    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
    if (t_tmp < 0) //时间为负,则说明没有交点
        return inter;
    // 更新交点信息
    inter.distance = t_tmp;
    inter.happened = true;
    inter.m = m;
    inter.coords = Vector3f(ray.origin + t_tmp * ray.direction);
    inter.normal = normal;
    inter.obj = this;

    return inter;
}

3.3 IntersectP in the Bounds3.hpp

  • 这个函数的作用是判断包围盒BoundingBox与光线是否相交。
    在这里插入图片描述

    • 第一幅图的红色线段与第二幅图的红色线段求交集,得到最终结果(第三幅图的线段)
      • 第一幅图的红色线段为光线和x0, x1两对面的交点连线
    • tenter = max{tmin}
      • tenter:光线射入包围盒的时间t
      • tmin:光线射入某一个对面的时间t
    • texit = min{tmax}
      • 小于零,表示盒子在光线背后(不满足光线与盒子有交点)
      • (texit >= 0 ) && (texit < 0): 在盒子内,射线可能和盒子由交点
    • if (tenter < texit) && (texit > 0):光线在盒内停留了一段时间

在这里插入图片描述

  • 包围盒是轴对齐的(垂直于某条坐标轴),方便求光线和面的交点t(如上图下方式子所示)
    • 光线方程:r(t) = o + td
      • o:起始点三维坐标;d:光线方向;t:常数
    • 对面垂直于x轴:t * 光线x轴分量 = 包围盒上一点的x分量 - 光源起始点x分量
      • 因为对面是无限大的两个平面,光线暂时看作直线,所以一定有交点;
      • 最后判断光线作为射线时是否与对面有交点
        • if (tenter < texit) && (texit > 0)光线作为射线沿着方向d与对面有交点
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
    double tx_min = (pMin.x - ray.origin.x) * invDir.x;
    double tx_max = (pMax.x - ray.origin.x) * invDir.x;
    //注意下pMin.x是指包围盒左边的面
    // 假如光线是反向(从右往左),那么光线离包围盒pMin.x距离(tx_min)远
    if (!dirIsNeg[0])
        std::swap(tx_min, tx_max);

    double ty_min = (pMin.y - ray.origin.y) * invDir.y;
    double ty_max = (pMax.y - ray.origin.y) * invDir.y;
    if (!dirIsNeg[1])
        std::swap(ty_min, ty_max);
    
    double tz_min = (pMin.z - ray.origin.z) * invDir.z;
    double tz_max = (pMax.z - ray.origin.z) * invDir.z;
    if (!dirIsNeg[2])
        std::swap(tz_min, tz_max);

    double t_enter = std::max(tx_min, std::max(ty_min, tz_min));
    double t_exit = std::min(tx_max, std::min(ty_max, tz_max));


    return t_enter < t_exit && t_exit >= 0;
}

3.4 getIntersection in BVH.cpp

  • 建立BVH之后,我们可以用它加速求交过程。该过程递归进行,你将在其中调用你实现的Bounds3::IntersectP
  • BVH特点
    在这里插入图片描述
    • 按照物体进行划分,因此一个物体只可能出现在一个包围盒内
      • 比如把三角形分成两部分,然后重新求包围盒
  • getIntersection伪代码
    在这里插入图片描述
Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const
{
    // TODO Traverse the BVH to find intersection
    Intersection isect;
    // 光线没有碰到包围盒
    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 isect;
    // 叶节点
    if(node->left == nullptr && node->right == nullptr)
        return node->object->getIntersection(ray);

    // 中间节点
    Intersection isect_left, isect_right;
    isect_left = getIntersection(node->left, ray);
    isect_right = getIntersection(node->right, ray);

    //返回最近的交点
    return isect_left.distance <= isect_right.distance ? isect_left : isect_right;
}

四、编译

mkdir build
cd ./build
cmake ..
make

./RayTracing

附件

作业6压缩包

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

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

相关文章

抖音矩阵云混剪系统源码多平台多账号一站式管理(免授权版)

抖音矩阵云混剪系统源码 短视频矩阵营销系统V2.2.1(免授权版) 中网智达矩阵营销系统多平台多账号一站式管理,一键发布作品。智能标题,关键词优化,排名查询,混剪生成原创视频,账号分组,意向客户自动采集,智能回复,多账号评论聚合回复,免切换,免登陆发布….助力您在…

浅谈对Mybatis的理解

一、Mybatis的概述 MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code&#xff0c;由谷歌托管&#xff0c;并且改名为MyBatis 。2013年11月迁移到Github。 MyBatis是支持普通SQL查询&#xff0c;存储过程和高级映射的优…

PLC-IoT 网关开发札记(2):Xamarin Forms 工程获取App当前的版本号

代码实现 在构建 Android App 时&#xff0c;写了一个 AboutPage。在 AboutPage 上显示 App 的当前版本号是常见的做法。使用 Xamarin.Foms 获取当前版本号的方法是使用 Xamarin.Forms 的 VersionTracking 类。 如下&#xff0c;我写了一个非常简单的 AboutPage&#xff0c;其…

1.单表查询

作业要求 素材&#xff1a; 表名&#xff1a;worker-- 表中字段均为中文&#xff0c;比如 部门号 工资 职工号 参加工作 等 CREATE TABLE worker ( 部门号 int(11) NOT NULL, 职工号 int(11) NOT NULL, 工作时间 date NOT NULL, 工资 float(8,2) NOT NULL, 政治面貌 varc…

使用lodash原地起飞,总结了几个常用的lodash方法

前言 &#x1f4eb; 大家好&#xff0c;我是南木元元&#xff0c;热爱技术和分享&#xff0c;欢迎大家交流&#xff0c;一起学习进步&#xff01; &#x1f345; 个人主页&#xff1a;南木元元 目录 什么是lodash lodash的按需引入 数组操作 求交集 求合集 求差集 求总和…

2024美赛数学建模思路 - 复盘:校园消费行为分析

文章目录 0 赛题思路1 赛题背景2 分析目标3 数据说明4 数据预处理5 数据分析5.1 食堂就餐行为分析5.2 学生消费行为分析 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 赛题背景 校园一卡通是集…

SystemC学习笔记 - Hello systemc world

Hello Systemc World 码农老规矩&#xff0c;先写一个hello world并输出&#xff0c;语法什么的后面再说&#xff0c;先能编译运行再说。 目录配置 使用examples里的配置&#xff0c;在examples/sysc目录下创建test目录&#xff0c;其下创建第一个test1的目录&#xff0c;如…

功能权限篇

文章目录 1. 如何设计一套权限系统1.1 目标1.2 权限模型1.2.1 模型一RBAC1.2.2 模型二ABAC 2.如何实现菜单的创建&#xff1f;2.1 表结构2.2 前端实现2.3 后端实现 3. 如何实现角色的创建&#xff1f;4.如何给用户分配权限 —— 将菜单赋予角色&#xff1f;5.如何给用户分配权限…

Linux学习之网络编程3(高并发服务器)

写在前面 Linux网络编程我是看视频学的&#xff0c;Linux网络编程&#xff0c;看完这个视频大概网络编程的基础差不多就掌握了。这个系列是我看这个Linux网络编程视频写的笔记总结。 高并发服务器 问题&#xff1a; 根据上一个笔记&#xff0c;我们可以写出一个简单的服务端…

您与此网站之间建立的连接不安全

连接不安全的主要原因之一是使用不安全的通信协议。在互联网传输中&#xff0c;如果使用的协议不加密&#xff0c;那么数据就容易受到窃听和篡改。另一个可能的原因是网站没有正确配置其安全证书&#xff0c;使得用户的连接没有得到适当的加密保护。 解决方法&#xff1a; 采用…

Android jar包编译及集成

Jar包编译和集成有两种编译方式&#xff0c;mk和bp&#xff0c;Android 7版本之后逐渐采用bp格式编译&#xff0c;目前14版本还是兼容mk方式编译&#xff0c;具体写法入下&#xff1a; Android jar包编译 mk&#xff1a; 如果需要打包到systemimg&#xff0c;则需要将此jar包添…

Ribbon学习思维导图

参考资料 1、OpenFeign与Ribbon源码分析总结与面试题 2、万字剖析OpenFeign整合Ribbon实现负载均衡的原理 3、扒一扒Nacos、OpenFeign、Ribbon、loadbalancer组件协调工作的原理 4、OpenFeign原来是这么基于Ribbon来实现负载均衡的

Gaara靶机练习

渗透测试 一.信息收集1.确定IP地址2.nmap扫描3.目录扫描 二.hydra爆破1.ssh连接2.信息探索 三.提权gdb提权提权 一.信息收集 1.确定IP地址 ┌──(root㉿kali)-[~/kali/web] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:10:3c:9b, IPv4: 192.168.9.10 S…

鸿蒙Harmony--AppStorage--应用全局的UI状态存储详解

无所求必满载而归&#xff0c;当你降低期待&#xff0c;降低欲望&#xff0c;往往会得到比较好的结果&#xff0c;把行动交给现在&#xff0c;用心甘情愿的态度&#xff0c;过随遇而安的生活&#xff0c;无论结果如何&#xff0c;都是一场惊喜的获得! 目录 一&#xff0c;定义 …

YOLOv7基础 | 手把手教你简化网络结构之yolov7.yaml(包括源码+封装步骤+网络结构图)

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。通过下载YOLOv7源码可知&#xff0c;原始的yolov7.yaml文件是拆开写的&#xff0c;比较混乱&#xff0c;也不好理解&#xff0c;并且为后续改进增添了很多困难。基于此种情况&#xff0c;笔者就给大家介绍一种将yolov7.yam…

算法训练day13Leetcode144 145 94 二叉树的前(中)(后)序遍历

今日学习的文章和视频链接 https://www.bilibili.com/video/BV1Hy4y1t7ij/?vd_source8272bd48fee17396a4a1746c256ab0ae 二叉树的种类 在我们解题过程中二叉树有两种主要的形式&#xff1a;满二叉树和完全二叉树。 满二叉树 满二叉树&#xff1a;如果一棵二叉树只有度为0的…

宝塔nginx部署前端页面刷新报404

问题&#xff1a; 当我们使用脚手架打包前端项目的时候&#xff0c;如果前端项目并没有静态化的配置&#xff0c;如以下 当我们刷新页面&#xff0c;或进行路由配置访问的时候就会报404的错误 原因&#xff1a; 这是因为通常我们做的vue项目属于单页面开发。所以只有index.html…

Abaqus汉化教程

用钢铁意志&#xff0c;成就不平凡人生。 今天博主整理了一下Abaqus2023汉化教程&#xff0c;希望大家学习。 第一步&#xff0c;在在菜单栏找到Abaqus CAE右键打开文件所在的位置 第二步&#xff1a;继续右键Abaqus CAE右键打开文件所在的位置 第三步&#xff1a;然后进入到…

PMP与NPDP证书:哪个更权威?哪个含金量更高?

&#x1f3af;PMP和NPDP都具有权威性&#xff0c;但它们在领域和目标人qun方面略有不同。 1️⃣PMP在项目管理领域有较高的国际认可度 &#x1f48e;PMP是由项目管理协会(PMI)颁发的项目管理专业认证&#xff0c;具有较高的国际认可度。 PMP证书持有者通常具备严谨的项目管理知…

银行储蓄系统的顶层数据流图及细化数据流图

绘制出银行储蓄系统的顶层数据流图及细化数据流图&#xff1b; 银行储蓄系统存、取款流程如下&#xff1a; 1&#xff09;业务员事先录入利率信息&#xff1b; 2&#xff09;如果是存款&#xff0c;储户填写存款单&#xff0c;业务员将存款单键入系统&#xff0c;系统更新储户存…