Ray-Sphere Intersection

  已知球体的球心位置是$(C_x, C_y, C_z)$ ,那么球体的数学公式表达如下:
  $$(x-C_x)^2+(y-C_y)^2+(z-C_z)^2=r^2$$
  在图形中,我们总是想将数学公式用$vector$来进行表示,因此,这里我们可以将$x/y/z$用一个$vec3$来表示,并且球心$\mathbf{C}=(C_x, C_y, C_z)$到一个点$\mathbf{P}=(x, y,z)$的距离是$\mathbf{(P-C)}$,因此,上述公式可以转换为如下形式:
  $$\mathbf{(P-C)\centerdot (P-C)} = (x-C_x)^2+(y-C_y)^2+(z-C_z)^2=r^2$$
  对于一根光线,在数学上可以这样表示:$\mathbf{P}(t)=\mathbf{A}+t\mathbf{b}$ ,其中$\mathbf{A}$表示光线原点,$\mathbf{b}$是光线的单位方向向量,$t$是光线的长度。如果光线射中了球体,那么将满足如下条件:
  $$(\mathbf{P}(t)-\mathbf{C})\centerdot (\mathbf{P}(t)-\mathbf{C})=r^2$$
  即:
  $$(\mathbf{A}+t\mathbf{b}-\mathbf{C})\centerdot (\mathbf{A}+t\mathbf{b}-\mathbf{C})=r^2$$
  继续展开可以得到如下结果:
  $$t^2\mathbf{b}\centerdot \mathbf{b}+2t\mathbf{b}\centerdot(\mathbf{A-C})+(\mathbf{A-C})\centerdot(\mathbf{A-C})-r^2=0$$
  上述等式是一个关于$t$的二元一次方程,要判断光线是否与球体有交点,只要判断该方程是否有解即可。根据二元一次方程求根公式,我们可以判断是否满足如下条件:
  $$\triangle=b^2-4ac>0$$
  其中,$a=\mathbf{b\centerdot b}$,$b=2\mathbf{b\centerdot (A-C)}$ ,$c=\mathbf{(A-C)\centerdot (A-C)}-r^2$

Diffuse Materials

  一般而言,材质需要和几何体强绑定在一起,这样才能获得对几何体的最终着色结果,为了能够将材质复用到不同的几何体,这里需要将材质类单独实现。
  不发光的漫反射物体只是呈现出周围环境的颜色,并且它们也会用自己固有的颜色来调节这种颜色。从漫反射表面反射的光的方向是随机的,所以,如果我们向两个扩散表面之间的裂缝发送三束射线,它们将有不同的随机行为:
  
  实际上,任何随机方向的算法都会产生看起来哑光的表面(漫反射结果)。其中一个最简单的方法被证明是完全正确的理想扩散表面。(我曾经把它当作一种懒惰的hack,近似于数学上理想的Lambertian。)
  有两个单位半径的球体,与曲面的命中点$p$相切。这两个球体的中心分别是$(𝐏+𝐧)$和$(𝐏−𝐧)$,其中$𝐧$是表面的法线。以$(𝐏-𝐧)$为中心的球体被认为是曲面内部,而以$(𝐏+𝐧)$为中心的球体被认为是曲面外部。选择与射线原点在同一面上的切线单位半径球,在这个单位半径球内选择一个随机点$𝐒$,并从命中点$𝐏$发送一条射线到随机点$𝐒$(这是矢量$(𝐒−𝐏)$),由此可以产生一条随机方向的反射方向,从而模拟出漫反射结果:
  
  我们需要一种在单位半径球中随机选取一点的方法,这里将使用使用通常最简单的算法:拒绝方法。首先,在单位立方体中选择一个$x$、$y$和$z$都在−1到1范围内的随机点。如果这个点在球面外,那么就拒绝这个点,再试一次,直到这个点在球面内,需要注意的是这个过程是递归的,我们需要设置一个最大的递归深度来避免栈溢出。
  漫反射的球体看起来应该不是暗黑色的(在现实生活中,浅灰色)。原因是几乎所有的图像查看器都假设图像是“gamma校正”的,这意味着0到1的值在被存储为字节之前进行了一些转换。这有很多很好的理由,但为了我们的目的,我们只需要意识到这一点。对于第一个近似,我们可以使用“gamma 2”,这意味着将颜色提升到$\frac{1}{𝑔𝑎𝑚𝑚𝑎}$的幂,或者在我们简单的情况下的$\frac{1}{2}$,也就是平方根。

  $Lambertian$分布是一种用$cos(\phi)$来描述射线与法线夹角大小的分布,当射线越靠近法线时,发生漫反射的概率就越大,这种情况更符合正态分布。这是通过选取单位球体表面上的随机点来实现的,沿着表面法线偏移。选取单位球上的随机点,然后进行归一化可以实现对单位球体表面上选取随机点。

Metal Materials

  对于我们已经有的$Lambertian$(diffuse)情况,它可以一直散射,并通过其反射率$𝑅$衰减,或者它可以散射而没有衰减,但吸收$1 -𝑅$的射线,或者它可以是这些情况的混合。请注意,我们也可以只以某种概率$𝑝$进行散射,并将衰减设为$𝑎𝑙𝑏𝑒𝑑𝑜/𝑝$。
  对于光滑的金属,射线不会随意发生散射。关键的一个数学问题是:光线如何从金属镜面中进行反射?我们可以通过向量来进行描述:

  上图中红色的反射射线方向就是$𝐯+2𝐛$。在我们的设计中,$𝐧$是单位向量,但$𝐯$可能不是。$𝐛$的长度应该是$𝐯\cdot𝐧$。因为$𝐯$指向内部,我们需要一个负号,即红色的反射射线的计算结果为:$v-2*dot(v, n)*n$。
  我们还可以通过使用一个小球体来随机反射方向,并为射线选择一个新的端点:

  球体越大,反射就越模糊。这意味着需要添加一个模糊参数,这个参数就是球体的半径(因此零是没有扰动)。问题是,对于大球体或射线,可能会分散在表面以下,让表面吸收它们:

Dielectrics Materials

  透明的材料,如水、玻璃和钻石都是介质。当光线击中它们时,它会分裂成反射光线和折射(透射)光线。我们将通过随机选择反射或折射来处理这个问题,并且每次相互作用只生成一个散射射线。
  最难调试的部分是折射光线,通常情况下,如果有折射光线的话,首先要让所有的光线都折射。对于这个项目,我尝试在我们的场景中放置两个玻璃球,我得到了这个(我还没有告诉你如何正确或错误地这样做,但很快!):

  上面的结果对吗?玻璃球在现实生活中看起来很奇怪。但是不,这是不对的。世界应该被颠倒过来,没有奇怪的黑色东西。我只是打印出了穿过图像中间的光线,这显然是错误的

  通常会用$Snell’s law$(斯涅尔定律)描述折射:
$$\eta \cdot \sin\theta = \eta’ \cdot \sin\theta’$$
  其中$\theta$和$\theta’$是射线与法线的角度,$\eta$和$\eta’$是折射率(通常是空气= 1.0,玻璃= 1.3-1.7,钻石= 2.4)。几何形状是:

为了确定折射光线的方向,我们必须求解$\sin\theta’$:
$$\sin\theta’ = \frac{\eta}{\eta’} \cdot \sin\theta$$
在表面的折射一侧有一条折射光线$\mathbf{R’}$和一条法线$\mathbf{n’}$,它们之间存在一个角度$𝜃’$。我们可以将$\mathbf{R’}$分割成垂直于$\mathbf{n’}$和平行于$\mathbf{n’}$的射线部分:
$$\mathbf{R}’ = \mathbf{R}’_\bot + \mathbf{R’}_\parallel$$
求解$\mathbf{R’}_\bot$和$\mathbf{R’}_\parallel$就得到:
$$\mathbf{R’}_\bot = \frac{\eta}{\eta’} (\mathbf{R} + \cos\theta \mathbf{n})$$
$$\mathbf{R’}_\parallel = -\sqrt{1 - {|\mathbf{R’}_\bot|}^2} \mathbf{n}$$

一个麻烦的实际问题是,当光线处于折射率较高的材料中时,斯涅尔定律没有真正的解,因此不可能发生折射。如果我们回顾斯内尔定律和罪的推导$\sin\theta’$:
$$\sin\theta’ = \frac{\eta}{\eta’} \cdot \sin\theta$$
如果光线在玻璃内部,外面是空气(${\eta}=1.5$,${\eta’} =1.0$):
$$\sin\theta’ = \frac{1.5}{1.0} \cdot \sin\theta$$

$\sin\theta’$的值不能大于1。所以,如果,
$$\frac{1.5}{1.0} \cdot \sin\theta > 1.0$$

两边的等式就被打破了,解就不存在了。如果溶液不存在,玻璃就不能折射,因此必须反射光线,对这种情况进行判断分别处理。
在这里,所有的光都被反射了,因为实际上光通常是在固体内部的,所以被称为“全内反射”。这就是为什么当你在水下时,水-空气边界有时就像一面完美的镜子。
我们可以用三角函数性质来解$\sin\theta$:
$$\sin\theta = \sqrt{1 - \cos^2\theta}$$
并且,其中$\cos\theta = \mathbf{R} \cdot \mathbf{n}$。


  真正的玻璃的反射率随角度的变化而变化,比如以一个较大的倾斜角度去看一扇的窗户时,它就会变成一面镜子。这有一个丑陋的大方程,但几乎每个人都使用克里斯托弗·施里克(Christophe Schlick)的廉价而精确的多项式来进行近似。
  电介质球的一个有趣而简单的技巧是,如果你使用负半径,几何形状不受影响,但表面法线将指向内部。这可以作为一个气泡,一个中空的玻璃球:

常见数值运算

  1、当$x\in[-1,1]$ 的时候,我们需要将其范围转换为$[0,1]$时,可以进行如下转换:
  $$x=(x+1)/2$$
  2、抗锯齿解决思路:在对一个像素进行着色时,可以增加采样次数,然后对累积的采样结果求平均,即可获得一个抗锯齿的效果。
  
  3、线性插值:
  $$\text{blendedValue} = (1-t)\cdot\text{startValue} + t\cdot\text{endValue}$$