Skip to content

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);
    }
  }
}