本篇本章是在vue框架下进行的网页搭建,使用原生版本也可以进行three.js的使用。不过现在网上流行的大多是框架内的搭建方法
首先需要安装three.js。
在vue里配置three.js的命令是
npm install three
这里稍微提一嘴想要在原生html的情况下使用three.js的话需要找到较早之前的版本,并且由于vue和原生还是有一些区别的,所以这里不会过多的提及。
关于找较早的版本可以去下面链接中去获取,通过查找可以通过133的版本找到原生html可以使用的版本,像是GLTFLoader文件也可以通过在目录three.js-r133\examples\js\loaders中找到。其他文件也可以在上级目录three.js-r133\examples\js中找到,可以看需求使用
Releases · mrdoob/three.js (github.com)https://github.com/mrdoob/three.js/releases
先展示下页面的效果,这里是在放入的3D模型中加入自己需求的设计。
首先需要把准备好的模型文件放到public文件夹中,这里准备的是gltf类型的模型文件
搭建页面需要一个位置来放置画布,其中id为container的位置被我指定为画布并为其设置一下样式表
<template>
<div id="box">
<div id="container"></div>
</div>
</template>
#container {
position: absolute;
width: 100%;
height: 100%;
}
然后需要绘制画布和设置摄像头和光源等各种参数
methods: {
MapCreate() {
// 指定场景位置
const canvas = document.getElementById("container");
// 创建场景
const scene = new THREE.Scene();
// model(这里是添加模型的位置)
const loader = new GLTFLoader();
loader.setCrossOrigin('anonymous');
loader.load('./model/scene.gltf', function (gltf) {
scene.add(gltf.scene)
});
// render(渲染器)
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
canvas.appendChild(renderer.domElement);
// Camera(摄像头)
const camera = new THREE.PerspectiveCamera(
45, //视角,视角越大,看的东西越多
window.innerWidth / window.innerHeight, //宽高比
0.1, //近平面,相机最近看到的物体
1000 //远平面,相机最远看到的物体
);
camera.position.z = 100;
camera.lookAt(0, 0, 0);
// Camera Control(镜头控制器)
const control = new OrbitControls(camera, renderer.domElement);
control.addEventListener('change', () => {
renderer.render(scene, camera);
});
// Light(光源)
const light = new THREE.AmbientLight(0xffffff);
scene.add(light);
// 窗口自适应
window.addEventListener("resize", () => {
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix();
// 更新渲染器
renderer.setSize(window.innerWidth, window.innerHeight);
// 设置渲染器的像素比
renderer.setPixelRatio(window.devicePixelRatio);
});
// 渲染函数
let timer = null;
// 实时更新动画
function animate() {
renderer.render(scene, camera);
timer = requestAnimationFrame(animate);
if (TWEEN.update()) {
cancelAnimationFrame(timer);
timer = requestAnimationFrame(animate);
}
}
animate();
}
}
当然要让效果显示的话不要忘了让他在开始就被调用。
mounted() {
this.MapCreate()
},
现在的效果是只有模型并没有图标的效果就如下图所示
想要加入图标只需要在页面加入图片的模块,这里的坐标使用其他位置的js文件来记录,Marks是我导入的模块。第一张图是模块里的数据格式
const mark_list = [
{
"x" : 0,
"y" : 35,
"z" : 0,
"Url":"https://www.baidu.com"
},
{
"x" : 0,
"y" : 3,
"z" : 10,
"Url":"https://www.bilibili.com"
}
]
export default mark_list
import Marks from '../data/marks.js'
let marks_box = [];
const textureLoader = new THREE.TextureLoader();
const material = new THREE.MeshBasicMaterial({
map: textureLoader.load("mark.png"),
transparent: true, //使用背景透明的png贴图,注意开启透明计算
});
for (let i = 0; i < Marks.length; i++) {
const geometry = new THREE.BoxGeometry(5, 5, 0);
const mesh = new THREE.Mesh(geometry, material);
const wp = Marks[i]
mesh.position.set(wp.x, wp.y, wp.z);
marks_box.push(mesh);
scene.add(mesh);
}
现在有了内容当时图片并没有任何功能,一下代码是实现功能的部分主要的效果是视图飞行移动的效果,主要是通过判断鼠标的位置和3D模型视图里的位置是否重合来判断是否发生事件,左键为移动视图,右键为跳转链接
// 创建射线检测器
let raycaster = new THREE.Raycaster();
let mouse = new THREE.Vector2();
// 添加鼠标事件监听器
document.addEventListener('click', onDocumentMouseDown, false);
// 鼠标事件处理函数
function onDocumentMouseDown(event) {
event.preventDefault();
// 计算鼠标在canvas中的坐标
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// 把鼠标坐标转换为three.js中的坐标
raycaster.setFromCamera(mouse, camera);
// 计算射线和立方体的交点
for (let i = 0; i < marks_box.length; i++) {
let cube = marks_box[i]
let intersects = raycaster.intersectObjects([cube]);
// 处理点击事件
if (intersects.length > 0) {
const pos = new THREE.Vector3();
cube.getWorldPosition(pos); //获取三维场景中某个对象世界坐标
// 相机飞行到的位置和观察目标拉开一定的距离
const pos2 = pos.clone().addScalar(30);//向量的x、y、z坐标分别在pos基础上增加30
// 相机从当前位置camera.position飞行三维场景中某个世界坐标附近
new TWEEN.Tween({
// 相机开始坐标
x: camera.position.x,
y: camera.position.y,
z: camera.position.z,
// 相机开始指向的目标观察点
tx: 0,
ty: 0,
tz: 0,
})
.to({
// 相机结束坐标
x: pos2.x,
y: pos2.y,
z: pos2.z,
// 相机结束指向的目标观察点
tx: pos.x,
ty: pos.y,
tz: pos.z,
}, 500)
.onUpdate(function (obj) {
// 动态改变相机位置
camera.position.set(obj.x, obj.y, obj.z);
// 动态计算相机视线
camera.lookAt(obj.tx, obj.ty, obj.tz);
})
.start().update();
}
}
}
document.addEventListener('mousedown', onDocumentLclick, false);
function onDocumentLclick(event) {
if(event.button == 2){
event.preventDefault();
// 计算鼠标在canvas中的坐标
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// 把鼠标坐标转换为three.js中的坐标
raycaster.setFromCamera(mouse, camera);
// 计算射线和立方体的交点
for (let i = 0; i < marks_box.length; i++) {
let cube = marks_box[i]
let intersects = raycaster.intersectObjects([cube]);
// 处理点击事件
if (intersects.length > 0) {
// 跳转到指定的 URL 地址
location.assign(Marks[i].Url);
}
}
}
}
其中有一部分让图标和摄像头移动的动画在摄影机处单独绘制
// Camera Control
const control = new OrbitControls(camera, renderer.domElement);
// edge鼠标指示功能兼容
// control.mouseButtons = {
// LEFT: THREE.MOUSE.PAN,
// MIDDLE: THREE.MOUSE.ROTATE,
// }
control.addEventListener('change', () => {
for (let i = 0; i < marks_box.length; i++) {
marks_box[i].rotation.y = camera.rotation.y;
}
// ;
renderer.render(scene, camera);
});