【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>