Irrlicht 源码学习笔记 【vector3d】


vector3d 模版表示一个 3D 向量

3D 点和 3D 向量是3D图形学里最基础的东西

里面有一些方法是需要一点 3D 图形学基础的,但是也非常简单喽


【运算符重载】

首先是一些运算符的重载

注意一点的是加法和减法运算符实现的是对向量的平移

乘法和除法运算符分别是对当前每一个分量乘以和除以 other 的每一个分量

而判断大于等于的条件是要向量每一个分量都大于 other

vector3d& operator=(const vector3d& other)	{ X = other.X; Y = other.Y; Z = other.Z; return *this; }

vector3d operator+(const vector3d& other) const { return vector3d(X + other.X, Y + other.Y, Z + other.Z);	}
vector3d& operator+=(const vector3d& other)	{ X+=other.X; Y+=other.Y; Z+=other.Z; return *this; }

vector3d operator-(const vector3d& other) const { return vector3d(X - other.X, Y - other.Y, Z - other.Z);	}
vector3d& operator-=(const vector3d& other)	{ X-=other.X; Y-=other.Y; Z-=other.Z; return *this; }

vector3d operator*(const vector3d& other) const { return vector3d(X * other.X, Y * other.Y, Z * other.Z);	}
vector3d& operator*=(const vector3d& other)	{ X*=other.X; Y*=other.Y; Z*=other.Z; return *this; }
vector3d operator*(const T v) const { return vector3d(X * v, Y * v, Z * v);	}
vector3d& operator*=(const T v) { X*=v; Y*=v; Z*=v; return *this; }

vector3d operator/(const vector3d& other) const { return vector3d(X / other.X, Y / other.Y, Z / other.Z);	}
vector3d& operator/=(const vector3d& other)	{ X/=other.X; Y/=other.Y; Z/=other.Z; return *this; }
vector3d operator/(const T v) const { T i=(T)1.0/v; return vector3d(X * i, Y * i, Z * i);	}
vector3d& operator/=(const T v) { T i=(T)1.0/v; X*=i; Y*=i; Z*=i; return *this; }

bool operator<=(const vector3d&other) const { return X<=other.X && Y<=other.Y && Z<=other.Z;};
bool operator>=(const vector3d&other) const { return X>=other.X && Y>=other.Y && Z>=other.Z;};

bool operator==(const vector3d& other) const { return other.X==X && other.Y==Y && other.Z==Z; }
bool operator!=(const vector3d& other) const { return other.X!=X || other.Y!=Y || other.Z!=Z; }


【 向量模长 】

获取向量长度即是 sqrt(x*x + y*y + z*z)  , 和 2D 向量比只是多了一个分量



【 3D向量点乘 】

3D 向量点乘即是三个分量分别乘积的加和,可以用来求两个向量的夹角余弦 cosine

T dotProduct(const vector3d& other) const
{
	return X*other.X + Y*other.Y + Z*other.Z;
}


【 3D向量叉乘 】

3D 向量叉乘结果是相乘的两个向量所在平面的法向量

在 Irrlicht 的 vector3d 中是使用 crossProduct() 方法实现来当前 3D向量 与 另外一个 3D向量 的叉积

如果学习过线性代数的话会比较容易去使用一点

博主简略的讲一下叉乘的运算方法,如果希望更一步了解的话,可以去找度娘约一下

/////////////////////////////////////// "学霸" buff 开始 ///////////////////////////////////////

假设当前两个向量分别为  向量 a = (xa, ya, za)      向量 b = (xb, yb, zb) 

计算叉乘  a * b 则先构造一个矩阵

|   i      j      k   |

| xa   ya   za  |

| xb   yb   zb  |

以计算 x 分量为例, 将 i 所在行和列忽略掉,就可以得到一个 二维 矩阵

| ya    za |

| yb    zb |

对这个 二阶行列式 求解, 得到 x = ya * zb - za * yb

计算 y 和 z 两个分量就是分别忽略 j 和 k 所在行,然后分别对得到的二阶行列式进行求解

最后计算得到结果即为 向量a 和 向量b 所构成平面的法向量

/////////////////////////////////////// "学霸" buff 结束 ///////////////////////////////////////

vector3d crossProduct(const vector3d& p) const
{
	return vector3d(Y * p.Z - Z * p.Y, Z * p.X - X * p.Z, X * p.Y - Y * p.X);
}



【 判断是否在两点之间 】

isBetweenPoints() 判断当前点是否在 start 和 end 之间

但是这里的实现只是判断当前点到 start 的距离是否小于 end 到 start 的距离

这个实现只有当 当前点 在 start->end 之内的时候才是正确的,可能在后面版本会有更新

至于为什么没直接调用 getLength() 大概因为 getLength() 中有开平方的操作,而这个耗时的操在这里不需要

bool isBetweenPoints(const vector3d& begin, const vector3d& end) const
{
	// this is very slow, i'll have to write a faster one later. (太慢,晚一点我必须写一个更快的版本)

	vector3df lv = end - begin;
	vector3df pv = *this - begin;

	T l1 = lv.X*lv.X + lv.Y*lv.Y + lv.Z*lv.Z;
	T l2 = pv.X*pv.X + pv.Y*pv.Y + pv.Z*pv.Z;

	return (l2 < l1);
}



【 围绕某一个轴旋转一定角度 】

与 2D向量 不同, 2D向量 直接围绕某一个点进行旋转就可以了

而 3D向量则需要围绕某一个轴进行旋转

 vector3d 中使用 rotateXZBy() 、 rotateXYBy() 以及 rotateYZBy() 实现分别围绕 y轴 、 z轴 和 x轴 旋转

实现的方法是大同小异的,所以这里只以围绕 x 轴 旋转为例  ——   rotateYZBy()

///////////////////////////////////////// "学霸" buff 开始 /////////////////////////////////////////

对 3D向量 围绕 z轴 旋转过程中只修改 x坐标 和 y坐标,这就和 2D向量旋转一样了

这里分享一下 3D点 旋转使用线性代数中矩阵的表示方法

假设当前有一个点 (x, y, z) , 旋转角度为 α 

围绕 x 轴旋转的旋转矩阵为  

|  1       0         0        |

|  0   cosα   sinα     |

|  0   -sinα   cosα    |


围绕 y 轴旋转的旋转矩阵为  

|  cosα     0     sinα   |

|     0         1        0      |

|  -sinα     0    cosα  |


围绕 z 轴旋转的旋转矩阵为  

| cosα     sinα     0  |

| -sinα     cosα    0  |

|      0          0        1  |


这样的矩阵可以做到围绕某个轴旋转该轴的坐标是不变的,而只对另外两个向量进行计算

( 里面的规律也很好记,博主从来更愿意记规律,不愿意记原理 )


以围绕 x 轴旋转为例,将 vector3d 看作一个 1×3 的矩阵

矩阵之间相乘结果为   (  x, y*cosα - z*sinα, y*sinα + z*cosα  )

和 vector3d 中 rotateYZBy() 的运算结果是相同的哦

///////////////////////////////////////// "学霸" buff 结束 /////////////////////////////////////////

void rotateYZBy(f64 degrees, const vector3d& center)
{
	degrees *= GRAD_PI2;
	T cs = (T)cos(degrees);
	T sn = (T)sin(degrees);
	Z -= center.Z;
	Y -= center.Y;
	set(X, Y*cs - Z*sn, Y*sn + Z*cs);
	Z += center.Z;
	Y += center.Y;
}



如果有博主错误或者没有理解到的地方,大家多提意见吧!



                       ( 昨天称了一下发现又长肉了,来个兔子嘲笑我一下   Ծ‸ Ծ   )

               Irrlicht 源码学习笔记 【vector3d】_第1张图片











你可能感兴趣的:(Irrlicht)