学习threejs,使用Physijs物理引擎

发布于:2025-05-09 ⋅ 阅读:(14) ⋅ 点赞:(0)

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



一、🍀前言

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

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)。

二、🍀使用Physijs物理引擎

1. ☘️实现思路

  • 1、引入‘physi.js’,创建Physijs物理引擎三维场景scene,设置scene场景重力信息。
  • 2、初始化camera相机,定义相机位置 camera.position.set,设置相机方向camera.lookAt,场景scene添加camera。
  • 3、创建THREE.SpotLight聚光灯光源light,设置light位置,scene场景加入light。
  • 4、加载几何模型:使用‘floor-wood.jpg’木纹贴图创建地面网格对象ground以及四周突出边框网格对象borderLeft、borderRight、borderTop、borderBottom,ground添加borderLeft、borderRight、borderTop、borderBottom。定义controls方法,内部定义addCubes方法用于添加物理碰撞立方体,定义addSpheres方法用于添加物理碰撞球体,定义clearMeshes方法用于清除添加的碰撞对象,定义碰撞物体restitution弹性形变和friction摩擦系数。定义render方法,实现ground的旋转动画。具体代码参考下面代码样例。
  • 5、加入gui控制。加入stats监控器,监控帧数信息。

2. ☘️代码样例

<!DOCTYPE html>
<html>
<style>
    body {
        margin: 0;
        overflow: hidden;
    }
</style>

<head>
    <title>学习threejs,使用Physijs物理引擎</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';
        //内部库ammo.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));
            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.zIndex = 100;
            document.getElementById('viewport').appendChild(render_stats.domElement);

            scene = new Physijs.Scene;
            scene.setGravity(new THREE.Vector3(0, -90, 0));

            camera = new THREE.PerspectiveCamera(
                    35,
                    window.innerWidth / window.innerHeight,
                    1,
                    1000
            );
            camera.position.set(80, 60, 80);
            camera.lookAt(scene.position);
            scene.add(camera);

            // Light
            light = new THREE.SpotLight(0xFFFFFF);
            light.position.set(20, 100, 50);


            scene.add(light);

            // Materials
            ground_material = Physijs.createMaterial(
                    new THREE.MeshPhongMaterial({map: THREE.ImageUtils.loadTexture('../assets/textures/general/floor-wood.jpg')}),
                    .9,
                    .6
            );
            ground_material.map.wrapS = ground_material.map.wrapT = THREE.RepeatWrapping;
            ground_material.map.repeat.set(4, 8);

            // Ground
            ground = new Physijs.BoxMesh(
                    new THREE.BoxGeometry(60, 1, 130),
                    ground_material,
                    0
            );
            ground.receiveShadow = true;


            var borderLeft = new Physijs.BoxMesh(
                    new THREE.BoxGeometry(2, 6, 130),
                    ground_material,
                    0
            );

            borderLeft.position.x = -31;
            borderLeft.position.y = 2;

            ground.add(borderLeft);

            var borderRight = new Physijs.BoxMesh(new THREE.BoxGeometry(2, 6, 130),
                    ground_material,
                    0
            );
            borderRight.position.x = 31;
            borderRight.position.y = 2;

            ground.add(borderRight);


            var borderBottom = new Physijs.BoxMesh(
                    new THREE.BoxGeometry(64, 6, 2),
                    ground_material,
                    0
            );

            borderBottom.position.z = 65;
            borderBottom.position.y = 2;
            ground.add(borderBottom);

            var borderTop = new Physijs.BoxMesh(
                    new THREE.BoxGeometry(64, 6, 2),
                    ground_material,
                    0
            );

            borderTop.position.z = -65;
            borderTop.position.y = 2;
            ground.add(borderTop)

            scene.add(ground);

            var meshes = [];

            var controls = new function () {
            	//参数 restitution(弹性形变)和friction(摩擦系数)
                this.cubeRestitution = 0.4;
                this.cubeFriction = 0.4;
                this.sphereRestitution = 0.9;
                this.sphereFriction = 0.1;


                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: true
                                        controls.sphereFriction,
                                        controls.sphereRestitution
                                )
                        );
                        box.position.set(
                                Math.random() * 50 - 25,
                                20 + Math.random() * 5,
                                Math.random() * 50 - 25
                        );
                        meshes.push(box);
                        scene.add(box);
                    }
                };

                this.addCubes = function () {

                    var colorBox = scale(Math.random()).hex();

                    for (var i = 0; i < 5; i++) {

                        box = new Physijs.BoxMesh(
                                new THREE.BoxGeometry(4, 4, 4),
                                Physijs.createMaterial(
                                        new THREE.MeshPhongMaterial({
                                            color: colorBox,
                                            opacity: 0.8,
                                            transparent: true
                                        controls.cubeFriction,
                                        controls.cubeRestitution
                                )
                        );
                        box.position.set(
                                Math.random() * 50 - 25,
                                20 + Math.random() * 5,
                                Math.random() * 50 - 25
                        );
                        box.rotation.set(
                                Math.random() * Math.PI * 2,
                                Math.random() * Math.PI * 2,
                                Math.random() * Math.PI * 2);

                        meshes.push(box);
                        scene.add(box);
                    }
                }
            };

            var gui = new dat.GUI();
            gui.add(controls, 'cubeRestitution', 0, 1);
            gui.add(controls, 'cubeFriction', 0, 1);
            gui.add(controls, 'sphereRestitution', 0, 1);
            gui.add(controls, 'sphereFriction', 0, 1);
            gui.add(controls, 'addCubes');
            gui.add(controls, 'addSpheres');
            gui.add(controls, 'clearMeshes');

            requestAnimationFrame(render);
            scene.simulate();
        };

        var stepX;
        var direction = 1;

        render = function () {
            requestAnimationFrame(render);
            renderer.render(scene, camera);
            render_stats.update();
            ground.rotation.x += 0.002 * direction;

            if (ground.rotation.x < -0.4) direction = 1;
            if (ground.rotation.x > 0.4) direction = -1;
            ground.__dirtyRotation = true;
            scene.simulate(undefined, 1);
        };
        
        window.onload = initScene;

    </script>
</head>

<body>
<div id="viewport"></div>
</body>

</html>

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


网站公告

今日签到

点亮在社区的每一天
去签到