【学习总结】Ray tracing in the next Weekend

这是Ray tracing三部曲的第二部,这一part稍微有点上了难度,很多地方其实比较难思考,但是书上是说了个大概思路就给出代码过了。我自己去尝试理解这些部分花了些时间(本来应该更早出这篇的)。

还是一样,因为原书其实写的很详细了,所以我只对不太好理解的地方做出补充~(或者评论区告诉哪里不太清楚,我们一起探讨一下)

如果有错误的地方,烦请指正~!

原书链接在这里:Ray Tracing The Next Week
推荐翻译在这里:日天大佬的翻译
自己的代码GitHub:Ray Tracing The Next Week

再次:请务必看看闫老师的Games101,越学这些教程越发现101教的有多好
(过段时间我感觉要二刷了)

关于BVH

教程的前半部分的渲染都是每一条光线去和每一个物体求交,有些无法被光线采样到的物体也会被拿出来做计算,因此,我们需要尽量避免这些重复计算,就需要一个场景加速结构。
一般来说,场景加速结构分为两类,一类是按空间划分的(比如四叉树,八叉树,KD-Tree)等结构,另一类就是以BVH为代表的按物体划分的加速结构了。
BVH简单来说,就是通过一个二叉查找树结构,存储场景中的物体,一般会和包围盒结合在一起使用。只要一条光线与父节点做判断,判定结果是不穿过父节点,则所有的子节点都可以在此次采样中被剔除。
以书上的图来举例:比如光线和紫色的包围盒做检测,判定是不会穿过紫色包围盒,则红色和蓝色的包围盒全部都不用做检测了。
在这里插入图片描述
我们使用的包围盒是AABB包围盒,直观来说,就是直接在物体外面套一个刚好能装下这个物体的盒子。
而判断是否与包围盒相交的算法我们使用的是堆叠法:
在这里插入图片描述
由于我们的光线具有唯一的表达式 p(t) = a + tb(a为起点,b为方向向量),所以你只需要分别求出和蓝色平面与绿色平面相交的t值。然后检查这个t值有没有重叠,有的话就代表有相交。同样的,这个方法可以推广至三维,下面这段伪代码总结的挺好的:
在这里插入图片描述
由于计算t值时会遇到NaN问题(分母为0,或者分子分母同时为0),因此需要人为限制他们的值。
接下来需要做的事情就是计算每一个物体的包围盒,对于球体来说,包围盒就是半径长度为边的正方形(也是最好算的),对于运动球体来说,需要接受两个时间变量,去计算运动球体在这个时间范围的两个包围盒(两个端点),然后用一个更大的包围盒将他们包裹起来

除此之外,我们还需要一个bvh_node来回答是否被击中的这个问题,如果被击中的话,则去计算左右节点的hit函数进行进一步递归。如果没有击中,则直接返回false。

这里构建BVH树的过程是个难点,我将代码拆解成了以下步骤(都是个人结合文章理解,有错误的地方请一定要指正):
在这里插入图片描述

  1. 首先BVH是按照物体的划分方式,在三维空间中,我们有三个轴可以选取。由于BVH是二叉查找树结构,我们需要每一次都对随机选取的轴进行按轴排序,然后计算出中点,进行进一步的递归分割。
  2. 这里递归分割的终止条件就是看每一次分割之后剩下多少物体。如果剩下的物体数量<=2,那么就终止分割,如果剩下的物体数量>2,那么就让左右子树指针指向一个新的BVH_NODE,并且对应调用bvh_node的构造函数
  3. 上述分割结束之后,此时构造应该来到了最底层,即左右指针指向的是基本图元。那么,我们又从最底层开始逐渐网上调用BouddingBox来构造包围盒。
  4. 在最底层的基本图元上,不同的类有对应的包围盒方法(代码里面写的很清楚,如果是个球体就用正方体包围起来)
  5. 在上升过程中,会在递归的本层构造刚好能覆盖左右子树的包围盒。(在递归代码中,left_box和right_box会等于下一层的BoudingBox,然后本层的BoudingBox等于在这两个盒子之上多加一个)
  6. 递归结束,整个BVH树也就构建好了
    在这里插入图片描述
    调用过程这里有一个小细节:
    在这里插入图片描述
    这个地方使用了一次强制转换,而且是两个子类,hittable_list和bvh_node之间的转换。所以这个地方应该是将最后一个bvh_node(即根节点)推入了hittable_list的队列里面。
    这里转换是有条件的:关于子类之间使用static_cast强制转换的问题

以上,我们的BVH树就构造好了,这样就不用和场景中的每一个物体去求交,而是和相交的包围盒里面的物体去求交,会加速不少。(注:BVH并不是只有这一种构造方式,前两天看到一个大场景0.58秒构建完毕的论文,那是真的牛逼。改天研究一下)

柏林噪声

柏林噪声是用于构造一些看起来杂乱无章但是却有规律性的事物(这里涉及到一些分形几何的内容)
柏林噪声的重要特点有两个:

  1. 可复现性。如果输入的是同一个三维空间中的点, 他的输出值总是相同的。(伪随机)
  2. ·实现方便,柏林噪声比较好写

实现柏林噪声比较关键的地方在于两点:

  1. 一个随机数生成器
  2. 一个插值函数

首先先聊一聊随机数生成器,最开始的随机数生成器是使用random函数去随机取值的:
在这里插入图片描述

在这里插入图片描述
上方的两个步骤之后,我们现在有了一个全是0到1之间的数值的ranfloat数组,然后,通过哈希表映射的方式,再去进一步的得到具体的随机值:
下方这个图里的两个函数所做的事情就是生成哈希表的索引,完成最终Noise函数的哈希映射
在这里插入图片描述
最开始的noise函数长下面这样:
在这里插入图片描述
前面的i,j,k与255相与的原因就是需要限制你的索引值在255的范围之内(位运算的知识,超过255的相与会变成0),之后通过一个或运算得到一个看起来随机的序列号,接着从ranfloat函数里面取值进行返回。这样,我们就得到了在点p处的一个伪随机值(因为再一次输入P点,会进行相同的运算,得到的结果必然是一样的。)
这个效果做出来大概长这样:(哈希表的扰动成功了)
在这里插入图片描述
因为我们现在只用了基本的随机值,并且使用int去强制转换了点p的坐标,所以表现出来的就是一个接着一个的方格子。因此,教程接下来用了一个基础的线性平滑去混合这个噪声值:
在这里插入图片描述
PS:这里为什么选择数组大小为2做线性插值呢?我个人的推测是这样:这个地方函数的名字其实说的很明白了,这是一个三线性插值法,因为我们有i,j,k三个轴,正好构成一个正方体,而我们需要去这个正方体区域里面去找到对应的值。
更多相关可以看这一篇文章,总之这里就是用三个for循环完成了插值的这么一个操作:
三线性插值
完成效果大概就长这样:
在这里插入图片描述
下一步是使用Hermit Cubic进行插值,虽然我觉得出来的效果和上面是差不多的。
在这里插入图片描述
接着,我们使用Scale加速一下颜色变化的频率,直接和p相乘然后去得到一个取值。(这里可以理解为,你直接提升了点的采样频率,因为我们哈希表的索引值与255做&,其实也是有采样周期的)
在这里插入图片描述
可以得到:
在这里插入图片描述
由于我们的每一个点还是强制落在一个整数上面的,所以看上去还是有网格的那种感觉,于是,这里就需要引入一个新的东西,随机梯度向量:
对于基本二维做法如下:

1.定义一个晶格结构(就当成一个单位正方形),每个晶格的顶点有一个“伪随机”的梯度向量(说它是伪随机的是因为:1.梯度向量是我们随机的选择我们自定义的向量,而不是随机的选择随机的向量。2.一旦确定了,固定的点就有了固定的梯度向量)。这个梯度向量就当做向量理解吧。对于二维的Perlin噪声来说,晶格结构就是一个平面网格,三维的就是一个立方体网格。
2.输入一个点(二维的话就是二维坐标,三维就是三维坐标,n维的就是n个坐标),我们找到和它相邻的那些晶格顶点(二维下有4个,三维下有8个,n维下有个),计算该点到各个晶格顶点的距离向量,再分别与顶点上的梯度向量做点乘,得到个点乘结果。
3.使用缓和曲线(ease curves)来计算它们的权重和
————————————————
版权声明:本文为CSDN博主「旅程TSH」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u010669231/article/details/97051705

在这里插入图片描述

那么教程里面的做法也是相似的:通过将随机单位向量放在每一个格子上(之前相当于直接放了浮点数),然后通过点积的方式来进行一个采样取值。
随机数生成器变为随机向量生成器:
在这里插入图片描述
三线性插值的顶点也变成随机向量的形式(想象一下三维的晶格图):
在这里插入图片描述
在这里插入图片描述
这里对点的小数部分做了进一步的平滑插值,然后去计算对应的插值点积
同时,由于得到的噪声值有可能是负数,影响伽马矫正的sqrt()函数,所以我们要人为把它置正(从-1~1置为 0 ~1)
在这里插入图片描述
于是我们可以得到这个效果:
在这里插入图片描述

接下来就是噪声叠加,这通常称为turblence(其实就是增加噪声采样然后做一个叠加,光追惯用手法了)
在这里插入图片描述
这里其实还有一些教程没有说的细节,就是关于倍频系数的选取,因为我们是多个噪声叠加,所以这里的weight,即权重值是需要改变的(全部都是1的话那么变化的就不会那么自然了,我们需要确定一个基调然后逐渐往细微的地方进行扰动,你可以想象成一个心电图,如果系数都为1叠加,那么就不会有小的波峰或者波谷)。关于权重值的计算其实是有一个算法的:
在这里插入图片描述
看看核心参数的计算过程是不是和教程里面一样~
于是,通过将noise函数替换为turb函数,同时让颜色与正弦函数相关,并用turb调整相位,就可以得到类似于大理石的入门级程序纹理:
在这里插入图片描述

可以渲染出下面的效果:
在这里插入图片描述
关于柏林噪声的这一部分,我也是第一次进行学习,所以可能有一些地方会有错漏和讲的不太好的地方,欢迎大家指出。
另外,在整理柏林噪声的时候,发现了一篇十分不错的讲解,推荐给大家:

具体可以从这里找:柏林噪声

小结

第二部分的内容还是比较充实的,除了物体划分的BVH算法,柏林噪声之外,如何计算纹理坐标完成纹理映射,以及体积雾也是比较重点的内容。

此外,第三部分Ray Tracing:The rest of your life可能没那么快出来了,因为是时候回去补基础了~ 包括但不限于高数,线性代数,算法,和101的回看。而且正好赶上秋招了,还有一些基本的东西需要复习,所以第三部分会暂时搁置~

  • 7
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值