Unity物理系统由浅入深第三节:物理引擎底层原理剖析

Unity物理系统由浅入深第一节:Unity 物理系统基础与应用
Unity物理系统由浅入深第二节:物理系统高级特性与优化
Unity物理系统由浅入深第三节:物理引擎底层原理剖析
Unity物理系统由浅入深第四节:物理约束求解与稳定性

Unity 物理系统底层使用的是 NVIDIA PhysX 引擎。理解 PhysX 或任何其他实时物理引擎(如 Havok, Bullet)的工作方式,对于一名进阶的 Unity 开发者来说,是极其宝贵的知识。本篇我们将从宏观到微观,逐步了解物理引擎的核心概念和主要算法。


1. 物理引擎概述:一个模拟真实世界的机器

物理引擎本质上是一个复杂的软件系统,它接收场景中所有具有物理属性(刚体、碰撞体等)的物体信息,然后在一个固定时间步(Fixed Timestep)内,迭代计算这些物体在各种力(重力、推力、摩擦力等)和约束(碰撞、关节)作用下的位置、速度和旋转,从而模拟出真实的物理行为。

一个典型的物理引擎循环(在每个 FixedUpdate 中执行)大致遵循以下步骤:

  1. 积分器 (Integrators): 根据当前的速度和受到的力,预测物体在下一个时间步的位置和速度。
  2. 宽相碰撞检测 (Broad-Phase Collision Detection): 快速找出场景中所有可能发生碰撞的物体对。
  3. 窄相碰撞检测 (Narrow-Phase Collision Detection): 对上一步筛选出的可能碰撞对,进行精确的碰撞检测,计算出详细的碰撞信息(接触点、法线、穿透深度等)。
  4. 接触生成 (Contact Generation): 根据窄相检测的结果,生成接触点(Contact Points)和接触法线(Contact Normals)。
  5. 约束求解器 (Constraint Solver): 处理所有碰撞(以及关节)产生的约束。这是物理引擎最复杂的部分,它通过迭代计算来解决物体之间的相互作用,确保它们不会相互穿透,并且能正确反弹、滑动或连接。
  6. 更新状态: 根据求解器的结果,更新所有刚体的位置和速度。

2. 刚体动力学基础:物理引擎的语言

在深入算法之前,我们需要回顾一下物理学中的一些基本概念,它们是物理引擎的“语言”。

  • 力 (Force):改变物体运动状态的原因。单位牛顿 (N)。
  • 质量 (Mass):物体惯性的量度。单位千克 (kg)。
  • 惯性 (Inertia):物体保持其运动状态的特性。质量越大,惯性越大。
  • 惯性张量 (Inertia Tensor):描述刚体旋转惯性的一个 3x3 矩阵。它表示了物体在不同轴上抵抗旋转的能力。对于非球对称的物体,其旋转惯性在不同方向上是不同的。在 Unity 中,对于常见的原始碰撞体,PhysX 会自动计算好。对于 Mesh Collider,可能需要外部工具或手动计算。
  • 线速度 (Linear Velocity):物体直线运动的速度向量。
  • 角速度 (Angular Velocity):物体旋转的速度向量。
  • 动量 (Momentum):物体的质量与其线速度的乘积。
    • P=mvP = mvP=mv
  • 角动量 (Angular Momentum):描述物体旋转运动的量。
    • L=IωL = I\omegaL=Iω (其中 III 是惯性张量,ω\omegaω 是角速度)
  • 冲量 (Impulse):在短时间内施加的巨大力。它直接导致动量的瞬间变化。
    • ΔP=FΔt\Delta P = F \Delta tΔP=FΔt (力乘以时间,或直接视为动量变化量)
  • 扭矩 (Torque):使物体产生旋转的力矩。它直接导致角动量的瞬间变化。
    • τ=r×F\tau = r \times Fτ=r×F (力臂与力的叉乘)

物理引擎通过这些概念来计算和更新物体的运动状态。例如,当施加一个力时,引擎会根据牛顿第二定律 F=maF = maF=ma 来计算加速度,然后用积分器更新速度和位置。当发生碰撞时,引擎会计算一个冲量,施加到碰撞的物体上,使其反弹或滑动。


3. 积分器(Integrators):连接现在与未来

积分器是物理引擎的“时间旅行者”,它们负责根据当前的速度和力,预测物体在下一个微小时间步长后的位置和速度。这是物理模拟的核心,因为我们需要连续地更新物体的状态。

  • 欧拉积分 (Euler Integration)

    • 原理: 最简单也是最直观的积分方法。它假设在整个时间步长内,速度或力是恒定的,并直接通过当前的速度和加速度来更新位置和速度。
    • 公式:
      • vnew=vcurrent+a⋅Δtv_{new} = v_{current} + a \cdot \Delta tvnew=vcurrent+aΔt
      • xnew=xcurrent+vcurrent⋅Δtx_{new} = x_{current} + v_{current} \cdot \Delta txnew=xcurrent+vcurrentΔt
    • 优点: 实现简单,计算速度快。
    • 缺点: 精度最低,容易产生累积误差,导致能量损失或增加(物体可能会越弹越低或越弹越高),特别是在较大的时间步长下,可能导致数值不稳定。
    • 示例: 在简单场景中可以接受,但在需要高精度模拟(如车辆、关节链)时则不够理想。
  • Verlet 积分 (Verlet Integration)

    • 原理: 一种更稳定且能更好地保持能量的积分方法。它不直接使用速度,而是利用当前位置和上一帧位置来计算新位置。速度是隐式包含的。
    • 公式(简化版):
      • xnew=2xcurrent−xprevious+a⋅Δt2x_{new} = 2x_{current} - x_{previous} + a \cdot \Delta t^2xnew=2xcurrentxprevious+aΔt2
    • 优点: 能量守恒性好,数值稳定性强,即使在较大时间步长下也不太容易发散。
    • 缺点: 无法直接访问速度,如果需要速度信息(例如用于碰撞响应),需要额外计算。
    • 示例: 常用于粒子系统、布料模拟等对稳定性要求高的场景。
  • 龙格-库塔积分 (Runge-Kutta Integration - RK4)

    • 原理: 一系列更高阶的积分方法,通过在时间步长内采样多个点来计算加权平均值,从而提高精度。RK4 是最常用的一种。
    • 优点: 精度高,稳定性好,能够更准确地模拟复杂的物理系统。
    • 缺点: 计算量远大于欧拉积分,因为需要在每个时间步内进行多次求值。
    • 示例: 理论上最接近真实物理的积分方法,但在实时游戏中因性能开销大而较少直接用于大量刚体,更常见于需要高精度模拟的特定场景或离线物理模拟。

Unity PhysX 采用的积分器: 尽管 PhysX 的具体实现是专有的,但它通常使用经过优化的隐式欧拉积分(Implicit Euler)或类似的半隐式积分。这种方法结合了欧拉的简洁和一些稳定性优势,通过在迭代求解器中同时考虑当前和下一帧的状态来提高稳定性。它并不是单纯的显式欧拉积分。


4. 碰撞检测(Collision Detection):寻找接触点

碰撞检测是物理引擎最核心、最耗性能的部分之一。它分为两个阶段:宽相检测和窄相检测。

4.1. 宽相检测(Broad-Phase Collision Detection)
  • 目的: 快速排除场景中绝大多数不可能发生碰撞的物体对。这一步不需要精确的计算,只需要找出“可能”碰撞的候选对。
  • 核心思想: 使用粗略的包围体(Bounding Volume),如 AABB (Axis-Aligned Bounding Box),来近似表示物体的空间范围。如果两个 AABB 不重叠,那么它们内部的物体肯定不碰撞。
  • 常用算法:
    • AABB 树 (AABB Tree):一种层次化的数据结构。将场景中的所有 AABB 组织成一棵树。检测时,只需遍历树,如果父节点的 AABB 不重叠,则其子节点也不可能重叠,从而快速排除大量物体。这是 PhysX 广泛使用的技术。
    • SAP (Sweep and Prune / Sort and Sweep):对所有物体在每个轴上的 AABB 投影进行排序。只有当两个物体的 AABB 在所有轴上都重叠时,才可能发生碰撞。通过维护排序列表,可以高效地找出重叠对。适用于物体数量不太多且运动方向较固定的场景。
    • 网格/格子(Grids/Spatial Hashing):将空间划分为规则的网格单元。每个单元存储其中包含的物体。检测时只检查相邻或同一单元格内的物体。适用于物体均匀分布的场景。
4.2. 窄相检测(Narrow-Phase Collision Detection)
  • 目的: 对宽相检测筛选出的“可能”碰撞对,进行精确的几何测试,判断它们是否真的发生了碰撞,并计算出详细的碰撞信息(接触点、法线、穿透深度等)。
  • 核心思想: 使用更精确的几何算法来处理复杂形状。
  • 常用算法:
    • GJK (Gilbert-Johnson-Keerthi Distance Algorithm)
      • 原理: GJK 算法并不是直接找到碰撞点,而是用来判断两个凸包(Convex Hull)是否相交。它通过迭代寻找两个物体之间的最短距离向量。如果这个最短距离为零或指向内,则表示它们相交。
      • 优点: 效率高,适用于任意凸包形状,是现代物理引擎的核心。
      • 缺点: 只能判断是否相交,不能直接得到碰撞点和法线。
    • EPA (Expanding Polytope Algorithm)
      • 原理: EPA 通常紧接着 GJK 之后使用。当 GJK 判断两个物体相交后,EPA 会从 GJK 找到的“穿透方向”开始,在闵可夫斯基差集(Minkowski Difference)中扩展一个多面体,直到找到最接近原点的那个面。这个面就定义了最小穿透向量(法线和深度)。
      • 优点: 与 GJK 结合,可以准确地计算穿透深度和碰撞法线。
    • SAT (Separating Axis Theorem - 分离轴定理)
      • 原理: 对于两个凸体,如果它们不相交,那么一定存在一个轴,使得它们在这条轴上的投影互不重叠。反之,如果找不到这样的分离轴,则它们相交。对于多边形,需要测试所有边以及垂直于这些边的轴。
      • 优点: 简单易懂,对于多边形(2D)和多面体(3D)非常有效。
      • 缺点: 对于复杂形状,需要测试的轴可能很多,效率不如 GJK/EPA 组合。
4.3. 接触生成 (Contact Generation)
  • 一旦窄相检测确定了碰撞,下一步就是生成一个或多个接触点 (Contact Point)。每个接触点包含了:
    • 位置 (Position):碰撞发生在世界空间中的坐标。
    • 法线 (Normal):指向从一个物体到另一个物体的方向。这决定了碰撞后的反弹方向。
    • 深度 (Separation/Penetration Depth):物体之间相互穿透的距离。这个信息对于约束求解器至关重要,因为它需要将物体推开。
  • 对于复杂的碰撞,可能会生成多个接触点(例如,一个盒子平躺在地面上)。这些接触点会被收集起来,传递给下一步的约束求解器。

总结

至此,我们已经基本了解了 Unity 物理引擎的底层工作原理:从宏观的物理循环,到驱动物体运动的刚体动力学基础,再到连接过去与未来的积分器,以及寻找和确定碰撞点的宽相和窄相碰撞检测算法。

我们现在应该对物理引擎如何“看”到和“理解”我们的游戏世界有了更清晰的认识。下一篇,我们将继续深入物理引擎最复杂也是最精妙的部分:物理约束求解与稳定性,了解它是如何解决碰撞穿透、摩擦和关节限制等问题的。

Unity物理系统由浅入深第一节:Unity 物理系统基础与应用
Unity物理系统由浅入深第二节:物理系统高级特性与优化
Unity物理系统由浅入深第三节:物理引擎底层原理剖析
Unity物理系统由浅入深第四节:物理约束求解与稳定性

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/88897.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/88897.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker一键安装中间件(RocketMq、Nginx、MySql、Minio、Jenkins、Redis)脚步

1、Docker安装RocketMq 2、Docker安装Nginx 3、Docker安装MySql 4、Docker安装Minio 5、Docker安装jenkins 6、Docker安装Redis 1、Docker安装RocketMq #!/bin/bash# 定义变量 NAMESRV_CONTAINER"rocketmq-namesrv" BROKER_CONTAINER"rocketmq-broker&quo…

WPF学习笔记(27)科学计算器

科学计算器1. 前端界面2. 功能代码3. 效果展示1. 前端界面 <Window x:Class"Cal.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http:/…

【Linux系列】unzip file 命令

博客目录掌握 unzip -o 命令&#xff1a;高效解压并覆盖文件的完整指南一、unzip 命令基础二、-o 选项的核心作用三、典型应用场景四、与其他选项的组合使用五、注意事项与风险防范六、替代方案与高级技巧掌握 unzip -o 命令&#xff1a;高效解压并覆盖文件的完整指南 在日常的…

1965–2022年中国大陆高分辨率分部门用水数据集,包含:灌溉用水、工业制造用水、生活用水和火电冷却

1965–2022年中国大陆高分辨率部门用水数据集 高质量用水数据集对推进变化环境下的水资源研究至关重要。然而&#xff0c;现有中国用水数据通常按行政区划或流域汇总&#xff0c;时空分辨率不足&#xff0c;难以支撑人类用水模式及时空变化特征的精细化分析。为此&#xff0c;…

git中的指令解释

在 Git 的 diff 输出中&#xff0c;---、 和 ... 块的含义如下&#xff1a; 1. --- a/1.py 和 b/1.py --- a/1.py&#xff1a;表示旧版本的文件路径&#xff08;通常是工作目录或上一次提交的版本&#xff09;。 b/1.py&#xff1a;表示新版本的文件路径&#xff08;通常是暂存…

STM32实现四自由度机械臂(SG90舵机)多功能控制(软件篇freertos)

书接上回的硬件篇STM32控制四自由度机械臂&#xff08;SG90舵机&#xff09;&#xff08;硬件篇&#xff09;&#xff08;简单易复刻&#xff09;-CSDN博客 此时硬件平台已经搭建完毕&#xff0c;软件总共设计了三种模式&#xff0c;分别为 模式1&#xff1a;摇杆&蓝牙模…

docker常用命令集(2)

接前一篇文章&#xff1a;docker常用命令集&#xff08;1&#xff09; 本文内容参考&#xff1a; Docker build 命令 | 菜鸟教程 docker基础(二)之docker build-CSDN博客 Docker push 命令 | 菜鸟教程 Docker pull 命令 | 菜鸟教程 特此致谢&#xff01; 3. docker build …

舒尔特方格训练小游戏流量主微信小程序开源

功能特点 游戏核心功能&#xff1a; 随机生成55舒尔特方格 按顺序点击数字1-25 实时计时和尝试次数统计 错误点击反馈&#xff08;视觉和触觉&#xff09; 数据统计&#xff1a; 记录每次完成时间 保存历史最佳成绩 保存最近5次尝试记录 统计尝试次数&#xff08;错误点击&…

在Spring Boot 开发中 Bean 的声明和依赖注入最佳的组合方式是什么?

在Spring Boot 开发中&#xff0c;社区和 Spring 官方已经形成了一套非常明确的最佳实践。这个黄金组合就是&#xff1a; Bean 声明&#xff1a;使用构造型注解&#xff08;Stereotype Annotations&#xff09;&#xff0c;如 Service, Repository, Component 等。依赖注入&…

Oxygen XML Editor 26.0编辑器

Oxygen XML Editor 26.0编辑器 欢迎使用Oxygen XML Editor 26.0编辑器准备工作安装javajdk安装jdk验证Oxygen XML Editor 26.0安装欢迎使用Oxygen XML Editor 26.0编辑器 准备工作安装java Java官网下载地址:https://www.oracle.com/java/technologies/ Oxygen XML Editor 2…

AWS Lambda Container 方式部署 Flask 应用并通过 API Gateway 提供访问

前言 一年前写过一篇 Lambda 运行 Flask 应用的博文: https://lpwmm.blog.csdn.net/article/details/139756140 当时使用的是 ZIP 包方式部署应用代码, 对于简单的 API 开发用起来还是可以的, 但是如果需要集成到 CI/CD pipeline 里面就有点不太优雅. 本文将介绍使用容器方式…

React虚拟DOM的进化之路

引言 在Web前端开发中&#xff0c;用户交互的流畅性和页面性能一直是核心挑战。早期&#xff0c;开发者直接操作真实DOM&#xff08;Document Object Model&#xff09;时&#xff0c;频繁的重排&#xff08;reflow&#xff09;和重绘&#xff08;repaint&#xff09;导致性能…

(7)机器学习小白入门 YOLOv:机器学习模型训练详解

— (1)机器学习小白入门YOLOv &#xff1a;从概念到实践 (2)机器学习小白入门 YOLOv&#xff1a;从模块优化到工程部署 (3)机器学习小白入门 YOLOv&#xff1a; 解锁图片分类新技能 (4)机器学习小白入门YOLOv &#xff1a;图片标注实操手册 (5)机器学习小白入门 YOLOv&#xff…

初识MySQL(三)之主从配置与读写分离实战

主重复制 主重复制原理master开启二进制日志记录slave开启IO进程&#xff0c;从master中读取二进制日志并写入slave的中继日志slave开启SQL进程&#xff0c;从中继日志中读取二进制日志并进行重放最终&#xff0c;达到slave与master中数据一致的状态&#xff0c;我们称作为主从…

RabbitMQ面试精讲 Day 2:RabbitMQ工作模型与消息流转

【RabbitMQ面试精讲 Day 2】RabbitMQ工作模型与消息流转 开篇 欢迎来到"RabbitMQ面试精讲"系列的第2天&#xff0c;今天我们将深入探讨RabbitMQ的工作模型与消息流转机制。这是面试中最常被问到的核心知识点之一&#xff0c;90%的RabbitMQ面试都会涉及消息流转流程…

基于SpringBoot3集成Kafka集群

1. build.gradle依赖引入 implementation org.springframework.kafka:spring-kafka:3.2.02. 新增kafka-log.yml文件 在resource/config下面新增kafka-log.yml&#xff0c;配置主题与消费者组 # Kafka消费者群组 kafka:consumer:group:log-data: log-data-grouptopic:log-data: …

wpf Canvas 导出图片

在WPF中将Canvas导出为图片主要涉及以下关键步骤和注意事项: ‌核心实现方法‌使用RenderTargetBitmap将Canvas渲染为位图,再通过PngBitmapEncoder保存为PNG文件。需注意临时移除Canvas的布局变换(LayoutTransform)以避免渲染异常‌1。示例代码片段:CanvasExporter.cs pu…

lvs负载均衡实操模拟

目录 一、配置准备 二、NET模式 修改LVS端 开启路由 修改对内网卡 ens160 修改对外网卡 ens224 加载网卡配置文件 修改web1端 修改网卡信息 重启网络 检测 配置web2 检测 验证配置是否正常 启动nginx服务 验证以上配置 添加lvs规则 验证 三、DR模式 修改…

Spring Boot 是如何简化 IoC 的配置的?

首先Spring Boot 并没有发明新的 IoC 理论&#xff0c;它做的也不是替换掉 Spring IoC 容器。相反&#xff0c;Spring Boot 是 Spring IoC 思想的实践者和简化者。它通过**“约定优于配置”&#xff08;Convention over Configuration&#xff09;**的理念&#xff0c;将原本繁…

Go语言中的组合式接口设计模式

文章目录Go语言中的组合式接口设计模式背景和需求组合式接口设计Go语言中的组合式接口设计模式 背景和需求 在微服务架构和复杂业务系统中&#xff0c;我们经常需要调用多个外部服务或内部模块。传统的做法是将所有方法都放在一个大接口中&#xff0c;但这种设计会导致接口臃…