当前位置: 首页 > news >正文

GAMES101——作业5 光线与三角形相交(菲涅尔反射率)

任务 

        需要修改的函数是:
         Renderer.cpp 中的 Render() :这里你需要为每个像素生成一条对应的光线,然后调用函数 castRay() 来得到颜色,最后将颜色存储在帧缓冲区的相应像素中。
        Triangle.hpp 中的 rayTriangleIntersect() : v0, v1, v2 是三角形的三个顶点,orig 是光线的起点, dir 是光线单位化的方向向量。 tnear, u, v 是你需要使用我们课上推导的 Moller-Trumbore 算法来更新的参数。

实现

        Render

        在这里我们要做的就是将像素的位置变换成像素在空间的坐标,然后根据像素在空间的坐标和相机的坐标,得到该像素对应的光线,从而实现光线追踪。

       一个像素是通过下面的步骤得来的 ,那么假如知道一个像素的位置,我们可以倒着推出其在世界坐标的位置。

        ①将像素坐标转换到图像坐标。

        像素坐标左上角为(0,0),范围是x∈[0,1],y∈[0,1],而图像坐标原点则为正中心,先转化成NDC坐标,坐标范围是x∈[-1,1],y∈[-1,1],再通过宽高比计算出图像的坐标,x∈[-width/2,width/2],y∈[-height/2,height/2],因此我们先计算出像素中心点的图像坐标。

        设某一个像素点的坐标为(x0,y0),则

        x = (2 * (x0+0.5)/width - 1 )*imageAspectRatio

        y = (1 -2*(y0+0.5)/scene.height )

        括号内是点在NDC坐标的位置,x乘以宽高比就得到了图像坐标。

        ②将图像坐标转化为相机坐标

        得到了点在图像坐标的位置后,就可以将其转化为相机坐标了,这时候只需要知道该图像与相机的距离,就可以推算出其大小。因为图像离相机无论多远,都会规范化到[-1,1]的NDC坐标上,而第一步就是将其变为NDC坐标,再调整了一下宽高而已,并且经过上面的处理后这里的高总为[-1,1]。和相机的距离可以通过视角的大小的一半的正切值求出。

        tan(forY/2) =( height/2 ) / 距离,在这里也就是,1/距离。因此距离就是 1/tan(forY/2),结合上面的推到,可以得到最终的相机坐标系的x,y位置。  

            float scale = std::tan(deg2rad(scene.fov * 0.5f));      

                ...............

            x = (2 * ((float)i+0.5)/scene.width - 1 )*imageAspectRatio*scale;

            y = (1.0f -2*((float)j+0.5)/scene.height )*scale;    

        ③将相机坐标转化为世界坐标

        其实就是乘以视图矩阵的逆矩阵就好了,该作业框架里,直接将相机放在了世界坐标的原点,所以我们不需要进行此变换。

void Renderer::Render(const Scene& scene)
{std::vector<Vector3f> framebuffer(scene.width * scene.height);float scale = std::tan(deg2rad(scene.fov * 0.5f));float imageAspectRatio = scene.width / (float)scene.height;Vector3f eye_pos(0);int m = 0;for (int j = 0; j < scene.height; ++j){for (int i = 0; i < scene.width; ++i){float x;float y;x = (2 * ((float)i+0.5)/scene.width - 1 )*imageAspectRatio*scale;y = (1.0f -2*((float)j+0.5)/scene.height )*scale;     Vector3f dir = normalize(Vector3f(x, y, -1)); framebuffer[m++] = castRay(eye_pos, dir, scene, 0);}UpdateProgress( j / (float)scene.height);}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] = (char)(255 * clamp(0, 1, framebuffer[i].x));color[1] = (char)(255 * clamp(0, 1, framebuffer[i].y));color[2] = (char)(255 * clamp(0, 1, framebuffer[i].z));fwrite(color, 1, 3, fp);}fclose(fp);    
}
rayTriangleIntersect

这里直接根据下面的公式代入数据了,过程的推导可以查阅相关教程,计算出结果后,需要先判断t是否大于0,重心坐标的三个值是否都大于0,都大于0说明光线与三角形相交。

bool rayTriangleIntersect(const Vector3f& v0, const Vector3f& v1, const Vector3f& v2, const Vector3f& orig,const Vector3f& dir, float& tnear, float& u, float& v)
{Vector3f E1 = v1-v0;Vector3f E2 = v2-v0;Vector3f S = orig - v0;Vector3f S1 = crossProduct(dir,E2);Vector3f S2 = crossProduct(S,E1);tnear = dotProduct(S2,E2)/dotProduct(S1,E1);u = dotProduct(S1,S)/dotProduct(S1,E1);v = dotProduct(S2,dir)/dotProduct(S1,E1);if(u>=0 && v >= 0 && (1-u-v)>=0 && tnear >= 0){return true;}return false;
}

结果

        

值得注意的点

在该作业中,对透明的球已经实现了菲涅尔反射的模型。观察渲染的图片里透明球的边缘,可以注意到比较亮,查询代码发现了菲涅尔系数的求解,因此这里提一下菲涅尔反射系数。

float fresnel(const Vector3f &I, const Vector3f &N, const float &ior)
{float cosi = clamp(-1, 1, dotProduct(I, N));    //确保光线合法float etai = 1, etat = ior;   //etai是入射介质的折射率,etat是出射物质的折射率//cosi>0,说明光是从物体内射向空气的,因此交换两个折射率if (cosi > 0) {  std::swap(etai, etat); }// 利用斯涅尔公式计算出射角的正弦值float sint = etai / etat * sqrtf(std::max(0.f, 1 - cosi * cosi));// 如果大于1,说明发生了全反射,因此反射系数为1。if (sint >= 1) {return 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));return (Rs * Rs + Rp * Rp) / 2;}// As a consequence of the conservation of energy, transmittance is given by:// kt = 1 - kr;
}

使用斯涅尔公式求解sint,其实这个高中就学过了。

菲涅尔反射系数的精确求解法

根据公式计算出s和p偏振光的反射系数,因为光源是非偏振光,因此将两个反射系数取平均就能得到最终的反射系数

菲涅尔系数的近似求解法

代码中的反射系数明显采取了精确的求法。在castRay的代码中,可以看到其用武之地

                Vector3f reflectionColor = castRay(reflectionRayOrig, reflectionDirection, scene, depth + 1);Vector3f refractionColor = castRay(refractionRayOrig, refractionDirection, scene, depth + 1);float kr = fresnel(dir, N, payload->hit_obj->ior);hitColor = reflectionColor * kr + refractionColor * (1 - kr);

这里是对应的反射与折射材质(REFLECTION_AND_REFRACTION),使用菲涅尔反射系数,可以真实地分配反射与折射光的强度。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Java笔试面试题AI答之线程(11)
  • 解决 Navicat 删除唯一键(unique)后保存失败的问题:1-near “)“:syntax error
  • arthas源码刨析:arthas 命令粗谈(3)
  • MySQL数据库锁机制(全面讲解)
  • 七、SPA单页面实现SEO优化之SSR服务器渲染
  • 8.17day bug
  • 国际校企合作|深信服、常州信息职业技术学院、马来西亚汽车工业大学三方国际化人才培养合作签约仪式圆满成功
  • 机器学习辅助复合材料预测,性能管理优化创新材料,这种王炸般的组合,还真是大开眼界!
  • XSS- - - DOM 破坏案例与靶场
  • java 中的设计模式
  • 【STM32】RTT-Studio中HAL库开发教程五:UART的DMA应用
  • 01 SSH--
  • Verilog刷题笔记57
  • 用Python插入SVG到PDF文档
  • WindowManager的使用
  • 【347天】每日项目总结系列085(2018.01.18)
  • Babel配置的不完全指南
  • CentOS 7 修改主机名
  • Git的一些常用操作
  • IDEA常用插件整理
  • JS 面试题总结
  • js算法-归并排序(merge_sort)
  • Material Design
  • MySQL QA
  • Python_网络编程
  • 从伪并行的 Python 多线程说起
  • 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践...
  • 开源地图数据可视化库——mapnik
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 实现简单的正则表达式引擎
  • 使用 Xcode 的 Target 区分开发和生产环境
  •  一套莫尔斯电报听写、翻译系统
  • 移动端 h5开发相关内容总结(三)
  • 中文输入法与React文本输入框的问题与解决方案
  • 【干货分享】dos命令大全
  • 从如何停掉 Promise 链说起
  • ​​​​​​​​​​​​​​汽车网络信息安全分析方法论
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • ​猴子吃桃问题:每天都吃了前一天剩下的一半多一个。
  • ​批处理文件中的errorlevel用法
  • # Python csv、xlsx、json、二进制(MP3) 文件读写基本使用
  • #define,static,const,三种常量的区别
  • #我与Java虚拟机的故事#连载07:我放弃了对JVM的进一步学习
  • (06)Hive——正则表达式
  • (1)(1.13) SiK无线电高级配置(五)
  • (不用互三)AI绘画:科技赋能艺术的崭新时代
  • (第一天)包装对象、作用域、创建对象
  • (二)JAVA使用POI操作excel
  • (二)Linux——Linux常用指令
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (汇总)os模块以及shutil模块对文件的操作
  • (三)终结任务
  • *算法训练(leetcode)第四十天 | 647. 回文子串、516. 最长回文子序列
  • .md即markdown文件的基本常用编写语法
  • .NET Standard 支持的 .NET Framework 和 .NET Core