three.js应用cannon物理引擎设置物体的相互作用

2023-11-07

一、cannon物理引擎介绍

cannon官网地址:https://pmndrs.github.io/cannon-es/
Cannon.js 是一个基于 JavaScript 开源 3D 物理引擎,可以用于开发和模拟真实世界中的物理效果。它提供了一系列的物理模拟功能,包括刚体碰撞、重力、碰撞检测、约束和材质等。

Cannon.js 的特点如下:

  1. 轻量级和高性能:Cannon.js 被设计为一个快速而轻便的物理引擎,代码简洁且易于理解。
  2. 真实的物理模拟:Cannon.js 提供了一套完整的 3D 物理模拟功能,包括刚体碰撞、力学模拟和约束等。这使得开发者可以模拟真实世界中的物理效果。
  3. 灵活的约束系统:Cannon.js 的约束系统非常灵活,并且支持各种类型的约束,如距离约束、弹簧约束、旋转约束等。开发者可以根据需要创建各种类型的约束。
  4. 基于 WebGL:Cannon.js 与 WebGL 技术结合使用,可以轻松实现在浏览器中展示 3D 物理效果,并且与其他 WebGL 应用程序进行集成。
  5. 跨平台兼容性:Cannon.js 可以在多种浏览器上运行,并且支持移动设备和桌面设备。这使得开发者可以轻松地在各种平台上开发和运行物理模拟应用程序。

Cannon.js 是一个功能强大、灵活且易于使用的 3D 物理引擎,它可以帮助开发者轻松实现真实世界中的物理效果,并在浏览器中展示出来。无论是游戏开发还是模拟应用程序,Cannon.js 都是一个优秀的选择。

官网示例:
在这里插入图片描述

这个示例展示了如何使用Cannon.js和Three.js实现鼠标拾取功能。

鼠标拾取是指通过鼠标点击来选择和交互3D场景中的物体。在"Three.js mousepick"示例中,首先创建了一个Three.js场景,并添加了一些3D物体,如球体和立方体。然后,使用Cannon.js创建了与这些物体对应的刚体,并将它们添加到物理世界中。

接下来是实现鼠标拾取功能的关键部分。通过监听鼠标点击事件,可以获取鼠标在屏幕上的坐标。通过将这些坐标转换为Three.js中的射线,可以判断该射线是否与场景中的物体相交。如果有相交,就可以选择和交互该物体。

为了实现鼠标拾取,"Three.js mousepick"示例中使用了Three.js的射线投射功能。当鼠标点击事件触发时,首先计算出鼠标点击位置的屏幕坐标,然后使用这些坐标创建一个射线。接下来,使用Three.js的射线投射方法,将射线投射到场景中。如果射线与某个物体相交,就可以获得相交点的坐标。

最后,将鼠标点击事件与鼠标拾取函数关联起来,当鼠标点击时调用该函数。在函数内部,可以根据相交点的坐标进行相应的处理,例如选择物体、移动物体等。

二、cannon.js的安装

这个很简单直接

npm i cannon-es

在项目中要用的地方导入就可以啦

// 导入connon引擎
import * as CANNON from "cannon-es";

// 目标:使用cannon引擎,小球与平面的碰撞案例
console.log(CANNON);

打印出来的CANNON如下
在这里插入图片描述
具体的每一个属性有何作用大家可以去官网看看
官网文档:https://pmndrs.github.io/cannon-es/docs/index.html
在这里插入图片描述

三、案例实战,实现小球和平面的碰撞效果

最终效果如图:
在这里插入图片描述
额、这只是一个静态的,真实的场景是小球在接触到平面之后会回弹,就类似显示中的你将一个篮球从空中放开让其自由落体接触地面的效果。
真实效果类似这个https://img.zcool.cn/community/01ef8959673ffda8012193a30a7d0a.gif

核心步骤如下

  1. 引入Three.js和Cannon.js库。
import * as THREE from "three";
// 导入connon引擎
import * as CANNON from "cannon-es";
  1. 创建Three.js场景。
var scene = new THREE.Scene();
  1. 创建Three.js渲染器。
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
  1. 创建Three.js相机。
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
  1. 创建Cannon.js物理世界。
// 创建物理世界
// const world = new CANNON.World({ gravity: 9.8 });
const world = new CANNON.World();
world.gravity.set(0, -9.8, 0); // 设置重力加速度
// 创建物理小球形状
const sphereShape = new CANNON.Sphere(1);

//设置物体材质
const sphereWorldMaterial = new CANNON.Material();

// 创建物理世界的物体
const sphereBody = new CANNON.Body({
  shape: sphereShape, // 小球形状
  position: new CANNON.Vec3(0, 0, 0), // 小球位置
  //   小球质量
  mass: 1,
  //   物体材质
  material: sphereWorldMaterial,
});

// 将物体添加至物理世界
world.addBody(sphereBody);
  1. 物理世界创建地面和球体
// 物理世界创建地面
const floorShape = new CANNON.Plane(); // 创建地面形状
const floorBody = new CANNON.Body(); // 创建地面物体
const floorMaterial = new CANNON.Material("floor");
floorBody.material = floorMaterial;
// 当质量为0的时候,可以使得物体保持不动
floorBody.mass = 0;
floorBody.addShape(floorShape); // 添加形状
// 地面位置
floorBody.position.set(0, -5, 0);
// 旋转地面的位置
floorBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2);
world.addBody(floorBody);
  1. 创建Three.js球和平面。
// 创建球和平面
const sphereGeometry = new THREE.SphereGeometry(1, 20, 20);
const sphereMaterial = new THREE.MeshStandardMaterial({ color: "#ff0000" });
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.castShadow = true;
scene.add(sphere);

const floor = new THREE.Mesh(
  new THREE.PlaneBufferGeometry(15, 15),
  new THREE.MeshStandardMaterial({ color: "#1670d5" })
);

floor.position.set(0, -5, 0);
floor.rotation.x = -Math.PI / 2;
floor.receiveShadow = true;
scene.add(floor);
  1. 创建动画循环,并将物理世界里的物体位置赋值给three.js里的物体。
function render() {
  //   let time = clock.getElapsedTime();
  let deltaTime = clock.getDelta();
  // 更新物理引擎里世界的物体
  world.step(1 / 120, deltaTime);

  sphere.position.copy(sphereBody.position); // 将物理世界里的物体位置赋值给three.js里的物体

  renderer.render(scene, camera);
  //   渲染下一帧的时候就会调用render函数
  requestAnimationFrame(render);
}

render();

这一步是核心,将three.js和物理世界的物体关联起来了,否则就是两个世界的东西,永远出不了效果

sphere.position.copy(sphereBody.position); // 将物理世界里的物体位置赋值给three.js里的物体

完整代码如下:

import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
// 导入动画库
import gsap from "gsap";
// 导入dat.gui
import * as dat from "dat.gui";
// 导入connon引擎
import * as CANNON from "cannon-es";

// 目标:使用cannon引擎,小球与平面的碰撞案例
console.log(CANNON);

// const gui = new dat.GUI();
// 1、创建场景
const scene = new THREE.Scene();

// 2、创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 300);

// 设置相机位置
camera.position.set(0, 0, 18);
scene.add(camera);

// 创建球和平面
// 创建立方体
// const cubeGeometry = new THREE.BoxBufferGeometry(1, 1, 1);
const sphereGeometry = new THREE.SphereGeometry(1, 20, 20);
const sphereMaterial = new THREE.MeshStandardMaterial({ color: "#ff0000" });
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.castShadow = true;
scene.add(sphere);

const floor = new THREE.Mesh(
  new THREE.PlaneBufferGeometry(15, 15),
  new THREE.MeshStandardMaterial({ color: "#1670d5" })
);

floor.position.set(0, -5, 0);
floor.rotation.x = -Math.PI / 2;
floor.receiveShadow = true;
scene.add(floor);

// 创建物理世界
// const world = new CANNON.World({ gravity: 9.8 });
const world = new CANNON.World();
world.gravity.set(0, -9.8, 0); // 设置重力加速度
// 创建物理小球形状
const sphereShape = new CANNON.Sphere(1);

//设置物体材质
const sphereWorldMaterial = new CANNON.Material();

// 创建物理世界的物体
const sphereBody = new CANNON.Body({
  shape: sphereShape, // 小球形状
  position: new CANNON.Vec3(0, 0, 0), // 小球位置
  //   小球质量
  mass: 1,
  //   物体材质
  material: sphereWorldMaterial,
});

// 将物体添加至物理世界
world.addBody(sphereBody);

// 物理世界创建地面
const floorShape = new CANNON.Plane(); // 创建地面形状
const floorBody = new CANNON.Body(); // 创建地面物体
const floorMaterial = new CANNON.Material("floor");
floorBody.material = floorMaterial;
// 当质量为0的时候,可以使得物体保持不动
floorBody.mass = 0;
floorBody.addShape(floorShape); // 添加形状
// 地面位置
floorBody.position.set(0, -5, 0);
// 旋转地面的位置
floorBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2);
world.addBody(floorBody);

// 创建击打声音
// const hitSound = new Audio("assets/metalHit.mp3");
// 添加监听碰撞事件
function HitEvent(e) {
  // 获取碰撞的强度
  //   console.log("hit", e);
  const impactStrength = e.contact.getImpactVelocityAlongNormal(); // 获取碰撞的强度
  console.log("碰撞强度", impactStrength);
//   if (impactStrength > 2) {
//     //   重新从零开始播放
//     hitSound.currentTime = 0;
//     hitSound.play(); // 播放声音
//   }
}
sphereBody.addEventListener("collide", HitEvent); // 监听碰撞事件

// 设置2种材质碰撞的参数
const defaultContactMaterial = new CANNON.ContactMaterial(sphereMaterial, floorMaterial, {
  //   摩擦力
  friction: 0.3,
  // 弹性
  restitution: 0.6,
});

// 将材料的关联设置添加的物理世界
world.addContactMaterial(defaultContactMaterial);

//添加环境光和平行光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); // 环境光
scene.add(ambientLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.5); // 平行光
dirLight.castShadow = true; // 设置光源产生阴影
scene.add(dirLight);

// 初始化渲染器
// 渲染器透明
const renderer = new THREE.WebGLRenderer({ alpha: true });
// 设置渲染的尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight);
// 开启场景中的阴影贴图
renderer.shadowMap.enabled = true;

// console.log(renderer);
// 将webgl渲染的canvas内容添加到body
document.body.appendChild(renderer.domElement);

// // 使用渲染器,通过相机将场景渲染进来
// renderer.render(scene, camera);

// 创建轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置控制器阻尼,让控制器更有真实效果,必须在动画循环里调用.update()。
controls.enableDamping = true;

// 添加坐标轴辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 设置时钟
const clock = new THREE.Clock();

function render() {
  //   let time = clock.getElapsedTime();
  let deltaTime = clock.getDelta();
  // 更新物理引擎里世界的物体
  world.step(1 / 120, deltaTime);

  sphere.position.copy(sphereBody.position); // 将物理世界里的物体位置赋值给three.js里的物体

  renderer.render(scene, camera);
  //   渲染下一帧的时候就会调用render函数
  requestAnimationFrame(render);
}

render();

// 监听画面变化,更新渲染画面
window.addEventListener("resize", () => {
  //   console.log("画面变化了");

  // 更新摄像头
  camera.aspect = window.innerWidth / window.innerHeight;
  //   更新摄像机的投影矩阵
  camera.updateProjectionMatrix();

  //   更新渲染器
  renderer.setSize(window.innerWidth, window.innerHeight);
  //   设置渲染器的像素比
  renderer.setPixelRatio(window.devicePixelRatio);
});

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

three.js应用cannon物理引擎设置物体的相互作用 的相关文章

随机推荐

  • HBase选择Store file做compaction的算法

    The algorithm is basically as follows Run over the set of all store files from oldest to youngest If there are more than
  • Maven : Log4j2 could not find a logging implementation

    1 美图 3 错误信息 Log4j2 could not find a logging implementation Please add log4j core to the classpath 解决办法 pom把log4j api也加进去
  • 开源电路仿真软件CircuitJS1介绍与使用入门

    文章目录 前言 基础介绍 使用入门 界面与显示 绘制与操作 保存为文件 子电路绘制与使用 总结 前言 在做电路设计的过程中经常需要用到电路仿真软件对设计的电路进行仿真 以确定电路工作特性或者元件的参数取值 使用电路仿真软件可以缩短电路开发时
  • CentOS系统安装Apache步骤详解

    1 通过 yum y install httpd 安装apache服务 2 使用 systemctl start httpd 启动Apache服务器 并测试启动情况 3 修改主页 4 访问测试 这样 centos的apache服务就搭建好了
  • 单片机毕设 基于stm32的WiFi监控小车

    文章目录 1 背景 2 系统设计方案 2 1 实现功能 2 1 1 硬件部分 2 1 2 软件部分 2 1 3 WIFI通信功能 2 2 系统架构 2 2 1 WiFi 通信 2 2 2 电机驱动 2 2 3 摄像头 2 2 4 舵机 2
  • 合宙Air724UG LuatOS-Air LVGL API控件-图片 (Image)

    图片 Image 图片IMG是用于显示图像的基本对象类型 图像来源可以是文件 或者定义的符号 示例代码 创建图片控件 img lvgl img create lvgl scr act nil 设置图片显示的图像 lvgl img set s
  • C# 执行 .bat 文件

    string path E a bat Process pro new Process FileInfo file new FileInfo path pro StartInfo WorkingDirectory file Director
  • 在react hook里使用mobx(配置mobx依赖)

    在powershell里安装依赖 直接npm i mobx或者npm i mobx react是会报错的 npm i mobx mobx react save save是下载到 dependencies 里 npm i mobx react
  • 图像边缘及matlab实现

    图像边缘是图像的重要特征 是图像中特性 如像素灰度 纹理等 分布的不连续处 图像周围特性有阶跃变化或屋脊状变化的那些像素集合 图像的边缘部分集中了图像的大部分信息 一幅图像的边缘结构与特点往往是决定图像特质的重要部分 图像边缘的另一个定义是
  • Spring Boot + Vue的网上商城之物流系统实现

    Spring Boot Vue的网上商城之物流系统实现 思路 当构建一个物流系统时 我们可以按照以下步骤进行 设计数据模型 首先确定系统中需要存储的数据 例如物流公司信息 物流订单信息等 根据需求设计相应的数据模型 包括实体类和数据库表结构
  • 软件工程考试归纳知识点

    软件工程 第一章 什么是软件 软件是计算机系统中与硬件子系统相互依存的另一个子系统 是一个包含程序及其文档资料的完整集合 提供了用户与硬件子系统之间的接口 软件的特征 1 软件固有的特性 复杂性 抽象性 依赖性 软件使用特性 2 软件生产特
  • Python之可变参数,*参数,**参数,以及传入*参数,进行解包

    1 定义了一个需要两个参数的函数 def print str first second print first print second if name main print str hello world 如果传一个参数调用 print
  • Blink 帮助文档 编译

    个人使用的是Centos7 1 安装rvm 参考 http rvm io rvm install gpg keyserver hkp pool sks keyservers net recv keys 409B6B1796C275462A1
  • Rocksdb Compaction原理

    概述 compaction主要包括两类 将内存中imutable 转储到磁盘上sst的过程称之为flush或者minor compaction 磁盘上的sst文件从低层向高层转储的过程称之为compaction或者是major compac
  • c++中引用及指针详解

    这里写目录标题 1 指针 1 1 什么是指针 指针的本质 指针与地址 程序中如何声明指针以及如何使用运算符 和 1 2 指针有什么作用 指针与函数参数 2 引用 2 1 什么是引用 2 2 引用的规则 2 3 引用和数组 引用的数组 非法
  • 在linux中,使用sh文件脚本启动jar项目

    使用方法 sh 执行脚本 sh start stop restart status sh文件内容 APP NAME XXXX jar 使用说明 用来提示输入参数 usage echo Usage sh 执行脚本 sh start stop
  • SpringBoot HTTP接口GET请求

    HTTP接口get请求 注解使用 1 RequestMapping 来映射请求 也就是通过它来指定控制器可以处理哪些URL请求 2 PathVariable 将 URL 中的占位符绑定到控制器的处理方法的参数中 占位符使用 括起来 3 Ge
  • 完全数(Perfect number),又称完美数或完备数,是一些特殊的自然数。 它所有的真因子(即除了自身以外的约数)的和(即因子函数),恰好等于它本身。 例如:28,它有约数1 style="">

    题目 完全数 Perfect number 又称完美数或完备数 是一些特殊的自然数 它所有的真因子 即除了自身以外的约数 的和 即因子函数 恰好等于它本身 例如 28 1 2 4 7 14 28 给定数计算n以内完全数的个数 计算范围 0
  • 《PRML》学习笔记2.2——多项式分布和狄利克雷分布

    上回讲完了伯努利分布 二项分布和Beta分布 以及从最大似然估计的非参数化思想和引入共轭先验 使得参数变成一个变量 建模求解的参数化方法两方面介绍了求解模型参数的方法 没有读过的朋友可以参考 PRML 学习笔记2 1 伯努利分布 二项分布和
  • three.js应用cannon物理引擎设置物体的相互作用

    一 cannon物理引擎介绍 cannon官网地址 https pmndrs github io cannon es Cannon js 是一个基于 JavaScript 的开源 3D 物理引擎 可以用于开发和模拟真实世界中的物理效果 它提