Threejs加载城市obj模型,加载人物gltf模型,Tweenjs实现人物根据规划的路线运动

简介: Threejs加载城市obj模型,加载人物gltf模型,Tweenjs实现人物根据规划的路线运动

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>
目录
相关文章
|
定位技术
百度地图开发:map.setViewport让标注显示在最佳视野内
百度地图开发:map.setViewport让标注显示在最佳视野内
326 0
|
4天前
|
人工智能 小程序 前端开发
【一步步开发AI运动小程序】六、人体骨骼图绘制
随着AI技术的发展,阿里体育等公司推出的AI运动APP如“乐动力”、“天天跳绳”等,使云上运动会、线上健身等概念广受欢迎。本文将引导您从零开始,利用“云智AI运动识别小程序插件”,在小程序中实现类似功能,包括人体骨骼图的绘制原理及其实现代码,确保骨骼图与人体图像精准重合。下篇将继续介绍运动分析方法。
|
3月前
|
API
【threejs教程】让你的场景更加真实:灯光对物体的影响
【8月更文挑战第6天】threejs教程:让你的场景更加真实,灯光对物体的影响
173 6
【threejs教程】让你的场景更加真实:灯光对物体的影响
|
5月前
|
图形学
【unity小技巧】手戳代码程序化绘制地形Terrain树和预制体物品、动物
【unity小技巧】手戳代码程序化绘制地形Terrain树和预制体物品、动物
51 0
|
程序员 定位技术 容器
ChatGPT工作提效之使用百度地图在首都为六一儿童节献爱心(多边形覆盖物、文本标注、自动获取经纬度、爱心函数)
ChatGPT工作提效之使用百度地图在首都为六一儿童节献爱心(多边形覆盖物、文本标注、自动获取经纬度、爱心函数)
122 0
|
数据可视化 物联网
Threejs物联网,养殖场3D可视化(三)模型展示,轨道控制器设置,模型沿着路线运动,模型添加边框,自定义样式显示标签,点击模型获取信息
Threejs物联网,养殖场3D可视化(三)模型展示,轨道控制器设置,模型沿着路线运动,模型添加边框,自定义样式显示标签,点击模型获取信息
945 15
Threejs物联网,养殖场3D可视化(三)模型展示,轨道控制器设置,模型沿着路线运动,模型添加边框,自定义样式显示标签,点击模型获取信息
|
机器学习/深度学习 人工智能 计算机视觉
华南理工TANGO项目原作解读: 文本驱动的三维物体风格化模型
华南理工TANGO项目原作解读: 文本驱动的三维物体风格化模型
170 0
|
数据可视化 物联网
Threejs物联网,工厂3D可视化,加载模型,水流监测,标签动态数据展示
Threejs物联网,工厂3D可视化,加载模型,水流监测,标签动态数据展示
928 15
Threejs物联网,工厂3D可视化,加载模型,水流监测,标签动态数据展示
|
定位技术
无须任何数据,六十秒快速制作三维影像图用以作为城市空间格局分析的底图
无须任何数据,六十秒快速制作三维影像图用以作为城市空间格局分析的底图
124 0
|
机器学习/深度学习 编解码 人工智能
首篇BEV感知生成工作!BEVGen:从鸟瞰图布局生成环视街景图像
本文提出了BEVGen,这是一个条件生成式模型,它合成了一组真实且空间一致的环视图像,这些图像与交通场景的BEV布局相匹配。BEVGen结合了一种新颖的交叉视图转换和空间注意力设计,学习相机和地图视图之间的关系,以确保它们的一致性。BEVGen可以精确地渲染道路和车道线,以及在不同的天气条件和时间生成交通场景。
首篇BEV感知生成工作!BEVGen:从鸟瞰图布局生成环视街景图像