在 Vue 项目中渲染 GLB 格式的 3D 模型需要结合 Three.js 和 GLTFLoader。以下是完整的实现步骤:
- 安装必要的依赖
首先安装 Three.js 和 GLTFLoader:
npm install three @types/three
- 页面中加载和渲染 GLB 模型
(glb模型放入本地/assets/public文件夹下了)
<template>
<div ref="container" style="width: 100%; height: 100vh;"></div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import url from '@/assets/public/jizu.glb'
const container = ref(null);
let scene, camera, renderer, controls, animationId;
onMounted(() => {
// 1. 初始化场景
scene = new THREE.Scene();
scene.background = new THREE.Color(0xaaaaaa);
// 2. 初始化相机
const width = container.value.clientWidth;
const height = container.value.clientHeight;
camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);
camera.position.set(0, 2, 5);
// 3. 初始化渲染器
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
container.value.appendChild(renderer.domElement);
// 4. 添加光源
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(5, 10, 7);
scene.add(directionalLight);
// 5. 添加轨道控制器
controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 1, 0);
controls.update();
// 6. 加载 GLB 模型
const loader = new GLTFLoader();
console.log(loader,'111')
loader.load(
url, // 替换成你的模型路径
(gltf) => {
console.log(gltf,'222')
scene.add(gltf.scene);
},
undefined,
(error) => {
console.error('加载模型失败:', error);
}
);
// 7. 动画循环
function animate() {
animationId = requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
// 8. 监听窗口大小变化
window.addEventListener('resize', onWindowResize);
});
function onWindowResize() {
if (!container.value) return;
const width = container.value.clientWidth;
const height = container.value.clientHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
}
onBeforeUnmount(() => {
window.removeEventListener('resize', onWindowResize);
cancelAnimationFrame(animationId);
controls.dispose();
renderer.dispose();
// 清理场景中的所有对象,防止内存泄漏
scene.traverse((obj) => {
if (obj.geometry) obj.geometry.dispose();
if (obj.material) {
if (Array.isArray(obj.material)) {
obj.material.forEach((m) => m.dispose());
} else {
obj.material.dispose();
}
}
});
scene.clear();
});
</script>
如果使用的是vite可能会报错,在vite.config.js文件下加入:assetsInclude: ['**/*.glb'], // 明确包含GLB文件
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
assetsInclude: ['**/*.glb'], // 明确包含GLB文件
server: {
https: false,
// host: "0.0.0.0",
port: 9001,
open: true,
cors: true,
strictPort: false,
proxy: {
'/api': {
target: 'http://1.117.236.127',
changeOrigin: true,
rewrite: (path) => path.replace("/api", "")
},
}
},
})