前言
大家好!今天我要和大家分享如何在UniApp项目中集成Three.js来实现3D模型的渲染。Three.js是一个强大的JavaScript 3D库,而UniApp则是一个使用Vue.js开发多端应用的框架。本文将详细介绍如何将两者结合使用,让你的UniApp应用具备3D渲染能力。
技术栈
- UniApp
- Vue.js
- Three.js
- WebGL
环境准备
1. 创建UniApp项目
首先,我们需要创建一个新的UniApp项目:
vue create -p dcloudio/uni-preset-vue uniapp-threejs-demo
2. 安装依赖
在项目根目录下运行以下命令安装Three.js:
npm install three
项目实现
1. 创建3D渲染页面
在pages目录下创建一个新的页面three-demo.vue
:
<template>
<view class="container">
<canvas type="webgl" id="webgl"
@touchstart="onTouchStart"
@touchmove="onTouchMove"
@touchend="onTouchEnd"
:style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }">
</canvas>
</view>
</template>
<script>
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
export default {
data() {
return {
canvasWidth: 375,
canvasHeight: 600,
renderer: null,
camera: null,
scene: null,
cube: null,
controls: null,
canvas: null,
animation: null
}
},
onLoad() {
// 获取设备信息以设置画布大小
const info = uni.getSystemInfoSync();
this.canvasWidth = info.windowWidth;
this.canvasHeight = info.windowHeight;
// 初始化3D场景
this.$nextTick(() => {
this.initThree();
});
},
methods: {
initThree() {
// 获取canvas上下文
const query = uni.createSelectorQuery().in(this);
query.select('#webgl')
.node()
.exec((res) => {
const canvas = res[0].node;
this.canvas = canvas;
// 创建渲染器
this.renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true
});
this.renderer.setSize(this.canvasWidth, this.canvasHeight);
this.renderer.setPixelRatio(uni.getSystemInfoSync().pixelRatio);
// 创建场景
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0xf0f0f0);
// 创建相机
this.camera = new THREE.PerspectiveCamera(
75,
this.canvasWidth / this.canvasHeight,
0.1,
1000
);
this.camera.position.z = 5;
// 创建控制器
this.controls = new OrbitControls(this.camera, canvas);
this.controls.enableDamping = true;
// 添加光源
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
this.scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(2, 2, 5);
this.scene.add(directionalLight);
// 创建一个示例立方体
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshPhongMaterial({ color: 0x00ff00 });
this.cube = new THREE.Mesh(geometry, material);
this.scene.add(this.cube);
// 开始动画循环
this.animate();
});
},
animate() {
this.animation = requestAnimationFrame(() => {
this.animate();
});
// 旋转立方体
if (this.cube) {
this.cube.rotation.x += 0.01;
this.cube.rotation.y += 0.01;
}
// 更新控制器
if (this.controls) {
this.controls.update();
}
// 渲染场景
if (this.renderer && this.scene && this.camera) {
this.renderer.render(this.scene, this.camera);
}
},
// 触摸事件处理
onTouchStart(event) {
const touch = event.touches[0];
const rect = this.canvas.getBoundingClientRect();
const x = touch.clientX - rect.left;
const y = touch.clientY - rect.top;
// 在这里处理触摸开始事件
},
onTouchMove(event) {
const touch = event.touches[0];
const rect = this.canvas.getBoundingClientRect();
const x = touch.clientX - rect.left;
const y = touch.clientY - rect.top;
// 在这里处理触摸移动事件
},
onTouchEnd() {
// 在这里处理触摸结束事件
}
},
onUnload() {
// 清理资源
if (this.animation) {
cancelAnimationFrame(this.animation);
}
if (this.renderer) {
this.renderer.dispose();
}
if (this.geometry) {
this.geometry.dispose();
}
if (this.material) {
this.material.dispose();
}
}
}
</script>
<style>
.container {
width: 100%;
height: 100vh;
}
</style>
2. 加载3D模型
下面展示如何加载外部3D模型(以GLTF格式为例):
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
// 在methods中添加加载模型的方法
methods: {
loadModel() {
const loader = new GLTFLoader();
loader.load(
'/static/models/model.gltf', // 模型路径
(gltf) => {
// 加载成功
const model = gltf.scene;
this.scene.add(model);
// 调整模型位置和大小
model.scale.set(0.5, 0.5, 0.5);
model.position.set(0, 0, 0);
// 自动计算模型包围盒,调整相机位置
const box = new THREE.Box3().setFromObject(model);
const center = box.getCenter(new THREE.Vector3());
const size = box.getSize(new THREE.Vector3());
const maxDim = Math.max(size.x, size.y, size.z);
const fov = this.camera.fov * (Math.PI / 180);
let cameraZ = Math.abs(maxDim / 2 / Math.tan(fov / 2));
this.camera.position.z = cameraZ * 1.5;
this.camera.lookAt(center);
// 更新控制器
this.controls.target.copy(center);
this.controls.update();
},
(progress) => {
// 加载进度
console.log('Loading progress:', (progress.loaded / progress.total * 100) + '%');
},
(error) => {
// 加载错误
console.error('Error loading model:', error);
}
);
}
}
性能优化建议
- 模型优化:
- 减少模型面数
- 压缩纹理大小
- 使用LOD(Level of Detail)技术
- 渲染优化:
// 在初始化渲染器时添加配置 this.renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true, powerPreference: "high-performance", precision: "mediump" }); // 开启阴影时要谨慎 this.renderer.shadowMap.enabled = true; this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
- 内存管理:
// 在不需要时释放资源 dispose() { // 释放几何体 this.scene.traverse((object) => { if (object.geometry) { object.geometry.dispose(); } // 释放材质 if (object.material) { if (Array.isArray(object.material)) { object.material.forEach(material => material.dispose()); } else { object.material.dispose(); } } }); // 释放渲染器 if (this.renderer) { this.renderer.dispose(); } // 清除场景 while(this.scene.children.length > 0) { this.scene.remove(this.scene.children[0]); } }
常见问题解决方案
- 跨平台兼容性问题
// 检测WebGL支持 checkWebGL() { try { const canvas = document.createElement('canvas'); return !!(window.WebGLRenderingContext && (canvas.getContext('webgl') || canvas.getContext('experimental-webgl'))); } catch(e) { return false; } }
- 触摸控制优化
// 优化移动端触摸控制 initTouchControls() { this.controls.enableDamping = true; this.controls.dampingFactor = 0.05; this.controls.rotateSpeed = 0.5; this.controls.pinchToZoom = true; this.controls.touchAngularSpeed = 8; this.controls.touchMoveSpeed = 8; }
- 性能监控
// 添加性能监控 initStats() { const Stats = require('three/examples/jsm/libs/stats.module'); this.stats = new Stats(); document.body.appendChild(this.stats.dom); // 在animate循环中添加 if (this.stats) { this.stats.update(); } }
总结
在UniApp中集成Three.js可以让我们的应用具备强大的3D渲染能力。本文介绍了基本的集成步骤、模型加载、性能优化等关键内容。希望这些内容对你有所帮助!
以下是一些可以进一步探索的方向:
- 添加更多交互效果
- 实现更复杂的材质和光照效果
- 优化大型模型的加载和渲染
- 实现更多的触摸控制功能
参考资料
- Three.js官方文档:three.js docs
- UniApp官方文档:uni-app官网
- WebGL官方文档:WebGL Overview - The Khronos Group Inc
如果你在实践过程中遇到任何问题,欢迎在评论区留言讨论!