# 【向量】

# 定义

把方向和大小构成一个值,称其为向量,若其向量的大小为1,则称其为单位向量。

沉浸式数学书 (opens new window)

# 运算

不同的向量运算,需要确保是同一个空间里的向量。

# 向量长度

public float magnitude { get { return (float)Math.Sqrt(x * x + y * y); } }

# 向量归一

让向量长度变为1。 也成为模向量。 x和y分别进行除以向量长度。

公式: Markdown 图片

# 向量加法

  • 空间里坐标点A和B, 那么 A+B的向量 = A与B 夹角中间的向量。
new Vector2(a.x + b.x, a.y + b.y);

动图示例2.5 (opens new window)

# 向量减法

  • 空间里坐标点A和B, 那么 A->B的向量 = B - A。
  • 比如地图上,存在玩家和怪物两个单位。 两个单位的坐标都知道了,那么角色与怪物在世界空间里构成的方向向量怎么求呢?这里就可以用到向量的减法了。 玩家 -> 怪物 的向量 = 怪物的坐标 - 玩家的坐标
  • 查看实战案例
new Vector2(a.x - b.x, a.y - b.y);

动图示例2.8 (opens new window)

# 向量加减法公式和示意图

Markdown 图片

Markdown 图片

Markdown 图片

# 向量点乘

  • 结果为所形成投影长度。
  • 经典应用场合:计算兰伯特光照模型,使用模型法线与光方向的反方向进行点乘,重合为1反向为-1,形成一个有一半球的面积有光渐变的效果,类似于被光照亮的光照模型。
  • 查看动画

公式1: Markdown 图片

公式2: Markdown 图片

public static float Dot(Vector2 lhs, Vector2 rhs) { return lhs.x * rhs.x + lhs.y * rhs.y; }

# 向量叉乘

  • 获取与两个向量交叉且垂直的第三个向量。
  • 计算面积,向量a与向量b进行叉乘,结果为a和b所构成平行四边形的面积。
  • 计算点是否在三角形内部。
  • 判断向量的左右关系(比如导弹自动跟踪怪物的功能)。
  • 查看动画

公式: Markdown 图片

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);
}

使用叉乘获取网格表面积

public static float GetMeshArea(mesh)
{
  Vector3[] verts = mesh.vertices;
  int[] tris = mesh.triangles;
  float area = 0f;
  for(int i = 0; i < tris.Length;i+=3)
  {
    int aIndex = tris[i];
    int bIndex = tris[i+1];
    int cIndex = tris[i+2];
    Vector3 a = verts[aIndex];
    Vector3 b = verts[bIndex];
    Vector3 c = verts[cIndex];
    area += Vector3.Cross(b-a,c-a).magnitude;
  }
  area /= 2;
  return area;
}

计算点是否在三角形内部:

static bool is_point_in_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) 
{
  Vector2 an = a - s;
  Vector2 bn = b - s;
  Vector2 cn = c - s;

  bool orientation = an.cross(bn) > 0;

  if ((bn.cross(cn) > 0) != orientation) {
    return false;
  }

  return (cn.cross(an) > 0) == orientation;
}

# 向量与标量运算

  • 乘以一个大于1的标量时,延长。
  • 乘以一个大于0小于1的标量时,缩短。
  • 乘以-1时,向量反向。

公式: Markdown 图片

# 3D向量旋转

// 把target向量绕X轴旋转degress值的角度
float3 RotateAroundX(float degress,float3 target)
{
  float rad = degress * UNITY_PI / 180;
  float2x2 m_rotate = float2x2(cos(rad),-sin(rad),sin(rad),cos(rad));
  float2 dir_rotate = mul(m_rotate,target.yz);
  target = float3(target.x,dir_rotate.x,dir_rotate.y);
  return target;
}

// 把target向量绕Y轴旋转degress值的角度
float3 RotateAroundY(float degress,float3 target)
{
  float rad = degress * UNITY_PI / 180;
  float2x2 m_rotate = float2x2(cos(rad),-sin(rad),sin(rad),cos(rad));
  float2 dir_rotate = mul(m_rotate,target.xz);
  target = float3(dir_rotate.x,target.y,dir_rotate.y);
  return target;
}

// 把target向量绕Z轴旋转degress值的角度
float3 RotateAroundZ(float degress,float3 target)
{
  float rad = degress * UNITY_PI / 180;
  float2x2 m_rotate = float2x2(cos(rad),-sin(rad),sin(rad),cos(rad));
  float2 dir_rotate = mul(m_rotate,target.xy);
  target = float3(dir_rotate.x,dir_rotate.y,target.z);
  return target;
}

# Projection And Rejection

Markdown 图片

// Projection
float3 projection(float3 A,float3 B)
{
  return B * dot(A,B) / dot(B,B);
}

// Rejection
float3 rejection(float3 A,float3 B)
{
  return A - (B * dot(A,B) / dot(B,B));
}