Vue3+Three.js 3D模型导入,小白也能在页面展示一个3D模型了

大家好,我是鱼樱!!!

关注公众号【鱼樱AI实验室】持续每天分享更多前端和AI辅助前端编码新知识~~

目前,3D模型的格式有成千上万种可供选择,但每一种格式都具有不同的目的、用途以及复杂性。 虽然three.js已经提供了多种导入工具, 但是选择正确的文件格式以及工作流程将可以节省很多时间,以及避免遭受很多挫折。某些格式难以使用,或者实时体验效率低下,或者目前尚未得到完全支持。

1. 支持的文件格式

1.1 推荐格式

  • glTF (.gltf, .glb)
    • 行业标准格式
    • 支持材质、动画、骨骼等
    • 文件体积小,加载快
    • 支持二进制格式(.glb)

1.2 其他常用格式

  • FBX (.fbx)

    • 支持复杂动画和骨骼
    • 文件较大
    • 需要额外加载器
  • OBJ (.obj)

    • 简单的几何体和材质
    • 广泛支持
    • 不支持动画
  • COLLADA (.dae)

    • 支持复杂场景
    • XML格式,文件大
    • 加载较慢

2. 模型加载器

2.1 GLTFLoader (推荐)

javascript 复制代码
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'

const loader = new GLTFLoader()
loader.load(
  'model.gltf',
  (gltf) => {
    scene.add(gltf.scene)
  },
  (progress) => {
    console.log('加载进度:', progress)
  },
  (error) => {
    console.error('加载错误:', error)
  }
)

2.2 加载管理器

javascript 复制代码
const manager = new THREE.LoadingManager()
manager.onProgress = (url, loaded, total) => {
  const progress = (loaded / total) * 100
}
const loader = new GLTFLoader(manager)

3. 性能优化

3.1 模型优化

  • 减少多边形数量
  • 压缩纹理
  • 使用 LOD (Level of Detail)
  • 合并小型网格
  • 删除不可见部分

3.2 加载优化

  • 使用 draco 压缩
  • 预加载重要模型
  • 延迟加载次要模型
  • 使用模型实例化

3.3 渲染优化

javascript 复制代码
// 实例化渲染
const instancedMesh = new THREE.InstancedMesh(
  geometry,
  material,
  instanceCount
)

4. 常见问题处理

4.1 材质问题

  • 检查材质路径
  • 确保纹理加载完成
  • 处理透明度
  • 调整光照设置

4.2 模型方向和缩放

javascript 复制代码
model.rotation.set(x, y, z)
model.scale.set(1, 1, 1)
model.position.set(0, 0, 0)

4.3 动画处理

javascript 复制代码
const mixer = new THREE.AnimationMixer(model)
const action = mixer.clipAction(gltf.animations[0])
action.play()

// 在动画循环中更新
mixer.update(deltaTime)

5. 最佳实践

5.1 资源管理

  • 及时释放不用的资源
  • 使用加载管理器跟踪进度
  • 实现错误处理和重试机制
javascript 复制代码
// 释放资源
model.traverse((node) => {
  if (node.isMesh) {
    node.geometry.dispose()
    node.material.dispose()
  }
})

5.2 交互优化

  • 添加加载进度提示
  • 实现模型交互控制
  • 添加相机控制器
javascript 复制代码
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
const controls = new OrbitControls(camera, renderer.domElement)

5.3 响应式处理

javascript 复制代码
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()
  renderer.setSize(window.innerWidth, window.innerHeight)
})

6. 调试工具

6.1 性能监控

  • Stats.js 监控帧率
  • Memory 监控内存使用
  • Scene Explorer 查看场景结构

6.2 开发工具

  • Three.js Editor
  • Blender 导出插件
  • glTF Validator

7. 注意事项

  1. 文件大小

    • 合理压缩模型和纹理
    • 考虑使用 LOD
    • 分块加载大型模型
  2. 跨域问题

    • 配置正确的 CORS 头
    • 使用相对路径
    • 检查服务器设置
  3. 内存管理

    • 及时释放资源
    • 监控内存使用
    • 避免内存泄漏
  4. 兼容性(结尾附代码了)

    • 检查 WebGL 支持
    • 提供降级方案
    • 测试不同设备

Vue3 + three 导入3d模型案例

参考代码

html 复制代码
<!--
 * @Description: Three.js 3D模型加载组件
-->
<template>
  <div class="three-container" ref="container">
    <!-- 加载提示 -->
    <div v-if="loading" class="loading">
      <div class="loading-text">加载模型中... {{ loadingProgress }}%</div>
    </div>
  </div>
</template>

<script setup>
import * as THREE from 'three'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { onMounted, onBeforeUnmount, ref } from 'vue'

const container = ref(null)
const loading = ref(true)
const loadingProgress = ref(0)
let scene, camera, renderer, controls
let animationFrameId
let model = null

const initScene = () => {
  // 创建场景
  scene = new THREE.Scene()
  scene.background = new THREE.Color(0x333333)

  // 创建相机
  camera = new THREE.PerspectiveCamera(
    75,
    container.value.clientWidth / container.value.clientHeight,
    0.1,
    1000
  )
  camera.position.set(0, 5, 10)

  // 创建渲染器
  renderer = new THREE.WebGLRenderer({ antialias: true })
  renderer.setSize(container.value.clientWidth, container.value.clientHeight)
  renderer.setPixelRatio(window.devicePixelRatio)
  renderer.shadowMap.enabled = true
  container.value.appendChild(renderer.domElement)

  // 添加轨道控制器
  controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true
  controls.dampingFactor = 0.05

  // 添加灯光
  const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)
  scene.add(ambientLight)

  const directionalLight = new THREE.DirectionalLight(0xffffff, 1)
  directionalLight.position.set(5, 5, 5)
  directionalLight.castShadow = true
  scene.add(directionalLight)

  // 添加地面
  const groundGeometry = new THREE.PlaneGeometry(20, 20)
  const groundMaterial = new THREE.MeshStandardMaterial({ 
    color: 0x999999,
    roughness: 0.8,
  })
  const ground = new THREE.Mesh(groundGeometry, groundMaterial)
  ground.rotation.x = -Math.PI / 2
  ground.receiveShadow = true
  scene.add(ground)

  // 加载3D模型
  const loader = new GLTFLoader()
  
  // 添加加载进度处理
  const loadingManager = new THREE.LoadingManager()
  loadingManager.onProgress = (url, itemsLoaded, itemsTotal) => {
    loadingProgress.value = Math.round((itemsLoaded / itemsTotal) * 100)
  }
  
  // 使用加载管理器创建加载器
  const gltfLoader = new GLTFLoader(loadingManager)
  
  // 加载模型
  gltfLoader.load(
    // 模型路径 - 使用 Three.js 官方示例模型
    'https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf',
    // 加载成功回调
    (gltf) => {
      model = gltf.scene
      model.position.set(0, 2, 0)
      model.traverse((node) => {
        if (node.isMesh) {
          node.castShadow = true
        }
      })
      scene.add(model)
      loading.value = false
    },
    // 加载进度回调
    (xhr) => {
      console.log(`模型加载进度: ${(xhr.loaded / xhr.total) * 100}%`)
    },
    // 加载错误回调
    (error) => {
      console.error('模型加载失败:', error)
      loading.value = false
    }
  )

  // 动画循环
  const animate = () => {
    animationFrameId = requestAnimationFrame(animate)
    controls.update()
    
    if (model) {
      model.rotation.y += 0.005
    }
    
    renderer.render(scene, camera)
  }
  animate()
}

// 处理窗口大小变化
const handleResize = () => {
  if (!renderer || !camera) return
  
  const width = container.value.clientWidth
  const height = container.value.clientHeight
  
  camera.aspect = width / height
  camera.updateProjectionMatrix()
  renderer.setSize(width, height)
}

onMounted(() => {
  initScene()
  window.addEventListener('resize', handleResize)
})

onBeforeUnmount(() => {
  window.removeEventListener('resize', handleResize)
  cancelAnimationFrame(animationFrameId)
  controls?.dispose()
  
  if (model) {
    model.traverse((node) => {
      if (node.isMesh) {
        node.geometry.dispose()
        node.material.dispose()
      }
    })
  }
  
  scene?.traverse((object) => {
    if (object instanceof THREE.Mesh) {
      object.geometry.dispose()
      object.material.dispose()
    }
  })
  renderer?.dispose()
})
</script>

<style scoped>
.three-container {
  width: 100%;
  height: 100vh;
  position: relative;
}

.loading {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: white;
  text-align: center;
}

.loading-text {
  font-size: 1.2em;
  margin-bottom: 1em;
}
</style> 

WebGL兼容性检查工具

封装成一个工具类了

js 复制代码
/**
 * WebGL兼容性检查工具
 * 检查浏览器是否支持WebGL 2
 */
import WebGL from 'three/addons/capabilities/WebGL.js'

/**
 * 检查WebGL2支持状态
 * @returns {Object} 包含支持状态和错误信息的对象
 */
export function checkWebGL2Support() {
  if (WebGL.isWebGL2Available()) {
    return {
      supported: true
    }
  } else {
    return {
      supported: false,
      reason: WebGL.getWebGL2ErrorMessage()
    }
  }
} 

至此,一个3d模型便加载进来了~是不是也不是很复杂呢代码~~~

相关推荐
浩~~5 分钟前
HTML5 中实现盒子水平垂直居中的方法
java·服务器·前端
互联网搬砖老肖10 分钟前
Web 架构之故障自愈方案
前端·架构·github
天上掉下来个程小白15 分钟前
添加购物车-02.代码开发
java·服务器·前端·后端·spring·微信小程序·苍穹外卖
网络空间小黑44 分钟前
WEB渗透测试----信息收集
服务器·前端·网络·安全·web安全·网络安全
水银嘻嘻1 小时前
web 自动化之 Unittest 应用:报告&装饰器&断言
前端·python·自动化
巴巴_羊2 小时前
AJAX原理
前端·javascript·ajax
良木林2 小时前
HTML难点小记:一些简单标签的使用逻辑和实用化
前端·html
一个游离的指针3 小时前
ES6基础特性
前端·javascript·es6
layman05283 小时前
ES6/ES11知识点
前端·ecmascript·es6
2501_915373887 小时前
Vue 3零基础入门:从环境搭建到第一个组件
前端·javascript·vue.js