Vue+Three.js实现三维管道可视化及流动模拟

最近在研究Geotoolkit过程中,发现很多三维的应用场景,用其实现起来比较复杂,就开展了利用Three.js实现海底管道流动的模拟。

推荐一个学习地址:Three.js教程,这这里的学习示例基本上H5+js,再多加以一点nodejs,对于vue+Three.js的开发方式比较少,包括官网的示例也是前者,因此就促使自己尝试用vue+Three.js的学习研究,这样后续和目前的前端集成起来就方便一些。

先说明一下,我的开发环境:vue-2.5.2,Three.js-0.142.0,开发工具webstorm2021.2.3,其他都默认。

示例:实现了海底管道的三维显示及三维液体流向的模拟,效果如下:

vue+Three.js,通过不断改变管道的Texture实现管道流动模拟,代码详见pipe4.vue。

核心代码如下:

<script>
import * as THREE from 'three';
// 这样引入一下灯光和材质 也可以使用 THREE.的方式
import {HemisphereLight,SphereGeometry} from 'three'
// 轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

// 此部分为了展示为hardcode
const pathArr = [
  10.99, 23.38, -58.11,
  10.47, 46.14, -58.36,
  10.69, 46.14, -18.40,
  -69.28, 46.14, -18.04,
  -70.43, 46.14, 10.46,
  -80.43, 46.14, 10.46,
  -200.43, 46.14, -150.46
]
const  radius =  3
const z=20;//管道下移数量

export default {
  name: "pipe4",
  data() {
    return {
      renderer: null,
      // camera 相机
      camera: null,
      // Scene场景
      scene: null,
      // 灯光
      light: null,
      // 形状 球形
      sphereGeometry: null,
      // 材质
      material: null,
      // 网格
      sphereMesh: null,
      // 控制器
      control: null,
      effect: null
    }
  },
  mounted() {
    this.container = this.$refs.container;
    this.init();
    // this.initGrid();
    this.animate();

  },
  methods: {
    /**
     * 初始化方法
     */
    init() {
      /*
        * 1. 渲染器:WebGLRenderer
        * 2. 相机:PerspectiveCamera
        * 3. 场景和灯光:Scene,Light
        * 4. Mesh:需要渲染的模型对象
        *   4.1 几何形状或体,如SphereGeometry(球),CylinderGeometry(圆柱),TubeGeometry(管道)等
        *   4.2 材质,MeshBasicMaterial,MeshPhongMaterial,需要设置颜色或者TextureLoader(指定材质图片的url)
        * 5. 刷新:实现渲染器的不断刷新,详见renderScene,包括了requestAnimationFrame
        * 6. 控制器:控制相机的运动,如OrbitControls
        * */
      // 1.antialias:true抗锯齿
      this.renderer = new THREE.WebGLRenderer({antialias: true})
      // 1.设置渲染器的大小和页面大小一样大
      this.renderer.setSize(window.innerWidth, window.innerHeight)
      // 切换背景颜色
      this.renderer.setClearColor('rgba(255,255,255,0.54)')
      // 将canvas装载在主体的模型上
      // document.body.appendChild(this.renderer.domElement)

      // 2.相机
      this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, window.innerWidth / window.innerHeight, 1000)
      // 监听窗口大小
      window.addEventListener('resize', () => {
        this.renderer.setSize(window.innerWidth, window.innerHeight)
        // 相机的宽高比
        this.camera.aspect = window.innerWidth / window.innerHeight
        // 设置了Aspect 之后必须更新相机的投影矩阵
        this.camera.updateProjectionMatrix()
      })
      // 设置相机的位置
      this.camera.position.set(0, 100, 200)

      // 3.创建一个场景
      this.scene = new THREE.Scene()
      // 3.1创建一个纹理图片加载器加载图片
      var textureLoader = new THREE.TextureLoader();
      // 3.2加载背景图片
      var texture = textureLoader.load('http://localhost:8082/static/data/background.png');
      // 纹理对象Texture赋值给场景对象的背景属性.background
      this.scene.background = texture

      // 3.3灯光
      this.light = new HemisphereLight('#2cef00', '#ef0038')
      // 将灯光添加到场景中
      this.scene.add(this.light)
      //环境光
      var ambient = new THREE.AmbientLight('#ef0038');
      // 将灯光添加到场景中
      this.scene.add(ambient)
//===============================================================
//       4.Mesh
      this.createTube();
//================================================================
      // 4.创建形状
      this.sphereGeometry = new SphereGeometry(radius*3, 12, 12)
      // 4.2创建材质
      // this.material = new THREE.MeshPhongMaterial();//已加入图片材质,见下一行
      let material3=new THREE.MeshBasicMaterial({map:new THREE.TextureLoader().load('/static/data/earth.jpg'),side:THREE.DoubleSide});
      // 根据形状和材质创建网格
      this.sphereMesh = new THREE.Mesh(this.sphereGeometry, material3)
      this.sphereMesh.position.set(-25, -47, -135)//x,z,y
      this.scene.add(this.sphereMesh)

      // 5.渲染场景和相机
      this.renderer.render(this.scene, this.camera)
      // 6.控制器 第一个参数是相机第二个参数是渲染器
      this.control = new OrbitControls(this.camera, this.renderer.domElement)

      // 将canvas装载在主体的模型上
      let myDiv = document.getElementById("myDiv")
      myDiv.appendChild(this.renderer.domElement);

      this.update()
    },
    // 4.Mesh 动态创建一个管道
    createTube(){
      let curveArr = []
      // 三个一组取出curve数据
      for(let i=0; i < pathArr.length; i+=3) {
        curveArr.push(new THREE.Vector3(pathArr[i], pathArr[i+1], pathArr[i+2]))
      }
      var curve = new THREE.CatmullRomCurve3(curveArr);
      /**
       * TubeGeometry(path : Curve, tubularSegments : Integer, radius : Float, radialSegments : Integer, closed : Boolean)
       */
      var tubeGeometry = new THREE.TubeGeometry(curve, 100, radius, 50, false);
      var textureLoader = new THREE.TextureLoader();
      var texture = textureLoader.load('http://localhost:8082/static/data/arrow1.jpg');

      // 设置阵列模式 RepeatWrapping
      // texture.wrapS = THREE.RepeatWrapping
      texture.wrapT = THREE.RepeatWrapping
      // 设置x方向的重复数(沿着管道路径方向)
      // 设置y方向的重复数(环绕管道方向)
      texture.repeat.x = 10;
      texture.repeat.y = 2;
      // 设置管道纹理偏移数,便于对中
      texture.offset.y = 0.5;
      var tubeMaterial = new THREE.MeshPhongMaterial({
        map: texture,
        transparent: true
      });
      var mesh = new THREE.Mesh(tubeGeometry, tubeMaterial);
      mesh.position.y = 2;
      mesh.rotateZ(3.14);
      mesh.scale.set(2, 2, 2);
      // 使用加减法可以设置不同的运动方向
      setInterval(() => {
        texture.offset.x -= 0.0036
      })
      this.scene.add(mesh)
    },

    /**
     * 定义一个刷新函数
     */
    update() {
      // 需要不断的进行渲染更新
      this.renderer.render(this.scene, this.camera)
      requestAnimationFrame(this.update)
    },
    animate() {
      requestAnimationFrame( this.animate );
      this.sphereMesh.rotation.y += 0.01;
      this.render();

    },
    render() {
      const timer = 0.001 * Date.now();
      // this.camera.position.x += ( mouseX - this.camera.position.x ) * .05;
      // this.camera.position.y += ( - mouseY - this.camera.position.y ) * .05;

      this.camera.lookAt( this.scene.position );

      for ( let i = 0, il = this.sphereGeometry.length; i < il; i ++ ) {

        const sphere = this.sphereGeometry[ i ];
        sphere.position.x = 5 * Math.cos( timer + i );
        sphere.position.y = 5 * Math.sin( timer + i * 1.1 );
      }
      // this.effect.render( this.scene, this.camera );
    },
    //初始化网格
    initGrid(){
      let grid = new THREE.GridHelper( 300, 8, 0xffffff, 0xffffff );
      grid.position.set(0,-100,-50);//xzy
      this.scene.add( grid );
    }
  }
}
</script>

  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
要在Vue中使用three.js加载一个场景,可以使用Vue的生命周期函数来加载和渲染three.js场景。以下是一个简单的示例代码: ```vue <template> <div ref="container"></div> </template> <script> import * as THREE from 'three'; export default { name: 'ThreeScene', data() { return { scene: null, camera: null, renderer: null, cube: null, }; }, mounted() { this.init(); this.animate(); }, methods: { init() { // 创建场景 this.scene = new THREE.Scene(); // 创建相机 this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); this.camera.position.z = 5; // 创建渲染器 this.renderer = new THREE.WebGLRenderer(); this.renderer.setSize(window.innerWidth, window.innerHeight); this.$refs.container.appendChild(this.renderer.domElement); // 创建立方体 const geometry = new THREE.BoxGeometry(); const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); this.cube = new THREE.Mesh(geometry, material); this.scene.add(this.cube); }, animate() { requestAnimationFrame(this.animate); this.cube.rotation.x += 0.01; this.cube.rotation.y += 0.01; this.renderer.render(this.scene, this.camera); }, }, }; </script> <style> canvas { width: 100%; height: 100%; } </style> ``` 以上代码创建了一个Vue组件,包含了一个ref为container的div元素。在mounted生命周期函数中,调用了init方法来创建场景、相机、渲染器和立方体,并将立方体添加到场景中。在animate方法中,使用requestAnimationFrame函数来更新立方体的旋转并渲染场景。最后,使用renderer.domElement将渲染器的canvas元素添加到container中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一望无际的大草原

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值