当前位置: 首页 > news >正文

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 解析

重点代码解析:

  1. 小球阴影:重点添加灯光,以及受灯光影响的材质。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;
    
  2. 动态交互事件renderer.domElement.addEventListener('mouseup', callback)这是鼠标交互的关键。
  3. 每次点击都动态生成可视小球及刚体小球,并且都放到数组里:
         spheresArr.push({geometry,sphereBody})
    
  4. 获取鼠标点击时的二维向量
    	const mouse = new THREE.Vector2();mouse.x = (event.clientX / window.innerWidth) * 2 - 1;mouse.y = -(event.clientY / window.innerWidth) *2 + 1;
    
  5. 射线方向及射线原点
    	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,)          
    

在学习的路上,如果你觉得本文对你有所帮助的话,那就请关注点赞评论三连吧,谢谢,你的肯定是我写博的另一个支持。

http://www.lryc.cn/news/436128.html

相关文章:

  • 详解新规|逐条分析《电子认证服务管理办法(征求意见稿)》修订重点
  • 哪个编程工具让你的工作效率翻倍?
  • SEW变频器的特点
  • 大象机械人------1、关节控制
  • 油电叉车倒车防撞报警系统精准探测
  • Java学习路线:从零基础到高级开发者的完整指南
  • 【Java算法】递归
  • NIDS——suricata(三)
  • 运动耳机哪个牌子最好用?年度精选五款好用的骨传导耳机推荐
  • 鞋服企业信息化建设若干架构分享
  • 比较顺序3s1和3s2的搜索难度
  • Vue3 el-switch @change事件在初始化时会自动调用问题
  • 全面解析性能测试中的瓶颈分析与优化策略!
  • 2018年Android面试题含答案--适合中高级(下)
  • 基于SSM的汽车租赁系统+LW示例参考
  • [晕事]今天做了件晕事44 wireshark 首选项IPv4:Reassemble Fragented IPv4 datagrams
  • Unity人工智能开发学习心得
  • 0911,类与类之间的关系,设计原则,工厂模式
  • 【2024最新版】零基础Python快速入门篇
  • 掌握Go语言中的映射、常量与指针
  • @35岁的网安人 答应我拿下这些证书
  • flutter Image
  • 基于RP2350 MCU的树莓派Pico 2开发板及MicroPython编程使用
  • Docker数据挂载本地目录
  • 身份证实名认证接口如何用C#实现
  • Java开发者无痛丝滑入门Python
  • 【STM32】呼吸灯实现
  • SQLSERVER查看包含某个字段的所有表
  • 【编程基础知识】mysql中的insert into ... on DUPLICATE key和replace into的性能对比
  • 【CSS in Depth 2 精译_024】4.2 弹性子元素的大小