vue-three 基础模板

<template>
  <div>
    <div id="three"></div>
  </div>
</template>

<script>
import * as THREE from "three";
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
export default {
  data() {
    return {
      scene: {}, // 场景
      camera: {}, // 摄像机
      renderer: {}, // 渲染器
      parts: [], // 几何图形储存器
      mixer: null, // 动画
      clock: {}
    };
  },
  mounted(){
    this.threes();
    this.resize(); // 自适应
    this.mousedown(); // 鼠标点击事件
    this.mod();
  },
  methods: {
    // 浏览器窗口发生变化时 自适应
    resize(){
      window.addEventListener('resize', () => {

        // 初始化摄像机
        this.camera.aspect = window.innerWidth/window.innerHeight;

        // 摄像机矩阵效果
        this.camera.updateProjectionMatrix();

        // 初始化渲染器
        this.renderer.setSize(window.innerWidth, window.innerHeight);

      })
    },

    threes() {
      // 创建场景
      this.scene = new THREE.Scene();

      // 创建摄像机 // 参数含义: 视角、窗口投影长宽比、摄像机从哪里开始渲染、距离摄像机多远截至渲染
      this.camera = new THREE.PerspectiveCamera(100, window.innerWidth/window.innerHeight, 0.1, 1000);

      // 创建three渲染器 参数: 去锯齿
      this.renderer = new THREE.WebGLRenderer({ antialias: true });

      // 设置渲染器场景背景颜色
      this.renderer.setClearColor(0x000000, .2)

      // 开启阴影,加上阴影渲染
      this.renderer.shadowMapEnabled = true;

      // 设置渲染器场景大小 参数: 宽,高
      this.renderer.setSize(window.innerWidth, window.innerHeight);
      
      // 把渲染器添加到页面中
      document.getElementById('three').appendChild(this.renderer.domElement);

      this.plane() // 平面模型

      this.wall({x: 500, y: 60, z: 10}, {x: 0, y: 30, z: -145}) // 外墙壁 1
      this.wall({x: 500, y: 60, z: 10}, {x: 0, y: 30, z: 145}) // 外墙壁 2
      this.wall({x: 10, y: 60, z: 300}, {x: 250, y: 30, z: 0}) // 外墙壁 3
      this.wall({x: 10, y: 60, z: 300}, {x: -250, y: 30, z: 0}) // 外墙壁 4

      this.wall({x: 10, y: 60, z: 120}, {x: 160, y: 30, z: -80}) // 内墙壁 5
      this.wall({x: 90, y: 60, z: 10}, {x: 200, y: 30, z: -20}) // 内墙壁 6

      for(let i = 0; i < 4; i ++){
        // this.chartlet(-220, 30, (-100 +  i * 41)) // 横排
        this.chartlet((-220 + i * 35), 30, -100) // 纵排
        this.chartlet((-220 + i * 35), 30, -55) // 纵排
        this.chartlet((-220 + i * 35), 30, 100) // 纵排
        this.chartlet((-220 + i * 35), 30, 55) // 纵排
      }
      this.vavAair(100,50,135)

      // 将网格对象添加到场景中
      this.parts.forEach(item => {
        this.scene.add(item);
      });
      
      // 创建光源
      this.createLight()

      // 辅助坐标系  参数250表示坐标系大小,可以根据场景大小去设置
      this.axesHelper = new THREE.AxesHelper(500);

      // 将坐标系添加到场景中
      this.scene.add(this.axesHelper);

      // 摄像机空间轴位置
      this.camera.position.z = 300
      this.camera.position.y = 350
      this.camera.position.x = 100

      // 创建鼠标控件对象
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);

      this.animate()
    },

    // 添加帧渲染
    animate(){
      let that = this
      requestAnimationFrame(this.animate);
      // 网格对象自动旋转
      // this.cube.rotation.x += 0.005
      // this.cube.rotation.y += 0.005

      if (this.mixer !== null){
        that.mixer.update(that.clock.getDelta());
      }

      // 初始化controls
      this.controls.update()

      this.render()
    },

    // 渲染器渲染场景和摄像机
    render(){
      this.renderer.render(this.scene, this.camera)
    },

    // 创建光源
    createLight () {
      // 环境光
      const ambint = new THREE.AmbientLight(0xffffff)
      this.scene.add(ambint)
      // 聚光灯光源
      this.spotLight(0x03FF6A, 245, 60, 140, 600)
      this.spotLight(0xff0000, -245, 60, 140, 500)
      // this.pointLight(0xffffff, 0, 900, 0, 1000)
      this.pointLight(0x0070ee, 205, 100, -80, 130)
    },

    // 聚光灯光源
    spotLight (color, x, y, z, dis) {
      const spotLight = new THREE.SpotLight(color)
      spotLight.position.set(x, y, z); // 光源位置
      spotLight.castShadow = true; //开启灯光投射阴影
      spotLight.intensity = 3 // 强度
      spotLight.angle = 0.3; // 角度
      spotLight.penumbra = 1; // 半影
      spotLight.decay = 1; // 衰退
      spotLight.distance = dis; // 距离

      this.scene.add(spotLight)

      // 辅助线
      // let spotLightHelper = new THREE.SpotLightHelper(spotLight, 0x976fb6);
      // this.scene.add(spotLightHelper)

      // 光源寄托
      this.createLightView(color, x, y, z)
    },
    // 点光灯光源
    pointLight (color, x, y, z, dis) {
      const pointLight = new THREE.PointLight(color)
      pointLight.position.set(x, y, z); // 光源位置
      pointLight.castShadow = true; //开启灯光投射阴影
      pointLight.intensity = 4 // 强度
      // pointLight.angle = 0.3; // 角度
      pointLight.penumbra = 1; // 半影
      // pointLight.decay = 1; // 衰退
      pointLight.distance = dis; // 距离

      this.scene.add(pointLight)

      // 辅助线
      // let spotLightHelper = new THREE.SpotLightHelper(pointLight, 0x976fb6);
      // this.scene.add(spotLightHelper)

      // 光源寄托
      this.createLightView(color, x, y, z)
    },

    // 光源寄托
    createLightView(color, x, y, z){
      let geometry = new THREE.SphereGeometry(3, 3, 3);
      let material = new THREE.MeshPhongMaterial({ color });
      let cube = new THREE.Mesh(geometry, material);
      cube.position.set(x, y, z)
      this.scene.add(cube)
    },

    // 平面模型
    plane(){
      let geometry = new THREE.PlaneGeometry(640, 400, 1, 1);
      let material = new THREE.MeshPhongMaterial({ color: 0xE5E5E5, wireframe: false });
      let cube = new THREE.Mesh(geometry, material);
      cube.rotation.x = -0.5 * Math.PI
      cube.position.set(0, 0, 0)
      cube.receiveShadow = true

      this.parts.push(cube)
    },

    // 墙壁
    wall(size, position){
      let texture = new THREE.TextureLoader().load(require('@/assets/three/tile.jpg'));
      texture.anisotropy = 4
      let data = {
        map: texture,
        // color: 0x07313F,
        transparent: true,
        opacity: 1
      }
      let geometry = new THREE.BoxGeometry(size.x, size.y,size.z);
      let material = new THREE.MeshLambertMaterial(data);
      let cube = new THREE.Mesh(geometry, material);
      cube.position.set(position.x, position.y, position.z)
      this.scene.add(cube);
    },

    // 机柜
    chartlet(x, y, z){
      let map = new THREE.TextureLoader().load(require('@/assets/three/jigui.jpg'));
      let group = new THREE.Mesh();
      let mats = [];
      mats.push(new THREE.MeshPhongMaterial({ map }));
      mats.push(new THREE.MeshPhongMaterial({
        color: 0x3C3B42
      }));
      mats.push(new THREE.MeshPhongMaterial({
        color: 0x3C3B42
      }));
      mats.push(new THREE.MeshPhongMaterial({
        color: 0x3C3B42
      }));
      mats.push(new THREE.MeshPhongMaterial({
        color: 0x3C3B42
      }));
      mats.push(new THREE.MeshPhongMaterial({
        color: 0x3C3B42
      }));

      let cubeGeom = new THREE.BoxBufferGeometry(15, 50, 30);
      let cube = new THREE.Mesh(cubeGeom, mats);
      cube.position.set(x, y, z)

      group.add(cube);
      this.scene.add(group)
    },

    // 空调
    vavAair(x, y, z){
      let map = new THREE.TextureLoader().load(require('@/assets/three/kt.png'));
      let group = new THREE.Mesh();
      let mats = [];
      mats.push(new THREE.MeshPhongMaterial({ color: 0xffffff }));
      mats.push(new THREE.MeshPhongMaterial({
        color: 0xffffff
      }));
      mats.push(new THREE.MeshPhongMaterial({
        color: 0xffffff
      }));
      mats.push(new THREE.MeshPhongMaterial({
        color: 0xffffff
      }));
      mats.push(new THREE.MeshPhongMaterial({
        color: 0xffffff
      }));
      mats.push(new THREE.MeshPhongMaterial({
        map
      }));

      let cubeGeom = new THREE.BoxBufferGeometry(40, 15, 15);
      let cube = new THREE.Mesh(cubeGeom, mats);
      cube.position.set(x, y, z)

      group.add(cube);
      this.scene.add(group)
    },

    // 鼠标点击事件
    mousedown(){
      let raycaster = new THREE.Raycaster(); //光线投射,用于确定鼠标点击位置
      let mouse1 = new THREE.Vector2()
      let mouse2 = new THREE.Vector2()

      let top = 69

      window.addEventListener("pointerdown", (event) => {
        mouse1.x = ( event.clientX / window.innerWidth ) * 2 - 1;
        mouse1.y = - ( (event.clientY - top) / window.innerHeight ) * 2 + 1;
      });
      window.addEventListener("pointerup", (event) => {
        mouse2.x = ( event.clientX / window.innerWidth ) * 2 - 1;
        mouse2.y = - ( (event.clientY - top) / window.innerHeight ) * 2 + 1;
        if(mouse1.x === mouse2.x && mouse1.y === mouse2.y){
          //以camera为z坐标,确定所点击物体的3D空间位置
          raycaster.setFromCamera(mouse1, this.camera);
          //确定所点击位置上的物体数量
          let intersects = raycaster.intersectObjects(this.scene.children);
          //选中后进行的操作
          if(intersects.length){
            console.log(intersects)
            // intersects[0].object.material.color.set( 0xff0000 );
          }
        }
      });
    },

    // 引入模型 人物
    mod(){
      let that = this
      let loader = new FBXLoader()
      loader.load('three/SambaDancing.fbx', function(obj){
        obj.scale.set(.35, .35, .35); // 放大缩小
        obj.position.set(205, 0, -80); // 位置
        obj.rotation.y += 1.55; // 旋转
        that.scene.add(obj)

        that.clock = new THREE.Clock()
        // obj作为参数创建一个混合器,解析播放obj及其子对象包含的动画数据
        that.mixer = new THREE.AnimationMixer(obj);
        let animationAction = that.mixer.clipAction(obj.animations[0]);
        // animationAction.timeScale = 1; //默认1,可以调节播放速度
        // animationAction.loop = THREE.LoopOnce; //不循环播放
        // animationAction.clampWhenFinished=true;//暂停在最后一帧播放的状态
        animationAction.play(); //播放动画
      }, undefined, function ( error ) {
        console.error( error );
      });
    },

  }

};
</script>

<style scoped>
body {
  margin: 0;
  padding: 0;
  background-color: #000;
  color: #fff;
  font-family: Monospace;
  font-size: 13px;
  line-height: 24px;
  overscroll-behavior: none;
}
#info {
  position: absolute;
  top: 0px;
  width: 100%;
  padding: 0;
  box-sizing: border-box;
  text-align: center;
  -moz-user-select: none;
  -webkit-user-select: none;
  -ms-user-select: none;
  user-select: none;
  pointer-events: none;
  z-index: 1; /* TODO Solve this in HTML */
}
</style>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容