别再死记硬背了!用Python+NumPy图解向量的点积与叉积,理解物理和游戏开发中的核心运算
2026/6/14 10:54:21 网站建设 项目流程

用Python+NumPy图解向量的点积与叉积:从物理引擎到游戏开发的实战指南

在计算机图形学和游戏开发中,向量运算就像空气一样无处不在却又容易被忽视。当你看到游戏角色流畅的移动、逼真的光影效果或是精确的物理碰撞时,背后都是向量在默默工作。传统的数学教材往往把向量运算讲得抽象难懂,让人望而生畏。本文将用Python和NumPy带你亲手实现这些运算,并通过可视化让你直观感受它们的威力。

1. 准备工作:搭建向量运算实验室

1.1 环境配置与基础工具

开始之前,确保你的Python环境已经安装了以下库:

pip install numpy matplotlib ipympl

我们将使用Jupyter Notebook进行交互式演示,这样可以看到实时的图形输出。创建一个新的notebook,首先导入必要的库:

import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D %matplotlib widget # 启用交互式绘图

1.2 向量的Python表示

在NumPy中,向量就是一维数组。让我们创建几个示例向量:

# 二维向量 v1 = np.array([2, 3]) v2 = np.array([-1, 2]) # 三维向量 v3d1 = np.array([1, 2, 3]) v3d2 = np.array([-2, 1, -1])

为了直观展示这些向量,我们可以编写一个简单的绘图函数:

def plot_vectors(vectors, colors, title="", lim=5): plt.figure(figsize=(6,6)) ax = plt.gca() for vec, col in zip(vectors, colors): ax.quiver(0, 0, vec[0], vec[1], angles='xy', scale_units='xy', scale=1, color=col) ax.set_xlim([-lim, lim]) ax.set_ylim([-lim, lim]) ax.set_title(title) ax.grid(True) plt.show()

2. 点积:从投影到光照计算

2.1 点积的数学定义与几何意义

点积(Dot Product)是两个向量之间最基本的运算之一。数学上定义为:

a·b = |a||b|cosθ = a₁b₁ + a₂b₂ + ... + aₙbₙ

在Python中计算点积非常简单:

dot_product = np.dot(v1, v2) # 或者使用 v1 @ v2

点积的几何意义是什么?它实际上衡量了两个向量的"相似程度":

  • 当两个向量方向相同时,点积为正最大值
  • 当两个向量垂直时,点积为零
  • 当两个向量方向相反时,点积为负最小值

2.2 点积在游戏开发中的应用

光照计算是点积最经典的应用之一。在Lambert光照模型中,表面亮度与光线方向和表面法线的点积成正比:

def lambert_diffuse(light_dir, normal): # 归一化向量 light_dir = light_dir / np.linalg.norm(light_dir) normal = normal / np.linalg.norm(normal) # 计算点积并确保非负 intensity = max(0, np.dot(light_dir, normal)) return intensity # 示例:计算表面亮度 light_direction = np.array([1, -1, 1]) surface_normal = np.array([0, 0, 1]) brightness = lambert_diffuse(light_direction, surface_normal)

注意:在实际游戏引擎中,光照计算会更复杂,但点积始终是基础

2.3 点积的投影特性实战

点积可以用来计算一个向量在另一个向量上的投影长度。这在角色移动、摄像机控制等场景中非常有用:

def project_vector(a, b): """返回a在b上的投影向量""" scale = (a @ b) / (b @ b) return scale * b # 示例:计算角色移动方向在斜坡上的投影 movement = np.array([2, 1]) slope = np.array([1, 0.5]) effective_movement = project_vector(movement, slope)

3. 叉积:从旋转到碰撞检测

3.1 叉积的定义与计算

叉积(Cross Product)是三维向量特有的运算,结果是一个垂直于两个输入向量的新向量。数学定义为:

a × b = |a||b|sinθ n̂

其中n̂是垂直于a和b的单位向量,方向由右手定则决定。在NumPy中计算叉积:

cross_product = np.cross(v3d1, v3d2)

3.2 叉积的几何意义可视化

让我们创建一个3D可视化函数来展示叉积:

def plot_3d_vectors(vectors, colors, title=""): fig = plt.figure(figsize=(8,6)) ax = fig.add_subplot(111, projection='3d') for vec, col in zip(vectors, colors): ax.quiver(0, 0, 0, vec[0], vec[1], vec[2], color=col, arrow_length_ratio=0.1) ax.set_xlim([-3,3]) ax.set_ylim([-3,3]) ax.set_zlim([-3,3]) ax.set_title(title) plt.show() # 绘制原始向量和它们的叉积 a = np.array([2, 0, 0]) b = np.array([0, 2, 0]) c = np.cross(a, b) plot_3d_vectors([a, b, c], ['r', 'g', 'b'], "叉积可视化")

3.3 叉积在物理引擎中的应用

扭矩计算是叉积的典型应用。当力作用在物体上产生旋转效应时:

def calculate_torque(position, force): """计算扭矩:τ = r × F""" return np.cross(position, force) # 示例:计算门把手上的扭矩 handle_position = np.array([0, 0.8, 0]) # 门轴到把手的向量 applied_force = np.array([10, 0, 0]) # 水平推力 torque = calculate_torque(handle_position, applied_force)

碰撞检测中,叉积可以用来判断点是否在三角形内部:

def point_in_triangle(p, a, b, c): """使用叉积判断点p是否在三角形abc内""" def sign(o, v1, v2): return np.cross(v1-o, v2-o)[2] d1 = sign(p, a, b) d2 = sign(p, b, c) d3 = sign(p, c, a) has_neg = (d1 < 0) or (d2 < 0) or (d3 < 0) has_pos = (d1 > 0) or (d2 > 0) or (d3 > 0) return not (has_neg and has_pos)

4. 综合应用:构建简易物理引擎

4.1 粒子系统基础

让我们用向量运算实现一个简单的2D粒子系统:

class Particle: def __init__(self, position, velocity, mass=1.0): self.position = np.array(position, dtype=float) self.velocity = np.array(velocity, dtype=float) self.acceleration = np.zeros(2) self.mass = mass self.forces = [] def apply_force(self, force): self.forces.append(force) def update(self, dt): # 计算合力 F=ma total_force = sum(self.forces, np.zeros(2)) self.acceleration = total_force / self.mass # 更新速度和位置 self.velocity += self.acceleration * dt self.position += self.velocity * dt # 重置力 self.forces = []

4.2 碰撞响应实现

使用点积和叉积实现弹性碰撞:

def resolve_collision(p1, p2, normal, restitution=0.8): """处理两个粒子之间的碰撞""" # 计算相对速度 relative_velocity = p2.velocity - p1.velocity velocity_along_normal = np.dot(relative_velocity, normal) # 如果物体正在分离,不处理碰撞 if velocity_along_normal > 0: return # 计算冲量大小 j = -(1 + restitution) * velocity_along_normal j /= 1/p1.mass + 1/p2.mass # 应用冲量 impulse = j * normal p1.velocity -= impulse / p1.mass p2.velocity += impulse / p2.mass

4.3 完整模拟循环

将所有这些组合起来,创建一个简单的物理世界:

def run_simulation(): # 创建粒子 ball1 = Particle([0, 5], [1, 0], mass=2.0) ball2 = Particle([3, 5], [-1, 0], mass=1.0) # 重力 gravity = np.array([0, -9.8]) # 模拟参数 dt = 0.016 # 约60FPS steps = 300 # 记录轨迹 trace1, trace2 = [], [] for _ in range(steps): # 应用力 ball1.apply_force(gravity * ball1.mass) ball2.apply_force(gravity * ball2.mass) # 更新状态 ball1.update(dt) ball2.update(dt) # 检查碰撞 distance = np.linalg.norm(ball1.position - ball2.position) if distance < 0.5: # 假设半径为0.5 normal = (ball2.position - ball1.position) / distance resolve_collision(ball1, ball2, normal) # 记录轨迹 trace1.append(ball1.position.copy()) trace2.append(ball2.position.copy()) return np.array(trace1), np.array(trace2)

5. 性能优化与高级技巧

5.1 向量化运算提升性能

当处理大量向量时,使用NumPy的向量化操作可以显著提升性能:

# 低效的循环方式 def slow_dot_products(vectors1, vectors2): results = [] for v1, v2 in zip(vectors1, vectors2): results.append(np.dot(v1, v2)) return np.array(results) # 高效的向量化方式 def fast_dot_products(vectors1, vectors2): return np.sum(vectors1 * vectors2, axis=1) # 生成测试数据 num_vectors = 100000 dims = 3 vecs1 = np.random.rand(num_vectors, dims) vecs2 = np.random.rand(num_vectors, dims) # 性能对比 %timeit slow_dot_products(vecs1, vecs2) # 约200ms %timeit fast_dot_products(vecs1, vecs2) # 约2ms

5.2 四元数与旋转

在3D游戏中,四元数常用于表示旋转。它们可以避免万向节锁问题,并且插值更平滑:

class Quaternion: def __init__(self, w, x, y, z): self.w = w self.vec = np.array([x, y, z]) def from_axis_angle(axis, angle): """从旋转轴和角度创建四元数""" axis = axis / np.linalg.norm(axis) half_angle = angle / 2 w = np.cos(half_angle) vec = axis * np.sin(half_angle) return Quaternion(w, *vec) def rotate_vector(self, v): """用四元数旋转向量""" q_vec = self.vec uv = np.cross(q_vec, v) uuv = np.cross(q_vec, uv) return v + 2 * (self.w * uv + uuv)

5.3 SIMD优化技巧

现代CPU支持SIMD(单指令多数据)并行处理。我们可以利用NumPy的底层优化:

# 普通点积计算 def regular_dot(a, b): return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] # 使用einsum优化 def einsum_dot(a, b): return np.einsum('i,i->', a, b) # 性能对比 v1 = np.random.rand(3) v2 = np.random.rand(3) %timeit regular_dot(v1, v2) # 约1.5μs %timeit np.dot(v1, v2) # 约0.5μs %timeit einsum_dot(v1, v2) # 约0.8μs

在实际项目中,我发现np.dot通常已经足够优化,但对于更复杂的运算,einsum可以提供更好的控制和性能。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询