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

news2025/1/24 22:45:06

我们用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 = 8
        camera.position.y = 5
        camera.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.2
        boxMaterialCon.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) // 因为渲染的东西不是无限衍生,这里给10x10
        const 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.coloradmin.cn/o/1495114.html

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

相关文章

【center-loss 中心损失函数】 原理及程序解释(完)

文章目录 前言问题引出open-set问题抛出 解决方法softmax函数、softmax-loss函数解决代码&#xff08;center_loss.py&#xff09;原理程序解释 代码运用 如何梯度更新首先了解一下基本的梯度下降算法然后代码解释见下面train() 补充&#xff1a;外围知识&#xff08;models.py…

同步通信和异步通信(RabbitMq学习前篇)

MQ学习前篇 文章目录 MQ学习前篇1、同步和异步通讯1.1、同步通讯和异步通讯1.2、同步调用存在的问题1.3、异步调用方案1.4、异步通信的缺点 1、同步和异步通讯 学习mq之前&#xff0c;就要先知道同步通讯和异步通讯的区别。 1.1、同步通讯和异步通讯 同步通讯就像是打电话&am…

部署LVS集群之DR模式

直接路由模式----DR模式 理念&#xff1a; 直接路由&#xff08;是lvs的默认模式&#xff09; DR模式和隧道模式唯一的区别&#xff1a;dr模式这四台服务器在同一网段&#xff0c;隧道模式 &#xff1a;这四台服务器不在同一网段 客户端 ------->代理服务器------->真实…

Unity中关于继承ScriptableObject的类

在游戏中我们会经常看到一些.asset的配置文件&#xff0c;而这些文件就是用一个自定义的类去继承ScriptableObject来生成的。比如当前有一些零散特效需要预加载&#xff0c;这个时候我们可以声明一个类去保存这些零散特效对象的信息&#xff0c;然后统一读取加载。 代码&#…

Pycharm与Anaconda安装

网址&#xff1a; Pycharm&#xff1a;https://www.jetbrains.com/pycharm/ Anaconda&#xff1a;https://www.anaconda.com/download/ 官网下载速度太慢可以选择到清华源下载&#xff1a;https://repo.anaconda.com/archive/ 一&#xff1a;Anaconda安装 安装&#xff1a; …

万丈高树平地起:通过中序与后序遍历数组构建二叉树

题目 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入&#xff1a;inorder [9,3,15,20,7], postorder [9,15,7,20,3] 输出&#xf…

机器学习---数据分割

之前的文章中写过&#xff0c;我们可以通过实验测试来对学习器的泛化误差进行评估并进而做出选择。 为此&#xff0c;需使用一个“测试集"(testing set)来测试学习器对新样本的判别能力&#xff0c;然后以测试集上的“测 试误差”(testing error)作为泛化误差的近似。通…

即插即用篇 | YOLOv8 引入 NAM 注意力机制 | 《NAM: Normalization-based Attention Module》

论文名称:《NAM: Normalization-based Attention Module》 论文地址:https://arxiv.org/pdf/2111.12419.pdf 代码地址:https://github.com/Christian-lyc/NAM 文章目录 1 原理2 源代码3 添加方式4 模型 yaml 文件template-backbone.yamltemplate-small.yamltemplate-large…

b站小土堆pytorch学习记录——P8-P9 Tensorboard的使用

文章目录 一、前置知识1.Tensorboard是什么2.SummaryWriter3.add_scalar()4.add_image() 二、代码1.一次函数2.蚂蚁和蜜蜂图片 一、前置知识 1.Tensorboard是什么 TensorBoard 是 TensorFlow 的可视化工具&#xff0c;它允许开发者可视化模型的图&#xff08;graph&#xff0…

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…

论文阅读:Dataset Quantization

摘要 最先进的深度神经网络使用大量&#xff08;百万甚至数十亿&#xff09;数据进行训练。昂贵的计算和内存成本使得在有限的硬件资源上训练它们变得困难&#xff0c;特别是对于最近流行的大型语言模型 (LLM) 和计算机视觉模型 (CV)。因此最近流行的数据集蒸馏方法得到发展&a…

如何构建用于物体和标志检测的自定义模型

让我们快速了解一下AWS的机器学习技术栈&#xff0c;它几乎提供了解决我们业务问题所需的所有机器学习方面的支持。 物体检测是什么&#xff1f; 物体检测是从图像或视频帧中检测特定类别实例的任务。我们的目标是在图像/视频帧中找出哪里有什么物体。它是其他依赖物体的任务…

Locust中wait_time中匿名函数使用方法浅析

前言 翻出之前做个压测项&#xff0c;看到locust中对等待时间的实现方式感到好奇&#xff0c;于是总结下来。 源代码实现 def between(min_wait, max_wait):"""Returns a function that will return a random number between min_wait and max_wait.Example:…

数据库:2024/3/6

作业1&#xff1a;使用C语言完成数据库的增删改 代码&#xff1a; #include <myhead.h>//定义添加员工信息函数 int Add_worker(sqlite3 *ppDb) {//准备sql语句printf("请输入要添加的员工信息:\n");//从终端获取员工信息char rbuf[128]"";fgets(r…

centos7安装maven离线安装

1、从官方网站下载maven文件包 官方下载网站&#xff1a;https://maven.apache.org/download.cgi 2、创建文件夹解压文件 将下载好的安装包&#xff0c;放到创建的目录下&#xff0c;并解压 a、创建/app/maven文件 mkdir /app/mavenb、解压文件 tar -zxvf apache-maven-…

AIOps常见问题

AIOps的自动化通常指什么&#xff1f; AIOps 平台的自动化一般包括以下几个方面&#xff1a; 数据收集和整合&#xff1a;AIOps 平台可以从多个 IT 基础架构组件、应用需求与性能监视工具以及服务工单系统等数据源中收集并整合运维数据&#xff0c;形成一个全面的数据平台。数…

FC-AE-1553 协议

FC-AE-1553 协议 MIL-STD-1553B总线协议总线结构字格式消息传输方式 FC协议FC协议栈拓扑结构服务类型帧/序列/交换FC帧格式 FC-AE-1553网络构成帧类型命令帧状态帧数据帧 Information UnitsNC1NC2NC3-4NC5-7NT1-7 传输模式1. NC-NT2. NT-NC3. NT-NT4. 无数据字的模式命令5. 带数…

C++ spfa判断负环

给定一个 n 个点 m 条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c; 边权可能为负数。 请你判断图中是否存在负权回路。 输入格式 第一行包含整数 n 和 m 。 接下来 m 行每行包含三个整数 x,y,z &#xff0c;表示存在一条从点 x 到点 y 的有向边&#xff0c;边…

GEE 依照范围裁剪 下载Sentinel-2数据

0. GEE介绍 Google Earth Engine&#xff08;GEE&#xff09; 是由Google开发的一种云端平台&#xff0c;旨在提供强大的地理空间数据处理和分析工具。GEE集成了大量的遥感影像数据和地理空间数据集&#xff0c;以及高性能的计算资源&#xff0c;使用户能够在云端高效地进行大规…

Java基础概念 7-计算机中的数据存储

目录 Java基础概念 7-计算机中的数据存储 计算机的存储规则 进制 十进制:0123456789 二进制:01 常见的进制 不同进制在代码中的表现形式 计算机为什么用二进制存储数据? 进制之间的转换 任意进制转十进制 公式: 系数*基数的权次幂 相加 二进制转十进制** 八进制转…