[toc]
# Vector3
通过重载运算符实现下标访问器
1 2 3 4 5 6 7 8 9 10 11 12 T operator [](int i) const { DCHECK (i >= 0 && i <= 2 ); if (i == 0 ) return x; if (i == 1 ) return y; return z; } T &operator [](int i) { DCHECK (i >= 0 && i <= 2 ); if (i == 0 ) return x; if (i == 1 ) return y; return z; }
通过重载名称实现方便的类型系统
1 2 3 4 typedef Vector2<Float> Vector2f;typedef Vector2<int > Vector2i;typedef Vector3<Float> Vector3f;typedef Vector3<int > Vector3i;
通过宏控制 Debug 信息,以及编译期检查的代码裁剪
1 Vector3 (T x, T y, T z) : x (x), y (y), z (z) { DCHECK (!HasNaNs ()); }
向量加法与向量减法等
重载运算符实现 (注意,+= 方法等需要添加引用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Vector3<T> operator +(const Vector3<T> &v) const { DCHECK (!v.HasNaNs ()); return Vector3 (x + v.x, y + v.y, z + v.z); } Vector3<T> &operator +=(const Vector3<T> &v) { DCHECK (!v.HasNaNs ()); x += v.x; y += v.y; z += v.z; return *this ; } Vector3<T> operator -(const Vector3<T> &v) const { DCHECK (!v.HasNaNs ()); return Vector3 (x - v.x, y - v.y, z - v.z); } Vector3<T> &operator -=(const Vector3<T> &v) { DCHECK (!v.HasNaNs ()); x -= v.x; y -= v.y; z -= v.z; return *this ; }
使用除法的倒数优化 CPU 上除法与乘法的性能差异
可以看出,下述代码实际上将三次除法改成了三次乘法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 template <typename U>Vector3<T> operator /(U f) const { CHECK_NE (f, 0 ); Float inv = (Float)1 / f; return Vector3 <T>(x * inv, y * inv, z * inv); } template <typename U>Vector3<T> &operator /=(U f) { CHECK_NE (f, 0 ); Float inv = (Float)1 / f; x *= inv; y *= inv; z *= inv; return *this ; }
点积与叉积实现
叉积需要在计算前转换成 double 来抵消误差
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 template <typename T>inline T Dot (const Vector3<T> &v1, const Vector3<T> &v2) { DCHECK (!v1.HasNaNs () && !v2.HasNaNs ()); return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; } template <typename T>inline T AbsDot (const Vector3<T> &v1, const Vector3<T> &v2) { DCHECK (!v1.HasNaNs () && !v2.HasNaNs ()); return std::abs (Dot (v1, v2)); } template <typename T>inline Vector3<T> Cross (const Vector3<T> &v1, const Vector3<T> &v2) { DCHECK (!v1.HasNaNs () && !v2.HasNaNs ()); double v1x = v1.x, v1y = v1.y, v1z = v1.z; double v2x = v2.x, v2y = v2.y, v2z = v2.z; return Vector3 <T>((v1y * v2z) - (v1z * v2y), (v1z * v2x) - (v1x * v2z), (v1x * v2y) - (v1y * v2x)); }
叉积特性:
1. 单位叉积的模等于 sinθ 的值
2. 叉积的模等于以两边为边的平行四边形的面积
3. 单位叉积的模 -> sinθ 等于平行四边形的面积 (单位下)
给单个向量规定坐标系
类似于广告牌的做法,确定出一个垂直于当前向量的分量,然后叉积得到第三个分量
# Ray
射线在 pbrt 中的存储方式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Ray { public : Ray () : tMax (Infinity), time (0.f ), medium (nullptr ) {} Ray (const Point3f &o, const Vector3f &d, Float tMax = Infinity, Float time = 0.f , const Medium *medium = nullptr ) : o (o), d (d), tMax (tMax), time (time), medium (medium) {} Point3f operator () (Float t) const { return o + d * t; } bool HasNaNs () const { return (o.HasNaNs () d.HasNaNs () isNaN (tMax)); } friend std::ostream &operator <<(std::ostream &os, const Ray &r) { os << "[o=" << r.o << ", d=" << r.d << ", tMax=" << r.tMax << ", time=" << r.time << "]" ; return os; } Point3f o; Vector3f d; mutable Float tMax; Float time; const Medium *medium; };
即 射线是以参数形式进行存储的 注意一点,接口中有一个 tMax
变量被限制为 mutable, 这个参数的主要作用是用来将射线变成一个线段,这种表示形式的一个好处是 可以认为我们默认在一个射线变量中,它的起点和方向是不可改变的
另外, medium
代表的是射线端点的介质,