|
@@ -1,11 +1,119 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div ref="container" class="model-container"></div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
<script setup>
|
|
<script setup>
|
|
|
|
|
+import { ref, onMounted, onBeforeUnmount } from 'vue'
|
|
|
|
|
+import * as THREE from 'three'
|
|
|
|
|
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
|
|
|
|
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
|
|
|
|
|
|
|
|
-</script>
|
|
|
|
|
|
|
+const container = ref(null)
|
|
|
|
|
|
|
|
-<template>
|
|
|
|
|
|
|
+// 初始化场景
|
|
|
|
|
+const scene = new THREE.Scene()
|
|
|
|
|
+// 相机
|
|
|
|
|
+const camera = new THREE.PerspectiveCamera(
|
|
|
|
|
+ 75,
|
|
|
|
|
+ window.innerWidth / window.innerHeight,
|
|
|
|
|
+ 0.1,
|
|
|
|
|
+ 1000
|
|
|
|
|
+)
|
|
|
|
|
+// 渲染器
|
|
|
|
|
+const renderer = new THREE.WebGLRenderer({
|
|
|
|
|
+ antialias: true,
|
|
|
|
|
+ alpha: true
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// 添加光源
|
|
|
|
|
+const ambientLight = new THREE.AmbientLight(0xffffff, 2)
|
|
|
|
|
+
|
|
|
|
|
+let controls = null
|
|
|
|
|
+let model = null
|
|
|
|
|
+
|
|
|
|
|
+// 初始化场景
|
|
|
|
|
+const initScene = () => {
|
|
|
|
|
+ if (!container.value) return
|
|
|
|
|
+ camera.position.set(30, 30, 30);
|
|
|
|
|
+ scene.add(ambientLight)
|
|
|
|
|
+
|
|
|
|
|
+ // 设置渲染器
|
|
|
|
|
+ renderer.setSize(container.value.clientWidth, container.value.clientHeight)
|
|
|
|
|
+ renderer.setPixelRatio(window.devicePixelRatio)
|
|
|
|
|
+ renderer.outputEncoding = THREE.sRGBEncoding
|
|
|
|
|
+ container.value.appendChild(renderer.domElement)
|
|
|
|
|
|
|
|
-</template>
|
|
|
|
|
|
|
|
|
|
-<style scoped lang="scss">
|
|
|
|
|
|
|
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8)
|
|
|
|
|
+ directionalLight.position.set(5, 5, 5)
|
|
|
|
|
+ scene.add(directionalLight)
|
|
|
|
|
+
|
|
|
|
|
+ // 添加轨道控制器
|
|
|
|
|
+ controls = new OrbitControls(camera, renderer.domElement);
|
|
|
|
|
+ controls.enableDamping = true; // 启用阻尼效果
|
|
|
|
|
+ controls.dampingFactor = 0.05;
|
|
|
|
|
+ scene.add(ambientLight);
|
|
|
|
|
+
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 加载模型
|
|
|
|
|
+const loadModel = () => {
|
|
|
|
|
+ const loader = new GLTFLoader()
|
|
|
|
|
+
|
|
|
|
|
+ loader.load(
|
|
|
|
|
+ '../public/隧道1.glb', // 模型路径
|
|
|
|
|
+ (gltf) => {
|
|
|
|
|
+ model = gltf.scene
|
|
|
|
|
+ scene.add(model)
|
|
|
|
|
+ },
|
|
|
|
|
+ undefined, // 进度回调(可选)
|
|
|
|
|
+ (error) => {
|
|
|
|
|
+ console.error('Error loading model:', error)
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 动画循环
|
|
|
|
|
+const animate = () => {
|
|
|
|
|
+ requestAnimationFrame(animate)
|
|
|
|
|
+ if (controls) controls.update()
|
|
|
|
|
+ renderer.render(scene, camera)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ initScene()
|
|
|
|
|
+ loadModel()
|
|
|
|
|
+ animate()
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+onBeforeUnmount(() => {
|
|
|
|
|
+ window.removeEventListener('resize', handleResize)
|
|
|
|
|
+ if (container.value && container.value.contains(renderer.domElement)) {
|
|
|
|
|
+ container.value.removeChild(renderer.domElement)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 清理模型资源
|
|
|
|
|
+ if (model) {
|
|
|
|
|
+ scene.remove(model)
|
|
|
|
|
+ model.traverse((child) => {
|
|
|
|
|
+ if (child.isMesh) {
|
|
|
|
|
+ child.geometry.dispose()
|
|
|
|
|
+ if (Array.isArray(child.material)) {
|
|
|
|
|
+ child.material.forEach(m => m.dispose())
|
|
|
|
|
+ } else {
|
|
|
|
|
+ child.material.dispose()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+</script>
|
|
|
|
|
|
|
|
|
|
+<style scoped>
|
|
|
|
|
+.model-container {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 100vh;
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+}
|
|
|
</style>
|
|
</style>
|