0%

一:生成第一张图片

前言:前段时间在学习C++ primer的同时,我还有幸接触到了一本非常有趣的书,它通过我们的vs写出的代码直接模拟光线追踪生成图像。地址:Ray Tracing in One Weekend。它非常的有趣也具有很高的学习价值,所以我想把学习的过程记录下来,也欢迎大家来一起学习并找出其中的错误,废话不多说,直接开始!

图片格式

首先明确我们这里使用的是ppm格式的图片格式,它是通过类似于vector<vector<int>>的方式记录下每个色素的RGB值。

image.png

此外在开头还需要做一些说明:

  • P3#代表颜色使用ASCII码表示
  • 3 2#代表3行两列,它使得我们接下来的数字不需要按照行列摆放工整,而是可以
  • 255#表示使用0-255来表示一个通道的颜色

可以试着通过代码来生成一张这样的图片:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>

int main() {

//定义图片的宽度和高度
const int image_width = 256;
const int image_height = 256;

//打印ppm文件的开头
std::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";

//以左下角为原点,从左上方依次打印像素
for (int j = image_height-1; j >= 0; --j) {
for (int i = 0; i < image_width; ++i) {
//将r和g通道控制在0-1内
auto r = double(i) / (image_width-1);
auto g = double(j) / (image_height-1);
auto b = 0.25;
//之后统一将它们映射到0-255
int ir = static_cast<int>(255.999 * r);
int ig = static_cast<int>(255.999 * g);
int ib = static_cast<int>(255.999 * b);
//依次输出rgb,注意中间空格
std::cout << ir << ' ' << ig << ' ' << ib << '\n';
}
}
}

写完代码之后,我们以release方式运行,之所以不选择debug模式,主要还是因为太耗费时间,在保证代码不出现错误的情况下,使用release可以节省一些时间。

命令行

以上代码只能让信息输出到控制台,而我们希望将其输出到一个文件内,所以我们可以使用命令行实现:

  • 打开命令行输入项目所在硬盘加冒号之后回车,如:D:
  • 接着输入,cd+空格+项目文件下的release文件夹地址后回车,如:cd D:\C++\RayTracing\x64\Release同时还要注意是X86还是X64。
  • 之后输入raycast.exe > image.ppm,raycast.exe为我们的程序,使用>重定向将数据输出到文件名为image1.1的文件中,同时指定文件格式为ppm

最后依次回车之后我们就可以在release文件夹下看到一个image的图片文件,如果打不开,可以下载一个极速看图软件。

image-20220819094425458

一张非常绚丽的图,正如我们代码中的那样,从左到右越来越红,从下到上越来越绿。

加载进度

现在我们并不知道图片能够多久加载完成,当所需要的图片像素点非常多时,这个问题会更加明显,所以我们使用std::cerr来显式加载的进度,它主要用于显示错误消息,且不被缓冲,可以发送到显示器,并且不被重定向。

1
2
3
4
5
6
7
8
9
10
11
12
   for (int j = image_height-1; j >= 0; --j) {

// 提示还有多少行数据没有处理完。
std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush;

for (int i = 0; i < image_width; ++i) {
//.....
}
}

//提示已经完全搞定。
std::cerr << "\nDone.\n";

同时使用“\r”可以把光标强行移回本行开头,这样这次输出的内容就会覆盖掉这一行原本的内容,就好像每次到来的新东西会“冲洗”掉之前输出的东西。

注意这一行的结尾是std::flush,它表示再输出完这一行之后,会强行把内存中缓冲区内的数据打出到错误输出流里(清空缓冲区)。endl、ends和flush的区别

再次使用命令行就可以看到

image.png

参考文献

https://raytracing.github.io/books/RayTracingInOneWeekend.html

参考自《RayTracingInOneWeekend》第2节。