1,介绍:
该示例使用的是 r95版本Three.js库。在上一个案例(文章首页可查找)基础上添加城市模型(obj格式),添加人物模型(gltf格式),初始化运动路线,这里使用了Tween.js实现的动画,人物模型按照路线进行运动。添加坐标轴。效果图如下:
2,主要说明
Three.js加载模型文件,并从中导入几何体。Three.js所支持的模型文件格式:json、obj/mtl、dae、ply、gltf、glb、svg、pbd...,这里说的都是扩展名,还有好多种这里不列举了。如果想加载bim模型如:ifc和rvt格式,则需要进行转换成Three所支持的格式,可以使用blender软件进行转换,具体教程百度。
添加城市模型obj/mtl格式,obj/mtl是相互配合的两种格式,经常一起使用。obj文件定义几何体,而mtl文件定义所用的材质。obj和mtl都是基于文本的格式。代码如下:
// 添加模型 function initModel() { var mtlLoader = new THREE.MTLLoader(); mtlLoader.setPath("assets/models/obj_mtl/") mtlLoader.load('city.mtl', function(materials) { materials.preload(); var objLoader = new THREE.OBJLoader(); objLoader.setMaterials(materials); objLoader.load('assets/models/obj_mtl/city.obj', function(object) { mesh = object; mesh.scale.set(3, 3, 3); mesh.position.set(18, 0, 18); scene.add(mesh); }); }); }
添加人物模型gltf格式,该格式专用于存储3D场景和模型,其优势在于可以最小化文件尺寸,并且可以高效地加载模型。代码如下:
// 添加人物模型 function initPeople() { var loader = new THREE.GLTFLoader(); loader.load('assets/models/CesiumMan/CesiumMan.gltf', function(result) { result.scene.scale.set(1, 1, 1); result.scene.translateY(0); aaa = result.scene; scene.add(result.scene); tweenComplete(); mixer = new THREE.AnimationMixer(result.scene); animationClip = result.animations[0]; clipAction = mixer.clipAction(animationClip).play(); animationClip = clipAction.getClip(); }); }
使用Tween.js实现的动画效果,通过Tween.js可以很容易地实现某个属性在两个值之间进行过渡,而且起始值和结束值之间的所有中间值都会自动计算出来,这个过程叫作tweening(补间)。
function tweenComplete() { if (i < points.length) { switch (i) { case 0: pobj.rotateY(Math.PI); break; case 1: case 5: case 8: case 9: pobj.rotateY(-0.5 * Math.PI); break; case 2: case 3: case 4: case 6: case 7: pobj.rotateY(0.5 * Math.PI); break; case 10: mixer.stopAllAction(); break; } tween = new TWEEN.Tween(points[i]) .to(points[i + 1], 3000) .easing(TWEEN.Easing.Linear.None) .onUpdate(function() { pobj.position.set(this.x, this.y, this.z); }) .onComplete(tweenComplete) .start(); i++; } }
3,源码如下:
<!DOCTYPE html> <html> <head> <title>Threejs加载城市obj模型,加载人物gltf模型,人物根据规划的路线运动</title> <script type="text/javascript" src="libs/three.js"></script> <script type="text/javascript" src="libs/OrbitControls.js"></script> <script type="text/javascript" charset="UTF-8" src="libs/other/Tween.min.js"></script> <script type="text/javascript" charset="UTF-8" src="libs/three/loaders/GLTFLoader.js"></script> <script type="text/javascript" src="libs/OBJLoader.js"></script> <script type="text/javascript" src="libs/MTLLoader.js"></script> <style> body { margin: 0; overflow: hidden; } </style> </head> <body> <div id="dom"></div> <script type="text/javascript"> var camera; var renderer; var clock = new THREE.Clock(); var mixer = new THREE.AnimationMixer(); var clipAction var animationClip var pobj function init() { // 创建一个场景,它将包含我们所有的元素,如物体,相机和灯光。 var scene = new THREE.Scene(); var urls = [ 'assets/textures/cubemap/flowers/posx.jpg', 'assets/textures/cubemap/flowers/negx.jpg', 'assets/textures/cubemap/flowers/posy.jpg', 'assets/textures/cubemap/flowers/negy.jpg', 'assets/textures/cubemap/flowers/posz.jpg', 'assets/textures/cubemap/flowers/negz.jpg' ]; var cubeLoader = new THREE.CubeTextureLoader(); scene.background = cubeLoader.load(urls); // 创建一个摄像机,它定义了我们正在看的地方 camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000); // 将摄像机对准场景的中心 camera.position.x = 20; camera.position.y = 15; camera.position.z = 35; camera.lookAt(scene.position); var orbit = new THREE.OrbitControls(camera); // 创建一个渲染器并设置大小,WebGLRenderer将会使用电脑显卡来渲染场景 // initialize basic renderer renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); // 将平面添加到场景中 var plane = createPlaneGeometryBasicMaterial(); scene.add(plane); // 在屏幕上显示坐标轴 var axes = new THREE.AxesHelper(100); scene.add(axes); // var trackballControls = initTrackballControls(camera, renderer); // 添加环境光 scene.add(new THREE.AmbientLight(0x666666)); scene.add(new THREE.AmbientLight("#ffffff", 1)); // 将呈现器的输出添加到HTML元素 document.getElementById("dom").appendChild(renderer.domElement); var points = initLine(); // 将球体添加到场景中 initModel(); initPeople(); // 启动动画 renderScene(); var i = 0; function tweenComplete() { if (i < points.length) { switch (i) { case 0: pobj.rotateY(Math.PI); break; case 1: case 5: case 8: case 9: pobj.rotateY(-0.5 * Math.PI); break; case 2: case 3: case 4: case 6: case 7: pobj.rotateY(0.5 * Math.PI); break; case 10: mixer.stopAllAction(); break; } tween = new TWEEN.Tween(points[i]) .to(points[i + 1], 3000) .easing(TWEEN.Easing.Linear.None) .onUpdate(function() { pobj.position.set(this.x, this.y, this.z); }) .onComplete(tweenComplete) .start(); i++; } } // 添加模型 function initModel() { var mtlLoader = new THREE.MTLLoader(); mtlLoader.setPath("assets/models/obj_mtl/") mtlLoader.load('city.mtl', function(materials) { materials.preload(); var objLoader = new THREE.OBJLoader(); objLoader.setMaterials(materials); objLoader.load('assets/models/obj_mtl/city.obj', function(object) { mesh = object; mesh.scale.set(3, 3, 3); mesh.position.y = -5; scene.add(mesh); }); }); } // 添加人物模型 function initPeople() { var loader = new THREE.GLTFLoader(); loader.load('assets/models/CesiumMan/CesiumMan.gltf', function(result) { result.scene.scale.set(1, 1, 1); result.scene.translateY(0); pobj = result.scene; scene.add(result.scene); tweenComplete(); mixer = new THREE.AnimationMixer(result.scene); animationClip = result.animations[0]; clipAction = mixer.clipAction(animationClip).play(); animationClip = clipAction.getClip(); }); } // 创建一个平面 function createPlaneGeometryBasicMaterial() { var textureLoader = new THREE.TextureLoader(); var cubeMaterial = new THREE.MeshStandardMaterial({ map: textureLoader.load("assets/textures/stone/cd.jpg"), }); cubeMaterial.map.wrapS = THREE.RepeatWrapping; cubeMaterial.map.wrapT = THREE.RepeatWrapping; cubeMaterial.map.repeat.set(18, 18) // 创建地平面并设置大小 var planeGeometry = new THREE.PlaneGeometry(500, 500); var plane = new THREE.Mesh(planeGeometry, cubeMaterial); // 设置平面位置并旋转 plane.rotation.x = -0.5 * Math.PI; plane.position.x = 0; plane.position.y = -5; plane.position.z = 0; return plane; } // 初始化线路 function initLine() { var pArr = [{ x: 5 * 3, y: -3.8, z: -0.7 * 3 }, { x: -0.6 * 3, y: -3.8, z: -0.7 * 3 }, { x: -0.6 * 3, y: -3.8, z: -1.8 * 3 }, { x: -4 * 3, y: -3.8, z: -1.8 * 3 }, { x: -4 * 3, y: -3.8, z: 2.8 * 3 }, { x: -1.2 * 3, y: -3.8, z: 2.8 * 3 }, { x: -1.2 * 3, y: -3.8, z: 4.3 * 3 }, { x: 1.7 * 3, y: -3.8, z: 4.3 * 3 }, { x: 1.7 * 3, y: -3.8, z: -0.4 * 3 }, { x: 4.4 * 3, y: -3.8, z: -0.4 * 3 }, { x: 4.4 * 3, y: -3.8, z: 5 * 3 }]; var points = []; var geometry = new THREE.Geometry(); for (var i = 0; i < pArr.length; i++) { var randomX = pArr[i].x; var randomY = pArr[i].y; var randomZ = pArr[i].z; var vector = new THREE.Vector3(randomX, randomY, randomZ); geometry.vertices.push(vector); points.push(vector); } var material = new THREE.LineBasicMaterial({ color: 0xff0000 }); var line = new THREE.Line(geometry, material); scene.add(line); return points; } // 动画渲染 var step = 5; function renderScene() { TWEEN.update(); orbit.update(); var delta = clock.getDelta(); mixer.update(delta); // 使用requestAnimationFrame函数进行渲染 requestAnimationFrame(renderScene); renderer.render(scene, camera); } // 渲染的场景 renderer.render(scene, camera); document.addEventListener('mousedown', onDocumentMouseDown, false); function onDocumentMouseDown(event) { // 点击屏幕创建一个向量 var vector = new THREE.Vector3((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window .innerHeight) * 2 + 1, 0.5); vector = vector.unproject(camera); // 将屏幕的坐标转换成三维场景中的坐标 var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize()); var intersects = raycaster.intersectObjects(mesh.children, true); console.log(intersects) if (intersects.length > 0) { // intersects[0].object.material.color.set("#ffffff"); } } // 创建一个球形几何体 function createSphereGeometryLambertMaterial(point) { // 创建一个球体 var sphereGeometry = new THREE.SphereGeometry(0.2, 20, 20); var sphereMaterial = new THREE.MeshBasicMaterial({ color: 0x7777ff, wireframe: true }); var sphereMaterial = new THREE.MeshLambertMaterial({ color: 0xff0000 }); var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); // 设置该物体投射阴影 sphere.castShadow = true; // 位置范围 sphere.position.x = point.x; sphere.position.y = point.y; sphere.position.z = point.z; return sphere; } } window.onload = init; function onResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } // 监听调整大小事件 window.addEventListener('resize', onResize, false); </script> </body> </html>