[光线追踪] 00 -- 前言 & 前置知识
前言
其实自己学习光追已经有挺长一段时间的了 (大概一年多?), 途中尝试过很多不同的光追模型和编程语言, 但都因为效率不太好所以一直没有写相关的专栏. 不过最近发现了一个效率挺高的模型, 就觉得用这个模型作为专栏里的主要部分了, 不过在介绍光追原理和大概结构的时候还会随便提提其他模型.
专栏里的主要编程语言就选用速度很快的 C++ 了, 但是由于 C++ 写起来还是挺麻烦的, 所以也许专栏里有部分代码会选用其他语言 (比如说 python) 作为示例, 不过实际上实现的最后还是 C++ 就是了. 不得不说明的一点, 自己真的不太会写 C++, 如果实际代码里有什么可以优化或者真的写得一团糟的地方, 非常欢迎指出.
关于排版的话, 因为屑阿b在一个专栏里只能放 100 张图片, 而数学公式也算是一张图片, 所以有些地方不得不使用 unicode 字符来代替 latex 公式了. 但是我发现有很多地方都不能显示 unicode 的上标箭头 (如下图所示), 所以向量就使用带颜色的粗体表示了: u

关于光追有一个不得不说的东西: 这里讨论的光追是指一种渲染手段, 而不是对物理的模拟仿真. 也就是说为了追求模型的简便和计算的速度, 有某些地方需要进行必要的假设, 而这些假设会使渲染结果与真实的场景产生偏差. 所以如果是在追求对真实场景的模拟仿真的话, 这篇专栏是非常不合适的. 另外, 在专栏里进行这种会产生偏差的假设时, 会用红色大字标明.

前置知识
因为光追涉及到很多向量计算, 所以相应的向量知识是必须的 (下面会进行一个复习的动作). 而对推导光追模型的过程需要运用微积分的知识, 所以如果想知道具体数学细节的话, 看懂微积分是必须的, 但仅限于看懂就行了, 并不会涉及微积分的求解. 跟传统的光栅化渲染不一样的是, 在光追里线性代数不是必须的, 倒不如说基本上不会遇上.
复习:
在三维空间里, 任意一个向量都有 3 个分量: .
向量的模长定义为 .
向量的归一化返回方向与原向量相同但模长为1的向量: .
两向量之间的点乘 dot 定义为向量之间逐元素乘积的和: , 点乘与向量的模长和夹角有关:
.
两向量之间的叉乘 cross 返回一个向量, 并且返回向量与原来的两个向量垂直, 模长与原向量的模长和夹角有关: , 叉乘的计算为:
, 并且叉乘是不可以左右互换的.

几种基本的类型和方法
数学上的向量可以在编程里使用数组实现, 并且在一些编程语言里都自带实现了数组, 比如说 python 的 list 或 numpy 的 ndarray, julia 的 Vector, C++ 的 std::vector. 这些数组都是可以改变长度的, 但是向量本身是不变长的, 所以为了优化性能, 应该使用不可变长的数组作为向量类型. 在 julia 里可以使用 StaticArrays 包, 而在 C++ 里可以使用 glm 库 [github.com/g-truc/glm]. 在这里我就多此一举自己实现了:
Vec2 是为了计算纹理或采样的, Vec3 就是计算光追里最主要的类了. 这里使用双精度浮点数是因为计算光追的精度要求较高, 如果使用单精度浮点数虽然计算速度较快, 但会产生较大的误差, 从而造成渲染错误.
另外需要实现两个向量类的四则运算:

然后实现向量的几何运算:

为了某些增加某些计算的速度, 应该提供某些不饶远路的几何运算: 比如说 abs2 直接返回模长的平方, 亦即向量元素的平方和, absnorm 修改向量的模长到1并且返回原来的模长:

另外别忘了还有球极坐标和相应的逆方法:

最后还有一个实用的东西: 因为有时候会在几何表面处计算临近的坐标, 所以在几何表面建立局部坐标系是很有用的. 一般来说几何表面会提供一个法向量: 法向量是一个垂直于表面并且模长为1的向量, 但是这样是不足以建立局部坐标系的, 所以还需要一个在表面上的纹理向量, 但纹理向量不存在时可以直接选取全局坐标的 x轴, 但如果法向量平衡与 x轴 计算会出问题, 这时候选取 y轴 就行, 下面是局部坐标系的实现:

其中 LocalCoord::at 是把局部坐标转为全局坐标.
最后, 储存渲染缓冲区和采样集都需要二维数组, 所以自己搓了一个 Buffer2D<T>, 大概是这个样子的

具体实现可以参考仓库里的 utils. (仓库链接: github.com/nyasyamorina/nyasRT)
另外关于采样的实现也可以参考仓库里的 samples.

写了一大篇废话, 总之, 下一篇开始就是开始推导并实现光追算法了.
最后再推一下仓库: github.com/nyasyamorina/nyasRT
最最后再推一下涩弔图群: 274767696