three.js项目,学习地址
简介
three.js是JavaScript编写的WebGL第三方库。提供了非常多的3D显示功能
什么是threejs,很简单,你将它理解成three + js就可以了。three表示3D的意思,js表示javascript的意思。那么合起来,three.js就是使用javascript来写3D程序的意思。Javascript是运行在网页端的脚本语言,那么毫无疑问Three.js也是运行在浏览器上的。
项目中使用到CSS2DObject,TWEEN结合镜头运动,模型动画,
初始化项目
npm i three@0.124.0
需要加上版本号,官网引入某些控制器时最新引入方法
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
旧版本引入方法
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
实际上不同版本的three库有差别
控件引入
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; //鼠标控制器
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; //模型解压缩
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js"; //模型加载器
import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' // 引入包
import Stats from 'three/examples/jsm/libs/stats.module.js'
let scene = null; //场景(在vue3中scene与mesh不能在data中定义,必须全局定义)
const TWEEN = require('@tweenjs/tween.js') //vue 中定义一个TWEEN的常量
three.js控制器
createOrbitControls(){
this.mouseControls = new OrbitControls(
this.camera,
this.renderer.domElement
); //创建控件对象
this.mouseControls.target = new THREE.Vector3(0, 0, 0)
this.mouseControls.enablePan = true; //右键平移拖拽
this.mouseControls.enableZoom = true; //鼠标缩放
// this.mouseControls.minDistance = 1600; //相机距离原点的距离范围
this.mouseControls.maxDistance = 20000;
this.mouseControls.enableDamping = true; //滑动阻尼
this.mouseControls.dampingFactor = 0.1; //(默认.25)
this.mouseControls.maxPolarAngle = (Math.PI / 4) * 3; //y旋转角度范围
this.mouseControls.minPolarAngle = Math.PI / 4;
},
three.js环境光
createLight() {
let lightColor = new THREE.Color('#ffffff');
let ambient = new THREE.AmbientLight(lightColor); //环境光
ambient.name = "环境光";
scene.add(ambient);
let directionalLight1 = new THREE.DirectionalLight(lightColor);
directionalLight1.position.set(0, 0, 500);
scene.add(directionalLight1); //平行光源添加到场景中
},
模型加载
注意这边用的是.glb的模型动画。.fbx模型的动画加载方式会有出入
loadModel() {
let that = this
//利用Promise加载模型
let lm = new Promise((resolve, reject) => {
let loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath( './draco/' ) // 注意路径,确保public下面有draco文件夹
loader.setDRACOLoader( dracoLoader );
loader.load('./model/moxing10.glb', (gltf) => {
resolve(gltf); //返回加载成功的模型
reject("model加载失败!");
this.isLoading = false; //关闭加载框
});
});
//加载成功
lm.then((res) => {
// 将模型放在页面中央
that.modelCenter(res.scene)
// 模型中自带的动画
this.mixer = new THREE.AnimationMixer(res.scene)
for (var i = 0; i < res.animations.length; i++) {
const clip = res.animations[i] //将第1帧动画设置为动画剪辑对象
const action = this.mixer.clipAction(clip) //使用动画剪辑对象创建AnimationAction对象
this.mixer.timeScale = 0.5 //默认1,可以调节播放速度
action.setDuration(2).play() //设置单此循环的持续时间(setDuration也可以调节播放速度)并开始动画
}
// 模型放大会造成卡顿
// res.scene.scale.set(modePar[3], modePar[4], modePar[5]);
scene.add(res.scene);
let len = 0
setTimeout(()=>{
// 模型中的有些模型名称需要显示,可以根据名称判断,这里只显示前面10个模型名称
res.scene.traverse((child) => {
if(len <= 10){
len += 1
that.createLableObj(child.name,child.position,child)
}
})
},3000)
});
//加载失败
lm.catch((err) => {
console.log(err)
});
},
three.js渲染
repeatRender() {
const delta = this.clock.getDelta();
//请求动画帧,屏幕每刷新一次调用一次,绑定屏幕刷新频率
this.anId = requestAnimationFrame(this.repeatRender);
// 动画
if (this.mixer) {
this.mixer.update(delta)
}
this.renderer.render(scene, this.camera); //将场景和相机进行渲染
// label标题
if(this.labelRenderer){
this.labelRenderer.render(scene, this.camera);
}
},
初始化渲染器
initScene(){
scene = new THREE.Scene(); //新建场景
let dom = document.getElementById("container");
document.body.clientWidth || document.documentElement.clientWidth;
let width = document.body.clientWidth || document.documentElement.clientWidth; //场景宽度
let height = document.body.clientHeight || document.documentElement.clientHeight; //场景高度
let k = width / height; //场景宽高比
this.mouse = new THREE.Vector2()
let cameraPar = [65, 0.01, 20000, -500, 2300, 4300]
// 相机
this.camera = new THREE.PerspectiveCamera(
cameraPar[0],
k,
cameraPar[1],
cameraPar[2]
); //透视相机
// 渲染器
this.renderer = new THREE.WebGLRenderer({
logarithmicDepthBuffer: true,
antialias: true, //抗锯齿
alpha: true,
});
this.renderer.logarithmicDepthBuffer = true;
this.renderer.setSize(width, height); //设置渲染区域尺寸
document.getElementById("container").appendChild(this.renderer.domElement); //将画布添加到container中
this.clock = new THREE.Clock();//用于更新轨道控制器
// 控制器
this.createOrbitControls();
// 光
this.createLight();
// 模型加载
this.loadModel();
// 渲染
this.repeatRender();
document.addEventListener('dblclick', this.onMouseMove2, false);
// 模型标题的CSS2DRenderer初始化
this.labelRenderer = new CSS2DRenderer()
this.labelRenderer.setSize(window.innerWidth, window.innerHeight)
this.labelRenderer.domElement.style.position = 'absolute'
this.labelRenderer.domElement.style.top = '0px';
document.getElementById("container").appendChild(this.labelRenderer.domElement)
},
创建label标题
createLableObj(text,positions,clickedObject) {
let laberDiv = document.createElement('div');//创建div容器
laberDiv.className = 'laber_name';
laberDiv.textContent = text;
laberDiv.style.height = '20px'
laberDiv.style.background = 'red'
laberDiv.style.color = '#ffffff'
let pointLabel = new CSS2DObject(laberDiv);
clickedObject.add(pointLabel)
this.mouseControls = new OrbitControls(
this.camera,
this.labelRenderer.domElement
); //创建控件对象
this.labelRenderer.render(scene, this.camera)
},
双击模型事件
onMouseMove2(e){
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
// 计算鼠标或触摸点的位置
mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
// 更新射线 注意——> this.camera 是相机 定义到data里的
raycaster.setFromCamera(mouse, this.camera);
// 计算与所有对象的交点
const intersects = raycaster.intersectObjects(scene.children, true);
console.log(intersects)
let target1
let obj
if (intersects.length > 0) {
// 获取到点击的模型
const clickedObject = intersects[0].object;
console.log(clickedObject.position)
target1 = clickedObject.position
obj = clickedObject
let boxMaxY = new THREE.Box3().setFromObject(intersects[0].object).max.y
let distance = boxMaxY + 10
let angel = Math.PI / 5
// 运动方向
let position = {
x: intersects[0].object.position.x + Math.cos(angel) * distance,
y: intersects[0].object.position.y,
z: intersects[0].object.position.z + Math.sin(angel) * distance
}
let tween = new TWEEN.Tween(this.camera.position).to(position, 3000)
// let tween1 = new TWEEN.Tween(this.mouseControls.target).to(intersects[0].object.position, 3000)
this.mouseControls.enabled = false;
tween.start()
// tween1.start()
}
},
总结
诸君共勉
2023年7月22日 纸上谈赖总觉浅,绝知此事要躬行