Vue、Three.js实现全景图

一、首先我们需要创建一个Vue工程

本文主要详细记录搭建全景图的过程,故搭建Vue工程不在过多描述。

二、安装Three.js

npm install three --save
npm install three-trackballcontrols --save
npm install three-orbit-controls --save
npm i three-obj-mtl-loader --save
npm i three-fbx-loader --save
npm i three-stats --save

three-trackballcontrols:轨迹球控件,最常用的控件,可以使用鼠标轻松的移动、平移,缩放场景。
three-orbit-controls :三维场景操作插件。其实在 npm install three 的时候已经下载对应的插件, 在 node_modules 文件夹下找到 three/examples/jsm/controls/OrbitControls 这个路径里面也能找到对应的插件。
three-obj-mtl-loader:加载 .obj 模型文件 , .mtl 材质信息文件的插件。
three-fbx-loader:fbx模型文件的插件。
three-stats:性能检测插件,可以监控动画运行的帧数。

三、渲染器-renderer, 场景-scene,相机-camera

  Three.js中,场景可理解为是一个容器,我们可以将需要的物体放入场景中。相机的作用在场景中取一个合适的景,把它拍下来。渲染器的作用就是将相机拍摄下来的图片,渲染至浏览器展示。

四、具体实现

  1. 引入依赖
import * as THREE from "three";
import * as ThreeStats from 'three-stats'
const OrbitControls = require('three-orbit-controls')(THREE);
  1. 定义变量
data() {
  return {
    renderer: '', //渲染器
    scene: '', //场景
    light: '', //光源
    camera: '', //相机
    controls: '', //控制器
    stats: '', //性能监控器
    mygroup: '', //模型组
    action: '', //控制动画的值
    clock: '', //时钟
    mixer: '', //混合实例
    rotateAnimate: '', //旋转动画
    sRotate: 1, //是否开启旋转
  }
}
  1. 初始化渲染器
rendererInit() {
    var width = 1000;
    var height = 800;
    this.renderer = new THREE.WebGLRenderer();
    this.renderer.setClearColor(0xffffff); 
    this.renderer.setSize(width, height); 
    // 通过 this.$refs获取页面的dom并绑定渲染器
    this.$refs.threeDom.appendChild(this.renderer.domElement);
}
  1. 初始化场景
sceneInit() {
    this.scene = new THREE.Scene();
    var ambient = new THREE.AmbientLight(0x444444, 3); //添加光源  颜色和光照强度
    var axisHelper = new THREE.AxesHelper(600); //添加辅助坐标系
    this.scene.add(ambient, axisHelper);
},

AmbientLight:环境光,会均匀的照亮场景中的所有物体。不能用来投射阴影,因为它没有方向。

AmbientLight( color : Integer, intensity : Float )
color - (参数可选)颜色的rgb数值。缺省值为 0xffffff。
intensity - (参数可选)光照的强度。缺省值为 1。

AxesHelper:用于简单模拟3个坐标轴的对象。红色代表 X 轴,绿色代表 Y 轴,蓝色代表 Z 轴。

AxesHelper( size : Number )
size -- (可选的) 表示代表轴的线段长度. 默认为 1

  1. 创建模型
modelling(){
      this.mygroup = new THREE.Group();

      var textureLoader = new THREE.TextureLoader(); //创建纹理贴图
      var img = textureLoader.load(require('../../public/img/home3.jpeg'));

      var geometry = new THREE.SphereGeometry(130, 256, 256); // 球体网格模型
      var material = new THREE.MeshLambertMaterial({
        map: img, //设置颜色贴图属性值
        side: THREE.DoubleSide, //双面渲染
      });
      var meshSphere = new THREE.Mesh(geometry, material); //网格模型对象Mesh
      meshSphere.name = '球体容器';
      this.mygroup.add(meshSphere);

      var canvasText = this.getcanvers('进门'); //生成一个canvers 文字图案对象
      var texture = new THREE.CanvasTexture(canvasText);
      var geometryText = new THREE.PlaneGeometry(16, 10, 60, 60);
      var materialText = new THREE.MeshPhongMaterial({
        map: texture, // 设置纹理贴图
        side: THREE.DoubleSide, //双面渲染
      });
      var meshText = new THREE.Mesh(geometryText, materialText);
      meshText.name = '进门';
      meshText.position.set(40, 20, -90)
      this.mygroup.add(meshText);

      this.scene.add(this.mygroup);
      this.addAnimation(); //添加并开启动画
      this.refresh();
    }
    // 生成一个canvers图案
    getcanvers(text) {
      var canvasText = document.createElement("canvas");
      var c = canvasText.getContext('2d');
      // 矩形区域填充背景
      c.fillStyle = "#FFFFFF"; //canver背景
      c.fillRect(0, 0, 300, 200); //生成一个矩形
      c.translate(160, 80);
      c.fillStyle = "#000000"; //文本填充颜色
      c.font = "bold 100px 宋体"; //字体样式设置
      c.textBaseline = "middle"; //文本与
      c.textAlign = "center"; //文本居中
      c.fillText(text, 0, 0);
      return canvasText;
    }

SphereGeometry:一个用于生成球体的类。

SphereGeometry(radius : Float, widthSegments : Integer, heightSegments : Integer, phiStart : Float, phiLength : Float, thetaStart : Float, thetaLength : Float)
radius — 球体半径,默认为1。
widthSegments — 水平分段数(沿着经线分段),最小值为3,默认值为8。
heightSegments — 垂直分段数(沿着纬线分段),最小值为2,默认值为6。
phiStart — 指定水平(经线)起始角度,默认值为0。。
phiLength — 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2。
thetaStart — 指定垂直(纬线)起始角度,默认值为0。
thetaLength — 指定垂直(纬线)扫描角度大小,默认值为 Math.PI。

MeshLambertMaterial:一种非光泽表面的材质,没有镜面高光。

MeshLambertMaterial( parameters : Object )
.map : Texture。颜色贴图。默认为null。
.side : Integer。定义将要渲染哪一面 - 正面,背面或两者。 默认为THREE.FrontSide。其他选项有THREE.BackSide和THREE.DoubleSide。

CanvasTexture:从Canvas元素中创建纹理贴图。

PlaneGeometry: 一个用于生成平面几何体的类。

MeshPhongMaterial:一种用于具有镜面高光的光泽表面的材质。

  1. 初始化相机
cameraInit() {
    var width = 800; //窗口宽度
    var height = 800; //窗口高度
    this.camera = new THREE.PerspectiveCamera(90, width / height, 1, 1000); //使用透视相机
    this.camera.position.set(0, 0, 10); //设置相机位置
    this.camera.lookAt(new THREE.Vector3(0, 0, 0)); // 相机看向
}

PerspectiveCamera:透视相机。

PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )
fov — 摄像机视锥体垂直视野角度
aspect — 摄像机视锥体长宽比
near — 摄像机视锥体近端面
far — 摄像机视锥体远端面

  1. 初始化控制器(三维场景操作插件)
controlInit() {
    this.controls = new OrbitControls(this.camera, this.$refs.threeDom); // 初始化控制器
    this.controls.target.set(0, 0, 0); // 设置控制器的焦点,使控制器围绕这个焦点进行旋转
    this.controls.minDistance = 10; // 设置移动的最短距离(默认为零)
    this.controls.maxPolarAngle = Math.PI; //绕垂直轨道的距离(范围是0-Math.PI,默认为Math.PI)
    this.controls.maxDistance = 30; // 设置移动的最长距离(默认为无穷)
    this.controls.enablePan = false; //禁用右键功能
    this.controls.addEventListener('change', this.refresh); //监听鼠标、键盘事件 让整个控件可以拖动
  },
  1. 旋转动画
addAnimation() {
    this.clock = new THREE.Clock(); 
    var times = [0, 3600]; //   创建帧动画序列
    var position_x = [0, 360];
    var keyframe = new THREE.KeyframeTrack('meshSphere.rotation[y]', times, position_x);
    var duration = 100; //持续时间
    var cilp = new THREE.AnimationClip('sphereRotate', duration, [keyframe]); //剪辑 keyframe对象
    this.mixer = new THREE.AnimationMixer(this.mygroup); //动画混合实例
    this.action = this.mixer.clipAction(cilp);
    this.action.timeScale = 1; //播放速度
    this.action.setLoop(THREE.LoopPingPong).play(); //开始播放 像乒乓球一样在起始点与结束点之间来回循环
    this.animate(); //开启动画
},
// 循环渲染
animate() {
    this.rotateAnimate = requestAnimationFrame(this.animate);
    this.renderer.render(this.scene, this.camera);
    this.update();
},

Clock:用于跟踪时间。

KeyframeTrack:关键帧轨道(KeyframeTrack)是关键帧(keyframes)的定时序列, 它由时间和相关值的列表组成, 用来让一个对象的某个特定属性动起来。

KeyframeTrack( name : String, times : Array, values : Array, interpolation : Constant )
name - 关键帧轨道(KeyframeTrack)的标识符。
times - 关键帧的时间数组, 被内部转化为 Float32Array。
values - 与时间数组中的时间点相关的值组成的数组, 被内部转化为 Float32Array。
interpolation - 使用的插值类型。

AnimationClip:动画剪辑(AnimationClip)是一个可重用的关键帧轨道集,它代表动画。

AnimationClip( name : String, duration : Number, tracks : Array )
name - 此剪辑的名称。
duration - 持续时间 (单位秒)。如果传入负数, 持续时间将会从传入的数组中计算得到。
tracks - 一个由关键帧轨道(KeyframeTracks)组成的数组。

AnimationMixer:动画混合器是用于场景中特定对象的动画的播放器。当场景中的多个对象独立动画时,每个对象都可以使用同一个动画混合器。

  1. 最后初始化加载
init() {
  this.$refs.threeDom.addEventListener('dblclick', this.onMouseDblclick); //监听双击事件
  this.rendererInit(); //创建渲染器
  this.sceneInit(); //创建场景    包含光源和辅助坐标系
  this.cameraInit(); //创建相机
  this.controlInit(); //初始化控制器
  this.propertyInit(); //性能监控
  this.modelling(); //建立模型
}

参考文章:
https://juejin.cn/post/6927193628724953096
https://blog.csdn.net/ithanmang/article/details/84062933

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容