持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第25天,点击查看活动详情
写在前面
本文用vue+threejs控制相机环绕物体。
说明:在创建的场景中,有一棵树,一个女孩和一只狗,这时,我们想要环绕展示小狗。
以下是演示gif:
完整代码和说明
- html和css
创建一个插入渲染器dom节点的id容器,创建一个按钮,用来控制开启相机环绕小狗和停止环绕小狗
<template>
<div class="item">
<div id="THREE51"></div>
<div class="btn_box">
<el-button @click="flyToPosi">{{
start ? "停止环绕" : "环绕小狗"
}}</el-button>
</div>
</div>
</template>
<style lang="less" scoped>
.btn_box {
position: absolute;
top: 10px;
left: 210px;
}
</style>
- 引入three.js和需要的模块
OrbitControls轨道控制器,GLTFLoader模型加载器
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
- data()中定义的变量
camera相机,cameraPerspective用来环绕的相机,scene场景,gltfLoader模型加载器,renderer场景渲染器,controls轨道控制器,manager模型加载进度管理,tree树模型,dog小狗模型,girl女孩模型,start是否开启相机环绕
data() {
return {
camera: null,
cameraPerspective: null,
scene: null,
gltfLoader: null,
renderer: null,
controls: null,
manager: null,
tree: null,
dog: null,
girl: null,
start: false,
};
},
- mounted()中调用的函数
每个函数的意义在注释中都有说明
mounted() {
this.manager = new THREE.LoadingManager();
this.gltfLoader = new GLTFLoader(this.manager);
this.initScene(); // 创建场景
this.initCamera(); // 创建相机
this.initLight(); // 创建灯光
this.initRenderer(); // 创建渲染器
this.initControls(); //创建轨道控制器
this.initModel(); // 加载模型
this.manager.onLoad = () => {
this.animate();
console.log("Loading complete!");
};
},
- 创建场景
initScene() {
this.scene = new THREE.Scene();
},
- 创建相机
创建两个透视相机,一个是camera,另一个是cameraPerspective,cameraPerspective用来环绕物体
initCamera() {
this.camera = new THREE.PerspectiveCamera(
35,
(window.innerWidth - 201) / window.innerHeight,
1,
600
); // 透视相机
this.camera.position.x = 10.5;
this.camera.position.y = 32.3; // 设置相机的位置
this.camera.position.z = 104.3;
this.cameraPerspective = new THREE.PerspectiveCamera(
35,
(window.innerWidth - 201) / window.innerHeight,
1,
30
);
},
- 创建灯光
创建两个灯光,一个是平行光DirectionalLight,另一个是环境光AmbientLight
initLight() {
const light = new THREE.DirectionalLight(0xffffff); // 平行光
light.position.set(0.5, 1.0, 0.5).normalize(); // 设置平行光的方向,从(0.5, 1.0, 0.5)->target一般(0, 0, 0)
this.scene.add(light); // 将灯光添加到场景中
const ambLight = new THREE.AmbientLight(0xf0f0f0, 0.1); // 环境光
this.scene.add(ambLight);
},
- 创建渲染器
创建渲染器,并将渲染器节点插入到dom中,我们的场景需要靠它来渲染
initRenderer() {
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.outputEncoding = THREE.sRGBEncoding;
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(window.innerWidth - 201, window.innerHeight);
document.getElementById("THREE51").appendChild(this.renderer.domElement);
},
- 创建轨道控制器
有了它可以通过鼠标来控制界面的平移缩放旋转
initControls() {
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.controls.addEventListener("change", this.render);
this.controls.target.set(22, 26, -7); // 控制器的焦点
this.controls.update();
},
- 加载模型
使用gltfLoader加载器来加载三个模型,分别是树模型,小狗模型和女孩模型
initModel() {
this.gltfLoader.load(
"/models/models/gltf/urban_tree/scene.gltf",
(gltf) => {
gltf.scene.scale.set(10, 10, 10);
gltf.scene.position.set(40, 0, 0);
this.tree = gltf.scene;
this.scene.add(gltf.scene);
}
);
this.gltfLoader.load("/models/models/gltf/shiba/scene.gltf", (gltf) => {
gltf.scene.scale.set(3, 3, 3);
gltf.scene.position.set(-5, 3, 1);
this.dog = gltf.scene;
this.scene.add(gltf.scene);
});
this.gltfLoader.load("/models/models/gltf/matilda/scene.gltf", (gltf) => {
gltf.scene.scale.set(0.1, 0.1, 0.1);
this.girl = gltf.scene;
this.scene.add(gltf.scene);
});
},
- 渲染场景和相机环绕的动画
当start为true时,开启了相机环绕,这时候相机就应该开始环绕展示小狗,我们让环绕的相机cameraPerspective一直朝向(lookAt)小狗的位置(this.dog.position),然后改变相机的位置,使其能环绕小狗运动,20是环绕的半径,(pos.x,pos.y)是环绕的中心,环绕开始后同时隐藏女孩的模型this.girl.visible = false;,以防在环绕过程中女孩的脚挡住视线,环绕停止后再显示女孩的模型this.girl.visible = true,环绕中我们渲染用的相机是我们创建的cameraPerspective相机,环绕停止后用camera相机
animate() {
requestAnimationFrame(this.animate);
this.render();
},
render() {
if (this.start) {
const r = Date.now() * 0.0007;
let pos = this.dog.position;
this.cameraPerspective.position.x = 20 * Math.cos(r) + pos.x;
this.cameraPerspective.position.z = 20 * Math.sin(r) + pos.z;
this.cameraPerspective.lookAt(pos);
this.girl.visible = false;
this.renderer.render(this.scene, this.cameraPerspective);
} else {
this.girl && (this.girl.visible = true);
this.renderer.render(this.scene, this.camera);
}
},
- 按钮事件
点击按钮后切换start的值
flyToPosi() {
this.start = !this.start;
},
写在最后
以上就是所有的代码和说明。