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

13 Games101 - 笔记 - 光线追踪(Whitted-Style光线追踪原理详解及实现细节)

13 光线追踪(Whitted-Style光线追踪原理详解及实现细节)

引入光线追踪的原因

光栅化的缺点:不能很好的处理全局光照。(因为Blinn-Phong这种局部模型无法处理全局效果!)

  • 光栅化:快 real-time 质量低
  • 光线追踪:慢 offline 质量高

1 Whitted-Style 光线追踪

1.1 光线追踪原理解释

光线追踪听名字就知道,讨论的核心是光线,因此我们首先对光线做一些假设。

1 光线一定沿着直线传播

2 光线之间无法碰撞

3 光线路径可逆,即从A发出的到B的光线,一定也可以从B发出到A(中途可发生反射和折射)

明白了光线的一些假设之后,想一想人为什么能看到不同的物体?是因为从物体表面上有光进入了人眼。那么能不能逆向思考一下,是不是也可理解为人眼发出了很多感知光线碰撞到了物体,所以可以看见呢?在古代可还真就有不少人这么想:

image-20240327171939365

当然,现代物理知识已经告诉我们这种观点是错误的,但是并不妨碍从中获取一些灵感,考虑一下对光线的第三条假设:光路可逆,所有进入到人眼的光,都可从人眼发出光按照原路反方向返回,那么利用这种模拟从人眼发射光线的方法不就可以还原出所有的光路了呢?没错这就是光线追踪的核心想法,从光源出发难以模拟那就反着从摄像机发射光线!

第一步 Ray Casting

从人眼或摄像机向近投影平面上的每一个像素点发射一条光线,判断与场景物体的交点,示意图如下:

image-20240327172710930

当然一条光线自然可能会与不止一个物体相交,但是考虑遮挡关系,只去找最近的交点。接着连接该交点和光源,只要判断这条连线之间是否有物体存在就可以知道该交点是否在阴影之中(怎么样,是不是比shadow mapping那一套简单了许多):

image-20240327172855369

紧接着,自然可以利用Blinn-Phong模型对这个点进行局部光照模型计算,得到该像素的颜色,那么遍历所有近投影平面上的像素就能得到一张完整的图像。但如果光线追踪仅仅是在第一步Ray Casting就停止的话,那么它的效果与局部光照模型是一样的,因此我们需要第二步,真正的考虑全局效果。

第二步 Recursive (Whitted-Style) Ray Tracing

考虑第一步中所做的Ray Casting,该条光线第一个与圆球物体相交,假设该圆球是一个玻璃球,那么便会发生镜面反射,如图:

image-20240327173024707

当然除了镜面反射之外,自然也存在折射,同时反射与折射出去的光线会可能与场景中的物体再次碰撞,发生第二次折射与反射:

image-20240327173401614

(为了图示清晰,图中仅以两次折射或反射的部分光线为例) 从图中可以见到,不仅仅是与圆球相交的那一点可以贡献光到达眼睛,折射与反射之后再与物体相交的点也可以贡献光(光路可逆原理)。简而言之,除了直接从光源照射到圆球交点再沿着 eye rays(从眼睛发射的第一条光线)到眼睛中,也可能存在这样一种情形,有光照射到其他物体,再沿着eye rays的反射或折射的光线方向传回人眼!

因此每一个交点的颜色贡献来自这样种几类型 直接光照,反射方向间接光,折射方向间接光(如果有折射的话)

定义三种光线:

  • primary ray:眼睛直接发出的光线
  • secondary ray:间接发出的光线
  • shadow ray:物体交点间接发出到光源的光线

下一步将这些所有交点与光源连接,称这些线为shadow rays(因为可以用来检测阴影),计算这些所有点的局部光照模型的结果,将其按照光线能量权重累加(该做法与递归过程等价,读者可以看看伪代码思考一下),最终得到近投影平面上该像素点的颜色!而这就是一个考虑全局效果的光照模型了,因为不仅仅考虑了直接光源的贡献,还考虑各种折射与反射光线的贡献。

以上就是光线追踪的整个过程了,还有额外几点要注意的 tips:

  1. 整体过程是一个递归的过程,因此需要一定的递归终止条件,比如说允许的最大反射或折射次数为10。
  2. 光线在每次反射和折射之后都有能量损耗的,由系数决定,因此越往后的折射和反射光贡献的能量越小,这也是为什么在上文中提到根据光线能量权重求和。 e.g. 反射系数为0.7,那么第一次反射折损30%,第二次反射折损1-(70%x70%),依次类推。
  3. 如果反射或折射光线没有碰撞到物体,一般直接返回一个背景色。
  4. 有一些关于光线表示,及如何求交点的实现细节在1.2节里讨论。

参考伪代码如下:

img

2 光线的表示方法

我们可以将每一条光线想象成一条射线,那么每一条光线都会由起点及方向这两个属性所固定,如下图所示:

image-20240327173844365

除了起点 o, 以及方向 d之外,还额外定义了一个参数 t 来表示光线行进的长度。

3 光线与物体求交的方法

3.1 光线与隐式曲面求交的方法

首先介绍如何计算光线与隐式曲面的交点的方法,以一个球体为例,二者表示方程如下:

image-20240327174055522

那么对于一个光线会在什么时候与球相交呢?

当然是在一个点即满足光线方程,又满足球体方程的时候,所以可以计算如下,把 �=�+��代入球体方程,利用一元二次方程的解法即可得到参数 t 值:

image-20240327181904979

同样的根据 b^2与 4ac 的正负关系,即可判断光线与球是一个交点还是两个交点又或是没有交点。

虽然这里只举了对一个球的隐式曲面交点的计算,对于所有其他隐式曲面过程都是类似的,只要将光线方程代入求解 t 即可,如下图所示

image-20240327182022845

3.2 光线与显示曲面求交的方法

当然,真正在图形学中大量运用的其实是显示曲面,更具体来说就是许许多多个三角形,因此如何判断一条光线与显示曲面的交点,其实也就是计算光线与三角形面的交点。对于任意一个平面,可以用如下图中的式子表达:

image-20240327182203647

图中对于平面方程的讲解已经很清楚。那么到这里其实已经成功把对显示曲面的求交又转化为了类似隐式曲面求交的方法,对于任意一个三角面来说,它一定处于一个平面之上,只需求出光线与平面的交点,再判断该交点是否在三角形内,就可以得到光线是否与三角形面相交的结果了!

首先给出如何计算光线与平面交点的过程:

image-20240327182256921

得到参数 t 之后,自然可以计算出交点,并且再去计算出重心坐标就能判断该交点是否在三角形内了,但是这种方法略显繁琐,能不能一步就得到结果呢?当然可以!

image-20240327182407667

直接将点的形式用重心坐标的形式表示,随后利用克莱姆法则求解线性方程组即可!(推导过程省略,但其实就是用了线代知识里面的克莱姆法则。)

4 反射与折射

4.1 反射方向的计算

反射方向计算相对容易,如下图所示,已知 l, n 想要求出反射方向 r

image-20240327182705301

4.2 折射方向的计算

image-20240327182822563

image-20240327182840039

4.3 菲涅耳反射(Fresnel Reflection)

image-20240327183150714

image-20240327183224419

image-20240327183241840

image-20240327183259722

Note:whited-style光线追踪该如何考虑漫反射?

在Blin-Phong模型中层提到过,漫反射是光线照射到粗糙物体表面从而发生向周围均匀反射光线的一种现象,反射的光线可以说是无数的!

那么对于这种反射,在光线追踪该怎么处理呢?借鉴**RayTracingInOneWeekend** 里的做法,对于漫反射表面每次进行反射的时候,随机的选取物体表面向外半圆内的一个方向作为该次反射的方向,对其再像镜面反射及折射一样进行递归的光线追踪计算。

但对每一个像素不仅仅只发出一条感知光线,利用多条光线RayTracing的结果求均值,最终作为该像素的颜色值

比如说我每个像素sample 1000条光线,如果撞到漫反射表面那就是1000条随机方向的 RayTracing结果的均值,这样便能较为准确的模拟了漫反射表面的特性了。 (对一个像素进行多次sample,其实也就把抗锯齿也给做了)

(tips: 该方法其实更多算是path tracing,经典的whited-style光线追踪遇到漫反射表面会直接利用blinn-phong模型计算颜色值返回,而不再递归下去)

更多的细节,读者可以自行的去连接里面看。

相关文章:

  • docker日志大小设置(doker logs)
  • Spring_MVC
  • IP如何异地共享文件?
  • Spring实战:采用Spring配置文件管理Bean
  • 项目搭建之统一返回值
  • 【机器学习】包裹式特征选择之序列前向选择法
  • HCIP作业
  • ES6(一)箭头函数,解构赋值,模板字符串,let 和 const,类和继承
  • 数据结构——二叉搜索树详解
  • XUbuntu22.04之激活Linux最新Typora版本(二百二十五)
  • 以太网与数据链路层
  • 两个有序序列的中位数(全网首篇递归、分治解决)
  • Glide
  • 使用IDEA的反编译插件 反编译jar包
  • 开源博客项目Blog .NET Core源码学习(12:App.Application项目结构分析)
  • 【译】JS基础算法脚本:字符串结尾
  • FastReport在线报表设计器工作原理
  • IndexedDB
  • Java深入 - 深入理解Java集合
  • JS函数式编程 数组部分风格 ES6版
  • mysql外键的使用
  • Vue全家桶实现一个Web App
  • 从零开始学习部署
  • 入口文件开始,分析Vue源码实现
  • 软件开发学习的5大技巧,你知道吗?
  • 微服务入门【系列视频课程】
  • 学习笔记TF060:图像语音结合,看图说话
  • 在Docker Swarm上部署Apache Storm:第1部分
  • #Z0458. 树的中心2
  • #微信小程序:微信小程序常见的配置传值
  • (html5)在移动端input输入搜索项后 输入法下面为什么不想百度那样出现前往? 而我的出现的是换行...
  • (非本人原创)史记·柴静列传(r4笔记第65天)
  • (附源码)小程序 交通违法举报系统 毕业设计 242045
  • (理论篇)httpmoudle和httphandler一览
  • (生成器)yield与(迭代器)generator
  • (四)Controller接口控制器详解(三)
  • (转)甲方乙方——赵民谈找工作
  • ***汇编语言 实验16 编写包含多个功能子程序的中断例程
  • .bat批处理(一):@echo off
  • .NET 4.0中使用内存映射文件实现进程通讯
  • .NET Core/Framework 创建委托以大幅度提高反射调用的性能
  • .net 获取url的方法
  • .Net7 环境安装配置
  • .net连接MySQL的方法
  • .py文件应该怎样打开?
  • /etc/shadow字段详解
  • @EnableConfigurationProperties注解使用
  • @德人合科技——天锐绿盾 | 图纸加密软件有哪些功能呢?
  • [ vulhub漏洞复现篇 ] ECShop 2.x / 3.x SQL注入/远程执行代码漏洞 xianzhi-2017-02-82239600
  • [20170728]oracle保留字.txt
  • [20180224]expdp query 写法问题.txt
  • [AutoSar]BSW_Memory_Stack_004 创建一个简单NV block并调试
  • [AX]AX2012 AIF(四):文档服务应用实例
  • [BUUCTF NewStarCTF 2023 公开赛道] week3 crypto/pwn
  • [c语言]小课堂 day2