You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

9.0 KiB

06 | 可视化中你必须要掌握的向量乘法知识

你好,我是月影。

上一节课我们学习了Canvas实现坐标系转换的方法以及利用向量描述点和线段来绘制基本图形的方法。接下来我们继续学习和向量有关的绘图知识教你用数学语言描述曲线。

不过,在讨论如何描述曲线之前,还有一些关于向量乘法的前置知识需要你掌握。虽然它们和如何描述曲线并没有直接的关系,但是使用它们可以计算曲线的控制点、判断曲线的方向以及对曲线进行变换。

因此向量的乘法在可视化中也是非常重要并且实用的内容。比如上节课的思考题3给你一个任意点让你判断这个点在不在这个扫描器范围内。这道题可以用我们前面学过的知识解决但用向量乘法可以更轻松解决。接下来就让我们从这一道题开始今天的学习吧

如果利用我们前面学过的知识来解题,你可能是直接使用向量的方向定义来做的,代码如下所示。

v.dir = function() {return Math.atan2(this.y, this.x)}

没错这道题我们可以使用向量的方向来解。因为这里的dir是由向量与x轴夹角决定的所以判断点是否在扫描器范围内我们只需要计算点坐标对应的向量的dir值是否在扫描器的范围内就可以了。代码如下

const isInRange = v0.dir > Math.PI / 3 && v0.dir < 2 * Math.PI / 3;

这是一个很简单、直观的解法但是它不够完美因为这个判断和扫描器的方向有关。什么意思呢从上面的图中你可以看到现在它正对着y轴正方向所以角度在π/3和2π/3之间。但如果将它的方向旋转或者允许它朝向任意的方向我们就必须要修改对应的角度值了。这个时候就会非常麻烦。

因此,我们会使用一个更通用的解法,也就是利用向量的乘法来解。那具体怎么做呢?别着急,我先带你来复习一下我们高中学过的向量乘法的知识,如果你记得不是特别清楚,正好可以借着这个机会来加深印象。

我们知道,向量乘法有两种,一种是点乘,一种是叉乘,它们有着不同的几何和物理含义。下面,我们一一来看。

向量的点乘

首先,我们来看向量的点乘。

假设现在有两个N维向量a和ba = [a1, a2, ...an]b = [b1, b2, ...bn],那向量的点积代码如下:

a•b = a1*b1 + a2*b2 + ... + an*bn

在N维线性空间中a、b向量点积的几何含义是a向量乘以b向量在a向量上的投影分量。它的物理含义相当于a力作用于物体产生b位移所做的功。点积公式如下图所示

好了现在你已经知道a、b向量点积的定义了。那关于还有两个比较特殊的情况你需要掌握。第一种是当a、b两个向量平行时它们的夹角就是0°那么a·b=|a|*|b|用JavaScript代码表示就是

a.x * b.x + a.y * b.y === a.length * b.length

第二种是当a、b两个向量垂直时它们的夹角就是90°那么a·b=0用JavaScript代码表示就是

a.x * b.x + a.y * b.y === 0;

向量的叉乘

叉乘和点乘有两点不同:首先,向量叉乘运算的结果不是标量,而是一个向量;其次,两个向量的叉积与两个向量组成的坐标平面垂直。怎么理解呢?我们接着往下看。

以二维空间为例向量a和b的叉积就相当于向量a蓝色带箭头线段与向量b沿垂直方向的投影红色带箭头线段的乘积。那如下图所示二维向量叉积的几何意义就是向量a、b组成的平行四边形的面积

那叉乘在数学上该怎么计算呢假设现在有两个三维向量a(x1, y1, z1)和b(x2, y2, z2)那么a与b的叉积可以表示为一个如下图的行列式

其中i、j、k分别是x、y、z轴的单位向量。我们把这个行列式展开就能得到如下公式

a X b = [y1 * z2 - y2 * z1, - (x1 * z2 - x2 * z1), x1 * y2 - x2 * y1]

我们计算这个公式得到的值还是一个三维向量它的方向垂直于a、b所在平面。因此我们刚才说的二维空间中向量a、b的叉积方向就是垂直纸面朝向我们的。那有什么办法可以很容易就确定出a、b的叉积方向呢

还记得吗第2节课我们提到过左手系和右手系其中x轴向右、y轴向下的坐标系是右手系。在右手系中求向量a、b叉积的方向时我们可以把右手食指的方向朝向a把右手中指的方向朝向b那么大拇指所指的方向就是a、b叉积的方向这个方向是垂直纸面向外即朝向我们。因此右手系中向量叉乘的方向就是右手拇指的方向那左手系中向量叉乘的方向自然就是左手拇指的方向了。

在二维空间里由于z的值为0因此我们得到的向量a X b的数值就等于x1 * y2 - x2 * y1。那它的物理意义是什么呢二维空间中向量叉乘的物理意义就是a和b的力矩力矩你可以理解为一个物体在力的作用下绕着一个轴转动的趋向。它是一个向量等于力臂L和力F的叉乘。这个概念你记住就好了我们今天不会用到后面用到的时候我会再详细来讲

还记得上一节课的思考题2求点到线段的距离吗在了解了向量叉积的几何意义之后我们通过向量叉积得到平行四边形面积再除以底边长就能得到点到向量所在直线的距离了。是不是很简单

同样向量叉积也可以解决思考题3。那具体该怎么做呢

首先对于任意一点v0来说我们都要先将它归一化。简单来说归一化就是让向量v0除以它的长度或者说是模。归一化后的向量方向不变长度为1。归一化是向量运算中一个非常重要的操作用处也非常多。比如说在向量乘法里如果a、b都是长度为1的归一化向量那么|a X b| 的结果就是a、b夹角的正弦值,而|a • b|的结果就是a、b夹角的**余弦值。**这个特性在图形学里用处非常大,你一定要记住它。

好了再说回来我们把归一化的向量a叉乘扫描器中线上的v(0,1)由于扫描器关于y轴对称所以扫描器边缘与y轴的夹角是正负30度。那么在与单位向量求叉积的时候就会出现2种情况

  1. 点在扫描范围内如向量a就一定满足 |a X v| <= ||a||v|sin(30°)| = |sin(30°)| = 0.5
  2. 点不在扫描范围内如向量b就一定满足|b X v| > ||b||v|sin(30°)| = |sin(30°)| = 0.5。

因此只要任意一点所在的向量与单位向量的叉积结果的绝对值不大于0.5即sin30°就说明这个点在扫描范围内。所以我们可以用如下代码来判断

const isInRange = Math.abs(new Vec2(0, 1).cross(v0.normalize())) <= 0.5; // v0.normalize()即将v0归一化

好了,关于向量乘法的内容,我们就全部复习完了,相信你已经很好地掌握它们了。不过,我还想再多说几句 ,对于图形学来说,向量运算是基础中的基础,非常重要。所以,我们不仅要熟练掌握,还要学会用向量的思路去解决问题。只有这样,你才能学好图形学,从而成为优秀的可视化工程师。

要点总结

这一节课,我们学习了向量的乘法,包括点乘与叉乘。

其中点乘的几何意义是向量a与它在向量b所在的轴的投影向量的乘积物理意义是力向量产生的位移向量所做的功。叉乘的几何意义是向量a和向量b构成的平行四边形的面积物理意义是力产生的力矩。

最后,我们还要记住,把向量归一化以后,我们就可以通过向量的点乘与叉乘快速求出向量夹角的正弦和余弦值。

小试牛刀

让我们延续上一节课的第2道思考题。已知平面上有一点P它的坐标是(x0, y0)还有一条直线直线上有两个点Q(x1, y1)和R(x2, y2)。你能求出点P到直线的距离以及点P到线段QR的距离吗这里面P、Q、R这三个值可以是任意的。你可以要试着用一段JavaScript代码把计算的过程写出来最好还能把直线、点和距离在Canvas上给直观地绘制出来。

欢迎在留言区和我讨论,分享你的答案和思考,也欢迎你把这节课分享给你的朋友,我们下节课见!