Appearance
Three.js相关
Three.js 方法封装
TIP
本项目中 Three.js 的 API 相关的操作方法都统一抽离封装在 utils/renderScene 和 utils/sceneModules 文件夹中。
初始化项目的 Three.js 场景实例
js
<template>
<div id="scene-render"></div>
</template>;
import renderScene from "@/utils/renderScene";
import { onMounted } from "vue";
onMounted(async () => {
const renderScene = new renderScene("#scene-render");
// 初始化场景内容
await renderScene.init();
// 加载进度条
renderScene.onProgress((progressNum: number, totalSize: number) => {
console.log(progressNum);
});
});注 ⚠️:完整的代码可查看 views/sceneEdit/index.vue 文件
全局使用 renderScene API
将 new 实例化出来的 renderScene 内容定义在 Pinia 全局 store 中实现多页面共享
js
<template>
<div id="scene-render"></div>
</template>;
import renderScene from "@/utils/renderScene";
import { useSceneStore } from "@/store/sceneEditStore";
import { onMounted } from "vue";
const store = useSceneStore();
onMounted(async () => {
const renderScene = new renderScene("#scene-render");
store.setSceneApi(renderScene);
// 初始化场景内容
await renderScene.init();
// 加载进度条
renderScene.onProgress((progressNum: number, totalSize: number) => {
console.log(progressNum);
});
});引入 useSceneStore 在其他页面中使用
js
import { useSceneStore } from "@/store/sceneEditStore";
const store = useSceneStore();
// 选择材质
const changeMaterialsNode = (node: MaterialNode) => {
store.setCurrentTransformMaterialUuid(node.uuid);
store.sceneApi?.chooseMaterial(node);
};
// 复制材质
const copyMaterial = (node: MaterialNode) => {
store.sceneApi?.copySceneMaterial(node.uuid);
};模块化开发
注意 ⚠️
Three.js 相关方法的操作本身就是相当繁琐且复杂的,一个功能的实现可能就需要几十行甚至上百行的代码,在企业级项目 3D 开发中如果你的代码设计不太合理,可能会导致一个文件出现几千行的代码情况,这对于项目后期的维护和扩展将会是灾难性的。
为了项目后期的扩展和可维护性,项目根据实际的功能模块将 Three.js 相关方法分为了几个大的模块分别用于实现不同的功能
@/utils/renderScene.ts 是 Three.js 方法内容的主文件,包含了场景、相机、渲染器、控制器、地面、第一人称、模型加载等方法。
对于灯光、动画、粒子、文本、特效、天气、变换控制器、自定义交互事件、webSocket事件、历史操作记录等模块内容我们也进行了单独的封装定义。
如果你在实现项目开发过程中也会有新的模块需求拆分,可借鉴这种模式
js
import AnimationModules from "./sceneModules/animationModules";
import PointerLockModules from './sceneModules/pointerLockModules';
import AnimationModules from './sceneModules/animationModules';
import TransformControlsModules from './sceneModules/transformControlsModules';
import LightModules from './sceneModules/lightModules';
import WeatherEffectsModules from './sceneModules/weatherEffectsModules';
import Css3DRendererModules from './sceneModules/css3DRendererModules';
import SpecialEffectsModules from './sceneModules/createEffectsModules';
import CreateTextModules from './sceneModules/createTextModules';
import CreateImageModules from './sceneModules/createImageModules';
import CreateVideoModules from './sceneModules/createVideoModules';
import CreateShaderModules from './sceneModules/createShaderModules';
class renderScene {
// 动画模块实例
animationModules: AnimationModules;
// 光源模块实例
lightModules: LightModules;
// 变换控制器模块实例
transformControlsModules: TransformControlsModules;
// 天气效果模块实例
weatherEffectsModules: WeatherEffectsModules;
// 历史记录模块实例
historyModules: HistoryModules;
//css3D实例
css3DRendererModules: Css3DRendererModules;
// 特效模块实例
specialEffectsModules: SpecialEffectsModules;
// 文本模块实例
createTextModules: CreateTextModules;
// 图片模块实例
createImageModules: CreateImageModules;
// 视频模块实例
createVideoModules: CreateVideoModules;
// 着色器模块实例
createShaderModules: CreateShaderModules;
constructor(selector: string) {
this.animationModules = new AnimationModules();
this.lightModules = new LightModules();
this.transformControlsModules = new TransformControlsModules();
this.weatherEffectsModules = new WeatherEffectsModules();
this.specialEffectsModules = new SpecialEffectsModules();
this.historyModules = new HistoryModules();
this.css3DRendererModules = new Css3DRendererModules();
this.createTextModules = new CreateTextModules();
this.createImageModules = new CreateImageModules();
this.createVideoModules = new CreateVideoModules();
this.createShaderModules = new CreateShaderModules();
}
}
export default renderScene;在任何一个页面中去使用
js
import { useSceneStore } from '@/store/sceneEditStore';
const store = useSceneStore();
// 变换控制器类型切换
const handleTransformControlsType = (type: TRANSFORM_CONTROLS_TYPE) => {
store.sceneApi?.transformControlsModules.transformControls?.setMode(type);
};
// 键盘 f 键 镜头聚焦模型位置
const keyDownEventListener =(event: KeyboardEvent)=>{
const { currentTransformMaterialUuid , sceneApi } = store
if (event.key.toLowerCase() === 'f') {
event.preventDefault();
const mesh = store.sceneApi?.scene?.getObjectByProperty('uuid',currentTransformMaterialUuid
);
if (mesh) {
sceneApi?.transformControlsModules.focusOnObject(mesh);
}
}
}