由于工作原因,近一年没怎么写代码,有朋友问你做过3D模型展示吗,之前都是做以vue为框架做定制业务,这次抽时间试试3d模型展示。
软件功能
使用ThreeJS框架实现加载GLB模型,并添加动画效果,实现3d展示模型。使用GLTFLoader加载模型,并使用OrbitControls实现鼠标拖拽旋转,滚轮缩放,点击模型显示信息等功能。参考代码
软件架构
工程化
使用webpack做工程化,安装webpack webpack-cli webpack-dev-server html-webpack-plugin @babel/core @babel/preset-env babel-loader
编写webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/app.js', // 入口文件路径
output: {
filename: 'bundle.js', // 输出文件名
path: path.resolve(__dirname, 'dist'), // 输出目录
clean: true, // 自动清理输出目录
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.(glb|gltf)$/,
type: 'asset/resource', // 确保可以加载GLB等资源文件
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html', // 模板文件路径
filename: 'index.html', // 输出的 HTML 文件名
}),
],
resolve: {
extensions: ['.js', '.json'], // 解析模块时尝试这些扩展名
alias: {
three: path.resolve(__dirname, 'node_modules/three'), // 如果需要,可以指定Three.js的位置
},
},
devServer: {
static: {
directory: path.join(__dirname, 'assets'), // 静态文件夹
},
open: true, // 启动时打开浏览器
hot: true, // 热更新
},
mode: 'development', // 开发模式
};
主程序代码
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 创建场景、相机、渲染器等...
console.log("THREE version:", THREE.REVISION);
// 创建场景
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 设置场景背景色
scene.background = new THREE.Color(0xcccccc);
// 添加环境灯光
const ambientLight = new THREE.AmbientLight(0x404040, 0.2);
scene.add(ambientLight);
// 场景中增加四个点光源
const pointLight1 = new THREE.PointLight(0xffffff, 0.3, 100);
pointLight1.position.set(5, 5, 5);
scene.add(pointLight1);
const pointLight2 = new THREE.PointLight(0xffffff, 0.3, 100);
pointLight2.position.set(-5, -5, -5);
scene.add(pointLight2);
const pointLight3 = new THREE.PointLight(0xffffff, 0.3, 100);
pointLight3.position.set(-5, 5, 5);
scene.add(pointLight3);
const pointLight4 = new THREE.PointLight(0xffffff, 0.3, 100);
pointLight4.position.set(5, -5, -5);
scene.add(pointLight4);
// 场景中增加四个平行光
const light1 = new THREE.DirectionalLight(0x001dff, 1);
light1.position.set(5, 5, 5).normalize();
scene.add(light1);
const light2 = new THREE.DirectionalLight(0xffe8e8, 1);
light2.position.set(5, -5, 5).normalize();
scene.add(light2);
const light3 = new THREE.DirectionalLight(0xfff8ef, 1);
light3.position.set(-5, 5, 5).normalize();
scene.add(light3);
const light4 = new THREE.DirectionalLight(0xffffff, 1);
light4.position.set(5, 5, -5).normalize();
scene.add(light4);
// 添加坐标轴辅助器
// const axesHelper = new THREE.AxesHelper(2)
// scene.add(axesHelper)
let model; // 用于存储加载的模型
// 加载GLB模型
const loader = new GLTFLoader();
loader.load(
"test2.glb", // 确保路径正确
function (gltf) {
model = gltf.scene; // 将加载的模型赋值给变量
scene.add(gltf.scene);
// 将模型的中心点设置为控制器中心点
const box = new THREE.Box3().setFromObject(model);
if (box !== null) {
const center = new THREE.Vector3();
box.getCenter(center);
// 将 OrbitControls 的 target 设置为模型的中心
controls.target.copy(center);
}
// 如果需要立即生效,可以调用 update 方法
controls.update();
},
undefined,
function (error) {
console.error(error);
}
);
// 创建 OrbitControls 实例,控制场景中的模型
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enableZoom = true;
controls.autoRotate = true
controls.autoRotateSpeed = 3
controls.target = new THREE.Vector3(0, 0, 0)
// 调整相机位置以适应模型
camera.position.z = 2;
// let time = 0;
function animate() {
requestAnimationFrame(animate);
if (model) {
// 更新时间变量
// time += 0.01;
// 让模型旋转
// model.rotation.x += 0.01;
// model.rotation.y += 0.01;
// 让模型上下浮动
// model.position.y = Math.sin(time) * 0.2;
}
renderer.render(scene, camera);
controls.update()
}
animate();
// 监听窗口大小变化
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});