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

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

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

前言

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

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

1.1 項(xiàng)目初始化(Vite)?

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

# 創(chuàng)建Vue3項(xiàng)目
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:核心庫(kù)(包含場(chǎng)景、相機(jī)、渲染器等基礎(chǔ)組件)?
  • @tweenjs/tween.js:用于視角平滑過(guò)渡(替代 Three.js 原生動(dòng)畫)

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

Three.js 需手動(dòng)創(chuàng)建場(chǎng)景、相機(jī)、渲染器,建議封裝工具函數(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} 場(chǎng)景、相機(jī)、渲染器、控制器實(shí)例
 */
export function initThree(container) {
  // 1. 創(chuàng)建場(chǎng)景(承載所有3D元素)
  const scene = new THREE.Scene()
  scene.background = new THREE.Color(0xf0f8ff) // 背景色(淡藍(lán)色)

  // 2. 創(chuàng)建相機(jī)(透視相機(jī),模擬人眼視角)
  const camera = new THREE.PerspectiveCamera(
    75, // 視野角度(FOV)
    container.clientWidth / container.clientHeight, // 寬高比
    0.1, // 近裁剪面(小于此距離的物體不渲染)
    1000 // 遠(yuǎn)裁剪面(大于此距離的物體不渲染)
  )
  camera.position.set(0, 5, 10) // 相機(jī)初始位置(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)拖動(dòng)、滾輪縮放視角)
  const controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true // 阻尼效果(視角移動(dòng)更平滑)
  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() // 更新相機(jī)投影矩陣
    renderer.setSize(container.clientWidth, container.clientHeight)
  })

  return { scene, camera, renderer, controls }
}

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

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

在 Vue 組件中調(diào)用工具函數(shù),初始化場(chǎng)景并啟動(dòng)渲染循環(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)
// 全局變量(存儲(chǔ)Three.js實(shí)例)
let scene, camera, renderer, controls, model // model為加載后的模型實(shí)例

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)境光(避免模型過(guò)暗)
  addLights()

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

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

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

  // 方向光(模擬太陽(yáng)光,產(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ù)更新場(chǎng)景)
 */
function renderLoop() {
  requestAnimationFrame(renderLoop)
  controls.update() // 更新控制器狀態(tài)(阻尼效果需此步驟)
  TWEEN.update() // 更新視角過(guò)渡動(dòng)畫
  renderer.render(scene, camera) // 渲染場(chǎng)景
}
</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 通過(guò)GLTFLoader加載模型,需處理加載進(jìn)度、成功回調(diào)與錯(cuò)誤捕獲:

/**
 * 加載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 // 保存模型實(shí)例(用于后續(xù)交互)
      
      // 模型縮放與位置調(diào)整(根據(jù)實(shí)際模型大小適配)
      model.scale.set(1, 1, 1) // 縮放比例(默認(rèn)1:1)
      model.position.set(0, 0, 0) // 模型初始位置(場(chǎng)景中心)
      
      // 允許模型投射陰影
      model.traverse((child) => {
        if (child.isMesh) {
          child.castShadow = true
          child.receiveShadow = true // 允許接收其他物體的陰影
        }
      })

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

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

2.3 模型交互:點(diǎn)擊高亮與信息彈窗?

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

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

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

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

  // 監(jiān)聽鼠標(biāo)點(diǎn)擊事件
  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. 更新射線方向(從相機(jī)指向鼠標(biāo)點(diǎn)擊位置)
    raycaster.setFromCamera(mouse, camera)

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

    if (intersects.length > 0) {
      const clickedMesh = intersects[0].object // 獲取點(diǎn)擊的模型網(wǎng)格
      
      // 4. 模型高亮(臨時(shí)修改材質(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)}個(gè)</div>`,
        '模型信息',
        { confirmButtonText: '確定' }
      )
    }
  })
}

/**
 * 統(tǒng)計(jì)模型中的網(wǎng)格數(shù)量
 * @param {THREE.Group} object - 模型實(shí)例(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 模型加載后黑屏 / 不可見?

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

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

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

3.3 模型紋理丟失?

  • 紋理路徑問題:GLB 模型若內(nèi)嵌紋理,直接加載即可;若紋理單獨(dú)存儲(chǔ),需確保紋理文件與模型在同一目錄,且導(dǎo)出時(shí)路徑正確?
  • 跨域問題:開發(fā)環(huán)境通過(guò) 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é)與擴(kuò)展方向?

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

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

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

相關(guān)文章

  • Vue axios獲取token臨時(shí)令牌封裝案例

    Vue axios獲取token臨時(shí)令牌封裝案例

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

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

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

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

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

    vue2老項(xiàng)目中node-sass更換dart-sass的操作方法

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

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

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

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

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

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

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

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

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

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

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

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

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

最新評(píng)論

太和县| 汉沽区| 芦溪县| 锡林郭勒盟| 辉南县| 东方市| 临湘市| 财经| 胶州市| 察隅县| 青冈县| 安乡县| 行唐县| 蚌埠市| 炉霍县| 海晏县| 樟树市| 上虞市| 古田县| 垦利县| 平定县| 武川县| 宝清县| 虞城县| 丹棱县| 富顺县| 恩施市| 莒南县| 宣威市| 长沙县| 濉溪县| 乌审旗| 惠水县| 抚州市| 观塘区| 汾西县| 南木林县| 保山市| 通许县| 柳河县| 溧水县|