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

学习threejs,使用Physijs物理引擎,使用DOFConstraint自由度约束,模拟小车移动

👨‍⚕️ 主页: gis分享者
👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅!
👨‍⚕️ 收录于专栏:threejs gis工程师


文章目录

  • 一、🍀前言
    • 1.1 ☘️Physijs 物理引擎
      • 1.1.1 ☘️代码示例
      • 1.1.2 ☘️核心方法/属性
      • 1.1.3 ☘️网格对象
      • 1.1.4 ☘️约束
      • 1.1.4 ☘️约束、材质Materials、暂停/恢复模拟、场景配置、更新对象的位置和旋转使用样例
        • 1.1.4.1 ☘️约束使用样例
        • 1.1.4.2 ☘️材质Materials使用样例
        • 1.1.4.3 ☘️暂停/恢复模拟使用样例
        • 1.1.4.4 ☘️场景配置使用样例
        • 1.1.4.5 ☘️更新对象的位置和旋转使用样例
  • 二、🍀使用Physijs物理引擎,使用DOFConstraint自由度约束,模拟小车移动
    • 1. ☘️实现思路
    • 2. ☘️代码样例


一、🍀前言

本文详细介绍如何基于threejs在三维场景中使用Physijs物理引擎,使用DOFConstraint自由度约束,模拟小车移动,亲测可用。希望能帮助到您。一起学习,加油!加油!

1.1 ☘️Physijs 物理引擎

Three.js 的 Physi.js 是一个基于 Physijs 的物理引擎插件,用于为 Three.js 场景添加物理模拟(如碰撞检测、重力、刚体动力学等)。

1.1.1 ☘️代码示例

// 初始化 Physi.js 场景
const scene = new Physijs.Scene();// 创建带有物理效果的立方体
const box = new Physijs.BoxMesh(new THREE.BoxGeometry(1, 1, 1),new THREE.MeshBasicMaterial({ color: 0xff0000 })
);
scene.add(box);// 监听碰撞事件
box.addEventListener('collision', (otherObject) => {console.log('发生碰撞!', otherObject);
});// 在动画循环中更新物理
function animate() {requestAnimationFrame(animate);scene.simulate(); // 更新物理模拟renderer.render(scene, camera);
}
animate();

1.1.2 ☘️核心方法/属性

Physijs.Scene
创建支持物理的 Three.js 场景。

mesh.setLinearVelocity()
设置物体的线性速度(移动速度)。

mesh.setAngularVelocity()
设置物体的角速度(旋转速度)。

mesh.addEventListener()
监听碰撞事件(如 ‘collision’)。

new Physijs.BoxMesh()
创建带有长方体碰撞体的物体。

new Physijs.SphereMesh()
创建带有球体碰撞体的物体。

scene.simulate()
在渲染循环中调用,更新物理模拟。

Physijs.createMaterial(material, friction, restitution)
创建物理材质,影响摩擦力和弹性。
参数:
material:Three.js 材质(如 THREE.MeshPhongMaterial)。
friction:摩擦系数(默认 0.8)。
restitution:弹性系数(默认 0)。

1.1.3 ☘️网格对象

Physijs.PlaneMesh // 这个网格可以用来创建一个厚度为0的平面。这样的平面也可以用BoxMesh对象包装一个高度很低的THREE.CubeGeometry来表示Physijs.BoxMesh // 如果是类似方块的几何体,你可以使用这个网格。例如,它的属性跟THREE.CubeGeometry的属性很相配Physijs.SphereMesh // 对于球形可以使用这个网格。它跟THREE.SphereGeometry的属性很相配Physijs.CylinderMesh // 通过设置THREE.Cylinder的属性你可以创建出各种柱状图形。Physijs为各种柱性提供了不同网格。Physijs.CylinderMesh可以用于一般的、上下一致的圆柱形Physijs.ConeMesh // 如果顶部的半径为0,底部的半径值大于0,那么你可以用THREE.Cylinder创建一个圆锥体。如果你想在这样一个对象上应用物理效果,那么可以使用的、最相匹配的网格类就是ConeMeshPhysijs.CapsuleMesh(胶囊网格) // 跟THREE.Cylinder属性很相似,但其底部和底部是圆的Physijs.ConvexMesh(凸包网格) // Physijs.ConvexMesh是一种比较粗略的图形,可用于多数复杂退行。它可以创建一个模拟复杂图形的凸包Physijs.ConcaveMesh // ConvexMesh是一个比较粗略的图形,而ConcaveMesh则可以对负责图形进行比较细致的表现。需要注意的是使用ConcaveMesh对效率的影响比较大Physijs.HeightfieldMesh(高度场网格) // 这是一种非常特别的网格。通过该网格你可以从一个THREE.PlaneGeometry对象创建出一个高度场。

1.1.4 ☘️约束

PointConstraint // 通过这个约束,你可以将一个对象与另一个对象之间的位置固定下来。例如一个对象动了,另一个对象也会随着移动,它们之间的距离和方向保持不变HingeConstraint // 通过活页约束,你可以限制一个对象只能像活页一样移动,例如门SliderConstraint // 将对象的移动限制在一个轴上。例如移门ConeTwistConstraint // 通过这个约束,你可以用一个对象限制另一个对象的旋转和移动。这个约束的功能类似于一个球削式关节。例如,胳膊在肩关节中的活动DOFConstraint // 通过自由度约束,你可以限制对象在任意轴上的活动,你可以设置对象活动的额最小、最大角度。这是最灵活的约束方式

1.1.4 ☘️约束、材质Materials、暂停/恢复模拟、场景配置、更新对象的位置和旋转使用样例

1.1.4.1 ☘️约束使用样例

点对点:

var constraint = new Physijs.PointConstraint(physijs_mesh_a, // 第一个被约束的对象physijs_mesh_b, // 限制第一个对象的物体,可以忽略,如果被忽略,第一个对象将被限制到点上new THREE.Vector3( 0, 10, 0 ) // 被限制到的点的位置
);
scene.addConstraint( constraint );

铰链约束:

var constraint = new Physijs.HingeConstraint(physijs_mesh_a, // 第一个被约束的对象physijs_mesh_b, // 限制第一个对象的物体,可以忽略,如果被忽略,第一个对象将被限制到点上new THREE.Vector3( 0, 10, 0 ), // 被限制到的点的位置new THREE.Vector3( 1, 0, 0 ) // 哪个轴向会被限制,当前默认是x轴
);
scene.addConstraint( constraint );
constraint.setLimits(low, // 最小运动角度,以弧度表示 high, // 最大运动角度,以弧度表示 bias_factor, // 设置误差范围relaxation_factor, // 控制极限反弹(0.0 ==不反弹) 
);
constraint.enableAngularMotor( target_velocity, acceration_force ); //可以设置力的方向(正方向或者负方向),当前的加速度
constraint.disableMotor(); //关闭当前的移动

滑块约束:

var constraint = new Physijs.SliderConstraint(physijs_mesh_a, // 被约束的对象physijs_mesh_b, // 限制第一个对象的物体,可以忽略,如果被忽略,第一个对象将被限制到点上new THREE.Vector3( 0, 10, 0 ), // 被限制到的点的位置new THREE.Vector3( 1, 0, 0 ) // 哪个轴向会被限制,当前默认是x轴
);
scene.addConstraint( constraint );
constraint.setLimits(linear_lower, // 线性移动的下限,以世界单位表示 linear_upper, // 线性移动的上限,以世界单位表示 angular_lower, // 角度移动的下限,以弧度表示 angular_upper // 角度上限运动,以弧度表示 
);
constraint.setRestitution(linear, // 达到线性限制时的恢复量angular // 达到角度限制时的恢复量
);
constraint.enableLinearMotor( target_velocity, acceration_force );//设置线性力的方向(正方向或者负方向),当前的加速度
constraint.disableLinearMotor();//关闭当前的线性移动
constraint.enableAngularMotor( target_velocity, acceration_force );//设置角旋转力的方向(正方向或者负方向),当前的加速度
constraint.disableAngularMotor();//关闭当前的旋转移动

锥形约束:

var constraint = new Physijs.ConeTwistConstraint(physijs_mesh_a, // 被约束的对象physijs_mesh_b, // 限制第一个对象的物体new THREE.Vector3( 0, 10, 0 ), // 被限制到的点的位置
);
scene.addConstraint( constraint );
constraint.setLimit( x, y, z ); // 每个轴限制的旋转限制,以弧度表示
constraint.setMotorMaxImpulse( max_impulse ); // 设置可以驱动第一个物体的力
constraint.setMotorTarget( target ); // target是约束的期望旋转,可以用THREE.Vector3,THREE.Matrix4或THREE.Quaternion
constraint.enableMotor(); //开启力
constraint.disableMotor(); //关闭力

自由度约束:

var constraint = new Physijs.DOFConstraint(physijs_mesh_a, // 被约束的对象physijs_mesh_b, // 限制第一个对象的物体new THREE.Vector3( 0, 10, 0 ), //  被限制到的点的位置
);
scene.addConstraint( constraint );
constraint.setLinearLowerLimit( new THREE.Vector3( -10, -5, 0 ) ); // 设置沿x,y和z轴的线性运动的下限
constraint.setLinearUpperLimit( new THREE.Vector3( 10, 5, 0 ) ); // 设置沿x,y和z轴线性运动的上限
constraint.setAngularLowerLimit( new THREE.Vector3( 0, -Math.PI, 0 ) ); // 沿着x,y和z轴以弧度设置角运动的下限
constraint.setAngularUpperLimit( new THREE.Vector3( 0, Math.PI, 0 ) ); // 沿着x,y和z轴以弧度设置角运动的上限
constraint.configureAngularMotor(which, // 指定马达工作的轴 0是x轴 1是y轴 2是z轴low_limit, // 设置马达的角度的下限high_limit, // 设置马达的角度的上限 下限设置的比上限高可以沿轴自由旋转velocity, // 目标速度max_force // 马达可以施加的最大力
);
constraint.enableAngularMotor( which ); // 开启哪个轴的马达0是x轴 1是y轴 2是z轴
constraint.disableAngularMotor( which ); // 关闭哪个轴的马达

冻结一个对象:
如果对象始终是静态的,例如地面,则可以0使用第三个参数创建网格时将其设置为质量:new Physijs.BoxMesh( geometry, material, 0)。任何具有质量的对象0将永远是静态的。

1.1.4.2 ☘️材质Materials使用样例

在THREE材质基础上增加了摩擦度和恢复度

var friction = 0.8; // 摩擦度
var restitution = 0.3; // 恢复度
var material = Physijs.createMaterial(new THREE.MeshBasicMaterial({ color: 0x888888 }),friction,restitution
);
var mesh = new Physijs.BoxMesh(new THREE.CubeGeometry( 5, 5, 5 ),material
);
1.1.4.3 ☘️暂停/恢复模拟使用样例
var render = function() {if (!isPaused) {scene.simulate();}renderer.render();
};
var unpauseSimulation = function() {isPaused = false;scene.onSimulationResume();
};

恢复模拟需要调用场景的onSimulationResume方法.

1.1.4.4 ☘️场景配置使用样例
  • fixedTimeStep default=1/60 此数字确定模拟步骤的模拟时间。数字越小,模拟越准确。
  • broadphase 指定将使用哪个宽带,选择是dynamic和sweepprune。
  • reportsize default 50 作为优化,包含对象位置的世界报告基于此数字预先初始化。最好将其设置为您的场景将具有的对象数量。
  • setGravity方法 default ( 0, -10, 0 ) 设定重力的数量和方向
  • setFixedTimeStep 在构造函数中default 1 / 60 重置fixedTimeStep给定的值
var scene = new Physijs.Scene({ reportsize: 50, fixedTimeStep: 1 / 60 });
1.1.4.5 ☘️更新对象的位置和旋转使用样例

有一个方面,无法与three.js进行无缝集成:更改对象的位置和/或旋转。如果这样做,您必须将该对象__dirtyPosition或__dirtyRotation标志设置为true,否则将从模拟中的最后一个已知值覆盖

var mesh = new Physijs.BoxMesh( geometry, material );
scene.add( mesh );var render = function() {// Change the object's positionmesh.position.set( 0, 0, 0 );mesh.__dirtyPosition = true;// Change the object's rotationmesh.rotation.set(0, 90, 180);mesh.__dirtyRotation = true;// You may also want to cancel the object's velocitymesh.setLinearVelocity(new THREE.Vector3(0, 0, 0));mesh.setAngularVelocity(new THREE.Vector3(0, 0, 0));scene.simulate();renderer.render();
};

二、🍀使用Physijs物理引擎,使用DOFConstraint自由度约束,模拟小车移动

1. ☘️实现思路

  • 1、引入‘physi.js’,创建Physijs物理引擎三维场景scene,设置scene场景重力信息。
  • 2、初始化camera相机,定义相机位置 camera.position.set,设置相机方向camera.lookAt,场景scene添加camera。
  • 3、创建THREE.SpotLight聚光灯光源light,设置light位置,scene场景加入light。
  • 4、加载几何模型:定义createGround方法,使用‘floor-wood.jpg’木纹贴图创建地面网格对象ground以及四周突出边框网格对象borderLeft、borderRight、borderTop、borderBottom,ground添加borderLeft、borderRight、borderTop、borderBottom,调用createGround方法。定义createCar方法,用于生成受DOFConstraint自由度约束控制的小汽车,调用createCar获取car对象。定义controls方法,内部定义changeVelocity方法控制前轮的的重力影响,定义changeOrientation方法控制后轮的放松限制。调定义render方法,进行三维场景的渲染。具体代码参考下面代码样例。
  • 5、加入gui控制。加入stats监控器,监控帧数信息。

2. ☘️代码样例

<!DOCTYPE html>
<html>
<head><style>body {margin: 0;overflow: hidden;background-color: #000000;}</style><title>学习threejs,使用Physijs物理引擎,使用DOFConstraint自由度约束,模拟小车移动</title><script type="text/javascript" src="../libs/three.js"></script><script type="text/javascript" src="../libs/stats.js"></script><script type="text/javascript" src="../libs/physi.js"></script><script type="text/javascript" src="../libs/dat.gui.js"></script><script type="text/javascript" src="../libs/chroma.js"></script><script type="text/javascript">'use strict';Physijs.scripts.worker = '../libs/physijs_worker.js';Physijs.scripts.ammo = '../libs/ammo.js';var scale = chroma.scale(['white', 'blue', 'red', 'yellow']);var initScene, render, applyForce, setMousePosition, mouse_position,ground_material, box_material,projector, renderer, render_stats, physics_stats, scene, ground, light, camera, box, boxes = [];initScene = function () {renderer = new THREE.WebGLRenderer({antialias: true});renderer.setSize(window.innerWidth, window.innerHeight);renderer.setClearColor(new THREE.Color(0x000000));renderer.shadowMapEnabled = true;document.getElementById('viewport').appendChild(renderer.domElement);render_stats = new Stats();render_stats.domElement.style.position = 'absolute';render_stats.domElement.style.top = '1px';render_stats.domElement.style.left = '1px';render_stats.domElement.style.zIndex = 100;document.getElementById('viewport').appendChild(render_stats.domElement);scene = new Physijs.Scene({reportSize: 10, fixedTimeStep: 1 / 60});scene.setGravity(new THREE.Vector3(0, -40, 0));camera = new THREE.PerspectiveCamera(35,window.innerWidth / window.innerHeight,1,1000);camera.position.set(90, 90, 90);camera.lookAt(new THREE.Vector3(30, 0, -20));scene.add(camera);// 添加THREE.SpotLight聚光灯光源light,设置光源位置和投影信息light = new THREE.SpotLight(0xFFFFFF);light.position.set(120, 70, 100);light.castShadow = true;light.shadowMapDebug = true;light.shadowCameraNear = 10;light.shadowCameraFar = 200;scene.add(light);var meshes = [];// 创建木质地面createGround();var car = createCar();var controls = new function () {this.velocity = -2;this.wheelAngle = 0.5;this.loosenXRight = 0.0001;this.loosenXLeft = 0.0001;this.changeVelocity = function () {// 配置小汽车左右前轮z轴马达car.flConstraint.configureAngularMotor(2, 0.1, 0, controls.velocity, 15000);car.frConstraint.configureAngularMotor(2, 0.1, 0, controls.velocity, 15000);// motor one is for left and right// frConstraint.enableAngularMotor(1);// 启动前轮z轴马达car.flConstraint.enableAngularMotor(2);car.frConstraint.enableAngularMotor(2);};this.changeOrientation = function () {car.rrConstraint.setAngularLowerLimit({x: 0, y: controls.wheelAngle, z: 0.1});car.rrConstraint.setAngularUpperLimit({x: controls.loosenXRight, y: controls.wheelAngle, z: 0});car.rlConstraint.setAngularLowerLimit({x: controls.loosenXLeft, y: controls.wheelAngle, z: 0.1});car.rlConstraint.setAngularUpperLimit({x: 0, y: controls.wheelAngle, z: 0});}};var gui = new dat.GUI();gui.add(controls, 'velocity', -10, 10).onChange(controls.changeVelocity);gui.add(controls, 'wheelAngle', -1, 1).onChange(controls.changeOrientation);gui.add(controls, 'loosenXRight', 0, 0.5).step(0.01).onChange(controls.changeOrientation);gui.add(controls, 'loosenXLeft', 0, 0.6).step(-0.01).onChange(controls.changeOrientation);controls.loosenXLeft = 0;controls.loosenXRight = 0;requestAnimationFrame(render);scene.simulate();};function createWheel(position) {var wheel_material = Physijs.createMaterial(new THREE.MeshLambertMaterial({color: 0x444444, opacity: 0.9, transparent: true}),1.0, // high friction.5 // medium restitution);var wheel_geometry = new THREE.CylinderGeometry(4, 4, 2, 10);var wheel = new Physijs.CylinderMesh(wheel_geometry,wheel_material,100);wheel.rotation.x = Math.PI / 2;wheel.castShadow = true;wheel.position.copy(position);return wheel;}function createCar() {var car = {};var car_material = Physijs.createMaterial(new THREE.MeshLambertMaterial({color: 0xff4444, opacity: 0.9, transparent: true}),.5, // high friction.5 // medium restitution);// 创建小汽车身体var geom = new THREE.BoxGeometry(15, 4, 4);var body = new Physijs.BoxMesh(geom, car_material, 500);body.position.set(5, 5, 5);body.castShadow = true;scene.add(body);// 创建小汽车轮子var fr = createWheel(new THREE.Vector3(0, 4, 10));var fl = createWheel(new THREE.Vector3(0, 4, 0));var rr = createWheel(new THREE.Vector3(10, 4, 10));var rl = createWheel(new THREE.Vector3(10, 4, 0));// scene场景添加轮子scene.add(fr);scene.add(fl);scene.add(rr);scene.add(rl);var frConstraint = createWheelConstraint(fr, body, new THREE.Vector3(0, 4, 8));scene.addConstraint(frConstraint);var flConstraint = createWheelConstraint(fl, body, new THREE.Vector3(0, 4, 2));scene.addConstraint(flConstraint);var rrConstraint = createWheelConstraint(rr, body, new THREE.Vector3(10, 4, 8));scene.addConstraint(rrConstraint);var rlConstraint = createWheelConstraint(rl, body, new THREE.Vector3(10, 4, 2));scene.addConstraint(rlConstraint);// 后轮不能自行移动,在行驶过程中受到限制rrConstraint.setAngularLowerLimit({x: 0, y: 0.5, z: 0.1});rrConstraint.setAngularUpperLimit({x: 0, y: 0.5, z: 0});rlConstraint.setAngularLowerLimit({x: 0, y: 0.5, z: 0.1});rlConstraint.setAngularUpperLimit({x: 0, y: 0.5, z: 0});// 前轮只能沿z轴移动。frConstraint.setAngularLowerLimit({x: 0, y: 0, z: 0});frConstraint.setAngularUpperLimit({x: 0, y: 0, z: 0});flConstraint.setAngularLowerLimit({x: 0, y: 0, z: 0});flConstraint.setAngularUpperLimit({x: 0, y: 0, z: 0});//如果添加电机,则当前约束将被覆盖//如果你想旋转,把最小值设置得比最大值高flConstraint.configureAngularMotor(2, 0.1, 0, -2, 1500);frConstraint.configureAngularMotor(2, 0.1, 0, -2, 1500);// motor one is for left and right//frConstraint.enableAngularMotor(1);flConstraint.enableAngularMotor(2);frConstraint.enableAngularMotor(2);car.flConstraint = flConstraint;car.frConstraint = frConstraint;car.rlConstraint = rlConstraint;car.rrConstraint = rrConstraint;return car;}function createWheelConstraint(wheel, body, position) {var constraint = new Physijs.DOFConstraint(wheel, body, position);return constraint;}function createGround() {var length = 120;var width = 120;// Materialsground_material = Physijs.createMaterial(new THREE.MeshPhongMaterial({
//                                color: 0xaaaaaa,map: THREE.ImageUtils.loadTexture('../assets/textures/general/floor-wood.jpg')}),1, // high friction.7 // low restitution);ground = new Physijs.BoxMesh(new THREE.BoxGeometry(length, 1, width),ground_material,0 // mass);ground.receiveShadow = true;var borderLeft = new Physijs.BoxMesh(new THREE.BoxGeometry(2, 6, width),ground_material,0 // mass);borderLeft.position.x = -1 * length / 2 - 1;borderLeft.position.y = 2;borderLeft.receiveShadow = true;ground.add(borderLeft);var borderRight = new Physijs.BoxMesh(new THREE.BoxGeometry(2, 6, width),ground_material,0 // mass);borderRight.position.x = length / 2 + 1;borderRight.position.y = 2;borderRight.receiveShadow = true;ground.add(borderRight);var borderBottom = new Physijs.BoxMesh(new THREE.BoxGeometry(width - 1, 6, 2),ground_material,0 // mass);borderBottom.position.z = width / 2;borderBottom.position.y = 1.5;borderBottom.receiveShadow = true;ground.add(borderBottom);var borderTop = new Physijs.BoxMesh(new THREE.BoxGeometry(width, 6, 2),ground_material,0 // mass);borderTop.position.z = -width / 2;borderTop.position.y = 2;borderTop.receiveShadow = true;ground.position.x = 20;ground.position.z = -20;ground.add(borderTop);ground.receiveShadow = true;scene.add(ground);}render = function () {requestAnimationFrame(render);renderer.render(scene, camera);render_stats.update();scene.simulate(undefined, 2);};window.onload = initScene;</script>
</head><body>
<div id="viewport"></div>
</body></html>

效果如下:
在这里插入图片描述

相关文章:

  • 为 Jenkins添加 Windows Slave远程执行 python项目脚本
  • 建筑墙壁红外热成像裂缝潮湿检测数据集VOC+YOLO格式306张2类别
  • C#基础:yield return关键字的特点
  • ubuntu下实时检测机械硬盘和固态硬盘温度
  • 飞桨paddle ‘ParallelEnv‘ object has no attribute ‘_device_id‘【已解决】
  • java每日精进 5.20【MyBatis 联表分页查询】
  • 【每天一个MCP】【记录向】:准备工作,创建github项目
  • 初始“扣子”--九五小庞
  • MySQL 数据库迁移方法汇总
  • Trae 04.22版本深度解析:Agent能力升级与MCP市场对复杂任务执行的革新
  • ANC--Active Noise Cancellation
  • [春秋云镜] Spoofing仿真场景
  • 链表面试题9之环形链表进阶
  • 数据中心Overlay解决方案
  • 游戏引擎学习第299天:改进排序键 第二部分
  • mapbox进阶,纯前端geojson转shape,并将shape相关文件压缩成zip压缩包并下载
  • Redis中SETNX、Lua 脚本和 Redis事务的对比
  • python打卡训练营打卡记录day31
  • 数据结构*排序
  • Docker网络全景解析:Overlay与Macvlan深度实践,直通Service Mesh集成核心
  • 太原一居民手机号被企业公示常遭骚扰,负责人称是用过的旧号
  • 凤阳文旅局长回应鼓楼瓦片脱落事件:楼宇是否属于文物?施工经费用在何处?
  • 印度空军为“阵风”战机换装国产导弹,以增强作战能力推动国防自主
  • 甘肃白银煤矿透水事故最新进展:3名被困矿工已无生命体征
  • 广东信宜一座在建桥梁暴雨中垮塌,镇政府:未造成人员伤亡
  • 外媒:哈马斯一名高级指挥官尸体被发现,系辛瓦尔弟弟