既然是光追,肯定离不开点和方向于颜色,在大多数工程中,颜色和方向是四维的向量,但是对于这个工程,三位向量可以满足需求,我们定义一个类来表示它们:
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| #ifndef VEC3_H #define VEC3_H
#include <cmath>
#include <iostream>
using std::sqrt;
class vec3 { public: vec3() : e{0,0,0} {} vec3(double e0, double e1, double e2) : e{e0, e1, e2} {} double x() const { return e[0]; } double y() const { return e[1]; } double z() const { return e[2]; }
vec3 operator-() const { return vec3(-e[0], -e[1], -e[2]); } double operator[](int i) const { return e[i]; } double& operator[](int i) { return e[i]; }
vec3& operator+=(const vec3 &v) { e[0] += v.e[0]; e[1] += v.e[1]; e[2] += v.e[2]; return *this; } vec3& operator*=(const double t) { e[0] *= t; e[1] *= t; e[2] *= t; return *this; }
vec3& operator/=(const double t) { return *this *= 1/t; } double length_squared() const { return e[0]*e[0] + e[1]*e[1] + e[2]*e[2]; } double length() const { return sqrt(length_squared()); }
public: double e[3]; };
using point3 = vec3; using color = vec3;
#endif
|
类外补充
接下来是一些加减乘除等操作,其中的有些操作并不可以写在类内,比如double类型*
vec3类型,所以统一写在类外:
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
inline std::ostream& operator<<(std::ostream &out, const vec3 &v) { return out << v.e[0] << ' ' << v.e[1] << ' ' << v.e[2]; }
inline vec3 operator+(const vec3 &u, const vec3 &v) { return vec3(u.e[0] + v.e[0], u.e[1] + v.e[1], u.e[2] + v.e[2]); }
inline vec3 operator-(const vec3 &u, const vec3 &v) { return vec3(u.e[0] - v.e[0], u.e[1] - v.e[1], u.e[2] - v.e[2]); }
inline vec3 operator*(const vec3 &u, const vec3 &v) { return vec3(u.e[0] * v.e[0], u.e[1] * v.e[1], u.e[2] * v.e[2]); }
inline vec3 operator*(double t, const vec3 &v) { return vec3(t*v.e[0], t*v.e[1], t*v.e[2]); }
inline vec3 operator*(const vec3 &v, double t) { return t * v; }
inline vec3 operator/(vec3 v, double t) { return (1/t) * v; }
inline double dot(const vec3 &u, const vec3 &v) { return u.e[0] * v.e[0] + u.e[1] * v.e[1] + u.e[2] * v.e[2]; }
inline vec3 cross(const vec3 &u, const vec3 &v) { return vec3(u.e[1] * v.e[2] - u.e[2] * v.e[1], u.e[2] * v.e[0] - u.e[0] * v.e[2], u.e[0] * v.e[1] - u.e[1] * v.e[0]); }
inline vec3 unit_vector(vec3 v) { return v / v.length(); }
|
抽离与简化
在第一章时,有三行将0-1的通道映射到0-255的代码,这部分代码是固定的,可以统一将它们抽离出去,创建、color.h
文件,以后的关于渲染颜色的代码统一放到这里:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #ifndef COLOR_H #define COLOR_H
#include "vec3.h"
#include <iostream>
void write_color(std::ostream &out, color pixel_color) { out << static_cast<int>(255.999 * pixel_color.x()) << ' ' << static_cast<int>(255.999 * pixel_color.y()) << ' ' << static_cast<int>(255.999 * pixel_color.z()) << '\n'; }
#endif
|
接下来main函数的代码将相当简洁:
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
| #include "color.h" #include "vec3.h"
#include <iostream>
int main() { const int image_width = 256; const int image_height = 256;
std::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";
for (int j = image_height-1; j >= 0; --j) { std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush; for (int i = 0; i < image_width; ++i) { color pixel_color(double(i)/(image_width-1), double(j)/(image_height-1), 0.25); write_color(std::cout, pixel_color); } }
std::cerr << "\nDone.\n"; }
|
参考文献
https://raytracing.github.io/books/RayTracingInOneWeekend.html
参考自《RayTracingInOneWeekend》第3节