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

学习threejs,使用Physijs物理引擎,各种constraint约束限制

👨‍⚕️ 主页: 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物理引擎,各种constraint约束限制
    • 1. ☘️实现思路
    • 2. ☘️代码样例


一、🍀前言

本文详细介绍如何基于threejs在三维场景中使用Physijs物理引擎,各种constraint约束限制,亲测可用。希望能帮助到您。一起学习,加油!加油!

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, // First object to be constrainedphysijs_mesh_b, // OPTIONAL second object - if omitted then physijs_mesh_1 will be constrained to the scenenew THREE.Vector3( 0, 10, 0 ) // point in the scene to apply the constraint
);
scene.addConstraint( constraint );

铰链约束:

var constraint = new Physijs.HingeConstraint(physijs_mesh_a, // First object to be constrainedphysijs_mesh_b, // OPTIONAL second object - if omitted then physijs_mesh_1 will be constrained to the scenenew THREE.Vector3( 0, 10, 0 ), // point in the scene to apply the constraintnew THREE.Vector3( 1, 0, 0 ) // Axis along which the hinge lies - in this case it is the X axis
);
scene.addConstraint( constraint );
constraint.setLimits(low, // minimum angle of motion, in radianshigh, // maximum angle of motion, in radiansbias_factor, // applied as a factor to constraint errorrelaxation_factor, // controls bounce at limit (0.0 == no bounce)
);
constraint.enableAngularMotor( target_velocity, acceration_force );
constraint.disableMotor();

滑块约束:

var constraint = new Physijs.SliderConstraint(physijs_mesh_a, // First object to be constrainedphysijs_mesh_b, // OPTIONAL second object - if omitted then physijs_mesh_1 will be constrained to the scenenew THREE.Vector3( 0, 10, 0 ), // point in the scene to apply the constraintnew THREE.Vector3( 1, 0, 0 ) // Axis along which the hinge lies - in this case it is the X axis
);
scene.addConstraint( constraint );
constraint.setLimits(linear_lower, // lower limit of linear movement, expressed in world unitslinear_upper, // upper limit of linear movement, expressed in world unitsangular_lower, // lower limit of angular movement, expressed in radiansangular_upper // upper limit of angular movement, expressed in radians
);
constraint.setRestitution(linear, // amount of restitution when reaching the linear limitsangular // amount of restitution when reaching the angular limits
);
constraint.enableLinearMotor( target_velocity, acceration_force );
constraint.disableLinearMotor();
constraint.enableAngularMotor( target_velocity, acceration_force );
constraint.disableAngularMotor();

锥形约束:

var constraint = new Physijs.ConeTwistConstraint(physijs_mesh_a, // First object to be constrainedphysijs_mesh_b, // Second object to be constrainednew THREE.Vector3( 0, 10, 0 ), // point in the scene to apply the constraint
);
scene.addConstraint( constraint );
constraint.setLimit( x, y, z ); // rotational limit, in radians, for each axis
constraint.setMotorMaxImpulse( max_impulse ); // float value of the maximum impulse the motor can apply toward its target
constraint.setMotorTarget( target ); // target is the desired rotation for the constraint and can be expressed by a THREE.Vector3, THREE.Matrix4, or THREE.Quaternion
constraint.enableMotor();
constraint.disableMotor();

自由度约束:

var constraint = new Physijs.DOFConstraint(physijs_mesh_a, // First object to be constrainedphysijs_mesh_b, // OPTIONAL second object - if omitted then physijs_mesh_1 will be constrained to the scenenew THREE.Vector3( 0, 10, 0 ), // point in the scene to apply the constraint
);
scene.addConstraint( constraint );
constraint.setLinearLowerLimit( new THREE.Vector3( -10, -5, 0 ) ); // sets the lower end of the linear movement along the x, y, and z axes.
constraint.setLinearUpperLimit( new THREE.Vector3( 10, 5, 0 ) ); // sets the upper end of the linear movement along the x, y, and z axes.
constraint.setAngularLowerLimit( new THREE.Vector3( 0, -Math.PI, 0 ) ); // sets the lower end of the angular movement, in radians, along the x, y, and z axes.
constraint.setAngularUpperLimit( new THREE.Vector3( 0, Math.PI, 0 ) ); // sets the upper end of the angular movement, in radians, along the x, y, and z axes.
constraint.configureAngularMotor(which, // which angular motor to configure - 0,1,2 match x,y,zlow_limit, // lower limit of the motorhigh_limit, // upper limit of the motorvelocity, // target velocitymax_force // maximum force the motor can apply
);
constraint.enableAngularMotor( which ); // which angular motor to configure - 0,1,2 match x,y,z
constraint.disableAngularMotor( which ); // which angular motor to configure - 0,1,2 match x,y,z

冻结一个对象:
如果对象始终是静态的,例如地面,则可以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物理引擎,各种constraint约束限制

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。定义createLeftFlipper、createRightFlipper方法,用于创建立方体物理网格对象,并设置活页约束,调用这两个方法。定义createSliderBottom、createSliderTop方法,用于创建立方体物理网格对象,并设置单轴线约束,调用这两个方法。定义createConeTwist方法,创建1个球体物理网格对象、一个立方体物理网格对象,并设置球削式关节约束,调用该个方法。定义createPointToPoint方法,创建2个球体物理网格对象,并设置点到点位置和方向固定约束,调用该个方法。定义controls方法,用于控制上面创建的PointConstraint、HingeConstraint、SliderConstraint和ConeTwistConstraint约束,定义添加、移除物理球体网格对象方法。调用createGround方法。定义render方法,进行三维场景的渲染。具体代码参考下面代码样例。
  • 5、加入gui控制。加入stats监控器,监控帧数信息。

2. ☘️代码样例

<!DOCTYPE html>
<html>
<head><style>body {margin: 0;overflow: hidden;background-color: #000000;}</style><title>学习threejs,使用Physijs物理引擎,各种constraint约束限制</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 () {projector = new THREE.Projector;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.right = '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, -10, 0));camera = new THREE.PerspectiveCamera(35,window.innerWidth / window.innerHeight,1,1000);camera.position.set(85, 65, 65);camera.lookAt(new THREE.Vector3(0, 0, 0));scene.add(camera);// 创建THREE.SpotLight聚光灯光源light,设置light的位置和投影light = new THREE.SpotLight(0xFFFFFF);light.position.set(20, 50, 50);light.castShadow = true;light.shadowMapDebug = true;light.shadowCameraNear = 10;light.shadowCameraFar = 100;// scene添加lightscene.add(light);var meshes = [];createGround();// 创建立方体物理网格对象,并设置活页约束var flipperLeftConstraint = createLeftFlipper();var flipperRightConstraint = createRightFlipper();//  创建立方体物理网格对象,并设置单轴线约束var sliderBottomConstraint = createSliderBottom();var sliderTopConstraint = createSliderTop();//  创建1个球体物理网格对象、一个立方体物理网格对象,并设置球削式关节约束var coneTwistConstraint = createConeTwist();// 创建2个球体物理网格对象,并设置点到点位置和方向固定约束var point2point = createPointToPoint(true);var controls = new function () {this.enableMotor = false;this.acceleration = 2;this.velocity = -10;this.enableConeTwistMotor = false;this.motorTargetX = 0;this.motorTargetY = 0;this.motorTargetZ = 0;this.updateCone = function () {if (controls.enableConeTwistMotor) {coneTwistConstraint.enableMotor();coneTwistConstraint.setMotorTarget(new THREE.Vector3(controls.motorTargetX, controls.motorTargetY, controls.motorTargetZ));} else {coneTwistConstraint.disableMotor();}};this.updateMotor = function () {if (controls.enableMotor) {// 启用重力flipperLeftConstraint.disableMotor();flipperLeftConstraint.enableAngularMotor(controls.velocity, controls.acceleration);flipperRightConstraint.disableMotor();flipperRightConstraint.enableAngularMotor(-1 * controls.velocity, controls.acceleration);} else {flipperLeftConstraint.disableMotor();flipperRightConstraint.disableMotor();}};this.sliderLeft = function () {sliderBottomConstraint.disableLinearMotor();sliderBottomConstraint.enableLinearMotor(controls.velocity, controls.acceleration);sliderTopConstraint.disableLinearMotor();sliderTopConstraint.enableLinearMotor(controls.velocity, controls.acceleration);};this.sliderRight = function () {sliderBottomConstraint.disableLinearMotor();sliderBottomConstraint.enableLinearMotor(-1 * controls.velocity, controls.acceleration);sliderTopConstraint.disableLinearMotor();sliderTopConstraint.enableLinearMotor(-1 * controls.velocity, controls.acceleration);};this.clearMeshes = function () {meshes.forEach(function (e) {scene.remove(e);});meshes = [];};this.addSpheres = function () {var colorSphere = scale(Math.random()).hex();for (var i = 0; i < 5; i++) {box = new Physijs.SphereMesh(new THREE.SphereGeometry(2, 20),Physijs.createMaterial(new THREE.MeshPhongMaterial({color: colorSphere,opacity: 0.8,transparent: truecontrols.sphereFriction,controls.sphereRestitution), 0.1);box.castShadow = true;box.receiveShadow = true;box.position.set(Math.random() * 50 - 25,20 + Math.random() * 5,Math.random() * 5);meshes.push(box);scene.add(box);}};};controls.updateMotor();var gui = new dat.GUI();gui.domElement.style.position = 'absolute';gui.domElement.style.top = '20px';gui.domElement.style.left = '20px';var generalFolder = gui.addFolder('general');generalFolder.add(controls, "acceleration", 0, 15).onChange(controls.updateMotor);generalFolder.add(controls, "velocity", -10, 10).onChange(controls.updateMotor);var hingeFolder = gui.addFolder('hinge');hingeFolder.add(controls, "enableMotor").onChange(controls.updateMotor);var sliderFolder = gui.addFolder('sliders');sliderFolder.add(controls, "sliderLeft").onChange(controls.sliderLeft);sliderFolder.add(controls, "sliderRight").onChange(controls.sliderRight);var coneTwistFolder = gui.addFolder('coneTwist');coneTwistFolder.add(controls, "enableConeTwistMotor").onChange(controls.updateCone);coneTwistFolder.add(controls, "motorTargetX", -Math.PI / 2, Math.PI / 2).onChange(controls.updateCone);coneTwistFolder.add(controls, "motorTargetY", -Math.PI / 2, Math.PI / 2).onChange(controls.updateCone);coneTwistFolder.add(controls, "motorTargetZ", -Math.PI / 2, Math.PI / 2).onChange(controls.updateCone);var spheresFolder = gui.addFolder('spheres');spheresFolder.add(controls, "clearMeshes").onChange(controls.updateMotor);spheresFolder.add(controls, "addSpheres").onChange(controls.updateMotor);requestAnimationFrame(render);scene.simulate();};function createGround() {// Materialsground_material = Physijs.createMaterial(new THREE.MeshPhongMaterial({
//                                color: 0xaaaaaa,map: THREE.ImageUtils.loadTexture('../assets/textures/general/floor-wood.jpg')}),.9, // high friction.7 // low restitution);// Groundground = new Physijs.BoxMesh(new THREE.BoxGeometry(60, 1, 65),ground_material,0);ground.receiveShadow = true;var borderLeft = new Physijs.BoxMesh(new THREE.BoxGeometry(2, 6, 65),ground_material,0);borderLeft.position.x = -31;borderLeft.position.y = 2;borderLeft.receiveShadow = true;ground.add(borderLeft);var borderRight = new Physijs.BoxMesh(new THREE.BoxGeometry(2, 6, 65),ground_material,0);borderRight.position.x = 31;borderRight.position.y = 2;borderRight.receiveShadow = true;ground.add(borderRight);var borderBottom = new Physijs.BoxMesh(new THREE.BoxGeometry(64, 6, 2),ground_material,0);borderBottom.position.z = 32;borderBottom.position.y = 1.5;borderBottom.receiveShadow = true;ground.add(borderBottom);var borderTop = new Physijs.BoxMesh(new THREE.BoxGeometry(64, 6, 2),ground_material,0);borderTop.position.z = -32;borderTop.position.y = 2;borderTop.receiveShadow = true;ground.add(borderTop);ground.receiveShadow = true;scene.add(ground);}function createConeTwist() {var baseMesh = new THREE.SphereGeometry(1);var armMesh = new THREE.BoxGeometry(2, 12, 3);var objectOne = new Physijs.BoxMesh(baseMesh, Physijs.createMaterial(new THREE.MeshPhongMaterial({color: 0x4444ff, transparent: true, opacity: 0.7}), 0, 0), 0);objectOne.position.z = 0;objectOne.position.x = 20;objectOne.position.y = 15.5;objectOne.castShadow = true;scene.add(objectOne);var objectTwo = new Physijs.SphereMesh(armMesh, Physijs.createMaterial(new THREE.MeshPhongMaterial({color: 0x4444ff, transparent: true, opacity: 0.7}), 0, 0), 10);objectTwo.position.z = 0;objectTwo.position.x = 20;objectTwo.position.y = 7.5;scene.add(objectTwo);objectTwo.castShadow = true;//position is the position of the axis, relative to the ref, based on the current positionvar constraint = new Physijs.ConeTwistConstraint(objectOne, objectTwo, objectOne.position);scene.addConstraint(constraint);// set limit to quarter circle for each axisconstraint.setLimit(0.5 * Math.PI, 0.5 * Math.PI, 0.5 * Math.PI);constraint.setMaxMotorImpulse(1);constraint.setMotorTarget(new THREE.Vector3(0, 0, 0)); // desired rotationreturn constraint;}function createPointToPoint() {var obj1 = new THREE.SphereGeometry(2);var obj2 = new THREE.SphereGeometry(2);var objectOne = new Physijs.SphereMesh(obj1, Physijs.createMaterial(new THREE.MeshPhongMaterial({color: 0xff4444, transparent: true, opacity: 0.7}), 0, 0));objectOne.position.z = -18;objectOne.position.x = -10;objectOne.position.y = 2;objectOne.castShadow = true;scene.add(objectOne);var objectTwo = new Physijs.SphereMesh(obj2, Physijs.createMaterial(new THREE.MeshPhongMaterial({color: 0xff4444, transparent: true, opacity: 0.7}), 0, 0));objectTwo.position.z = -5;objectTwo.position.x = -20;objectTwo.position.y = 2;objectTwo.castShadow = true;scene.add(objectTwo);// if no position two, its fixed to a position. Else fixed to objectTwo and both will movevar constraint = new Physijs.PointConstraint(objectOne, objectTwo, objectTwo.position);scene.addConstraint(constraint);}function createSliderBottom() {var sliderCube = new THREE.BoxGeometry(12, 2, 2);var sliderMesh = new Physijs.BoxMesh(sliderCube, Physijs.createMaterial(new THREE.MeshPhongMaterial({color: 0x44ff44, opacity: 0.6, transparent: true}), 0, 0), 0.01);sliderMesh.position.z = 20;sliderMesh.position.x = 6;sliderMesh.position.y = 1.5;sliderMesh.castShadow = true;scene.add(sliderMesh);var constraint = new Physijs.SliderConstraint(sliderMesh, new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 1, 0));scene.addConstraint(constraint);constraint.setLimits(-10, 10, 0, 0);constraint.setRestitution(0.1, 0.1);return constraint;}function createSliderTop() {var sliderSphere = new THREE.BoxGeometry(7, 2, 7);var sliderMesh = new Physijs.BoxMesh(sliderSphere, Physijs.createMaterial(new THREE.MeshPhongMaterial({color: 0x44ff44, transparent: true, opacity: 0.5}), 0, 0), 10);sliderMesh.position.z = -15;sliderMesh.position.x = -20;sliderMesh.position.y = 1.5;scene.add(sliderMesh);sliderMesh.castShadow = true;//position is the position of the axis, relative to the ref, based on the current positionvar constraint = new Physijs.SliderConstraint(sliderMesh, new THREE.Vector3(-10, 0, 20), new THREE.Vector3(Math.PI / 2, 0, 0));scene.addConstraint(constraint);constraint.setLimits(-20, 10, 0.5, -0, 5);constraint.setRestitution(0.2, 0.1);return constraint;}function createLeftFlipper() {var flipperLeft = new Physijs.BoxMesh(new THREE.BoxGeometry(12, 2, 2), Physijs.createMaterial(new THREE.MeshPhongMaterial({opacity: 0.6, transparent: true})), 0.3);flipperLeft.position.x = -6;flipperLeft.position.y = 2;flipperLeft.position.z = 0;flipperLeft.castShadow = true;scene.add(flipperLeft);var flipperLeftPivot = new Physijs.SphereMesh(new THREE.BoxGeometry(1, 1, 1), ground_material, 0);flipperLeftPivot.position.y = 1;flipperLeftPivot.position.x = -15;flipperLeftPivot.position.z = 0;flipperLeftPivot.rotation.y = 1.4;flipperLeftPivot.castShadow = true;scene.add(flipperLeftPivot);// 当观察轴时,使用的是物体二的轴。// 只要物体二的轴与场景的轴一致,就不会有问题。// 旋转和轴是相对于物体二的。如果位置等于立方体二的位置,那么效果就会如预期一样var constraint = new Physijs.HingeConstraint(flipperLeft, flipperLeftPivot, flipperLeftPivot.position, new THREE.Vector3(0, 1, 0));scene.addConstraint(constraint);constraint.setLimits(-2.2,-0.6,0.1,0);return constraint;}function createRightFlipper() {var flipperright = new Physijs.BoxMesh(new THREE.BoxGeometry(12, 2, 2), Physijs.createMaterial(new THREE.MeshPhongMaterial({opacity: 0.6, transparent: true})), 0.3);flipperright.position.x = 8;flipperright.position.y = 2;flipperright.position.z = 0;flipperright.castShadow = true;scene.add(flipperright);var flipperLeftPivot = new Physijs.SphereMesh(new THREE.BoxGeometry(1, 1, 1), ground_material, 0);flipperLeftPivot.position.y = 2;flipperLeftPivot.position.x = 15;flipperLeftPivot.position.z = 0;flipperLeftPivot.rotation.y = 1.4;flipperLeftPivot.castShadow = true;scene.add(flipperLeftPivot);// 当观察轴时,使用的是物体二的轴。// 只要物体二的轴与场景的轴一致,就不会有问题。// 旋转和轴是相对于物体二的。如果位置等于立方体二的位置,那么效果就会如预期一样var constraint = new Physijs.HingeConstraint(flipperright, flipperLeftPivot, flipperLeftPivot.position, new THREE.Vector3(0, 1, 0));
//            var constraint = new Physijs.HingeConstraint(cube1, new THREE.Vector3(0,0,0), new THREE.Vector3(0,1,0));scene.addConstraint(constraint);constraint.setLimits(-2.2, // 从点对象1开始(向后)的最小运动角度(以弧度为单位)-0.6, // 从点对象1开始(向前)的最大运动角度(以弧度为单位)0.1, // 作为约束错误的一个因素应用,当约束被触发时,kantelpunt被移动的程度有多大0 // 控制在极限时的弹跳(0.0表示无弹跳));return constraint;}var direction = 1;render = function () {requestAnimationFrame(render);renderer.render(scene, camera);render_stats.update();ground.__dirtyRotation = true;scene.simulate(undefined, 2);};window.onload = initScene;</script>
</head>
<body>
<div id="viewport"></div>
</body>
</html>

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

相关文章:

学习threejs,使用Physijs物理引擎,各种constraint约束限制

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️Physijs 物理引擎1.1.1 ☘️…...

城市排水管网流量监测系统解决方案

一、方案背景 随着工业的不断发展和城市人口的急剧增加&#xff0c;工业废水和城市污水的排放量也大量增加。目前&#xff0c;我国已成为世界上污水排放量大、增加速度快的国家之一。然而&#xff0c;总体而言污水处理能力较低&#xff0c;有相当部分未经处理的污水直接或间接排…...

redis数据结构-11(了解 Redis 持久性选项:RDB 和 AOF)

了解 Redis 持久性选项&#xff1a;RDB 和 AOF Redis 提供了多个持久性选项&#xff0c;以确保数据持久性并防止在服务器发生故障或重启时丢失数据。了解这些选项对于为您的特定使用案例选择正确的策略、平衡性能和数据安全至关重要。本章节将深入探讨 Redis 中的两种主要持久…...

掌握 Kotlin Android 单元测试:MockK 框架深度实践指南

掌握 Kotlin Android 单元测试&#xff1a;MockK 框架深度实践指南 在 Android 开发中&#xff0c;单元测试是保障代码质量的核心手段。但面对复杂的依赖关系和 Kotlin 语言特性&#xff0c;传统 Mock 框架常显得力不从心。本文将带你深入 MockK —— 一款专为 Kotlin 设计的 …...

2025/5/16

第一题 A. 例题4.1.2 潜水 题目描述 在马其顿王国的ohide湖里举行了一次潜水比赛。 其中一个项目是从高山上跳下水&#xff0c;再潜水达到终点。 这是一个团体项目&#xff0c;一支队伍由n人组成。在潜水时必须使用氧气瓶&#xff0c;但是每只队伍只有一个氧气瓶。 最多两…...

Detected for tasks ‘compileDebugJavaWithJavac‘ (17) and ‘kspDebugKotlin‘ (21).

1.报错 在导入Android源码的时候出现以下错误&#xff1a;Inconsistent JVM-target compatibility detected for tasks compileDebugJavaWithJavac (17) and kspDebugKotlin (21).。 Execution failed for task :feature-repository:kspDebugKotlin. > Inconsistent JVM-ta…...

嵌入式单片机中STM32F1演示寄存器控制方法

该文以STM32F103C8T6为示例,演示如何使用操作寄存器的方法点亮(关闭LED灯),并讲解了如何调试,以及使用宏定义。 第一:操作寄存器点亮LED灯。 (1)首先我们的目的是操作板子上的LED2灯,对其实现点亮和关闭操作。打开STM32F103C8T6的原理图,找到LED2的位置。 可以看到…...

【带文档】网上点餐系统 springboot + vue 全栈项目实战(源码+数据库+万字说明文档)

&#x1f4cc; 一、项目概括 本系统共包含三个角色&#xff1a; 管理员&#xff1a;系统运营管理者 用户&#xff1a;点餐消费用户 美食店&#xff1a;上传菜品与处理订单的店铺账号 通过对这三类角色的权限与业务分工设计&#xff0c;系统实现了点餐流程的全链路数字化&a…...

Spring Cloud:Gateway(统一服务入口)

Api 网关 也是一种服务&#xff0c;就是通往后端的唯一入口&#xff0c;类似于整个微服务架构的门面&#xff0c;所有的外部客户端进行访问&#xff0c;都需要经过它来进行过滤和调度&#xff0c;类似于公司的前台 而Spring Cloud Gateway就是Api网关的一种具体实现 网关的核心…...

Perl测试起步:从零到精通的完整指南

阅读原文 5.2 为什么你的Perl代码总是出问题&#xff1f;因为你还没开始测试&#xff01; "我的代码昨天还能运行&#xff0c;今天就莫名其妙报错了&#xff01;"、"我只是改了一个小功能&#xff0c;结果整个系统都崩溃了"、"这段代码不是我写的&am…...

【前端优化】vue2 webpack4项目升级webpack5,大大提升运行速度

记录一下过程 手里有个老项目&#xff0c;vue2webpack4 项目很大&#xff0c;每次运行、运行都要将近10分钟 现在又要往里面写很多东西&#xff0c;再不优化&#xff0c;开发着会更难受&#xff0c;所以决定先将它升级至webpack5 最初失败的尝试 直接在项目里安装了webpack5 但…...

【蓝桥杯省赛真题50】python字母比较 第十五届蓝桥杯青少组Python编程省赛真题解析

python字母比较 第十五届蓝桥杯青少年组python比赛省赛真题详细解析 博主推荐 所有考级比赛学习相关资料合集【推荐收藏】1、Python比赛 信息素养大赛Python编程挑战赛 蓝桥杯python选拔赛真题详解...

学习以任务为中心的潜动作,随地采取行动

25年5月来自香港大学、OpenDriveLab 和智元机器人的论文“Learning to Act Anywhere with Task-centric Latent Actions”。 通用机器人应该在各种环境中高效运行。然而&#xff0c;大多数现有方法严重依赖于扩展动作标注数据来增强其能力。因此&#xff0c;它们通常局限于单一…...

《数据结构初阶》【二叉树 精选9道OJ练习】

【二叉树 精选9道OJ练习】目录 前言&#xff1a;二叉树的OJ练习[144. 二叉树的前序遍历](https://leetcode.cn/problems/binary-tree-preorder-traversal/)题目介绍方法一&#xff1a;[104. 二叉树的最大深度](https://leetcode.cn/problems/maximum-depth-of-binary-tree/)题目…...

协议不兼容?Profinet转Modbus TCP网关让恒压供水系统通信0障碍

在现代工业自动化领域中&#xff0c;通信协议扮演着至关重要的角色。ModbusTCP和Profinet是两种广泛使用的工业通信协议&#xff0c;它们各自在不同的应用场合中展现出独特的优势。本文将探讨如何通过开疆智能Profinet转Modbus TCP的网关&#xff0c;在恒压供水系统中实现高效的…...

基于大模型预测的脑出血全流程诊疗技术方案

目录 一、系统架构设计技术架构图二、核心算法实现1. 多模态数据融合算法伪代码2. 风险预测模型实现三、关键模块流程图1. 术前风险预测流程图2. 术中决策支持流程图3. 并发症预测防控流程图四、系统集成方案1. 数据接口规范五、性能优化策略1. 推理加速方案2. 分布式训练架构六…...

掌握 LangChain 文档处理核心:Document Loaders 与 Text Splitters 全解析

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《深度探秘&#xff1a;AI界的007》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、什么是LangChain 2、LangChain 在智能应用中的作用 …...

Oracle — 总结

Oracle 公司及产品概述 公司背景 Oracle&#xff08;甲骨文&#xff09;是全球领先的数据库软件和服务提供商&#xff0c;成立于1977年&#xff0c;核心产品包括&#xff1a; Oracle Database&#xff1a;关系型数据库管理系统&#xff08;RDBMS&#xff09;。Java&#xff1a…...

【Vue 3全栈实战】从响应式原理到企业级架构设计

目录 &#x1f31f; 前言&#x1f3d7;️ 技术背景与价值&#x1fa79; 当前技术痛点&#x1f6e0;️ 解决方案概述&#x1f465; 目标读者说明 &#x1f9e0; 一、技术原理剖析&#x1f4ca; 核心概念图解&#x1f4a1; 核心作用讲解&#x1f527; 关键技术模块说明⚖️ 技术选…...

升级kafka4.0.0,无ZK版本

设备规划&#xff1a; 172.20.192.47 kafka-0 172.20.192.48 kafka-1 172.20.192.49 kafka-2 单机块7TB Nvme磁盘一共9块 # 格式化成GPT分区 sudo parted /dev/nvme0n1 --script mklabel gpt sudo parted /dev/nvme1n1 --script mklabel gpt sudo parted /dev/nvme2n1 --s…...

GESP2025年3月认证C++二级( 第三部分编程题(1)等差矩阵)

参考程序&#xff1a; #include <bits/stdc.h> using namespace std;int n, m; // 声明矩阵的行数 n 和列数 mint main() {// 输入两个正整数 n 和 mscanf("%d%d", &n, &m);// 遍历每一行for (int i 1; i < n; i)// 遍历每一列for (int j 1; j &…...

Linux系统启动相关:vmlinux、vmlinuz、zImage,和initrd 、 initramfs,以及SystemV 和 SystemD

目录 一、vmlinux、vmlinuz、zImage、bzImage、uImage 二、initrd 和 initramfs 1、initrd&#xff08;Initial RAM Disk&#xff09; 2、initramfs&#xff08;Initial RAM Filesystem&#xff09; 3、initrd vs. initramfs 对比 4. 如何查看和生成 initramfs 三、Syste…...

单序列双指针---初阶篇

目录 相向双指针 344. 反转字符串 125. 验证回文串 1750. 删除字符串两端相同字符后的最短长度 2105. 给植物浇水 II 977. 有序数组的平方 658. 找到 K 个最接近的元素 1471. 数组中的 k 个最强值 167. 两数之和 II - 输入有序数组 633. 平方数之和 2824. 统计和小于…...

K8s CoreDNS 核心知识点总结

文章目录 一、章节介绍背景与主旨核心知识点及面试频率 二、知识点详解1. CoreDNS 概述2. 工作原理&#xff08;高频考点&#xff09;服务发现流程 3. 配置与插件系统&#xff08;高频考点&#xff09;核心配置文件&#xff1a;Corefile常用插件 4. Pod DNS策略&#xff08;中频…...

Java视频流RTMP/RTSP协议解析与实战代码

在Java中实现视频直播的输入流处理&#xff0c;通常需要结合网络编程、多媒体处理库以及流媒体协议&#xff08;如RTMP、HLS、RTSP等&#xff09;。以下是实现视频直播输入流的关键步骤和技术要点&#xff1a; 1. 视频直播输入流的核心组件 网络输入流&#xff1a;通过Socket或…...

卓力达电铸镍网:精密制造与跨领域应用的创新典范

目录 引言 一、电铸镍网的技术原理与核心特性 二、电铸镍网的跨领域应用 三、南通卓力达电铸镍网的核心优势 四、未来技术展望 引言 电铸镍网作为一种兼具高精度与高性能的金属网状材料&#xff0c;通过电化学沉积工艺实现复杂结构的精密成型&#xff0c;已成为航空航天、电…...

label-studio功能常用英文翻译

Projects 项目 Settings 设置 Labeling Interface 标注界面 1、Computer Vision 计算机视觉 Semantic Segmentation with Polygons 多边形语义分割 Semantic Segmentation with Masks 掩码语义分割 Object Detection with Bounding Boxes 边界框目标检测 Keypoint Label…...

2025年PMP 学习十六 第11章 项目风险管理 (总章)

2025年PMP 学习十六 第11章 项目风险管理 &#xff08;总章&#xff09; 第11章 项目风险管理 序号过程过程组1规划风险管理规划2识别风险规划3实施定性风险分析规划4实施定量风险分析规划5规划风险应对执行6实施风险应对执行7监控风险监控 目标: 提高项目中积极事件的概率和…...

Jenkins 执行器(Executor)如何调整限制?

目录 现象原因解决 现象 Jenkins 构建时&#xff0c;提示如下&#xff1a; 此刻的心情正如上图中的小老头&#xff0c;火冒三丈&#xff0c;但是不要急&#xff0c;因为每一次错误&#xff0c;都是系统中某个环节在说‘我撑不住了’。 原因 其实是上图的提示表示 Jenkins 当…...

Jenkins 安装与配置指南

Jenkins 安装与配置指南&#xff08;MD 示例&#xff09; markdown Jenkins 安装与配置指南 ## 一、环境准备 1. **系统要求** - 操作系统&#xff1a;Linux/macOS/Windows - Java 版本&#xff1a;JDK 8 或更高&#xff08;建议 JDK 11&#xff09;2. **安装方式** - **L…...

使用unsloth对Qwen3在本地进行微调

Fine-tune Qwen3(100% locally) 使用unsloth进行微调,使用huggingface在本地运行model。 load model from unsloth import FastLanguageModel import torchMODEL = "unsloth/Qwen3-14B" model,tokenizer = FastLanguageModel.from_pretrained(model_name=MODE…...

GpuGeek 实操指南:So-VITS-SVC 语音合成与 Stable Diffusion 文生图双模型搭建,融合即梦 AI 的深度实践

GpuGeek 实操指南&#xff1a;So-VITS-SVC 语音合成与 Stable Diffusion 文生图双模型搭建&#xff0c;融合即梦 AI 的深度实践 前言 本文将详细讲解 So-VITS-SVC 语音合成与 Stable Diffusion 文生图的搭建方法&#xff0c;以及二者与即梦 AI 融合的实践技巧&#xff0c;无论你…...

CSS- 3.1 盒子模型-块级元素、行内元素、行内块级元素和display属性

本系列可作为前端学习系列的笔记&#xff0c;代码的运行环境是在HBuilder中&#xff0c;小编会将代码复制下来&#xff0c;大家复制下来就可以练习了&#xff0c;方便大家学习。 HTML系列文章 已经收录在前端专栏&#xff0c;有需要的宝宝们可以点击前端专栏查看&#xff01; 点…...

使用exceljs将excel文件转化为html预览最佳实践(完整源码)

前言 在企业应用中&#xff0c;我们时常会遇到需要上传并展示 Excel 文件的需求&#xff0c;以实现文件内容的在线预览。经过一番探索与尝试&#xff0c;笔者最终借助 exceljs 这一库成功实现了该功能。本文将以 Vue 3 为例&#xff0c;演示如何实现该功能&#xff0c;代码示例…...

7. 进程控制-进程替换

目录 1. 进程替换 1.1 单进程版&#xff1a; 1.2 进程替换的原理 1.3 多进程版-验证各种程序替换接口 2. 进程替换的各种接口 2.1 execl 2.2 execlp 2.3 execv 2.4 execvp 2.5 execle 1. 进程替换 上图为程序替换的接口&#xff0c;之后会详细介绍。 1.1 单进程版&am…...

关于计算机系统和数据原子性的联系

目录 1、计算机架构 1.1、处理器架构 1.2、内存寻址能力 1.3、性能差异 1.4、软件兼容性 1.5、指令集 1.6、开发和维护 2.、基本数据类型 3、原子类型 3.1、基本概念 3.2、基本数据类型的原子性 3.3、原子操作的解释 3.4、不保证原子性 3.5、解决方案 4、原子性…...

Armijo rule

非精线搜索步长规则Armijo规则&Goldstein规则&Wolfe规则_armijo rule-CSDN博客 [原创]用“人话”解释不精确线搜索中的Armijo-Goldstein准则及Wolfe-Powell准则 – 编码无悔 / Intent & Focused...

从数据包到可靠性:UDP/TCP协议的工作原理分析

之前我们已经使用udp/tcp的相关接口写了一些简单的客户端与服务端代码。也了解了协议是什么&#xff0c;包括自定义协议和知名协议比如http/https和ssh等。现在我们再回到传输层&#xff0c;对udp和tcp这两传输层巨头协议做更深一步的分析。 一.UDP UDP相关内容很简单&#xf…...

Prometheus实战教程:k8s平台-Mysql监控案例

配置文件优化后的 Prometheus 自动发现 MySQL 实例的完整 YAML 文件。该配置包括&#xff1a; MySQL Exporter 部署&#xff1a;使用 ConfigMap 提供 MySQL 连接信息。Prometheus 自动发现&#xff1a;通过 Kubernetes 服务发现自动抓取 MySQL 实例。 1、mysql 配置文件 &…...

执行apt-get update 报错ModuleNotFoundError: No module named ‘apt_pkg‘的解决方案汇总

Ubuntu版本ubuntu18.04 报错内容&#xff1a; //执行apt-get upgrade报错&#xff1a; Traceback :File “/usr/lib/cnf-update-db”, line 8, in <module>from CommandNotFound.db.creator import DbcreatorFile “/usr/lib/python3/dist-packages/CommandNotFound/db…...

QT6 源(101)篇一:阅读与注释 QPlainTextEdit,其继承于QAbstractScrollArea,属性学习与测试

&#xff08;1&#xff09; &#xff08;2&#xff09; &#xff08;3&#xff09;属性学习与测试 &#xff1a; &#xff08;4&#xff09; &#xff08;5&#xff09; 谢谢...

Redis(2):Redis + Lua为什么可以实现原子性

Redis 作为一款高性能的键值对存储数据库&#xff0c;与 Lua 脚本相结合&#xff0c;为实现原子性操作提供了强大的解决方案&#xff0c;本文将深入探讨 Redis Lua 实现原子性的相关知识 原子性概念的厘清 在探讨 Redis Lua 的原子性之前&#xff0c;我们需要明确原子性的概念…...

ios打包ipa获取证书和打包创建经验分享

在云打包或本地打包ios应用&#xff0c;打包成ipa格式的app文件的过程中&#xff0c;私钥证书和profile文件是必须的。 其实打包的过程并不难&#xff0c;因为像hbuilderx这些打包工具&#xff0c;只要你输入的是正确的证书&#xff0c;打包就肯定会成功。因此&#xff0c;证书…...

Python生成器:高效处理大数据的秘密武器

生成器概述 生成器是 Python 中的一种特殊迭代器&#xff0c;通过普通函数的语法实现&#xff0c;但使用 yield 语句返回数据。生成器自动实现了 __iter__() 和 __next__() 方法&#xff0c;因此可以直接用于迭代。生成器的核心特点是延迟计算&#xff08;lazy evaluation&…...

C++11(2)

文章目录 右值引用和移动语义在传参中的提效list容器push_back & insert右值版本的模拟实现类型分类 (了解即可&#xff09;引用折叠万能引用 完美转发&#xff08;跟引用折叠有关&#xff09; 简介&#xff1a;这篇文章是继续介绍C11的一些新语法知识点&#xff0c;也是对…...

unity terrain 在生成草,树,石头等地形障碍的时候,无法触发碰撞导致人物穿过模型

1.terrain地形的草&#xff0c;石头之类要选择模型预制体 2.在人物身上挂碰撞器和刚体&#xff0c;或者单挂一个character controller组件也行 3.在预制体上挂碰撞盒就好了&#xff0c;挂载meshcollider会导致碰撞无效...

以项目的方式学QT开发C++(二)——超详细讲解(120000多字详细讲解,涵盖qt大量知识)逐步更新!

API 描述 函数原型 参数说明 push_back() 在 list 尾部 添加一个元素 void push_back(const T& value); value &#xff1a;要添 加到尾部的元 素 这个示例演示了如何创建 std::list 容器&#xff0c;并对其进行插入、删除和迭代操作。在实际应用中&am…...

养生:健康生活的极简攻略

在追求高效生活的当下&#xff0c;养生也能化繁为简。通过饮食、运动、睡眠与心态的精准调节&#xff0c;就能轻松为健康续航。 饮食上&#xff0c;遵循 “均衡、节制” 原则。早餐用一杯热豆浆搭配水煮蛋和半个苹果&#xff0c;唤醒肠胃活力&#xff1b;午餐以糙米饭为主食&am…...

C语言-8.数组

8.1数组 8.1.1初试数组 如何写一个程序计算用户输入的数字的平均数? #include<stdio.h> int main() {int digit;//输入要求平均数的数字double sum=0;//记录输入数字的和int count=0;//记录输入数字的个数printf("请输入一组数字,用来求平均数,以-1结束\n&quo…...

代码随想录算法训练营第四十一天

LeetCode题目: 739. 每日温度496. 下一个更大元素 I503. 下一个更大元素 II 其他: 今日总结 往期打卡 739. 每日温度 跳转: 739. 每日温度 学习: 代码随想录公开讲解 问题: 给定一个整数数组 temperatures &#xff0c;表示每天的温度&#xff0c;返回一个数组 answer &…...