构建渲染器准备¶
1.输出格式¶
使用最简单的格式ppm(几行、几列、像素最大颜色、像素点)
- 实现方式非常简单,两层循环遍历即可 https://www.cs.rhodes.edu/welshc/COMP141_F16/ppmReader.html
2.cmake构建文件¶
使用优化的构建,将exe重定向到iamge.ppm文件中
3.进度指示器¶
clog
4.向量运算类vec3¶
- using取别名,区分point3和color
- 默认构造
- 带参构造
- x、y、z获取
- 重载负号、下标获取、下标修改
- +=,* =, /=
- 模长length()
- 输出<<
- 加减
- 向量点乘(内积)
- 标量乘向量(左右两种情况)
- 标量/
- 点积dot()
- 叉积cross()
- 单位向量
5.颜色实用程序函数¶
- 可以引用向量类后新建个color.h
- using取color别名
- 写颜色(将rgb的01转换为0255)
光追器准备¶
1.光线Ray类¶
起点p、方向v、长度t
2.渲染图像设置¶
- 通过设置aspect_radio来设置图像宽高比,注意高度至少为1
3.光线创建¶
- 相机中心,了解焦距,uv
- 坐标轴(右手系:y上,x右、z负)
- 由于y图像应该与轴方向相反(左上(0,0)),使用边缘向量和像素增量 从左到右,从上到小扫描像素
- 创建场景光线:对场景中的每一个像素点,发送一条光线。
添加球体¶
1.射线球交点¶
推导:
求交:利用
可以获得三种求交情况
2.曲面法线和多对象¶
最近表面光线 = 判别式负号的那一个 法线 = 表面-圆中心
3.区分正面和背面¶
使用光线和向外的法向量进行计算,若点乘之和小于0,则为异方向,为正面 * 设计一个类来记录相交hit * 设计可命中对象列表
4.可命中的抽象¶
- hittable作为父类,提供常用命中抽象
- 先使用一个球体作为基类
4.可命中对象列表¶
- 需要用到我们的只能指针了
6.新的一些C++特性¶
- vector
- sharedPtr
7.常用常量和实用函数类¶
- rtweekend.h 公共标头
- 最后改一下主文件,删去一些重复的引用就可以了
- 设置一些无穷大、pi、角度转弧度、随机数
[0,1)
,两值间的随机数等 两个球的效果
8.An Interval Class 区间类¶
- 将之前代码中的min,max(光线)用interval类来表示
- size = max-min
- contains包含
- surround 包围
- empty
- universe
9.移动相机到类 camera.h¶
几乎所有代码都转移到camera,主函数留下世界构造 * 把ray_color放在相机中
抗锯齿¶
吐槽,这么计算采样不是每个像素算了100次?
1.添加随机数方法¶
用于设置采样点
2.添加区间类的Clamp方法¶
防止超出界限的代码造成抗锯齿颜色的错误计算
3.修改颜色代码¶
确保颜色被限制
4.相机实现采样¶
多次取平均值
漫反射¶
1. 生成随机矢量的方法¶
2. rejection method使用拒绝方法生成理想向量¶
1.在单位球体内生成一个随机向量 2. 归一化此向量 3.如果归一化向量落在错误的半球上,则反转它
3.将输出的法线改为输出颜色¶
4.限制递归次数¶
无限反弹极有可能炸毁堆栈
5.修复阴影痤疮Shadow acne¶
if(world.hit(r, interval(0.0001, infinity), rec)){
如果光线的原点就在表面下方,0有可能导致再次光线相交
0变成0.001
6.True Lambertian Reflection 兰伯特反射模型¶
normal再加随机射出方向,可以比较明显的看出来画面的提亮(偏天空蓝色),而阴影处更加明显
伽马测试(从0到1,颜色逐渐提升)
反射率0.1的gamma图像前后对比
材质--金属¶
1.材质抽象类¶
- 先定义hittable类,等待后面实现
2.hittable类,描述射线-物体交集的数据结构¶
- 描述射线-物体交集的数据结构
- 子类sphere也要添加上材质了(智能指针)
3.Scatter&Reflectance散射与反射¶
- albedo反照率:描述部分反射(会随着颜色和入射发生变化)
- 构建新的lambertion材料类
4.临界处理¶
- 和法线相反方向时,光线投射矢量会逼近于0,这会导致临意外的错误
- 新增方向,逼近于0时,向量默认为法线向量
5.镜面反射¶
* 反射向量计算函数reflect() * 添加新的金属材质,并进行光线反射计算 * 修改RayColor,可以根据材质提供散射反射率 * 修改sphere构造,可以传入材质
6.金属球的场景¶
添加四个材质,再添加到4个球上
7.模糊反射 Fuzzy Reflection¶
原理很简单,在反射光线处再加一个随机向量,并乘以模糊因子来控制反射。 1. 构造增添模糊度(金属度) 2. 原金属反射向量归一化(方便控制fuzz) 3. 加上fuzz计算新的反射向量 4. 给金属材质传入(模糊)金属度 实际上,当fuzz为一的时候,金属模型就很接近lambertain模型了
模糊度0.3,0.9
材质-电介质Dielectrics(Water、Glass、Diamond)¶
1.Refraction 折射¶
- 由Snell 定律描述
* 将折射光拆分成平面方向和法向 * 那么我们可以求解得到 * cosθ怎么计算? -R·n(限制为单位向量) * 将整个计算工时作为向量vec3.h的一个公式 * 新增材料子类,添加新的透明材质。 * *
2.全内反射 Total Internal Reflection¶
- 棘手的问题是,存在无法使用斯涅尔定律解决的射线角
- 当光线以足够的掠视角度进入折射率较低的介质时,它可以以大于 90° 的角度折射。
- 算出来的折射角会大于90度,sinθ’的值也不能大于1。因此等式两边之间的等式就被打破了,不存在则必须反射光线
- 1.00/1.33(水里)比较
3. Schlick Approximation 斯科利近似¶
真正的玻璃具有随角度变化的反射率,但是方程非常丑陋且复杂 * Schlick的廉价方程能有有效的模拟近似这种效果
static double reflectance(double cosine, double refraction_idx) {
// Use Schlick's approximation for reflectance.
auto r0 = (1 - refraction_idx) / (1 + refraction_idx);
r0 = r0 * r0;
return r0 + (1 - r0) * pow((1 - cosine), 5);
}
4.中空玻璃球建模¶
- 实则就是在中间再造一个半径较小的圆,但是折射率和外面的圆相反
可定位相机¶
1.相机视野几何体¶
- 确定Fov,焦距,可算出高度的比率
2.确定相机的位置和定向¶
- 设置相机起始点lookfrom,相机视野朝向lookat,相机上面的方向vup
- 设置相机渲染帧基础向量u、v、w
- 焦距变为起始点和朝向之差的长度
- w为(起始点-朝向)的反向
- u、v通过vup、w、u等进行叉乘计算
- 边缘向量viewport-u、v也是通过u,-v乘上视野宽度进行计算
- 左上角的点也通过focal_length和w的改变而改变 最终
Zoom in;
Defocus Blur散焦模糊(景深)¶
注意区分焦距focal distance 和focal length的区别(虽然这里确实当一样的实现了) * 摄像机需要”光圈“,而虚拟相机可以实现完美传感器,只有散焦模糊时需要模拟
1.A Thin Lens Approximation 薄透镜近似¶
2.生产采样射线¶
- defocus disk 散焦盘(半径为1的圆,z = 0)里随机取点
- 设置散焦角度--即我们相机中的光圈
- 根据散焦角度,计算出散焦盘半径
- 散焦uv向量:则等于散焦半径 * uv
- 那么散焦返回的光点位置,则等于我们随机取点的位置X散焦uv+相机原点位置
- 最后我们就可以根据光圈大小(散焦角度),就能判定是否使用景深了。
大光圈散焦10.0,焦距3.0¶
无散焦¶
大焦距10.0¶
中焦距4.0¶
小焦距1.0¶
可以看到过近或者过远,都会导致结果的模糊