随笔-Unity中Vector3的点乘、叉乘、投影等的几何意义及应用

1.Vector3的几何意义:

        Vector3有x,y,z三个变量,我们在Unity最常见用它来表示坐标数据,但是它同时也可以代表距离、速度、位移、加速度以及方向。至于它在我们使用过程具体代表什么,完全基于我们程序员自己为它赋予了什么意义,也就是取决于我们用它计算的过程。这么说可能是有点抽象,我们来具体举例说明一下。

        (1)设两个 Vector3 的坐标点分别为 v1 和 v2,当我们用 v1 - v2 时,就可以得到一个从 v2 点到 v1 点的向量 v3。那么这个向量 v3 我们就可以认为是一个从 v1 点到 v2 点的长度且拥有 v2 点到 v1 点方向的一个向量,但是他的类型依旧是 Vector3。

    private void Sample()
    {
        // 此处的v1和v2的计算意义是两个坐标点
        Vector3 v1 = new Vector3(1, 1, 1);
        Vector3 v2 = new Vector3(2, 2, 2);

        // 此时的v3的计算意义就是v2到v1点的一个方向向量
        Vector3 v3 = v1 - v2;
    }

        (2)还是设两个 Vector3 的坐标点分别为 v1 和 v2,当我们用 v1 - v2 之后再除以一个常量 1 ,那么我们就得到了一个具有方向的速度 v3 。因为我们可以认为这个常量 1 ,是一个时间单位即为1秒,此时的 v3就具有速度矢量的几何意义。因为 距离/时间 = 速度。

    private void Sample()
    {
        // 此处的v1和v2的计算意义是两个坐标点
        Vector3 v1 = new Vector3(1, 1, 1);
        Vector3 v2 = new Vector3(2, 2, 2);

        // 此时的v3的意义就是v2到v1点的速度矢量
        Vector3 v3 = (v1 - v2) / 1;
    }

         (3)再比如我定义了四个 Vector3 的坐标点位 v1、v2、v3、v4。我用 v1 - v2 后再除以1,得到 v2 点到 v1 点的速度,在用 v3 - v4 后除以1,得到 v4 点到 v3点的速度。此时我将两个速度相减再除以1,就得到了两个速度的加速度 va。因为加速的的公式为:a = (v1-v2)/t。而此时va 依旧是 Vector3 类型。

    private void Sample()
    {
        // 此处的 v1、v2、v3、v4 的计算意义是坐标点
        Vector3 v1 = new Vector3(1, 1, 1);
        Vector3 v2 = new Vector3(2, 2, 2);
        Vector3 v3 = new Vector3(3, 3, 3);
        Vector3 v4 = new Vector3(4, 4, 4);

        // 此时的v3的意义就是v2到v1点的速度矢量
        Vector3 va = ((v1 - v2) / 1 - (v3 - v4) / 1) / 1;
        // 当然我们也以简化一下公式变成:
        // Vector3 va = v1 - v2;
    }

        此时我们就发现了,上述三个例子最后得出来的变量类型都是 Vector3 ,但是他们的意义完全不同。所以赋予 Vector3 什么样的意义,就是取决于我们用它的计算的过程。

 2.点乘的几何意义

         首先我们先看一下Unity中的点乘函数:Vector3.Dot。先贴出来函数接口的源码:

    // 此为源码
    public static float Dot(Vector3 lhs, Vector3 rhs)
    {
        return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z;
    }

         由此函数我们就可以看出他的计算公式以及函数的返回类型是一个 float 类型。这与我们向量的点乘公式是一样的。

        向量 a 与向量 b 的点乘公式如下:

\large a\cdot b=\left ( x1 ,y1,z1\right )\cdot \left ( x2,y2,z2\right )= x1\times x2+y1\times y2+z1\times z2

 \large a\cdot b= \left \| a \right \|\times \left \| b \right \|\times \cos \theta

         用一张图来说明更加的直观:

         a 向量与 b 向量的夹角为 θ,在向量 a 与向量 b 的长度不变的情况下,计算出来的值越大,cosθ 的值也就越大,而 θ 角也就越小,所以得出来 ab 的夹角也就越小。当 θ 大于 90° 时,计算出来的是一个负数,这就说明此时 b 指向的方向与 a 指向的方向相反。

        点乘除了可以判断两个向量的方向外,还可以计算两个向量的夹角也就是 θ 的角度,公式如下:

\large \theta =\arccos (\left ( a\cdot b \right )/\left ( \left | a \right | \times \left | b \right |\right ))

         现在我们用程序来表达这个公式,其实我们已经知道点乘的函数为 Vector3.Dot,同样我们也有获得向量的矢量距离值的的办法,即为 Vector3.Magnitude。代码如下:

    private void Sample()
    {
        Vector3 a = new Vector3(1, 1, 1);
        Vector3 b = new Vector3(2, 2, 2);

        float x = Mathf.Acos(Vector3.Dot(a, b) / a.magnitude * b.magnitude);
    }

    // 点乘源码
    public static float Dot(Vector3 lhs, Vector3 rhs)
    {
        return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z;
    }

    // 矢量距离值源码
    public static float Magnitude(Vector3 vector)
    {
        return (float)Math.Sqrt(vector.x * vector.x + vector.y * vector.y + vector.z * vector.z);
    }

3.叉乘的几何意义

         与点乘相同,叉乘在Unity中也给我们提供了用于计算的函数 Vector3.Cross。同样先贴出来函数的源码:

    public static Vector3 Cross(Vector3 lhs, Vector3 rhs)
    {
        return new Vector3(lhs.y * rhs.z - lhs.z * rhs.y, lhs.z * rhs.x - lhs.x * rhs.z, lhs.x * rhs.y - lhs.y * rhs.x);
    }

        首先我们发现点乘与叉乘的不同点,叉乘函数的返回值是一个 Vector3 类型。即,两个向量 ab 的叉乘得出的向量 c 是一条垂直于向量 a 与向量 b 所形成的平面相垂直的一条向量。我们来看看叉乘的公式:

\large a\times b=(a1,a2,a3)\times (b1,b2,b3))=(a2\times b3-a3\times b3,a3\times b1-a1\times b3,a1\times b2-a2\times b1))

        由公式可以看出与函数内的计算方法是一致的。那么叉乘可以帮助我们做哪些计算或者求哪些数据呢。

        首先我们看一条叉乘相关的公式:

\large \left | c \right |=\left | a\times b \right |=\left | a \right |\times \left | b \right |\times \sin \beta

        可以看出 c 的长度等于 a x b 的长度等于向量的大小与向量的夹角的 sin 值的积。也就是说 c 的长度与 ab 向量的夹角大小相关。如此,就可以得出,当 c 的长度为 0 时,a b 是两条相互平行的矢量,当 a b 的叉乘的模等于 a 的模乘以 b 的模时,a b 是两个互相垂直的向量。

        第二,因为叉乘满足我们的右手法则,所以我们可以通过叉乘来判断一个物体是在另一个体的左侧还是右侧。上代码举例:

    private void Sample()
    {
        Vector3 a = new Vector3(1, 1, 1);
        Vector3 b = new Vector3(2, 2, 2);

        Vector3 c = Vector3.Cross(a, b);
        if (c.y > 0)
        {
            // b 在 a 的右边
        }
        else if (c.y < 0)
        {
            // b 在 a 的左边
        }
        else
        { 
            // b 与 a 的方向相同
        }
    }

4.向量之间的投影

        在Unity中计算投影是我们经常使用的一种计算方式。尤其在计算摄像机方向、点击方向、物体移动方向这几个变量同时出现时。

        首先我们Unity里给我们提供了两个关于计算投影的方法:Vector3.Project 和 Vector3.ProjectOnPlane。他们分别是求一条向量在另一条向量上的投影,和一条向量在一个平面上的投影。我们先来看看源码:

    // 求向量在另一条向量上的投影
    public static Vector3 Project(Vector3 vector, Vector3 onNormal)
    {
        float num = Dot(onNormal, onNormal);
        if (num < Mathf.Epsilon)
        {
            return zero;
        }

        float num2 = Dot(vector, onNormal);
        return new Vector3(onNormal.x * num2 / num, onNormal.y * num2 / num, onNormal.z * num2 / num);
    }

    // 求向量在一个平面上的投影,其中planeNormal是该平面的法线
    public static Vector3 ProjectOnPlane(Vector3 vector, Vector3 planeNormal)
    {
        float num = Dot(planeNormal, planeNormal);
        if (num < Mathf.Epsilon)
        {
            return vector;
        }

        float num2 = Dot(vector, planeNormal);
        return new Vector3(vector.x - planeNormal.x * num2 / num, vector.y - planeNormal.y * num2 / num, vector.z - planeNormal.z * num2 / num);
    }

        我们可以看到两个方法所需要传递的参数都是两个 Vector3 类型的变量。

        在 Project 中:第一个参数 vector 是我们需要求投影的原始向量,onNormal 是我们需要投影的目标向量。

        在 ProjectOnPlane 中:第一个参数 vector 同样是我们需要求投影的原始向量,而planeNormal 是我们需要投影的平面的法线(因为通过法线我们就可以确定到一个平面)。

猜你喜欢

转载自blog.csdn.net/zhangchong5522/article/details/128147933
今日推荐