计算机图形学数学基础译自 ScratchaPixel 网站,数学基础部分共八篇,本篇为第一篇,感兴趣的同学可以参考我的 GitBook 镜像。
向量长度 Vector Length
向量可以被看作是一个箭头从一个点出发到另外一个点结束。向量不仅可以看作为从A点到B点的方向,还可以看作A点到B点的距离。下列是计算向量长度的公式:
在几何学中,复纵线(||V||)表示矢量V的长度。向量长度通常被称为 模(norm) 或 大小(magnitude)。
在C++中可以表示为:
template <typename T>
class Vec3 {
public:
// length can be a method from the class
T length() {
return sqrt(x * x + y * y + z * z);
}
}
标准化向量 Normalizing a Vector
一个 标准化的(normalised) 向量,是指一个向量的长度为1。这种向量也被称为 单位向量(unit vector),标准化向量非常简单,我们先计算出向量的长度,然后用向量在每个轴的坐标乘以这个长度。公式为:
需要注意在C++代码里除法是可以优化的,首先计算向量的长度len,如果大于0,然后计算出长度len的倒数invLen
,最后用向量在每个轴上的坐标乘以invLen
。这样做的原因是程序设计中乘法的开销要小于除法,尤其是在 渲染(renderer) 过程中,标准化向量极为常见,这个过程会有成百上千甚至百万个向量。在这种级别,任何可能的优化都会直接影响渲染的时间。(需要支持有的编译器会自动将乘法优化为除法)。
template <typename T>
class Vec3 {
public:
// as a method of the class Vec3
Vec3<T> & normalize() {
T len = length();
if (len > 0) {
T invLen = 1 / len;
x *= invLen;
y *= invLen;
z *= invLen;
}
return *this;
}
}
几何学中常定义 norm
函数来为向量的长度或距离赋值,这里定义的函数被称为 欧几里得范数(Euclidean norm).
点积 Dot Product
点积(dot product) 或 数积(scalar product),需要两个向量A和B,可以被看作一个向量在另一个向量上的 投影(projection)。点积的结果是一个实数(float或double类型)。A和B的点积被记为: (有时也会被记为<A,B>
)。
点积由A向量的每个元素乘以B向量每个元素并求和。在3D的场景下,向量有三个 系数(coefficients) 或 元素(elemment),即x,y和z。公式为:
需要指出的是这与我们计算向量长度的方法十分相似,如果我们取两个相等向量(A=B)的点积的平方根,
然后就可以得到向量的长度。可以写做:
这可以被用于标准化方法:
template<typename T>
class Vec3 {
public:
...
T dot(const Vec3<T> &v) const {
return x * v.x + y * v.y + z * v.z;
}
Vec3<T> & normalize() {
T len2 = dot(*this);
if (len2 > 0) {
T invLen = 1 / sqrt(len2);
x *= invLen;
y *= invLen;
z *= invLen;
}
return *this;
}
...
};
两个向量的点积在3D程序中非常常见,因为点积的结果是两个向量夹角的余弦值。
+ 如果B是单位向量,则可以得到。如果得到的点积结果为负,表示A和B的方向相反,这被称为A在B上的标投影。
+ 当A和B都不是单位向量时,可以写作,因为B作为一个单位向量是
+ 当两个向量都标准化后,通过计算点积的反余弦可以得到两个向量的夹角:
或者:
(在几何学中,表示cos的反函数,在计算机图形学中,这个函数通常记为acos()
).
点积在3D场景中用途非常广泛:
+ 测试向量的正交性。当两个向量(A,B)相互垂直时,A和B的点积为0,当两个向量(A,C)为反方向时,A和C的点积为-1,当两个向量(A,D)为相同方向时,A和D的点积为1.
+ 计算两向量间的夹角或向量和坐标轴之间的夹角(在坐标轴转化为球形坐标时非常有用)。
叉积 Cross Product
叉积同样是两个向量的间操作,与点积不同的是,叉积返回的是一个向量。由叉积产生的向量C垂直于产生叉积的向量A和B,如图:
叉积的表达为: C = A x B
,为了计算叉积需要如下公式:
叉积产生的向量是垂直于产生叉积的两个向量。向量A和B的叉积记为A x B
。两个向量A和B决定一个平面,叉积向量C垂直于这个平面。向量A和B不需要相互垂直,但是在A和B是单位向量时A,B,C可以形成笛卡尔坐标系(Cartesian Coordinate System).这个特性在创建本地坐标系时非常有用。
template<typename T>
class Vec3 {
public:
...
// as a method of the class
Vec3<T> cross(cosnt Vec3<T> &v) const {
return Vec3<T>(y * v.z - z * v.y,
z * v.x - x * v.z,
x * v.y - y * v.x);
}
...
};
// or a utility function
template<typename T>
Vec3<T> cross(const Vec3<T> &a, const Vec3<T> &b) {
return Vec3<T>(
a.y * b.z - a.z * b.y,
a.z * b.x - a.x * b.z,
a.x * b.y - a.y * b.x
);
}
需要指出的是,向量A和B的顺序会直接影响叉积的值,如A x B = (1,0,0) x (0,1,0) = (0,0,1)
而
B x A = (0,1,0) x (1,0,0) = (0,0,-1).
我们常说叉积是反交换率的(anticommutative),因为AxB=C而BxA=-C。
在几何学中,叉积的结果被称为 伪向量(pseudo vector),计算叉积的向量的顺序可以直接影响曲面法线(surface normals)的方向。依据这个顺序,叉积的结果可以是指向 曲面内部(inward-pointing normal) 或者 指向 外部(outward-pointing normal)。