【three.js练习程序】创建简单物理场景

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ceshi</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
    <script src="./build/three.js"></script>
    <script src="./examples/js/libs/ammo.js"></script>
    <script src="./examples/js/controls/OrbitControls.js"></script>
</head>
<body>
    <div id="ThreeJs">
    </div>
    <script>
        var camera, controls, scene, renderer;
        var clock = new THREE.Clock();

        // 物理引擎相关变量
        var gravityConstant = -9.8;
        var collisionConfiguration;
        var dispatcher;
        var broadphase;
        var solver;
        var physicsWorld;
        var rigidBodies = [];
        var margin = 0.05;
        var transformAux1 = new Ammo.btTransform();
        var time = 0;

        init();
        animate();

        function init() {
            initGraphics();
            initPhysics();
            createObjects();
        }

        function initGraphics() {
            // three.js基本场景配置
            camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 2000);
            camera.position.x = 30;
            camera.position.y = 30;
            camera.position.z = 30;

            controls = new THREE.OrbitControls(camera);
            controls.target.y = 2;

            renderer = new THREE.WebGLRenderer();
            renderer.setClearColor(new THREE.Color("#bfd1e5"));
            renderer.shadowMapEnabled = true;
            renderer.setSize(window.innerWidth, window.innerHeight);
            // 场景
            scene = new THREE.Scene();
            // 环境光
            var ambientLight = new THREE.AmbientLight(0x404040);
            scene.add(ambientLight);
            // 线性光
            var light = new THREE.DirectionalLight(0xffffff, 1);
            light.position.set(-20, 20, 10);
            light.castShadow = true;
            var d = 50;
            light.shadow.camera.left = -d;
            light.shadow.camera.right = d;
            light.shadow.camera.top = d;
            light.shadow.camera.bottom = -d;

            light.shadow.camera.near = 2;
            light.shadow.camera.far = 50;

            light.shadow.mapSize.x = 1024;
            light.shadow.mapSize.y = 1024;
            scene.add(light);

            var axes = new THREE.AxisHelper(50);               //创建三轴表示
            scene.add(axes);
            // 添加窗口大小变化监听
            window.addEventListener('resize', onWindowResize, false);
        }

        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }

        function initPhysics() {
            // bullet基本场景配置
            collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
            dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
            broadphase = new Ammo.btDbvtBroadphase();
            solver = new Ammo.btSequentialImpulseConstraintSolver();
            physicsWorld = new Ammo.btDiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration);
            physicsWorld.setGravity(new Ammo.btVector3(0, gravityConstant, 0));
        }

        function createObjects() {
            var pos = new THREE.Vector3();
            var quat = new THREE.Quaternion();
            //创建地面
            pos.set(0, 0, 0);
            quat.set(0, 0, 0, 1);
            var ground = createParallellepiped(40, 1, 40, 0, pos, quat, new THREE.MeshPhongMaterial({ color: 0xffffff }));
            ground.castShadow = true;       // 开启投影
            ground.receiveShadow = true;    // 接受阴影(可以在表面上显示阴影)

            //创建50个小球
            for (var i = 0; i < 50; i++) {
                var ballMass = 1.2;
                var ballRadius = 0.5;

                var ball = new THREE.Mesh(new THREE.SphereGeometry(ballRadius, 20, 20), createRendomColorObjectMeatrial());
                ball.castShadow = true;
                ball.receiveShadow = true;
                var ballShape = new Ammo.btSphereShape(ballRadius);
                ballShape.setMargin(margin);
                pos.set(Math.random() + 10, 2 * (i + 1), Math.random() - 10);
                quat.set(0, 0, 0, 1);
                createRigidBody(ball, ballShape, ballMass, pos, quat);
                ball.userData.physicsBody.setFriction(1.5);
            }

            //创建50个方块
            for (var i = 0; i < 50; i++) {
                pos.set(Math.random() - 10, 2 * (i + 1), Math.random() + 10);
                quat.set(0, 0, 0, 1);
                createParallellepiped(1, 1, 1, 1, pos, quat, createRendomColorObjectMeatrial());
            }
        }
        
        function createRendomColorObjectMeatrial() {
            var color = Math.floor(Math.random() * (1 << 24));
            return new THREE.MeshPhongMaterial({ color: color });
        }

        function createParallellepiped(sx, sy, sz, mass, pos, quat, material) {
            var threeObject = new THREE.Mesh(new THREE.BoxGeometry(sx, sy, sz, 1, 1, 1), material);
            threeObject.castShadow = true;
            threeObject.receiveShadow = true;
            var shape = new Ammo.btBoxShape(new Ammo.btVector3(sx * 0.5, sy * 0.5, sz * 0.5));
            shape.setMargin(margin);
            createRigidBody(threeObject, shape, mass, pos, quat);
            return threeObject;
        }

        function createRigidBody(threeObject, physicsShape, mass, pos, quat) {
            threeObject.position.copy(pos);
            threeObject.quaternion.copy(quat);
            var transform = new Ammo.btTransform();
            transform.setIdentity();
            transform.setOrigin(new Ammo.btVector3(pos.x, pos.y, pos.z));
            transform.setRotation(new Ammo.btQuaternion(quat.x, quat.y, quat.z, quat.w));
            var motionState = new Ammo.btDefaultMotionState(transform);
            var localInertia = new Ammo.btVector3(0, 0, 0);
            physicsShape.calculateLocalInertia(mass, localInertia);
            var rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, physicsShape, localInertia);
            var body = new Ammo.btRigidBody(rbInfo);
            threeObject.userData.physicsBody = body;
            scene.add(threeObject);
            if (mass > 0) {
                rigidBodies.push(threeObject);
                body.setActivationState(4);
            }
            physicsWorld.addRigidBody(body);
            return body;
        }

        function animate() {
            requestAnimationFrame(animate);
            var deltaTime = clock.getDelta();
            updatePhysics(deltaTime);
            controls.update(deltaTime);
            renderer.render(scene, camera);
            time += deltaTime;
        }

        function updatePhysics(deltaTime) {
            physicsWorld.stepSimulation(deltaTime);
            // 更新物体位置
            for (var i = 0, iL = rigidBodies.length; i < iL; i++) {
                var objThree = rigidBodies[i];
                var objPhys = objThree.userData.physicsBody;
                var ms = objPhys.getMotionState();
                if (ms) {
                    ms.getWorldTransform(transformAux1);
                    var p = transformAux1.getOrigin();
                    var q = transformAux1.getRotation();
                    objThree.position.set(p.x(), p.y(), p.z());
                    objThree.quaternion.set(q.x(), q.y(), q.z(), q.w());
                }
            }
        }

        document.getElementById("ThreeJs").appendChild(renderer.domElement);
    </script>
</html>

原文地址:https://www.cnblogs.com/tiandsp/p/8453969.html