大家好,我是鱼樱!!!
关注公众号【鱼樱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. 注意事项
-
文件大小
- 合理压缩模型和纹理
- 考虑使用 LOD
- 分块加载大型模型
-
跨域问题
- 配置正确的 CORS 头
- 使用相对路径
- 检查服务器设置
-
内存管理
- 及时释放资源
- 监控内存使用
- 避免内存泄漏
-
兼容性(结尾附代码了)
- 检查 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模型便加载进来了~是不是也不是很复杂呢代码~~~