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;
}
如果有博主错误或者没有理解到的地方,大家多提意见吧!
( 昨天称了一下发现又长肉了,来个兔子嘲笑我一下 Ծ‸ Ծ )