计算机图形学实验9 RayBounding Valume求交和 BVH查找

计算机图形学实验9 RayBounding Valume求交和 BVH查找

Github地址

RBV,BVH参考文章

前言

光线追踪的基本流程

  • 像素网格生成:首先,我们需要为最终的图像建立一个二维像素网格。每个像素代表屏幕上的一个点,我们的目标是计算出这个点应该显示为什么颜色。
  • 发射光线:对于每个像素,从相机的位置出发,沿着该像素指向场景的方向发射一条光线。这条光线是我们想象中的从观察者的眼睛到屏幕这一段空间中的延续。
  • 光线与物体的交点检测:我们计算这条光线是否与场景中的任何物体相交,以及交点的位置。在这个例子中,就是判断光线是否击中了立方体的一个面。这通常涉及到射线与几何体求交的数学运算。
  • 着色计算:
  • 漫反射:如果光线击中了立方体,我们会计算光线击中面后的基本颜色(比如立方体表面的颜色)。这是最简单的光照模型,假设光线均匀地散射到各个方向。
    • 光照计算:点光源对这个交点的影响。计算光线从光源到交点的入射角,使用兰伯特余弦定律来确定光照强度。简单来说,光线垂直于表面时最亮,斜射时逐渐变暗。
    • 环境光与反射
  • 累积颜色:根据上述计算得到的光照信息,为当前像素分配一个颜色值。如果光线没有击中任何物体,则可能根据背景色或环境光来着色。
  • 重复步骤:对图像中的每个像素重复执行上述过程,直到所有像素的颜色都被计算出来,形成最终的图像。

实验部分

修改Render

  • 和之前的实验8一样,修改一些东西
    • dir为对应的像素视锥体的方向向量
    • ray光线
    • 调用scene类中的castRay来返回光线与物体交点的颜色
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(-1, 5, 10);int m = 0;for (uint32_t j = 0; j < scene.height; ++j) {for (uint32_t i = 0; i < scene.width; ++i) {// generate primary ray directionfloat 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)); // Don't forget to normalize this direction!// ori = eye_posRay ray(eye_pos, dir, 0);framebuffer[m++] = scene.castRay(ray, 0);}UpdateProgress(j / (float)scene.height);}UpdateProgress(1.f);// save framebuffer to fileFILE* 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 * clamp(0, 1, framebuffer[i].x));color[1] = (unsigned char)(255 * clamp(0, 1, framebuffer[i].y));color[2] = (unsigned char)(255 * clamp(0, 1, framebuffer[i].z));fwrite(color, 1, 3, fp);}fclose(fp);    
}

修改Traingle.cppgetInsection函数

  • 修改为返回一个insection对象,包含了交点的一些信息
  • 修改后的代码
inline Intersection Triangle::getIntersection(Ray ray)
{Intersection inter;//交点信息结构体if (dotProduct(ray.direction, normal) > 0)//两者点积大于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属性  inter.happened=true;inter.coords=ray(t_tmp);//计算对应的交点坐标(Ray重载了那个()操作符)inter.normal=normal;//法向量  inter.distance=t_tmp;//距离inter.obj=this;//交点所在的三角形  inter.m=m;//材质,类的成语阿return inter;
}

RayBoundding Valume

上一个实验我们直接计算从射线与每个三角形的交点,这里首先在场景中生成立方体,检测光线是否与立方体有交点,如果没有的话就直接跳过。有的话按照原来的方法算

  • Bounds3.hpp

    这个是对应的包围盒的类,需要实现的是其中的 IntersectP

    • 成员变量
      • pMax,p_Min就是立方体最大和最小的点(前边的左下方的点和后边的右上方的点)
    • 函数
      • Diagonal():返回包围盒的对角线向量
      • maxExtent():返回包围盒最大尺寸的方向索引(0对应X轴,1对应Y轴,2对应Z轴)
      • SurfaceArea():计算并返回包围盒的表面积
      • Centroid():计算并返回包围盒的中心点
      • Intersect(const Bounds3& b):返回与另一个包围盒b相交部分的新包围盒
      • Offset(const Vector3f& p):给定一个点p,计算该点在包围盒内的归一化偏移坐标
      • Overlaps(const Bounds3& b1, const Bounds3& b2):判断两个包围盒是否重叠
      • Inside(const Vector3f& p, const Bounds3& b):判断点p是否在包围盒b内部
      • 括号运算符重载operator[]:允许通过索引访问pMin,pMax
      • Union(const Bounds3& b1, const Bounds3& b2):计算并返回包含两个包围盒的最小包围盒
      • Union(const Bounds3& b, const Vector3f& p):计算并返回同时包含包围盒b和点p的最小包围盒
  • 待实现IntersectP(const Ray& ray, const Vector3f& invDir, const std::array<int, 3>& dirIsNeg)

判断光线是否与包围盒相交

  • 传入参数
    • ray : 光线
    • invDir : 光线的方向向量的到数
    • dirIsNeg : 光线在x,y,z轴上的正负
    • 后两者的传入都是为了加快速度的
  • 计算步骤
    • 首先计算从光线的源点到立方体的两个点pMin,pMaxx,y,z方向的参数,用于代表光线到立方体的两个点的时间
    • 根据光线在各轴上的方向(正或负),调整最小和最大的t值。如果光线在某轴上的方向为负(即向量分量为负),则进入包围盒的临界时间实际上是与最大边界相交的时间,退出时间是与最小边界相交的时间,那就需要交换t_Min和t_Max(自己比划一下就比较好理解了)
    • 然后就是计算x,y,z轴各自t_Min中的最大值就是进入立方体的时间t_enterx,y,z轴各自的t_Max的最小值就是立方体出来立方体的时间t_exit
    • 如果t_exit>=0 而且 t_enter< t_exit那就说明相交了
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 intersectsfloat t_Min_x = (pMin.x - ray.origin.x)*invDir[0];float t_Min_y = (pMin.y - ray.origin.y)*invDir[1];float t_Min_z = (pMin.z - ray.origin.z)*invDir[2];float t_Max_x = (pMax.x - ray.origin.x)*invDir[0];float t_Max_y = (pMax.y - ray.origin.y)*invDir[1];float t_Max_z = (pMax.z - ray.origin.z)*invDir[2];//dirIsNeg表面光线的方向,如果是正方向则为1,pmin-O为最短路径//反之为负方向0,pmax-O是最短路径if(!dirIsNeg[0]){float t = t_Min_x;t_Min_x = t_Max_x;t_Max_x = t;}if(!dirIsNeg[1]){float t = t_Min_y;t_Min_y = t_Max_y;t_Max_y = t;}if(!dirIsNeg[2]){float t = t_Min_z;t_Min_z = t_Max_z;t_Max_z = t;}float t_enter = std::max(t_Min_x,std::max(t_Min_y,t_Min_z));float t_exit =  std::min(t_Max_x,std::min(t_Max_y,t_Max_z));if(t_enter<t_exit&&t_exit>=0)return true;elsereturn false;}

BVH

递归判断应用上面我们写的算法
在参考文章中详细的解释了这一部分的原理,我就不解释了

Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const
{// TODO Traverse the BVH to find intersectionstd::array<int, 3> dirIsNeg; // 和getInsectin一样光线在各坐标轴方向的正负dirIsNeg[0] = (ray.direction[0] > 0); //xdirIsNeg[1] = (ray.direction[1] > 0); //y方向dirIsNeg[2] = (ray.direction[2] > 0); //z方向Intersection inter; //初始化一个表示相交信息的对象,初始默认为未找到交点// 如果当前的节点其包围盒与光线没有交点,则直接返回if (!node->bounds.IntersectP(ray, ray.direction_inv, dirIsNeg)) {return inter;}// 如果当前节点是叶子节点if (node->left == nullptr && node->right == nullptr) {//直接看看这个节点的包围盒中的物体与光线的交点信息return node->object->getIntersection(ray);}//左Intersection leftInter = getIntersection(node->left, ray);//右Intersection rightInter = getIntersection(node->right, ray);//比较左右子树返回的交点信息,返回距离光线源更近的那个交点信息return leftInter.distance < rightInter.distance ? leftInter : rightInter;
}

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

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

相关文章

重庆耶非凡科技人力rpo蓝海项目能不能做?

随着市场经济的不断发展和企业对人才需求的日益增长&#xff0c;人力资源外包(HRO)已成为众多企业优化资源配置、提升管理效率的重要手段。其中&#xff0c;招聘流程外包(RPO)作为HRO的重要组成部分&#xff0c;更是凭借其专业性和高效性受到了广泛关注。在此背景下&#xff0c…

idea2024年最新激活码,即拿即用

1、Idea2024年最新激活码 idea2024年最新激活码&#xff0c;即拿即用 K384HW36OB-eyJsaWNlbnNlSWQiOiJLMzg0SFczNk9CIiwibGljZW5zZWVOYW1lIjoibWFvIHplZG9uZyIsImxpY2Vuc2VlVHlwZSI6IlBFUlNPTkFMIiwiYXNzaWduZWVOYW1lIjoiIiwiYXNzaWduZWVFbWFpbCI6IiIsImxpY2Vuc2VSZXN0cmljdG…

linux业务代码性能优化点

planning优化的一些改动----------> 减少值传递&#xff0c;多用引用来传递 <---------- // ----------> 减少值传递&#xff0c;多用引用来传递 <---------- // 例1&#xff1a; class A{}; std::vector<A> v; // for(auto elem : v) {} // 不建议&#xff…

HiWoo Cloud物联网云平台

在科技日新月异的今天&#xff0c;物联网&#xff08;IoT&#xff09;已经成为推动社会进步的重要力量。而物联网云平台&#xff0c;作为连接万物、实现智能交互的核心&#xff0c;更是备受瞩目。今天&#xff0c;就让我们一起走进HiWoo Cloud的世界&#xff0c;探寻这款物联网…

云计算中网络虚拟化的核心组件——NFV、NFVO、VIM与VNF

NFV NFV&#xff08;Network Functions Virtualization&#xff0c;网络功能虚拟化&#xff09;&#xff0c;是一种将传统电信网络中的网络节点设备功能从专用硬件中解耦并转换为软件实体的技术。通过运用虚拟化技术&#xff0c;NFV允许网络功能如路由器、防火墙、负载均衡器、…

学习axios拦截器

axios拦截器的作用&#xff1a;用于在请求发送前和响应返回后对请求和响应进行统一处理&#xff0c;例如添加公共请求头、处理请求参数、统一处理错误信息等。拦截器提供了一种灵活、高效的方式来管理HTTP请求和响应&#xff0c;帮助在前端开发中更好地处理数据交互。 这是一个…

YY2/BUB3轴通过调节染色体不稳定性促进SAC过度激活并抑制结直肠癌进展

引用信息 文 章&#xff1a;YY2/BUB3 Axis promotes SAC Hyperactivation and Inhibits Colorectal Cancer Progression via Regulating Chromosomal Instability. 期 刊&#xff1a;Advanced Science&#xff08;影响因子&#xff1a;15.1&#xff09; 发表时间&#…

外文文献下载阅读工具有哪些?

下载和阅读外文文献的工具有很多&#xff0c;其中一些常用的工具包括&#xff1a; Google 学术&#xff1a;可以通过 Google 学术搜索引擎查找和下载大量的学术文献&#xff0c;包括英文文献。 ResearchGate&#xff1a;一个学术交流平台&#xff0c;提供了许多学术论文的下载…

关于vue开发的几个问题

vue前端代码放置了几个月时间&#xff0c;再开发发现一些版本或者配置的问题&#xff0c;记录下来。 一、yarn add总报错certificate has expired 尝试了各种办法&#xff0c;更新或重装yarn&#xff08;npm install --global yarn&#xff09;&#xff0c;清除缓存&#xff…

揭秘业务系统数据安全三大核心问题:“谁在用”、“用什么”和“怎么用”

数据库宛如一座坚固的宝库&#xff0c;守护着无尽的智慧与财富—数据&#xff0c;如同熠熠生辉的金币。当宝库的门紧闭时&#xff0c;金币得以安然无恙。 然而&#xff0c;在业务系统的广阔天地中&#xff0c;这些数据金币被精心挑选、流通使用&#xff0c;每一枚都承载着无尽…

Master-Worker 架构的灰度发布难题

作者&#xff1a;石超 一、前言 Master-Worker 架构是成熟的分布式系统设计模式&#xff0c;具有集中控制、资源利用率高、容错简单等优点。我们数据中心内的几乎所有分布式系统都采用了这样的架构。 &#xfeff; 我们曾经发生过级联故障&#xff0c;造成了整个集群范围的服…

晶科能源分享:晶科移动化建设实践和思考

下文为晶科能源IT总监朱元伟的演讲全文&#xff1a; 大家好。我是晶科能源IT的负责人。晶科能源是一个新能源公司&#xff0c;我们主要的业务有两个&#xff0c;太阳能业务&#xff0c;和储能业务。这两年公司发展比较快&#xff0c;大约是在2023年&#xff0c;公司业绩重回了光…

个人课设---玩家血条(包括攻击掉血,复活重生功能)

血条UI操作 渲染模式改为世界空间 导入面板组件并通过画布调整大小 在面板上导入文本组件&#xff0c;显示玩家名称&#xff08;旧版&#xff09; 为了使血条填满&#xff0c;Fill Area 数值全部置为0 改变背景颜色和填充物颜色&#xff0c;并设置血条方向 改变数值方便计算 …

7 - 产品销售分析 I(高频 SQL 50 题基础版)

7- 产品销售分析 I select product_name,year,price from Sales left join Product on Sales.product_id Product.product_id;

窗口重叠之鼠标事件透传

目的 Qt webview在下层展示url&#xff0c;上层覆盖最小化等按钮&#xff0c;支持大小拖拽&#xff0c;窗口移动。 如下图&#xff0c;除红框部分&#xff0c;其余异形部分作为url展示区 构想 想要实现该效果&#xff0c;需要在webview窗口上方动态创建两个窗口&#xff0c;…

四川古力未来科技抖音小店创新经营,引领电商新潮流

在数字化浪潮的推动下&#xff0c;电商行业蓬勃发展&#xff0c;抖音小店作为新兴的电商平台&#xff0c;正以其独特的魅力和优势吸引着越来越多的消费者。四川古力未来科技&#xff0c;作为抖音小店的经营者之一&#xff0c;凭借其可靠的经营模式和创新的经营理念&#xff0c;…

医用腕带朔源用的条形码与二维码如何选择

在医疗环境中的医用腕带作为患者身份识别和管理的重要工具&#xff0c;做为条形码和二维码腕带上的溯源技术&#xff0c;更是为患者信息快速获取、准确传递的保障&#xff0c;实现更加高效和准确的患者身份识别和管理&#xff0c;这种技术可以大大提高医疗服务的效率和质量&…

React路由(React笔记之五)

本文是结合实践中和学习技术文章总结出来的笔记(个人使用),如有雷同纯属正常((✿◠‿◠)) 喜欢的话点个赞,谢谢! React路由介绍 现在前端的项目一般都是SPA单页面应用,不再是以前多个页面多套HTML代码项目了,应用内的跳转不需要刷新页面就能完成页面跳转靠的就是路由系统 R…

小米路由器如何设置去广告功能,如何设置小米路由器的自定义Hosts(小米路由器如何去除小米广告、去除小米电视盒子开屏广告、视频广告)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 实现方案 📒📝 操作步骤📝 注意事项⚓️ 相关链接 ⚓️📖 介绍 📖 小米设备的广告一直是用户头疼的问题,无论是开屏广告、应用内广告还是系统广告,都影响了用户体验。本文将详细介绍如何通过小米路由器实现去除广告…

探索Linux网络利器:netstat命令

探索Linux网络利器&#xff1a;netstat命令 在Linux系统管理中&#xff0c;了解系统的网络状态是至关重要的。而netstat命令就是这样一个强大的工具&#xff0c;它可以帮助我们查看网络连接、路由表、接口统计等与网络相关的信息。下面&#xff0c;我们将详细探讨如何使用nets…