Ray tracing in one weekend 疑难点解析与拓展

Ray tracing in one weekend》(一周末搞定光线追踪), 由Peter Shirley所编写的的软渲光追三部曲第一本, 是一本非常好的入门级书籍, 篇幅不多, 一共只有54页, 适合新手学习。

剩下的两本为:
Ray tracing: The next week(光线追踪: 下一周)
Ray tracing: The rest of your life(光线追踪: 余生)

作者已将本书免费发布到公共领域(cc0协议), 点击这里在线阅读本书

本文将解析书中的一些疑难点, 并对原有的程序稍作修改

有时间的话, 可能会考虑来做个全书翻译翻完啦

1 Overview

2 Output an Image

作者虽然在本书使用PPM格式来输出图片, 但在本章最后他提到自己对stb_image.h的喜爱, 那我们就顺水推舟, 不妨使用这个常用的图像库把颜色信息储存为更加便于查看的jpg格式。

stb_image.h, stb_image_write.h等是由C语言编写的类库文件,遵从MIT协议开源协议(stb是作者Sean T. Barrett名字的缩写)。我们这里使用stb来将数据写入文件,并不需要读取, 所以仅需要导入stb_image_write.h即可, 点击这里下载

下载完成后将其导入你的项目

1
#include "stb_image_write.h"

为我们的图像分配空间

1
unsigned char* data = new unsigned char[x * y * 3];

然后将颜色信息写入

1
2
3
4
5
6
7
8
for (int j = 0; j <y; j++)
for (int i = 0; i < x; i++)
{
vec3 col = ......;
data[(y - j - 1)*x * 3 + i * 3] = unsigned char(255.99f * col[0]);
data[(y - j - 1)*x * 3 + i * 3 + 1] = unsigned char(255.99f * col[1]);
data[(y - j - 1)*x * 3 + i * 3 + 2] = unsigned char(255.99f * col[2]);
}

最后写入文件并释放空间

1
2
stbi_write_jpg("..//output.jpg", x, y, 3, data, 100);
delete[] data;

配合github desktop 我们可以很舒服的来观察输出结果的变化
使用github desktop看图

3 The vec3 Class

c++ 重载教学

4 Rays, a Simple Camera, and Background

5 Adding a Sphere

射线与球相交

6 Surface Normals and Multiple Objects

c++ 继承教学

7 Antialiasing

这里的抗锯齿做法十分暴力: SSAA, SuperSampling Anti-Aliasing(超级采样抗锯齿), 在每个像素周边发射s条射线然后取颜色的平均值, 本书中s取100

Calculating the end color value (Wikipedia)

这样程序的运行时间就直接翻了s倍, 十分的慢
我们先把程序由Debug切换到Release, 实测可以大幅提升运行速度
再通过OpenMP来进行多线程的加速

首先我们先打开编译器对OpenMP的支持 右键项目属性C/C++ → 语言 → OpenMP支持(是)

VS2015

1
2
3
4
5
6
7
8
#include <omp.h>

int main(){
#pragma omp parallel for num_threads(4)
for(){
//your code
}
}

这里的num_threads在默认情况下是你cpu的核心数, 开启超过cpu核心数的线程并不会提升程序执行效率。原理很简单, 不优化时程序是在单核跑, 优化后OpenMP将代码块编译, 拆分, 让剩下的3个核心也加入了运算, 这时再开启更多线程也没用了, 一共只有4个核心, 而他们全部都在运算中。
我们可以省略for num_threads()参数, 直接

1
2
3
4
#pragma omp parallel for
for(){
//your code
}

当for循环嵌套时,即两个循环之间存在关连时, 复数的优化指令也无法做到加速

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#pragma omp parallel for
for(){
//outer loop
#pragma omp parallel for //this line is useless
for(){
//inner loop
hit();
}
}

void hit(){
#pragma omp parallel for //this line is also useless
for(){
//loop in func
}
}

#pragma omp parallel for 写在哪个循环前, 就会把该循环的工作拆分, 于是就打乱该循环的次序。使用大括号{}将目标代码段括起来, 会将内外部循环次序全部打乱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#pragma omp parallel for
for(i=0;i<10;i++){
cout<<i<<endl;//i will not in order
for(j=0;j<10;j++){
cout<<j<<endl;//j will IN order
}
}
//time 0.018
#pragma omp parallel for
{
for(i=0;i<10;i++){
cout<<i<<endl;//i will not in order
for(j=0;j<10;j++){
cout<<j<<endl;//j will NOT IN order
}
}
}
//time 0.018

在该项目中加速, 只要在遍历像素前加一句#pragma omp parallel for即可

1
2
3
4
5
6
7
8
9
#include <omp.h>
...

int main{
...
#pragma omp parallel for
for (int j = 0; j <ny; j++)
...
}

现在程序的运行速度就会快上大约你核心数的倍数, 再加上切换到Release模式,原来渲上好几个小时的图只需渲上几分钟

8 Diffuse Materials

光线打到粗糙不平的物体表面, 会随机的往各个方向散射。在生活中大部分物体都属于这类。
生成一个随机的单位向量来模拟光线的无规则散射

9 Metal

在计算dot(v,n)时, v与n的夹角大于90°, 运算结果为负值, 所以需要补正

10 Dielectrics

求折射光线的方向
全内反射250px-Total_internal_reflection_of_Chelonia_mydas.jpg
Fresnel - Schlick approximation近似求借菲涅尔方程

11 Positionable Camera

这里和其他地方的摄像机没有太大的区别fov, depth

12 Defocus Blur

和chapter7的做法一样,将摄像机发出每条射线的位置进行偏移, 然后在SSAA时就会暴力的取平均值

13 Where Next?

output.jpg

Ray tracing in one weekend 疑难点解析与拓展

https://matrix4f.com/Graphic/ray-tracing-in-one-weekend-explanation/

Author

oxine

Posted on

2020-03-03

Updated on

2020-04-10

Licensed under

Comments

昵称处填入QQ号,自动同步QQ头像与ID