统治游戏几何运算,碾压海伦公式丨数学江湖的刀光剑影

点击上方码不了一点+关注和★ 星标

统治游戏几何运算,碾压海伦公式丨数学江湖的刀光剑影

引言

在游戏开发的广阔天地中,几何计算无处不在。从精确的碰撞检测到高效的路径规划,从逼真的地形生成到炫酷的粒子效果,几何运算都扮演着核心角色。而作为几何图形基石的三角形,其面积计算更是众多复杂运算的基础。

海伦公式(Heron’s formula)作为计算三角形面积的经典方法,虽有着悠久历史和广泛应用,但在实际开发中却暗藏隐忧。浮点数误差的累积可能导致计算结果偏离,而多次开方运算也带来不小的性能开销。在追求精确和高效的游戏开发领域,这些缺陷常常成为无法忽视的瓶颈。

本文将为你揭秘一种更为优雅、高效的三角形面积计算方法——向量积法。这种方法不仅计算过程简洁直观,而且数值稳定性出色,已在众多主流游戏引擎和图形库中得到广泛应用。通过深入对比两种方法的实现原理和性能表现,我们将领略数学之美如何转化为高效代码的精彩过程。

本文以以往文章代码库

GitHub - haiyoucuv/Wechat_article

涉及知识

  • TypeScript
  • CocosCreator3.x
  • 计算几何学

海伦公式

  • 海伦公式是用来计算三角形面积的公式。
  • 海伦公式的表达式是:
    • S = √[p(p-a)(p-b)(p-c)]
    • p = (a+b+c)/2
    • a,b,c 是三角形的三条边。
    • S 是三角形的面积。
    • p 是周长的一半。

由于本文不是具体介绍海伦公式,所以这里不做推导

本文重点不在推导海伦公式,因此略过具体证明过程。

海伦公式的代码实现

function calcTriangleByHeron(p1, p2, p3) {
  // 计算三条边长度
  const a = Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
  const b = Math.sqrt(Math.pow(p3.x - p2.x, 2) + Math.pow(p3.y - p2.y, 2));
  const c = Math.sqrt(Math.pow(p1.x - p3.x, 2) + Math.pow(p1.y - p3.y, 2));

  // 计算半周长
  const s = (a + b + c) / 2;

  // 计算面积
  return Math.sqrt(s * (s - a) * (s - b) * (s - c));
}

const triangle = [
  {x: 0, y: 0},
  {x: 10, y: 0},
  {x: 10, y: 10},
];

console.log("Heron:", calcTriangleByHeron(...triangle));   // 50

01

从输出结果可以看到,我们成功计算出该三角形的面积为 50 平方单位。

海伦公式的局限性

尽管海伦公式在数学上很优雅,但在实际编程中却存在明显缺陷:

  • 浮点数误差累积导致计算结果不够准确
  • 多次开方运算带来较高的计算成本

比如下面这个例子,我们计算另一个三角形的面积

const triangle2 = [
    {x: 0, y: 0},
    {x: 1, y: 0},
    {x: 1, y: 1},
];

console.log("Heron:", calcTriangleByHeron(...triangle2));   // 0.5

02

向量积法:更优雅的解决方案

向量积法是通过向量叉乘计算三角形面积的方法,其核心步骤非常简洁:

  1. 选择一个顶点作为参考点
  2. 计算该顶点与其他两个顶点的向量
  3. 对这两个向量进行叉乘
  4. 取叉乘结果的绝对值除以2

向量积法的数学原理

众所周知,计算三角形面积的经典公式是"底乘高除以2"。然而在游戏开发中,我们通常只能直接获取顶点坐标,而非底和高。

如果你幼儿园课上没有打瞌睡,你肯定知道下面这个三角形面积公式

04

其中:

  • |AB| 表示向量AB的长度
  • |AC| 表示向量AC的长度
  • sin(α) 是向量AB与AC之间夹角的正弦值

为什么使用sin(α)?

这是因为,当我们从B点向AC做垂线时,垂线长度恰好等于sin(α) × |AB|。此时,|AC|作为三角形的底边,垂线长度则为高,完美对应了"底乘高除以2"的公式。

那么得出结论这个公式其实就是众所周知的底乘高除以2

如果你小学线性代数课认真听讲,一定能发现上面这个公式的后边部分和叉乘非常像

在三维空间中,向量叉乘的结果是一个新向量,其模长正好等于:

07

破案!

向量积法的代码实现

// 向量叉乘
const cross = (p1, p2) => p1.x * p2.y - p1.y * p2.x;

function calcTriangleByVector(p1, p2, p3) {
    const v1 = { x: p2.x - p1.x, y: p2.y - p1.y };  // 向量p1p2
    const v2 = { x: p3.x - p1.x, y: p3.y - p1.y };  // 向量p1p3
    return Math.abs(cross(v1, v2)) / 2;
}

const triangle = [
  {x: 0, y: 0},
  {x: 10, y: 0},
  {x: 10, y: 10},
];
console.log("Vector:", calcTriangleByVector(...triangle));   // 50

const triangle2 = [
  {x: 0, y: 0},
  {x: 1, y: 0},
  {x: 1, y: 1},
];
console.log("Vector:", calcTriangleByVector(...triangle2));   // 0.5

运行结果显示,向量积法不仅计算正确,而且相比海伦公式,明显减少了浮点数误差的累积。

08

实时对比演示

在Cocos Creator中,我们编写了一个互动Demo,直观展示两种方法的计算差异:

从演示中可以明显看出,海伦公式在特定情况下会产生浮点数误差,而向量积法则保持了稳定的计算精度。

完整代码已上传至仓库,感兴趣的读者可自行下载体验:

GitHub - haiyoucuv/Wechat_article

本文以以往文章代码库

GitHub - haiyoucuv/Wechat_article

结语

三角形面积计算虽是几何运算中最基础的问题,却在游戏和图形开发中有着不可替代的地位。通过本文对比,我们看到了海伦公式虽然在数学上很优雅,但在实际应用中可能因浮点数误差和计算效率问题而不够理想。相比之下,向量积法不仅计算逻辑更为简洁,而且数值稳定性更好,已成为众多游戏引擎的首选方案。

这种向量方法的价值远不止于此,它还可以优雅地扩展到计算任意多边形面积、判断点是否在多边形内部等更复杂的几何问题。在性能至上的游戏开发领域,这类算法优化能带来显著的效率提升。

数学之美不仅在于公式的优雅表达,更在于如何将抽象原理转化为高效的代码实现。希望本文能为你的游戏开发之旅增添一把锋利的几何运算利器。

欢迎在评论区分享你在游戏开发中遇到的几何计算难题,让我们一起探索更多优雅高效的解决方案!


点击上方码不了一点+关注和★ 星标

7赞

mark一下

如果只是理论的话,三角形面积用矩阵运算这种结论性的定理,AI 能回答得非常全面了。

个人认为更有价值的是:我在做某某功能的时候,需要确定三角形面积,因为什么导致性能遇到了瓶颈,因而触发了我的研究,从而选择了这个定理,使性能得到了怎样的提升。
从应用角度讲,核心价值其实在:定理被应用的具体案例。

mark mark