【Three.js】第二十一章 Physics 物理

2023-11-02

介绍

物理是WebGL可以添加到项目体验中最酷的功能之一。人们喜欢真实物理感的物体,看到它们碰撞、倒塌、坠落和弹跳,就像我的作品集一样: https: //bruno-simon.com/
有很多方法可以将物理功能添加到您的项目中,这取决于您想要实现的目标。您可以使用一些数学和解决方案(例如Raycaster)来创建自己的物理学

理论

这个想法很简单。我们将创建一个物理世界。这个物理世界是纯理论的。在这个物理世界上,东西会产生掉落、碰撞、摩擦、滑动等等交互。
当我们创建一个 Three.js 网格时,我们还将在物理世界中创建该网格的一个物理版本。如果我们在 Three.js 中创建一个 Box,我们也会在物理世界中创建一个Box框。
然后,在每一帧上,在渲染任何东西之前,我们告诉物理世界进行自我更新;我们获取物理对象的坐标(位置和旋转)并将它们应用于相应的 Three.js 网格。
就是这么简单的原理。这里最困难的是将我们的代码组织成一个合理的结构。这是一个完全和原本文件路径分开的路径部分。每个开发人员都会有自己的习惯,这也取决于你想做什么以及你想把这个物理世界变得多复杂。
首先,我们将简单地创建球体和盒子。

物理功能依赖库

物理功能有多个可用的库。首先,您必须决定是需要 3D 库还是 2D 库。虽然您可能认为它必须是一个 3D 库,因为 Three.js 完全是关于 3D 的,但您可能错了。2D 库通常性能更高,如果您可以总结 2D 碰撞的物理经验,则最好使用 2D 库。
举一个例子是如果你想创建一个类似⚾️弹球游戏。球可以在墙上碰撞和弹跳,您就可以使用 2D 库将所有东西投影到二维平面上。您可以将球设计成物理世界中的圆圈,而墙壁是简单的矩形。事实上,这么做您将无法通过击球底部来使球跳过其他球。
像这样完成的项目的一个很好的例子是Merci MichelOuigo Let’s play。他们使用了 2D 物理库,因为每个碰撞和动画都可以在 2D 空间中表示。

3D物理

对于 3D 物理,主要有三个库:

ammo.js

cannon.js

Oimo.js

2D物理

对于 2D 物理,有很多库,但这里是最流行的:

matter.js

P2.js

planck.js

Box2D.js

我们不会在本课中使用 2D 库,但 2D 库代码与 3D 库代码非常相似。主要区别在于您必须更新的轴。
已经有尝试将 Three.js 与Physijs等库结合起来的解决方案。尽管如此,我们不会使用这些已经做好封装的现成解决方案,我们要手动结合物理库来获得更好的学习体验并更好地理解内部运行的逻辑。
虽然 Ammo.js 是最常用的库,尤其是在 Three.js中,正如您在示例中看到的那样,我们将选择 Cannon.js。这个库在我们的项目中实现起来更舒服,也更容易使用。

导入 Cannon.js

要将 Cannon.js 添加到我们的项目中,我们首先需要添加依赖项。
在您的终端的项目文件夹中,运行此命令npm install --save cannon
我们现在可以使用经典的 JavaScript 在我们的 JavaScript 中import导入 Cannon.js :

import CANNON from 'cannon'

我们需要的一切都在CANNON变量中可用。

设置

我们的启动器由平面上的一个球体组成,并且出于美学原因已经启用了阴影。

基础

世界

首先,我们需要创建一个 Cannon.js世界

/**
 * Physics
 */
const world = new CANNON.World()

现在我们获得了一个,感觉在没有重力漂浮在太空中的 WebGL 体验感,让我们增加重力脚踏实地。您可以使用Cannon.js Vec3gravity属性更改重力。
**Cannon.js **Vec3就像 Three.js Vector3一样。它也有**x****y****z**属性,还有一个**set(...)**方法:

world.gravity.set(0, - 9.82, 0)

我们把第二个参数值 改为 - 9.82 是因为,- 9.82它是地球上的重力常数,但如果您想让物体下落得更慢或者如果您的场景发生在火星上,您可以使用其他重力值。

目的

因为我们的场景中已经有了一个球体,所以让我们在 Cannon.js World中也创建一个球体。
为此,我们必须创建一个Body。Body是会掉落的并与其他物体碰撞的。
在我们创建一个Body之前,我们必须决定一个形状。有许多可用的基本形状,如BoxCylinderPlane等。我们将选择一个与 Three.js 球体具有相同半径的Sphere :

const sphereShape = new CANNON.Sphere(0.5)

然后我们可以创建我们的body并指定质量和位置:

const sphereBody = new CANNON.Body({
    mass: 1,
    position: new CANNON.Vec3(0, 3, 0),
    shape: sphereShape
})

最后,我们可以将Body 通过addBody(...)添加到世界中:

world.addBody(sphereBody)

现在页面里什么都没有发生,因为我们仍然需要更新我们的 Cannon.js 世界并相应地更新我们的 Three.js 球体。

更新 Cannon.js 世界和 Three.js 场景

要更新我们的world世界,我们必须使用step(...). 该方法底层的代码很难理解,我们不会在本课中对其进行解释,但您可以在本文中找到更多相关信息。
要让它工作,您必须提供一个固定的时间步长、自上一步以来经过了多少时间,以及世界world可以应用多少次迭代来赶上潜在的延迟。
我们不会解释什么是时间步长,但我们希望体验以 60fps 的速度运行,所以我们将使用1 / 60来表示. 别担心,在帧率更高和更低的设备上,体验将以相同的速度运行。
迭代次数由你决定,但体验是否流畅就没那么重要了。
对于三角洲时间,它有点复杂。我们需要计算自上一帧以来经过了多少时间。不要使用Clock类中的getDelta()方法。你不会得到预期的结果,而且你会搞乱类的内部逻辑。
为了获得正确的增量时间,我们需要从前一帧elapsedTime减去当前帧elapsedTime获得:

const clock = new THREE.Clock()
let oldElapsedTime = 0

const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()
    const deltaTime = elapsedTime - oldElapsedTime
    oldElapsedTime = elapsedTime

    // ...
}

我们终于可以更新我们的世界了:

const tick = () =>
{
    // ...

    // Update physics
    world.step(1 / 60, deltaTime, 3)
}

似乎没有任何东西在移动。其实现实是我们的sphereBody正在无限的堕入深渊,只是因为相机一直跟着物体坠落所以你难以发现,你可以通过在更新world世界后记录它的位置来看到:

world.step(1 / 60, deltaTime, 3)
    console.log(sphereBody.position.y)

我们现在需要做的是使用sphereBody坐标更新我们的sphere。 Three.js 有两种方法可以做到这一点。您可以单独更新每个position属性:

sphere.position.x = sphereBody.position.x
    sphere.position.y = sphereBody.position.y
    sphere.position.z = sphereBody.position.z

或者您可以使用以下方法将所有属性作为一个复制copy(...)

sphere.position.copy(sphereBody.position)

copy(...)在许多类中可用,例如Vector2Vector3EulerQuaternion,甚至类如MaterialObject3DGeometry等。
tutieshi_640x301_2s.gif
你最终应该看到你的项目中球体正在自由落体。问题是我们的球体似乎从地板上掉了下来。这是因为该地板存在于 Three.js 场景中,但不存在于 Cannon.js 世界中。
我们可以使用Plane形状简单地添加一个新的Body,但我们不希望我们的地板受到重力影响而掉落。换句话说,我们希望我们的地板是静态的。要使Body静态,请将其设置为:mass = 0

const floorShape = new CANNON.Plane()
const floorBody = new CANNON.Body()
floorBody.mass = 0
floorBody.addShape(floorShape)
world.addBody(floorBody)

tutieshi_640x303_2s.gif
如您所见,这次我们的做法大不相同。我们创建了一个没有参数的Body ,然后我们设置了这些参数。结果是一样的,我们这样做的唯一原因是为了上课讲解。一件有趣的事情是您可以创建一个由多个Shapes组成的Body。它对于复杂但坚固的物体很有用。
您应该看到球体朝一个方向(可能朝向相机)跳跃。这不是预期的结果。原因是我们的地板plane默认正对着相机。我们需要像在 Three.js 中旋转地板一样旋转它让他转到离开相机的区域。
使用 Cannon.js 进行旋转比使用 Three.js 稍微困难一些,因为您必须使用Quaternion来实现。有多种旋转Body的方法,但必须使用其quaternion属性。我们将使用setFromAxisAngle(...)方法旋转body.
第一个参数是一个轴。您可以将其想象成穿过身体的一根线。第二个参数是角度。这是你围绕这条线旋转身体的角度。

floorBody.quaternion.setFromAxisAngle(new CANNON.Vec3(- 1, 0, 0), Math.PI * 0.5)

tutieshi_640x400_1s.gif

我们将轴设置为负轴(相对于相机的左侧)穿过身体的线,并将x角度设置为(四分之一圆)。Math.PI * 0.5
您现在应该看到球体下落然后停在地板上。
我们不需要用 Cannon.js 地板更新 Three.js 地板,因为这个对象不会再移动了。

ContactMaterial 关联材料

如您所见,球落地后基本不会弹跳。这是默认行为,我们可以使用Material(不是 Three.js 中的 Material)和ContactMaterial来让它变的富有弹性。
材料只是一个参考您可以给它起一个名字并将它与一个Body相关联。然后为场景中的每种材质创建一个材质。
如果场景中有多种材质,假设一种木料材质用于地板,一种金属材质用于球。然后,您应该创建各种材质并为它们命名,例如'concrete''plastic'
(假设你世界里的一切都是塑料材质制成的。在这种情况下,您只需创建一种材料并将其命名为'default’即可。)
你可以给他们互相关联到'ground''ball'中。尽管如此,如果您想对墙壁和立方体等其他对象使用相同的材质,都名为'ground'即可.
在创建球体和地板之前,创建这两个材质

const concreteMaterial = new CANNON.Material('concrete')
const plasticMaterial = new CANNON.Material('plastic')

现在我们有了Material,我们必须创建一个ContactMaterial它是两种材质的组合,用来关联两种材料并模拟

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Three.js】第二十一章 Physics 物理 的相关文章

  • 如何根据普通 JavaScript 中的属性对对象数组进行分组

    你怎么groupBy基于普通 JavaScript 中特定属性的对象数组 例如给出的 const products category Sporting Goods price 49 99 stocked true name Football
  • 浏览器安全错误:“由于安全违规,此页面无法显示”[关闭]

    Closed 这个问题是无法重现或由拼写错误引起 help closed questions 目前不接受答案 我在一家网络开发公司工作 我为我们的一些营销人员设计了一个页面 以便将自定义页脚添加到我们应用程序中的各个页面 在部署我们的产品供
  • Puppeteer - 错误:协议错误 (Network.getResponseBody):找不到具有给定标识符的资源

    我正在尝试使用此代码使用 puppeteer 从网站获取响应正文 usr bin env node require dotenv config const puppeteer require puppeteer const readline
  • 角度单元格 xlsx 着色

    我有一个问题 我想根据一个值在我的 exel 行中添加红色或绿色来下载 如何在工作表中设置指定单元格的颜色 这是处理 exel 格式的 ts 类 表达 import Injectable from angular core import a
  • 客户端 GitHub 身份验证

    我正在使用 Javascript 对 GitHub 进行基本身份验证 例如 以下 shell 命令从 Github 获取令牌 curl i u uaername password k d scopes repo https api gith
  • Webpack、Sass - 超出最大调用堆栈大小

    我正在为我的 JS 应用程序使用 Webpack 对于样式 我使用 Sass 我的应用程序非常大 所以我使用了很多 mixins 和 includes 在过去的几天里 虽然应用程序的 SASS 数据增长了一些 我多次遇到相同的以下错误 未捕
  • 如何捕获jquery中的任何点击事件[重复]

    这个问题在这里已经有答案了 我有一个按钮 当单击它时 会显示一个带有图像的 div 例如聊天的表情符号面板 如果我再次单击它 div 会隐藏 但我想要做的是 如果 div 已经显示 然后我单击页面的任何其他内容 我想隐藏它 我试过这个 my
  • 获取访客的 Optimizely A/B 测试和变化

    当我在网站上运行实验时 我希望能够找出当前访问者看到的测试和变体 我无法找到如何做到这一点优化Javascript API https www optimizely com docs api 您可以获得第一个正在运行的实验的 ID 假设您有
  • 防止 Node.js 中的 SQL 注入

    是否有可能以与 PHP 具有防范 SQL 注入的预准备语句相同的方式防止 Node js 中的 SQL 注入 最好使用模块 如果是这样 怎么办 如果不 有哪些例子这可能会绕过我提供的代码 见下文 一些背景 我正在制作一个 Web 应用程序
  • 将值传递给映射函数 - CouchDB

    我想知道是否可以将值传递给 couchDB 设计文档中的映射函数 例如 在下面的代码中 可以传递用户输入的值并使用该值来运行地图函数 也许我可以传递用户UserName当他们登录时 然后根据地图功能显示视图 function doc if
  • 如何在 Chrome 中将 Set 转换为数组?

    如何将集合转换为数组 https stackoverflow com questions 20069828 how to convert set to array给出了将 Set 转换为 Array 的三个答案 目前在 Chrome 浏览器
  • 将jQueryUI datepicker附加到div(显示位置错误)

    我在输入上使用 jQueryUI datepicker 默认情况下 jQueryUI 会附加 ui datepicker div to the body该文件的 有问题的输入位于屏幕上的 弹出 div 中 这意味着该 div 之外的任何点击
  • 发送带有图像的嵌套 JSON

    我一直在尝试研究一种能够通过 Ajax 将嵌套 JSON 请求发送回服务器的方法 根据我的理解 我们主要用于向服务器发送图像或文件的 formdata 在这种情况下不起作用 因为 FormData 似乎不处理嵌套对象 这就是我需要发送的有效
  • jQuery onclick 隐藏其父元素[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我想隐藏 li tag on a 使
  • 在 Bootstrap 选择器上使用 jQuery 取消选择选项

    我对一些 UI 元素使用 Bootstrap SelectPicker 它允许用户选择多个选项并将其呈现在段落标签中的屏幕上 他们还应该能够删除选定的选项 这是我的代码 用于将选定的选项渲染到屏幕上 以便每个选项旁边都会显示一个 X 单击它
  • JQuery mouseover 函数多次触发

    我很长时间以来一直使用这种方法来为整个类 按钮等 设置事件 div bigButton mouseover function this style backgroundColor dfdfdf 然而 在进行一些测试时 我刚刚注意到 当将鼠标
  • 抓取 Shopee API v4

    我有一个最终项目 其中我想要检索的数据是通过在shopee上抓取数据来获取的 但是当我在隐藏的API上抓取shopee时遇到问题 当我在Insomnia脚本上尝试时 脚本会运行 但是当我尝试时在本地或 google colab 脚本上 这是
  • HTML 画布从 getImageData 返回“偏离一些”字节

    我找到getImageDataHTML 画布似乎返回不正确的字节值 我使用以下 Python 代码生成了 1x1 px 图像 from PIL import Image import numpy as np a np array 12 18
  • 如何使用 JQuery 创建新的 img 标签,并使用 JavaScript 对象中的 src 和 id?

    我从基本意义上了解 JQuery 但对它绝对是新手 并且怀疑这很容易 我在 JSON 响应中获得了图像 src 和 id 转换为对象 因此在 responseObject imgurl 和 responseObject imgid 中获得了
  • ASP.NET MVC3 Ajax.ActionLink - 条件确认对话框

    我有一个 Ajax ActionLink 仅当满足某些条件 用户有未保存的更改 时 我才希望显示一个确认对话框 我创建了一个 JavaScript 函数 它根据需要显示确认对话框 并根据响应返回 true 或 false 我将其绑定到 Ac

随机推荐