Three.js - JS三维模型库在Vue2中的基础教程

 threejs 官网 :https://threejs.org/

 threejs 案例: https://threejs.org/examples/#webgl_animation_keyframes

threejs API:https://threejs.org/docs/index.html#manual/zh/introduction/Creating-a-scene  

开始使用

npm install --save three;

全局引用

import * as THREE from 'three';

按需引用 ,demo均使用全局

import {class} from 'three';

package-lock.json

"three": "^0.151.3",

"three-orbitcontrols": "^2.110.3",

"vue": "^2.6.14",

一、基础使用

三大核心

new THREE.WebGLRenderer()// 创建渲染器

new THREE.Scene()  // 实例化场景

new THREE.PerspectiveCamera()// 实例化相机

创建四个文件方便场景管理

渲染控制器ThreeController.js 作为 3d渲染的主要入口文件

Three.js - JS三维模型库在Vue2中的基础教程_第1张图片

ThreeController.js

import * as THREE from 'three';

export const renderer = new THREE.WebGLRenderer()  // 创建渲染器

export const scene = new THREE.Scene()  // 实例化场景

export const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000) 相机

export class ThreeController {

   Model = null;

   scene = null;

   constructor(Model) {  //构造器函数

    Model.appendChild(renderer.domElement) //容器

    renderer.setSize(Model.offsetWidth, Model.offsetHeight, true)

    this.Model = Model

    this.scene = scene

    camera.position.set(100, 100, 100) // 设置相机位置

    camera.lookAt(new THREE.Vector3(0, 0, 0))  // 设置相机看先中心点

    camera.up = new THREE.Vector3(0, 1, 0)  // 设置相机自身的方向

    renderer.shadowMap.enabled = true;

    renderer.render(scene, camera);

}

// 外部访问将模型添加到场景中

    addObject(...object) {

      object.forEach(elem => {

        this.scene.add(elem)

      })

    }

}

HomeView.vue

 





import {ThreeController,} from "@/components/ThreeController";

import { ModelListConfig} from"@/components/ModelListConfig";

import { LightList } from "@/components/LightListConfig";

import { allHelper } from "@/components/AxesHelperConfig";

return { ThreeController: null,



}

mounted() {

this.init(); 

},

methods: {


  init() {

      this.ThreeController = newThreeController(this.$refs.threeTarget);

      this.ThreeController.addObject(...ModelListConfig);

      this.ThreeController.addObject(...LightList);

      this.ThreeController.addObject(...allHelper);

    },

此时场景中一片漆黑

Three.js - JS三维模型库在Vue2中的基础教程_第2张图片

接下来添加模型  ModelListConfig.js

import * as THREE from 'three';

具体模型类型参考Api

export const ModelListConfig = []  存储模型数组,也可某个模型单独导出

const sky = new THREE.TextureLoader().load(Require('sky.jpg'))

创建材质贴图

export const MeshModel = new THREE.Mesh( 创建几何体

  new THREE.BoxGeometry(20, 20, 20), 正方体 size 20

  new THREE.MeshStandardMaterial({  材质纹理

    color: 'rgb(36, 172, 242)',

    // roughness: 0 , 光滑度 0最光滑

    // metalness: 0, 金属度 1最像金属

    map: sky

  })

)

ModelListConfig.push(MeshModel) 将模型添加到数组中



// 多人开发用来存储数据

MeshModel.userData = {

  name: 'MeshModel',

  user: '我是正方体模型'

}

 可以看到场景依旧是漆黑,所以要“开灯”即添加光线

Three.js - JS三维模型库在Vue2中的基础教程_第3张图片

 接下来添加光线 LightListConfig.js

import * as THREE from 'three';
export const LightList = []
// // 添加环境光(自然光),设置自然光的颜色,设置自然光的强度(0 最暗, 1 最强)
const hemiLight = new THREE.HemisphereLight("#A09E9E", 0.5);
hemiLight.position.set(0, 40, 15);
LightList.push(hemiLight)
const hemiLighthelper = new THREE.HemisphereLightHelper(hemiLight, 5);
LightList.push(hemiLighthelper)
export const pointLight = new THREE.PointLight(
  'rgb(255,255,255)',
  0.7,
  600,
  0.2
)
pointLight.position.set(0, 1, 50)  // 设置点光源位置 (x,y,z)
LightList.push(pointLight)

这时模型就可以正常显示了Three.js - JS三维模型库在Vue2中的基础教程_第4张图片

创建辅助线 AxesHelperConfig.js

import { AxesHelper ,GridHelper } from 'three'
 /**
     * 场景中添加辅助线
     * @param  {allHelper.push(new AxesHelper())}  
     * 添加栅格
     * @param  {allHelper.push(new GridHelper())}  
     */
 export const allHelper = []
 // 坐标辅助
 export const axesHelper = new AxesHelper(200)  // 创建坐标辅助 (500 为辅助线的长度)
 export const gridHelper = new GridHelper(500, 20, 'green', 'rgb(255, 255, 255)')
 allHelper.push(gridHelper,axesHelper)  // 添加到辅助列表

 /*
gridHelper Config
size -- 坐标格尺寸. 默认为 10. 这就是网格组成的大正方形最大是多少
divisions -- 坐标格细分次数. 默认为 10. 组成的最大的正方向平均分多少份
colorCenterLine -- 中线颜色. 值可以为 Color 类型, 16进制 和 CSS 颜色名. 默认为 0x444444。这个是指网格和坐标的 x轴 z 轴重合线的颜色。
colorGrid -- 坐标格网格线颜色. 值可以为 Color 类型, 16进制 和 CSS 颜色名. 默认为 0x888888

  */

 效果Three.js - JS三维模型库在Vue2中的基础教程_第5张图片

二、射线控制器 

相机视角拖拽 ThreeController.js 需要射线控制器 OrbitControls 因为渲染成是三维之后,我们点击的是相机呈现的二维浏览器画面,距离模型到页面上是有一定距离的,简单来说就像我们站在一个物体面前,用手机拍照时,点击的照片中的物体,实际上我们点击的并不是物体,而是相机渲染给我们的一个二维照片。OrbitControls,会穿透整个三维场并返回一个list,第[0]项就是我们想要点击模型。

ThreeController.js

import { OrbitControls }from'three/examples/jsm/controls/OrbitControls'
import { TransformControls } from 'three/examples/jsm/controls/TransformControls'
const mouse = new THREE.Vector2() // 初始化鼠标位置
const raycaster = new THREE.Raycaster()//初始化射线发射器
//  屏幕鼠标x,屏幕鼠标y  视图宽度,视图高度
let x = 0; let y = 0; let width = 0; let height = 0
constructor(Model) {
     …
      EventInjection(camera) //要在渲染器之前
     …
      renderer.render(scene, camera);

}
export const EventInjection=()=>{
   // 鼠标移动事件
const transformControls = new TransformControls(camera, renderer.domElement)
renderer.domElement.addEventListener("mousemove", event => {
x = event.offsetX
y = event.offsetY
width = renderer.domElement.offsetWidth
height = renderer.domElement.offsetHeight
mouse.x = x / width * 2 - 1
mouse.y = -y * 2 / height + 1
})
let transing = false
transformControls.addEventListener("mouseDown", event => {
transing = true
return event
})
// 鼠标点击事件
renderer.domElement.addEventListener("click", event => {
if (transing) {
transing = false
return
}
scene.remove(transformControls) // 移除变换控制器
transformControls.enabled = false // 停用变换控制器
raycaster.setFromCamera(mouse, camera)  // 配置射线发射器,传递鼠标和相机对象
const intersection = raycaster.intersectObjects(scene.children) // 获取射线发射器捕获的模型列表,传进去场景中所以模型,穿透的会返回我们
if (intersection.length) {
const object = intersection[0].object  // 获取第一个模型
console.log(object) 
scene.add(transformControls) // 添加变换控制器
transformControls.enabled = true // 启用变换控制器
transformControls.attach(object)
}
return event
})
// 监听变换控制器模式更改
document.addEventListener("keyup", event => {
if (transformControls.enabled) {  // 变换控制器为启用状态执行
if (event.key === 'e') { // 鼠标按下e键,模式改为缩放
transformControls.mode = 'scale'
return false
}
if (event.key === 'r') { // 鼠标按下r键,模式改为旋转
transformControls.mode = 'rotate'
return false
}
if (event.key === 't') { // 鼠标按下t键,模式改为平移
transformControls.mode = 'translate'
return false
}
}
return event
})
// three.js自带的方法
const orbitControls = new OrbitControls(camera, renderer.domElement)
// console.log(MOUSE)//查看MOUSE中的配置项
orbitControls.mouseButtons = {  // 设置鼠标功能键(轨道控制器)
LEFT: null,  // 左键无事件
MIDDLE: THREE.MOUSE.DOLLY,  // 中键缩放
RIGHT: THREE.MOUSE.ROTATE// 右键旋转
  }
  scene.add(transformControls)
}

这时我们在log里可以看到之前添加的 userdata

Three.js - JS三维模型库在Vue2中的基础教程_第6张图片

 同时也可以做拽查看

Three.js - JS三维模型库在Vue2中的基础教程_第7张图片

 三、进阶使用

1、外部导入模型  obj、 gltf、 json、 glb等。

开源模型地址 https://github.com/mrdoob/three.js/blob/master

开源的模型 

 可以自己改成其他格式,使用别的引用方法尝试Three.js - JS三维模型库在Vue2中的基础教程_第8张图片

 ThreeController.js 示例演示,导入glb文件

import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
export const LoadingGLTFMethod=(GltfModel)=> {
  loader.load(`${process.env.BASE_URL}model/${GltfModel}`, (gltf) => {
    gltf.scene.scale.set(15, 15, 15)
    gltf.scene.position.set(0, 0, 0)
    gltf.scene.userData={
      name:"LoadingGLTFModel",
      data:"123",
      id:"1212121212",
      title:"人物模型"
    }
    let axis = new THREE.Vector3(0, 1, 0);//向量axis
    gltf.scene.rotateOnAxis(axis, Math.PI);
    gltf.scene.traverse(function (object) {
      if (object.isMesh) {
        object.castShadow = true; //阴影
        object.receiveShadow = true; //接受别人投的阴影
      }
    })
    scene.Model = gltf.scene;
    scene.add(gltf.scene) //公用访问时使用常量向场景中添加引入的模型
    return  gltf.scene
  })
}
LoadingGLTFMethod("Soldier.glb");//看一看模型有没有出现

可以看到已经成功导入

Three.js - JS三维模型库在Vue2中的基础教程_第9张图片

创建地板  ModelListConfig.js

export const Ground = new THREE.Mesh(new THREE.PlaneGeometry(300, 300), new THREE.MeshPhongMaterial({
  color: 0x888888, depthWrite: true,
}));
ModelListConfig.push(Ground) 

 Three.js - JS三维模型库在Vue2中的基础教程_第10张图片

 当然你也可以自己做一个材质,来作为地板的纹理

const Require = (src) => {
  return require(`../assets/${src}`)
}
const GroundTexture = new THREE.TextureLoader().load(Require('RC.jpg'))
export const Ground = new THREE.Mesh(new THREE.PlaneGeometry(300, 300), new THREE.MeshPhongMaterial({
  color: 0x888888, depthWrite: true,
  map: GroundTexture
}));
Ground.rotation.x = - Math.PI / 2;
Ground.receiveShadow = true;
ModelListConfig.push(Ground)  // 添加到模型数组

贴图效果:Three.js - JS三维模型库在Vue2中的基础教程_第11张图片

上述我们在导入文件时,设定了导入的物体是接受投影的,也添加过自然光,这时发现并没有影子。在方向光的作用下,物体会形成阴影投影效果,Three.js物体投影模拟计算主要设置三部分,一个是设置产生投影的模型对象,一个是设置接收投影效果的模型,最后一个是光源对象本身的设置,光源如何产生投影。

LightListConfig.js 添加平行光  

const directionalLight = new THREE.DirectionalLight(0xFFFFFF);
directionalLight.position.set(0, 35, 20);// 设置光源位置
directionalLight.castShadow = true; // 设置用于计算阴影的光源对象
// 设置计算阴影的区域,最好刚好紧密包围在对象周围
// 计算阴影的区域过大:模糊  过小:看不到或显示不完整
directionalLight.shadow.camera.near =0.5;
directionalLight.shadow.camera.far = 50;
directionalLight.shadow.camera.left = -10;
directionalLight.shadow.camera.right = 10;
directionalLight.shadow.camera.top = 100;
directionalLight.shadow.camera.bottom = -10;
// 设置mapSize属性可以使阴影更清晰,不那么模糊
// directionalLight.shadow.mapSize.set(1024,1024)
LightList.push(directionalLight)

 Three.js - JS三维模型库在Vue2中的基础教程_第12张图片

 这时候我们加上外部控制  HomeView.vue


import {
  ThreeController,
  LoadingGLTFMethod,
scene,
} 
from "@/components/ThreeController"; //中央渲染控制
return {
      …
      userData: {},
      test: {},
};
methods: {
…
LoadingMethod() {
      LoadingGLTFMethod("Soldier.glb");
},
    logs() {
        this.test = this.ThreeController.scene.children.find((item) => {
        if(item.userData.name){
              return item.userData.name == "LoadingGLTFModel";
        }
      });
     this.userData=this.test.userData
    },
}