Threejs之球发射实战
本文目录
- 前言
- 一、效果预览
- 二、代码实现及解析
- 2.1 代码
- 2.2 解析
前言
本篇将基于Threejs之模拟小球反弹基础上以及Threejs这个专栏学习过的知识点上进行小球更加真实的物理运动轨迹,并且还会与鼠标进行交互的操作。由于知识点都在上篇均有涉及,本篇就不过多赘述了。
一、效果预览
最终效果预览:

可以看到,我们完成鼠标点击生成小球,并且小球带有阴影以及贴合现实世界的物理行为,下落,旋转,碰撞。
二、代码实现及解析
2.1 代码
废话不多说直接上代码:
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>html,body {margin: 0;padding: 0;width: 100%;height: 100%;}</style>
</head><body><script type="module">// 倒入轨道控制器import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';import * as THREE from "three";import * as CANNON from "cannon";// 创建场景const scene = new THREE.Scene();scene.background = new THREE.Color(0xffffff);// 创建相机const camera = new THREE.PerspectiveCamera( // 透视相机45, // 视角 角度数window.innerWidth / window.innerHeight, // 宽高比 占据屏幕0.1, // 近平面(相机最近能看到物体)1000, // 远平面(相机最远能看到物体));camera.position.set(0, 2, 20);// 创建渲染器const renderer = new THREE.WebGLRenderer({antialias: true, // 抗锯齿});// 设置渲染器宽高renderer.setSize(window.innerWidth, window.innerHeight);// renderer(渲染器)的dom元素添加到我们的HTML文档中document.body.appendChild(renderer.domElement);// 加入灯光const light = new THREE.PointLight( 0xffffff, 1000, 100 );light.position.set(0,20,0);const pointLightHelper = new THREE.PointLightHelper( light, 1 );scene.add( pointLightHelper );scene.add( light );const ambientLight = new THREE.AmbientLight( 0x404040, 100 ); // 柔和的白光scene.add( ambientLight );// 地面const plane = new THREE.Mesh(new THREE.PlaneGeometry(40, 40),new THREE.MeshStandardMaterial({color: 0x817936}));plane.rotation.x = -Math.PI/2;// 添加到场景中scene.add(plane);renderer.shadowMap.enabled = true;plane.receiveShadow = true;light.castShadow = true;// 创建物理世界const physicsWorld = new CANNON.World();// 设置y轴重力physicsWorld.gravity.set(0, -9.82, 0);// 创建物理材料const groundMaterial = new CANNON.Material('groundMaterial');const sphereMaterial = new CANNON.Material('sphereMaterial');const contactMaterial = new CANNON.ContactMaterial(groundMaterial, sphereMaterial, {restitution: 0.7 // 弹性});physicsWorld.addContactMaterial(contactMaterial);// 创建物理地面const groundBody = new CANNON.Body({mass: 0, // 为0表示地面不受重力影响shape: new CANNON.Plane(), // 物体的形状,cannon中地面形状物体material: groundMaterial,});groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0); // 将地面绕着x轴旋转90度成为真正的地面physicsWorld.addBody(groundBody);// 加载贴图const loader = new THREE.TextureLoader();const texture = loader.load("../images/rabbit.png")// 多个小球,创建小球数组let spheresArr = [];// 动态生成小球const createSphere = (position, direction) => { // 接收两个参数 position:小球生成的位置,direction小球超哪发射位置const radius = 1;// 创建可视化小球const geometry = new THREE.Mesh(new THREE.SphereGeometry(radius, 32, 32), new THREE.MeshPhongMaterial({map: texture}));geometry.castShadow = true;geometry.position.copy(position);scene.add(geometry);// 创建物理小球刚体const sphereBody = new CANNON.Body({mass: 0.5, // 质量设为1material: sphereMaterial,linearDamping: 0.5, // 模拟空气阻力});// 创建物理小球const sphereShape = new CANNON.Sphere(radius);sphereBody.position.copy(position);sphereBody.addShape(sphereShape); // 刚体添加形状也可这种写法sphereBody.applyLocalForce(direction.scale(600), // 施加的力的向量new CANNON.Vec3(0, 0, 0) // 力作用的点在刚体的局部坐标系中的位置); // 在刚体的局部坐标系中的指定点上施加一个力physicsWorld.addBody(sphereBody);spheresArr.push({geometry,sphereBody})}renderer.domElement.addEventListener('mouseup', (event) => {// console.log(spheresArr);const mouse = new THREE.Vector2();mouse.x = (event.clientX / window.innerWidth) * 2 - 1;mouse.y = -(event.clientY / window.innerWidth) *2 + 1;const raycaster = new THREE.Raycaster();raycaster.setFromCamera(mouse,camera);const pos = new THREE.Vector3();pos.copy(raycaster.ray.direction); // 鼠标点击的射线方向pos.add(raycaster.ray.origin); // 射线原点const direction = new CANNON.Vec3(raycaster.ray.direction.x,raycaster.ray.direction.y,raycaster.ray.direction.z,)createSphere({x:pos.x, y:pos.y, z:pos.z}, direction);})const updatePhysic = () => { // 因为这是实时更新的,所以需要放到渲染循环动画animate函数中physicsWorld.step(1/60);spheresArr.forEach(({geometry,sphereBody}) => {geometry.position.copy(sphereBody.position); // 将物理刚体小球的位置赋值给threejs的小球geometry.quaternion.copy(sphereBody.quaternion); // 将物理刚体小球的旋转赋值给threejs的小球})}// 控制器const control = new OrbitControls(camera, renderer.domElement);// 开启阻尼惯性,默认值为0.05control.enableDamping = true;// 渲染循环动画function animate() {// 在这里我们创建了一个使渲染器能够在每次屏幕刷新时对场景进行绘制的循环(在大多数屏幕上,刷新率一般是60次/秒)requestAnimationFrame(animate);updatePhysic();// 更新控制器。如果没在动画里加上,那必须在摄像机的变换发生任何手动改变后调用control.update();renderer.render(scene, camera);};// 执行动画animate();</script>
</body>
</html>
2.2 解析
重点代码解析:
- 小球阴影:重点添加灯光,以及受灯光影响的材质。
const light = new THREE.PointLight( 0xffffff, 1000, 100 );添加点光源。地面为受灯光影响的new THREE.MeshStandardMaterial({color: 0x817936})材质以及小球受灯光影响材质ew THREE.MeshPhongMaterial({map: texture})。并且打开阴影开光:renderer.shadowMap.enabled = true; plane.receiveShadow = true; light.castShadow = true; geometry.castShadow = true;- 动态交互事件
renderer.domElement.addEventListener('mouseup', callback)这是鼠标交互的关键。- 每次点击都动态生成可视小球及刚体小球,并且都放到数组里:
spheresArr.push({geometry,sphereBody})- 获取鼠标点击时的二维向量
const mouse = new THREE.Vector2();mouse.x = (event.clientX / window.innerWidth) * 2 - 1;mouse.y = -(event.clientY / window.innerWidth) *2 + 1;- 射线方向及射线原点
const raycaster = new THREE.Raycaster();raycaster.setFromCamera(mouse,camera);const pos = new THREE.Vector3();pos.copy(raycaster.ray.direction); // 鼠标点击的射线方向pos.add(raycaster.ray.origin); // 射线原点const direction = new CANNON.Vec3(raycaster.ray.direction.x,raycaster.ray.direction.y,raycaster.ray.direction.z,)
在学习的路上,如果你觉得本文对你有所帮助的话,那就请关注点赞评论三连吧,谢谢,你的肯定是我写博的另一个支持。
相关文章:
Threejs之球发射实战
本文目录 前言一、效果预览二、代码实现及解析2.1 代码2.2 解析 前言 本篇将基于Threejs之模拟小球反弹基础上以及Threejs这个专栏学习过的知识点上进行小球更加真实的物理运动轨迹,并且还会与鼠标进行交互的操作。由于知识点都在上篇均有涉及,本篇就不过…...
详解新规|逐条分析《电子认证服务管理办法(征求意见稿)》修订重点
近日,工信部就《电子认证服务管理办法(征求意见稿)》公开征求意见。 来源|公开资料 图源|Pixabay 编辑|公钥密码开放社区 《电子认证服务管理办法》(以下简称《办法》)于2009年2…...
哪个编程工具让你的工作效率翻倍?
✍️作者简介:小北编程(专注于HarmonyOS、Android、Java、Web、TCP/IP等技术方向) 🐳博客主页: 开源中国、稀土掘金、51cto博客、博客园、知乎、简书、慕课网、CSDN 🔔如果文章对您有一定的帮助请…...
SEW变频器的特点
SEW变频器是德国SEW-EURODRIVE GmbH公司生产的一种变频器产品,该公司是全球领先的驱动技术和系统解决方案提供商之一。以下是关于SEW变频器的详细介绍: 一、产品特点 高效节能:SEW变频器采用先进的电力电子技术和控制技术,能够实…...
大象机械人------1、关节控制
回到首页 目录 1 单关节控制 角度控制:1.1 send_angle(id, degree, speed)电位值控制:1.2 set_encoder(joint_id, encoder) 2 多关节控制 获取所有角度:2.1 get_angles()角度控制:2.2 send_angles(degrees, speed)电位值控制&…...
油电叉车倒车防撞报警系统精准探测
油电叉车倒车防撞报警系统通过集成最新的传感器技术、图像识别算法以及智能控制技术,通过实时监测叉车周围环境中的障碍物、行人和其他叉车,及时发出警报,避免可能的碰撞事故。 油电叉车倒车防撞报警系统功能详解 精准探测 叉车倒车时&a…...
Java学习路线:从零基础到高级开发者的完整指南
初学者入门指南 1. 环境搭建 安装JDK: 下载并安装最新版本的JDK(Java Development Kit)。配置环境: 设置JAVA_HOME环境变量,并将bin目录添加到PATH中。选择IDE: 使用Eclipse、IntelliJ IDEA或其他任何你喜欢的Java集成开发环境。 2. Java基…...
【Java算法】递归
🔥个人主页: 中草药 🔥专栏:【算法工作坊】算法实战揭秘 🍇一.递归 概念 递归是一种解决问题的方法,其中函数通过调用自身来求解问题。这种方法的关键在于识别问题是否可以被分解为若干个相似但规模更小…...
NIDS——suricata(三)
一、监控ICMP流量 1、ICMP流量特征 四大特征分别为:消息类型(Type)、代码(Code)、校验和(Checksum)、数据字段(Data Field)。这里我们使用 type消息类型。 ICMP 消息的类…...
运动耳机哪个牌子最好用?年度精选五款好用的骨传导耳机推荐
相信大家都已经深有体会,拿那种常规的入耳式无线蓝牙耳机来做运动耳机,很难满足运动需要。如果选择前两年流行的颈挂式无线运动蓝牙耳机,虽然简单轻巧,但也是入耳式设计,长时间佩戴耳朵不舒服。这样看来,运…...
鞋服企业信息化建设若干架构分享
鞋服企业的信息化建设有着自身的一些特点,这些特点主要体现在以下几个方面: 集成化:鞋服企业的信息化建设往往需要集成多种系统,如企业资源规划系统(ERP)、客户关系管理系统(CRM)、供…...
比较顺序3s1和3s2的搜索难度
在行列可自由变换的平面上,3点结构只有6个 (A,B)---6*30*2---(0,1)(1,0) 分类A和B,让A是6个3点结构,让B全是0。当收敛误差为7e-4,收敛199次取迭代次数平均值, 让训练集A-B矩阵的高分别是3,4,5…...
Vue3 el-switch @change事件在初始化时会自动调用问题
接收一个vue3项目,突然有一天,table里有个switch开关,请求数据之后就开始执行switch的change事件,我还啥都没操作,就报一推重复请求 <template><el-switch v-model"rec" inline-prompt :active-val…...
全面解析性能测试中的瓶颈分析与优化策略!
在软件开发的生命周期中,性能测试是确保应用程序在不同负载下稳定运行的关键步骤。性能瓶颈是导致系统性能下降的主要原因,及时发现并解决这些瓶颈,能够显著提升系统的响应速度和用户体验。本文将深入探讨性能测试中的瓶颈分析方法与优化策略…...
2018年Android面试题含答案--适合中高级(下)
熟悉Android系统的童鞋都知道,系统出于体验和性能上的考虑,app在退到后台时系统并不会真正的kill掉这个进程,而是将其缓存起来。打开的应用越多,后台缓存的进程也越多。在系统内存不足的情况下,系统开始依据自身的一套…...
基于SSM的汽车租赁系统+LW示例参考
系列文章目录 1.基于SSM的洗衣房管理系统原生微信小程序LW参考示例 2.基于SpringBoot的宠物摄影网站管理系统LW参考示例 3.基于SpringBootVue的企业人事管理系统LW参考示例 4.基于SSM的高校实验室管理系统LW参考示例 5.基于SpringBoot的二手数码回收系统原生微信小程序LW参考示…...
[晕事]今天做了件晕事44 wireshark 首选项IPv4:Reassemble Fragented IPv4 datagrams
不知不觉,已经来到了晕事系列的第四十四个晕事。今天办的晕事和Wireshark查看网络包相关。说,在Wireshark的编辑-首选项协议里的IPv4协议,有一个参数设置是:Reassemble Fragented IPv4 datagrams。 这个参数的含义是指定Wireshar…...
Unity人工智能开发学习心得
在Unity中进行人工智能研究与应用主要集中在几个关键领域,包括使用Unity ML-Agents插件进行强化学习、利用神经网络技术和深度学习技术训练AI,以及基于行为树技术设计游戏人工智能。 使用Unity ML-Agents插件进行强化学习:Unity ML-Agent…...
0911,类与类之间的关系,设计原则,工厂模式
01_figure.cc //简单工厂 #include <math.h> #include <iostream> #include <string> #include <memory>using std::cout; using std::endl; using std::string; using std::unique_ptr;//-------------------------------------------------// /…...
【2024最新版】零基础Python快速入门篇
完整代码已打包,需要的小伙伴可以戳这里 [学习资料] 安装和运行 1.安装 要使用"Python"首先要把它安装到你电脑里。打开 [Python官网]下载安装包。 在Windows上安装 打开安装包,选择"Use admin privileges when installing py.exe&qu…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
AD学习(3)
1 PCB封装元素组成及简单的PCB封装创建 封装的组成部分: (1)PCB焊盘:表层的铜 ,top层的铜 (2)管脚序号:用来关联原理图中的管脚的序号,原理图的序号需要和PCB封装一一…...
C# WPF 左右布局实现学习笔记(1)
开发流程视频: https://www.youtube.com/watch?vCkHyDYeImjY&ab_channelC%23DesignPro Git源码: GitHub - CSharpDesignPro/Page-Navigation-using-MVVM: WPF - Page Navigation using MVVM 1. 新建工程 新建WPF应用(.NET Framework) 2.…...
Yolo11改进策略:Block改进|FCM,特征互补映射模块|AAAI 2025|即插即用
1 论文信息 FBRT-YOLO(Faster and Better for Real-Time Aerial Image Detection)是由北京理工大学团队提出的专用于航拍图像实时目标检测的创新框架,发表于AAAI 2025。论文针对航拍场景中小目标检测的核心难题展开研究,重点解决…...
GC1808:高性能音频ADC的卓越之选
在音频处理领域,高质量的音频模数转换器(ADC)是实现精准音频数字化的关键。GC1808,一款96kHz、24bit立体声音频ADC,以其卓越的性能和高性价比脱颖而出,成为众多音频设备制造商的理想选择。 GC1808集成了64倍…...
codeforces C. Cool Partition
目录 题目简述: 思路: 总代码: https://codeforces.com/contest/2117/problem/C 题目简述: 给定一个整数数组,现要求你对数组进行分割,但需满足条件:前一个子数组中的值必须在后一个子数组中…...
c++算法学习3——深度优先搜索
一、深度优先搜索的核心概念 DFS算法是一种通过递归或栈实现的"一条路走到底"的搜索策略,其核心思想是: 深度优先:从起点出发,选择一个方向探索到底,直到无路可走 回溯机制:遇到死路时返回最近…...
