最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue3?+?Three.js實現(xiàn)自定義3D模型加載與交互實戰(zhàn)全流程

 更新時間:2025年12月19日 10:33:09   作者:YAY_tyy  
Three.js是一個用于創(chuàng)建3D圖形的JavaScript庫,它基于WebGL技術(shù),并提供了一系列的工具和API,這篇文章主要介紹了Vue3?+?Three.js實現(xiàn)自定義3D模型加載與交互實戰(zhàn)的相關(guān)資料,需要的朋友可以參考下

前言

在 Web 3D 開發(fā)中,Three.js 是輕量且靈活的核心框架,尤其適合自定義模型可視化場景。本文基于 Vue3 + Vite + Three.js 技術(shù)棧,從環(huán)境搭建到模型加載、交互控制逐步拆解,覆蓋 GLB 模型加載、視角控制、點擊高亮等核心需求,附帶完整代碼與避坑指南。?

一、環(huán)境搭建:Vue3 + Three.js 基礎(chǔ)配置?

1.1 項目初始化(Vite)?

Three.js 包體積遠(yuǎn)小于 Cesium,配合 Vite 可實現(xiàn)秒級編譯,初始化命令如下:

# 創(chuàng)建Vue3項目
npm create vite@latest threejs-3d-model-demo -- --template vue
cd threejs-3d-model-demo
npm install

# 安裝核心依賴(Three.js + 模型加載器 + 控制器)
npm install three @tweenjs/tween.js
  • three:核心庫(包含場景、相機、渲染器等基礎(chǔ)組件)?
  • @tweenjs/tween.js:用于視角平滑過渡(替代 Three.js 原生動畫)

1.2 基礎(chǔ)工具封裝(簡化重復(fù)代碼)?

Three.js 需手動創(chuàng)建場景、相機、渲染器,建議封裝工具函數(shù)統(tǒng)一管理。在src/utils/threeHelper.js中添加:

import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js' // 視角控制器

/**
 * 初始化Three.js基礎(chǔ)組件
 * @param {HTMLElement} container - 渲染容器
 * @returns {Object} 場景、相機、渲染器、控制器實例
 */
export function initThree(container) {
  // 1. 創(chuàng)建場景(承載所有3D元素)
  const scene = new THREE.Scene()
  scene.background = new THREE.Color(0xf0f8ff) // 背景色(淡藍(lán)色)

  // 2. 創(chuàng)建相機(透視相機,模擬人眼視角)
  const camera = new THREE.PerspectiveCamera(
    75, // 視野角度(FOV)
    container.clientWidth / container.clientHeight, // 寬高比
    0.1, // 近裁剪面(小于此距離的物體不渲染)
    1000 // 遠(yuǎn)裁剪面(大于此距離的物體不渲染)
  )
  camera.position.set(0, 5, 10) // 相機初始位置(x,y,z)

  // 3. 創(chuàng)建渲染器(WebGL渲染)
  const renderer = new THREE.WebGLRenderer({ antialias: true }) // 抗鋸齒開啟
  renderer.setSize(container.clientWidth, container.clientHeight) // 渲染尺寸
  container.appendChild(renderer.domElement) // 掛載渲染畫布

  // 4. 創(chuàng)建控制器(支持鼠標(biāo)拖動、滾輪縮放視角)
  const controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true // 阻尼效果(視角移動更平滑)
  controls.dampingFactor = 0.05 // 阻尼系數(shù)(值越小越平滑)
  controls.screenSpacePanning = false // 禁止屏幕平移(僅圍繞物體旋轉(zhuǎn))
  controls.minDistance = 5 // 最小縮放距離
  controls.maxDistance = 50 // 最大縮放距離

  // 5. 監(jiān)聽窗口resize事件(自適應(yīng)畫布尺寸)
  window.addEventListener('resize', () => {
    camera.aspect = container.clientWidth / container.clientHeight
    camera.updateProjectionMatrix() // 更新相機投影矩陣
    renderer.setSize(container.clientWidth, container.clientHeight)
  })

  return { scene, camera, renderer, controls }
}

二、核心功能實現(xiàn):3D 模型加載與控制?

2.1 組件初始化(掛載 Three.js 核心實例)?

在 Vue 組件中調(diào)用工具函數(shù),初始化場景并啟動渲染循環(huán):

<template>
  <!-- Three.js渲染容器 -->
  <div id="threeContainer" class="three-container"></div>
  <!-- 模型加載進(jìn)度條 -->
  <div v-if="loadProgress < 100" class="load-progress">
    加載中:{{ loadProgress }}%
  </div>
</template>

<script setup>
import { onMounted, ref } from 'vue'
import * as THREE from 'three'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js' // GLB模型加載器
import { initThree } from '@/utils/threeHelper.js'
import TWEEN from '@tweenjs/tween.js'

// 響應(yīng)式數(shù)據(jù)(加載進(jìn)度)
const loadProgress = ref(0)
// 全局變量(存儲Three.js實例)
let scene, camera, renderer, controls, model // model為加載后的模型實例

onMounted(() => {
  // 1. 獲取容器并初始化Three.js
  const container = document.getElementById('threeContainer')
  const threeInstance = initThree(container)
  scene = threeInstance.scene
  camera = threeInstance.camera
  renderer = threeInstance.renderer
  controls = threeInstance.controls

  // 2. 添加環(huán)境光(避免模型過暗)
  addLights()

  // 3. 加載3D模型
  loadGLBModel('/models/building.glb') // 模型路徑(public/models目錄下)

  // 4. 啟動渲染循環(huán)(動畫幀)
  renderLoop()
})

/**
 * 添加場景燈光(環(huán)境光+方向光)
 */
function addLights() {
  // 環(huán)境光(均勻照亮所有物體,無陰影)
  const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)
  scene.add(ambientLight)

  // 方向光(模擬太陽光,產(chǎn)生陰影)
  const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8)
  directionalLight.position.set(10, 20, 10) // 光源位置
  directionalLight.castShadow = true // 開啟陰影投射
  // 優(yōu)化陰影質(zhì)量(降低鋸齒)
  directionalLight.shadow.mapSize.set(2048, 2048)
  scene.add(directionalLight)
}

/**
 * 渲染循環(huán)(持續(xù)更新場景)
 */
function renderLoop() {
  requestAnimationFrame(renderLoop)
  controls.update() // 更新控制器狀態(tài)(阻尼效果需此步驟)
  TWEEN.update() // 更新視角過渡動畫
  renderer.render(scene, camera) // 渲染場景
}
</script>

<style scoped>
.three-container {
  width: 100vw;
  height: 100vh;
}
.load-progress {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 18px;
  color: #333;
  background: rgba(255, 255, 255, 0.8);
  padding: 10px 20px;
  border-radius: 4px;
  z-index: 100;
}
</style>

2.2 GLB 模型加載(帶進(jìn)度監(jiān)聽)?

Three.js 通過GLTFLoader加載模型,需處理加載進(jìn)度、成功回調(diào)與錯誤捕獲:

/**
 * 加載GLB格式3D模型
 * @param {string} modelPath - 模型路徑
 */
function loadGLBModel(modelPath) {
  const loader = new GLTFLoader()

  // 1. 監(jiān)聽加載進(jìn)度
  loader.onProgress = (xhr) => {
    loadProgress.value = Math.floor((xhr.loaded / xhr.total) * 100)
  }

  // 2. 加載成功回調(diào)
  loader.load(
    modelPath,
    (gltf) => {
      model = gltf.scene // 保存模型實例(用于后續(xù)交互)
      
      // 模型縮放與位置調(diào)整(根據(jù)實際模型大小適配)
      model.scale.set(1, 1, 1) // 縮放比例(默認(rèn)1:1)
      model.position.set(0, 0, 0) // 模型初始位置(場景中心)
      
      // 允許模型投射陰影
      model.traverse((child) => {
        if (child.isMesh) {
          child.castShadow = true
          child.receiveShadow = true // 允許接收其他物體的陰影
        }
      })

      scene.add(model) // 將模型添加到場景
      loadProgress.value = 100 // 標(biāo)記加載完成

      // 視角自動聚焦到模型(使用TWEEN實現(xiàn)平滑過渡)
      new TWEEN.Tween(camera.position)
        .to({ x: 0, y: 3, z: 8 }, 1500) // 目標(biāo)位置與過渡時間(ms)
        .easing(TWEEN.Easing.Quadratic.InOut) // 緩動函數(shù)
        .start()
    },
    undefined, // onProgress已單獨處理
    (error) => {
      console.error('模型加載失敗:', error)
      alert(`模型加載失敗,請檢查路徑:${modelPath}`)
    }
  )
}

2.3 模型交互:點擊高亮與信息彈窗?

基于 Three.js 的Raycaster(射線檢測)實現(xiàn)點擊交互,配合 Element Plus 彈窗展示信息:

// 引入Element Plus(需提前安裝:npm install element-plus)
import { ElMessageBox } from 'element-plus'
import { onMounted } from 'vue'

onMounted(() => {
  // ... 原有初始化代碼 ...
  // 初始化點擊交互
  initModelClick()
})

/**
 * 初始化模型點擊交互
 */
function initModelClick() {
  const raycaster = new THREE.Raycaster() // 射線檢測器
  const mouse = new THREE.Vector2() // 存儲鼠標(biāo)坐標(biāo)

  // 監(jiān)聽鼠標(biāo)點擊事件
  window.addEventListener('click', (event) => {
    // 1. 將鼠標(biāo)屏幕坐標(biāo)轉(zhuǎn)換為Three.js標(biāo)準(zhǔn)坐標(biāo)(-1 ~ 1)
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1

    // 2. 更新射線方向(從相機指向鼠標(biāo)點擊位置)
    raycaster.setFromCamera(mouse, camera)

    // 3. 檢測射線與模型的交點
    const intersects = raycaster.intersectObjects(model.children, true) // true:檢測子物體

    if (intersects.length > 0) {
      const clickedMesh = intersects[0].object // 獲取點擊的模型網(wǎng)格
      
      // 4. 模型高亮(臨時修改材質(zhì)顏色)
      const originalMaterial = clickedMesh.material // 保存原始材質(zhì)
      clickedMesh.material = new THREE.MeshBasicMaterial({ color: 0xffff00 }) // 黃色高亮

      // 5. 2秒后恢復(fù)原始材質(zhì)
      setTimeout(() => {
        clickedMesh.material = originalMaterial
      }, 2000)

      // 6. 彈出模型信息(可從模型userData中獲取自定義數(shù)據(jù))
      ElMessageBox.alert(
        `<div>模型名稱:${model.name || '自定義建筑模型'}</div>
         <div>位置:(${model.position.x.toFixed(2)}, ${model.position.y.toFixed(2)}, ${model.position.z.toFixed(2)})</div>
         <div>網(wǎng)格數(shù)量:${getMeshCount(model)}個</div>`,
        '模型信息',
        { confirmButtonText: '確定' }
      )
    }
  })
}

/**
 * 統(tǒng)計模型中的網(wǎng)格數(shù)量
 * @param {THREE.Group} object - 模型實例(Group或Scene)
 * @returns {number} 網(wǎng)格數(shù)量
 */
function getMeshCount(object) {
  let count = 0
  object.traverse((child) => {
    if (child.isMesh) count++
  })
  return count
}

三、Three.js 專屬問題與優(yōu)化方案?

3.1 模型加載后黑屏 / 不可見?

  • 相機位置問題:確保相機在模型 “可視范圍內(nèi)”(如模型過大時,相機需遠(yuǎn)離),可通過camera.lookAt(model.position)讓相機朝向模型?
  • 燈光缺失:Three.js 默認(rèn)無環(huán)境光,需手動添加AmbientLight或DirectionalLight(參考 2.1 節(jié)addLights函數(shù))?
  • 模型縮放異常:若模型過小 / 過大,調(diào)整model.scale.set(x,y,z)(如scale.set(0.1,0.1,0.1)縮小 10 倍)?

3.2 渲染性能優(yōu)化(解決卡頓)?

  • 減少面數(shù):BlenderMeshLab簡化模型(建議單模型面數(shù) < 5 萬)?
  • 材質(zhì)優(yōu)化:復(fù)雜場景用MeshLambertMaterial替代MeshStandardMaterial(后者計算 PBR 物理效果,性能消耗高)?
  • 開啟抗鋸齒權(quán)衡:若性能不足,關(guān)閉渲染器抗鋸齒(new THREE.WebGLRenderer({ antialias: false })),通過renderer.setPixelRatio(window.devicePixelRatio)優(yōu)化顯示?

3.3 模型紋理丟失?

  • 紋理路徑問題:GLB 模型若內(nèi)嵌紋理,直接加載即可;若紋理單獨存儲,需確保紋理文件與模型在同一目錄,且導(dǎo)出時路徑正確?
  • 跨域問題:開發(fā)環(huán)境通過 Vite 配置代理,生產(chǎn)環(huán)境需服務(wù)器添加Access-Control-Allow-Origin響應(yīng)頭:
// vite.config.js 跨域配置示例
export default defineConfig({
  server: {
    proxy: {
      '/models': {
        target: 'http://your-server.com', // 模型服務(wù)器地址
        changeOrigin: true
      }
    }
  }
})

四、總結(jié)與擴展方向?

本文實現(xiàn)了 Vue3 + Three.js 加載 3D 模型的核心流程,相比 Cesium,Three.js 更輕量、自定義程度更高,適合非 GIS 類 3D 場景。后續(xù)可擴展的方向:?

  1. 模型動畫控制:通過gltf.animations獲取模型動畫,用THREE.AnimationMixer實現(xiàn)播放 / 暫停 / 進(jìn)度控制?
  2. 光影增強:添加HemisphereLight(半球光)模擬地面反光,或PointLight(點光源)模擬燈光照射效果?
  3. 粒子系統(tǒng)結(jié)合:用THREE.Points創(chuàng)建粒子云,圍繞模型生成動態(tài)效果(如建筑周圍的粒子流)

到此這篇關(guān)于Vue3 + Three.js實現(xiàn)自定義3D模型加載與交互的文章就介紹到這了,更多相關(guān)Vue3 Three.js自定義3D模型加載與交互內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Vue axios獲取token臨時令牌封裝案例

    Vue axios獲取token臨時令牌封裝案例

    這篇文章主要介紹了Vue axios獲取token臨時令牌封裝案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • Vue中extends繼承和組件復(fù)用性詳解

    Vue中extends繼承和組件復(fù)用性詳解

    這篇文章主要給大家介紹了關(guān)于Vue中extends繼承和組件復(fù)用性的相關(guān)資料,組件(Component)是Vue.js最強大的功能之一,組件可以擴展HTML元素,封裝可重用的代碼,需要的朋友可以參考下
    2023-11-11
  • vue實現(xiàn)自定義H5視頻播放器的方法步驟

    vue實現(xiàn)自定義H5視頻播放器的方法步驟

    這篇文章主要介紹了vue實現(xiàn)自定義H5視頻播放器的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • vue2老項目中node-sass更換dart-sass的操作方法

    vue2老項目中node-sass更換dart-sass的操作方法

    這篇文章主要介紹了vue2老項目中node-sass更換dart-sass的操作方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2024-07-07
  • vue+elementUI中使用Echarts之餅圖問題

    vue+elementUI中使用Echarts之餅圖問題

    這篇文章主要介紹了vue+elementUI中使用Echarts之餅圖問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • element el-input directive數(shù)字進(jìn)行控制

    element el-input directive數(shù)字進(jìn)行控制

    本文介紹了vue使用directive 進(jìn)行控制的方法,使用element開發(fā)的過程中遇到循環(huán)的數(shù)據(jù)只能輸入數(shù)字,并且有不要小數(shù)點,有需要小數(shù)點的,就有一定的參考價值,有興趣的可以了解一下
    2018-10-10
  • 分析 Vue 中的 computed 和 watch 的區(qū)別

    分析 Vue 中的 computed 和 watch 的區(qū)別

    這篇文章分析 Vue 的 computed 和 watch 的區(qū)別,computed 用來監(jiān)控自己定義的變量,頁面上可直接使用。watch 是監(jiān)測 Vue 實例上的數(shù)據(jù)變動,通俗地講,就是檢測 data 內(nèi)聲明的數(shù)據(jù),需要的朋友可以參考一下
    2021-09-09
  • 在vue中使用G2圖表的示例代碼

    在vue中使用G2圖表的示例代碼

    這篇文章主要介紹了在vue中使用G2圖表的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-03-03
  • VUE+element開發(fā)后臺管理的搜索功能

    VUE+element開發(fā)后臺管理的搜索功能

    這篇文章主要為大家詳細(xì)介紹了VUE+element開發(fā)后臺管理的搜索功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Vue數(shù)據(jù)劫持詳情介紹

    Vue數(shù)據(jù)劫持詳情介紹

    這篇文章主要介紹了Vue數(shù)據(jù)劫持詳情介紹,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下,希望對你的學(xué)習(xí)有所幫助
    2022-08-08

最新評論

阿合奇县| 铁岭县| 隆昌县| 合江县| 买车| 天镇县| 邳州市| 乐陵市| 南充市| 凯里市| 波密县| 吉木萨尔县| 门源| 固阳县| 收藏| 安岳县| 双峰县| 龙江县| 林芝县| 紫阳县| 合川市| 鞍山市| 台江县| 太原市| 广安市| 梅河口市| 平江县| 朝阳区| 新安县| 区。| 阜南县| 苍溪县| 平昌县| 贵州省| 彰化市| 来安县| 洪泽县| 新郑市| 德兴市| 嫩江县| 沈阳市|