RT基础知识:向量(Vecor)

| 分类 Graphics  | 标签 RayTracing  基础知识 

在图形学领域里,向量是一个非常重要的概念,它是构建三维世界的数学基石。向量可以用来表示空间中的点,比如场景中物体的位置。它也可以表示方向,比如摄像机的朝向。在这里,我们仅看其在光线追踪里的作用,包括光线的方向、相交点位置和方向、反射模型等等。

从数学上看,向量中的元素可以是无穷的,但一般在图形应用中,三维向量比较常用,根据需要也可以自由扩展(DOOM3里面有 idVec2——idVec6idVecX 六个向量类)。

向量可表示为有向线段,由长度与方向定义。对于3D向量,可采用3个浮点数分别表示其在笛卡尔坐标系中x,y,z轴的投影。

接下来我们实现一个3D向量结构(类)。


##属性/成员

首先是向量的属性(成分/元素/分量),我们的 Vector3 如下:

typedef struct {
	double x, y, z;
}Vector3;

当然,为了方便快捷地访问其中的元素,也可以这样定义:

typedef struct {
	double e[3];
}Vector3;

这里使用的是 double 而不是 float,只是因为光线追踪需要更高的精度。如果硬件不支持 double,那使用 float 也是可以的。再或者,也可以用 #ifdef 条件编译指令来进行选择。

如果喜欢C++的类,可以这样写:

class Vector3 {
	double x;
	double y;
	double z;
	
	// or 
	// double e[3];	
};

##方法/函数

向量类支撑着图形世界的架构,会以工具函数的形式穿插在各种计算里。向量计算范围广泛,但其基本计算类型不多,主要就是 加、减、乘(常数)、除(常数)、点乘、叉乘、规范化(normalization,就是单位向量化)

由于向量计算的调用非常频繁,一般都写成 或者 内联函数的形式。

// 宏的形式
#define vinit(v, a, b, c) { (v).x = a; (v).y = b; (v).z = c; } //初始化
#define vadd(v, a, b) vinit(v, (a).x+(b).x, (a).y+(b).y, (a).z+(b).z) //加
#define vsub(v, a, b) vinit(v, (a).x-(b).x, (a).y-(b).y, (a).z-(b).z) //减
#define vmul(v, a, b) { double k=(a); vinit(v, k*(b).x, k*(b).y, k*(b).z) } //乘(常数)
#define vdot(a, b) ((a).x*(b).x + (a).y*(b).y + (a).z*(b).z) //点乘
#define vcross(v a, b) vinit(v, (a).y*(b).z-(a).z*(b).y, (a).z*(b).x-(a).x*(b).z, (a).x*(b).y-(a).y*(b).x) //叉乘
#define vnorml(v) { double l = 1.0 / sqrt(vdot(v, v)); vmul(v, l, v); } // normalization

宏定义的形式除非必不得已,还是少用为好。可读性与安全性均不能绝对保证。如果使用C++的话,我们可以进行操作符重载,这样其可读性大大增强。

// 操作符重载和内联形式
Vector3(double x_=0, double y_=0, double z_=0){ x=x_; y=y_; z=z_; } // 构造函数
Vector3 operator+(const Vector3 &b) const { return Vector3(x+b.x,y+b.y,z+b.z); } // 加
Vector3 operator-(const Vector3 &b) const { return Vector3(x-b.x,y-b.y,z-b.z); } // 减
Vector3 operator*(double b) const { return Vector3(x*b,y*b,z*b); } // 乘
double dot(const Vector3 &b) const { return x*b.x+y*b.y+z*b.z; } //点乘
Vector3 operator%(Vector3&b){return Vector3(y*b.z-z*b.y,z*b.x-x*b.z,x*b.y-y*b.x);} // 叉乘
Vector3& norml(){ return *this = *this * (1/sqrt(x*x+y*y+z*z)); } // normalization

##总结

基本的向量构造就完成了,这些也足够一个简单的光线追踪器使用了。如果未来需要扩展更多更方便的函数,直接添加在后面即可。下面是两种完整的 Vector3

// 第一种
typedef struct {
	double x, y, z; 
} Vector3;

#define vinit(v, a, b, c) { (v).x = a; (v).y = b; (v).z = c; }
#define vadd(v, a, b) vinit(v, (a).x + (b).x, (a).y + (b).y, (a).z + (b).z)
#define vsub(v, a, b) vinit(v, (a).x - (b).x, (a).y - (b).y, (a).z - (b).z)
#define vmul(v, a, b) { double k = (a); vinit(v, k * (b).x, k * (b).y, k * (b).z) }
#define vdot(a, b) ((a).x * (b).x + (a).y * (b).y + (a).z * (b).z)
#define vnorml(v) { double l = 1.0 / sqrt(vdot(v, v)); vmul(v, l, v); }
#define vcross(v, a, b) vinit(v, (a).y * (b).z - (a).z * (b).y, (a).z * (b).x - (a).x * (b).z, (a).x * (b).y - (a).y * (b).x)

// 第二种
class Vector3 {        
  double x, y, z;                  
  Vector3(double x_=0, double y_=0, double z_=0){ x=x_; y=y_; z=z_; }
  Vector3 operator+(const Vector3 &b) const { return Vector3(x+b.x,y+b.y,z+b.z); }
  Vector3 operator-(const Vector3 &b) const { return Vector3(x-b.x,y-b.y,z-b.z); }
  Vector3 operator*(double b) const { return Vector3(x*b,y*b,z*b); }
  Vector3& norml(){ return *this = *this * (1/sqrt(x*x+y*y+z*z)); }
  double dot(const Vector3 &b) const { return x*b.x+y*b.y+z*b.z; } 
  Vector3 operator%(Vector3 &b){return Vector3(y*b.z-z*b.y,z*b.x-x*b.z,x*b.y-y*b.x);}
};

Previous     Next