Three.js--》探寻Cannon.js构建震撼的3D物理交互体验(二)

我们用three.js可以绘制出各种酷炫的画面,但是当我们想要一个更加真实的物理效果的话,这个时候我们就需要一个物理的库,接下来我们就讲解一下今天要学习的canon,它可以给我们提供一个更加真实的物理效果,像物体的张力、摩擦力、拉伸、反弹等等各种真实的物理效果。该库都能够有一个非常好的模拟。

PS:目前博主在一家互联网公司工作,该公司的编码风格是vue+tsx,所以接下来的项目以该编码风格进行举例,详细了解参考我之前的文章:地址 。

目录

碰撞与碰撞事件

休眠与休眠事件

物体形状组合

物体施加作用力


碰撞与碰撞事件

在上一篇文章我们讲解到了碰撞的一些基本概念:地址 ,接下来我们开始学习如何监听和获取碰撞的事件和信息,如下:

得到的结果如下所示:

我们可以点击监听信息e查看一下,有如下重要的信息:

我们点击contact进入到里面,可以找到一个函数来查看当前物体碰撞的碰撞速度:

接下来通过函数来对当前物体的碰撞速度度进行一个监听:

// 监听立方体
boxBody.addEventListener("collide", (e) => {console.log("碰撞了", e)let impactStrength = e.contact.getImpactVelocityAlongNormal()console.log("当前的碰撞速度:", impactStrength)
})

可以看到,随着时间的流逝,碰撞的高度逐渐减小,物体相互之间的碰撞速度也在逐渐减少:

休眠与休眠事件

物体的休眠指的是当物体在物理世界中没有发生运动时,会进入休眠状态以减少计算负担。当物体处于休眠状态时,物理引擎会暂停对该物体的碰撞检测和运动模拟,从而节省计算资源。只有在物体被外力唤醒(如碰撞或施加力)时,物体才会从休眠状态中唤醒,重新参与物理模拟。休眠状态有助于提高物理引擎的性能并减少不必要的计算开销。

我们在开始的时候允许物理世界的物体可以进入休眠状态:

然后我们可以设置立方体可以进入休眠状态,以及进入休眠状态的一些条件:

如下可以看到我们已经实现了立方体休眠的效果:

当然如果想监听休眠事件的话可以通过如下的方式进行:

在最后的那一刻物体进入到了休眠的状态:

物体形状组合

接下来我们通过设置物体的位置,让物体之间相互组合在一起,如下:

// 创建由两个球加一个圆柱体组成的胶囊体
const capsuleBody = new CANNON.Body({mass: 1,position: new CANNON.Vec3(0, 5, 0),material: boxMaterialCon,
})
// 创建一个球的几何体
const sphereShape = new CANNON.Sphere(0.5)
// 创建一个圆柱体几何体
const cylinderShape = new CANNON.Cylinder(0.5, 0.5, 1.5, 20)
// 将球体和圆柱体添加到body中
capsuleBody.addShape(sphereShape, new CANNON.Vec3(0, 0.75, 0))
capsuleBody.addShape(cylinderShape, new CANNON.Vec3(0, 0, 0))
capsuleBody.addShape(sphereShape, new CANNON.Vec3(0, -0.75, 0))
// 将body添加到物理世界中
world.addBody(capsuleBody)
phyMeshes.push(capsuleBody)// 创建胶囊体网格
const capsuleGeometry =new THREE.CylinderGeometry(0.5, 0.5, 1.5, 20);
const capsuleMaterial=new THREE.MeshBasicMaterial({color: 0x00ff00 })
const capsuleMesh = new THREE.Mesh(capsuleGeometry, capsuleMaterial);
capsuleMesh.position.copy(capsuleBody.position);
capsuleMesh.quaternion.copy(capsuleBody.quaternion);
scene.add(capsuleMesh);
meshes.push(capsuleMesh);
//创建一个球体
const sphereGeometry = new THREE.SphereGeometry(0.5, 20, 20);
const sphereMaterial=new THREE.MeshBasicMaterial({ color: 0x0000ff })
const sphereMesh =new THREE.Mesh(sphereGeometry, sphereMaterial);
sphereMesh.position.set(0, 0.75, 0);
capsuleMesh.add(sphereMesh);
const sphereMesh2 = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphereMesh2.position.set(0, -0.75, 0);
capsuleMesh.add(sphereMesh2);

得到的代码效果如下所示:

接下来我们给胶囊添加一个水平的速度,然后减少弹性系数增加摩擦系数,看看胶囊会不会倒下

得到的效果如下所示:

具体操作代码如下:

import { defineComponent } from "vue";
import * as THREE from 'three'
import { OrbitControls }  from 'three/examples/jsm/controls/OrbitControls.js'
import * as CANNON from 'cannon-es'
import './index.scss'export default defineComponent({setup() {// 初始化物理世界const world = new CANNON.World()// 初始化物理世界的重力world.gravity.set(0, -9.82, 0)// 设置物理世界允许休眠world.allowSleep = true// 初始化3D世界const scene = new THREE.Scene()// 初始化相机const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)camera.position.z = 8camera.position.y = 5camera.position.x = 2// 初始化渲染器const renderer = new THREE.WebGLRenderer({ antialias: true })renderer.setSize(window.innerWidth, window.innerHeight)document.body.appendChild(renderer.domElement)// 初始化控制器const controls = new OrbitControls(camera, renderer.domElement)controls.enableDamping = true// 创建网格数组let phyMeshes: any[] = [] // 物理世界let meshes: any[] = [] // 渲染世界// 设置立方体的材质const boxMaterialCon = new CANNON.Material("boxMaterial")boxMaterialCon.friction = 0.2boxMaterialCon.restitution = 0.1 // 设置弹性// 创建由两个球加一个圆柱体组成的胶囊体const capsuleBody = new CANNON.Body({mass: 1,position: new CANNON.Vec3(0, 5, 0),material: boxMaterialCon,})// 创建一个球的几何体const sphereShape = new CANNON.Sphere(0.5)// 创建一个圆柱体几何体const cylinderShape = new CANNON.Cylinder(0.5, 0.5, 1.5, 20)// 将球体和圆柱体添加到body中capsuleBody.addShape(sphereShape, new CANNON.Vec3(0, 0.75, 0))capsuleBody.addShape(cylinderShape, new CANNON.Vec3(0, 0, 0))capsuleBody.addShape(sphereShape, new CANNON.Vec3(0, -0.75, 0))// 将body添加到物理世界中world.addBody(capsuleBody)phyMeshes.push(capsuleBody)// 创建胶囊体网格const capsuleGeometry =new THREE.CylinderGeometry(0.5, 0.5, 1.5, 20);const capsuleMaterial=new THREE.MeshBasicMaterial({color: 0x00ff00 })const capsuleMesh = new THREE.Mesh(capsuleGeometry, capsuleMaterial);capsuleMesh.position.copy(capsuleBody.position);capsuleMesh.quaternion.copy(capsuleBody.quaternion);scene.add(capsuleMesh);meshes.push(capsuleMesh);// 给胶囊一个水平的速度capsuleBody.velocity.set(2, 0, 0)//创建一个球体const sphereGeometry = new THREE.SphereGeometry(0.5, 20, 20);const sphereMaterial=new THREE.MeshBasicMaterial({ color: 0x0000ff })const sphereMesh =new THREE.Mesh(sphereGeometry, sphereMaterial);sphereMesh.position.set(0, 0.75, 0);capsuleMesh.add(sphereMesh);const sphereMesh2 = new THREE.Mesh(sphereGeometry, sphereMaterial);sphereMesh2.position.set(0, -0.75, 0);capsuleMesh.add(sphereMesh2);// 创建一个物理世界的平面// const planeShape = new CANNON.Plane()const planeShape = new CANNON.Box(new CANNON.Vec3(5, 0.1, 5))// 创建一个刚体const planeBody = new CANNON.Body({// mass: 0, // 设置质量为0,不受碰撞的影响shape: planeShape,position: new CANNON.Vec3(0, 0.1, 0),type: CANNON.Body.STATIC, // 设置物体为静态,不受碰撞的影响material: boxMaterialCon,})// 将刚体添加到物理世界当中world.addBody(planeBody)// 物理世界创建的东西不显示,所以我们要再通过three.js再创建一个平面// const planeGeometry = new THREE.PlaneGeometry(10, 10) // 因为渲染的东西不是无限衍生,这里给10x10const planeGeometry = new THREE.BoxGeometry(10, 0.2, 10)// 创建一个平面材质const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 })// 创建一个平面网格const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial)// 旋转平面90度让其平铺// planeMesh.rotation.x = 0.1// 将网格添加到3D场景当中scene.add(planeMesh)// 渲染let clock = new THREE.Clock()const animate = () => {// 获取了两次渲染之间的时间差,通常用于控制动画和物理模拟。let delta = clock.getDelta()world.step(delta)// 使用时间差来推进物理世界的模拟for(let i = 0; i < phyMeshes.length; i++) {meshes[i].position.copy(phyMeshes[i].position)meshes[i].quaternion.copy(phyMeshes[i].quaternion)}controls.update()renderer.render(scene, camera)requestAnimationFrame(animate)}animate()return () => {<div></div>}}
})

物体施加作用力

在cannon中我们也可以给物体施加一些作用力,体验网站:地址 ,这里面动态的给我们显示了给物体施加作用力的效果:

接下来我们通过代码进行一个简单的实现,创建一个物理球:

然后我们设置一个点击事件,当发生点击之后,球会沿着水平方向移动,并且会发生旋转:

// 监听点击事件
window.addEventListener("click", () => {sphereBody.applyForce(new CANNON.Vec3(10, 0, 0), new CANNON.Vec3(0, 0.5, 0),)
})

效果如下,我不断的点击鼠标,小球就往前移动:

当然还有如下的操作方式,这里就不再一一赘述了,感兴趣的可以自行尝试:

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

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

相关文章

程序员超强大脑——提高命名的质量(二)

哪些类型的标识符更容易理解 使用完整的单词似乎是一个合理的选择。虽然单词构成的标识符便于理解&#xff0c;但较长的标识符也可能产生负面影响。设计高质量的标识符是需要仔细衡量单词的清晰性与缩写的简洁性&#xff1a;注意单词的清晰性便于代码阅读者理解程序和排查错误…

粒子群算法优化ELMAN神经网络的分类预测,pso-elman

目录 背影 ELMAN神经网络的原理 ELMAN神经网络的定义 受限玻尔兹曼机(RBM) 灰狼算法原理 灰狼算法优化elman神经网络回归分析 基本结构 主要参数 数据 MATALB代码 结果图 展望 完整代码下载:粒子群算法优化ELMAN神经网络的分类预测资源-CSDN文库 https://download.csdn.net…

2024护网面试题精选(一)

0x00.基础漏洞篇 00-TOP10漏洞 1.SQL注入 2.失效的身份认证和会话管理 3.跨站脚本攻击XSS 4.直接引用不安全的对象 5.安全配置错误 6.敏感信息泄露 7.缺少功能级的访问控制 8.跨站请求伪造CSRF 9.实验含有已知漏洞的组件 10.未验证的重定向和转发 01-SQL注入漏洞 …

java多线程 简简单单的学它

java多线程 什么是线程 通俗的讲&#xff0c;就是一个软件里相互独立&#xff0c;同时运行的功能。比如我们打开B站&#xff0c;看视频时&#xff0c;我们会看到画面&#xff0c;听到声音&#xff0c;闪过的弹幕&#xff0c;这些都可以看作是一个线程。 在理解线程前&#x…

【Java_JSON】如何从JSON数据中提取value值

如何从JSON数据中提取value值&#xff1f; 首先将JSON数据转成字符串 创建JSONObject 对象 通过kv键值对的特性 使用key值来获取value 值 并输出 结果&#xff1a;

c++中锁定数据出现读写错误的例子

2010-4 程序出现了问题&#xff0c;引出了数据逻辑问题。 下面两个函数GetNextDataIndexW();和SetNextDataIndexW分别锁定&#xff0c; 导致了可能两个线程通过了GetNextDataIndexW();但是在SetNextDataIndexW出现数据写入错误。 这是一个典型的数据同步错误&#xff0c;也是…

mysql 中的一些重要函数

show create table user_profile 查看表结构 1.datediff(end_date,start_date)函数&#xff0c;now(), curdate() curtime() date_add(日期,interval num 时间) date_format(日期,格式) 4.select IFNULL(null,0); oracle 中nvl 函数 5.select IF(2 > 1, 2,0)&#xff…

c++: 缺省参数/默认参数的详解及其应用

c缺省参数/默认参数的详解及其应用 缺省参数是什么 #include<iostream> using namespace std;void func(int a 666) {cout << "a " << a << endl; } int main() {func(); //没有传参func(10); //传参return 0; }缺省参数就是在我们不进…

计算机体系结构:VLIW

原文来自知乎 计算机体系结构&#xff1a;VLIW 本文主要介绍计算机体系结构中的VLIW&#xff0c;以供读者能够理解该技术的定义、原理、应用。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;计算机杂记 &#x1f…

腾讯云服务器99元一年购买入口链接

腾讯云服务器99元一年购买入口链接如下&#xff0c;现在已经降价到61元一年&#xff0c;官方活动链接如下&#xff1a; 腾讯云99元服务器一年购买页面腾讯云活动汇聚了腾讯云最新的促销打折、优惠折扣等信息&#xff0c;你在这里可以找到云服务器、域名、数据库、小程序等等多种…

【uniapp】小程序自定义一个通用的返回按钮组件

左边箭头&#xff0c;右边文字可以自定义&#xff0c;但是不要太长&#xff0c;太长可以自己改 .back的width值&#xff0c;改宽一点。 用这个组件的时候首先要在pages.json里把导航栏变成自定义的&#xff1a; ,{"path" : "pages/test/test","style&…

C#常识篇(三)

内置类型字节大小 以下是 C# 中常见内置数据类型的字节大小&#xff1a; bool&#xff08;布尔&#xff09;类型&#xff1a;通常为 4 或者 8 字节。在不同平台上可能会有所不同。 byte&#xff08;无符号字节&#xff09;类型&#xff1a;始终为 1 字节。 sbyte&#xff08;有…

Goose:Golang中的数据库迁移工具

Goose&#xff1a;Golang中的数据库迁移工具 在Golang开发中&#xff0c;数据库迁移是一个常见的任务&#xff0c;用于管理数据库模式的演化和版本控制。Goose是一个轻量级的、易于使用的数据库迁移工具&#xff0c;专为Golang开发者设计。本文将介绍Goose的基本概念、用法和优…

智能驾驶规划控制理论学习01-自动驾驶系统介绍、规划控制模块介绍

目录 一、自动驾驶系统概述 二、规划控制模块介绍 1、规划控制架构 2、规划控制目标 3、Cartesian和Frenet坐标系 4、Frenet坐标系概览 5、解耦式规划和联合式规划 一、自动驾驶系统概述 目前被国内外广为接受的自动驾驶级别划分标准是 SAE&#xff…

Nano 33 BLE Sense Rev2学习第二节——手机蓝牙接收数据

Nano 33 BLE Sense Rev2需要下载的程序 #include <ArduinoBLE.h> #include "Arduino_BMI270_BMM150.h"float x, y, z; int degreesX 0; int degreesY 0;BLEService ledService("19B10010-E8F2-537E-4F6C-D104768A1214"); // create service// cre…

2024年两会-区块链方向-新质生产力-先进制造业集群

区块链寒冬还未散去&#xff0c;但区块链引发的信任革命&#xff0c;对生产关系的变革&#xff0c;对数字金融产生的广泛影响&#xff0c;对货币金融体系的完全重构&#xff0c;对数据融合和隐私安全带来的巨大冲击才刚刚开始&#xff0c;没有不好的技术&#xff0c;只有不好的…

XSS漏洞--概念、类型、实战--分析与详解[结合靶场pikachu]

目录 一、XSS概念简述 1、XSS简介&#xff1a; 2、XSS基本原理&#xff1a; 3、XSS攻击流程&#xff1a; 4、XSS漏洞危害&#xff1a; 二、XSS类型&#xff1a; 1、反射型XSS&#xff1a; 2、存储型XSS&#xff1a; 3、DOM型XSS&#xff1a; 三、靶场漏洞复现(pikach…

LVS四层负载均衡集群

简介 LVS&#xff08;Linux Virtual Server&#xff09;即Linux虚拟服务器&#xff0c;是由章文嵩博士主导的开源负载均衡项目&#xff0c;目前LVS已经被集成到Linux内核模块中。该项目在Linux内核中实现了基于IP的数据请求负载均衡调度方案&#xff0c;终端互联网用户从外部访…

基于 Guava 的 RateLimiter 实现预热功能

基于 Guava 的 RateLimiter 实现预热功能 Guava 的 RateLimiter 类提供了一种限流的机制&#xff0c;它可以控制在给定的时间间隔内允许的操作速率。虽然 Guava 的 RateLimiter 类本身并没有提供预热模型的功能&#xff0c;但是你可以通过自定义的方式实现一个简单的预热模型。…

html地铁跑酷

下面是一个简单的HTML代码来展示一个地铁跑酷游戏&#xff1a; <!DOCTYPE html> <html> <head><title>地铁跑酷</title><style>#player {position: absolute;top: 0;left: 0;width: 50px;height: 50px;background-color: red;}</style…