vue3 + TS + three初始化搭建

发布时间 2023-09-08 10:59:28作者: 风紧了

这段时间一直在学习three,vue3 和TS 一直有了解但做项目都没有使用,趁着有时间,想着自己做个小demo检测所学成果。

安装three,@types/three,我使用vue创建项目,TS版本和three 不兼容,然后又下载的最新版本。

然后封装一个类,初始化场景,相机,控制器,渲染器,后期处理,以及加载模型和hdr资源的方法,需要注意是加载GLTFLoader文件,如果需要DRACOLoader解码,需要将node_modules/three/examples/js/libs/draco 文件拷贝到public目录下。

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
// 导入后期效果合成器
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer";

import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass";
import { DotScreenPass } from "three/examples/jsm/postprocessing/DotScreenPass";
import { SMAAPass } from "three/examples/jsm/postprocessing/SMAAPass";
import { UnrealBloomPass } from "three/examples/jsm/postprocessing/UnrealBloomPass";


import gsap from "gsap";
export default class World{
    public scene: THREE.Scene;
    public camera: THREE.PerspectiveCamera;
    public renderer: THREE.WebGLRenderer;
    public domElement: HTMLElement;
    public width: number;
    public height: number;
    public control: OrbitControls;
    public effectComposer: EffectComposer;
    constructor(id:string) {
        this.domElement = document.getElementById(id)!;
     
        this.width = this.domElement.clientWidth;
        this.height = this.domElement.clientHeight;
        this.init();
    }

    public  initScene():void {
        this.scene = new THREE.Scene();
        this.scene.background = new THREE.Color("rgba(0,25,20,0)");
    }
    public initCamera(): void{
      
        this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, 1, 1000);
        this.camera.position.set(20, 10, 20);
        this.scene.add(this.camera);
        this.camera.aspect = this.width / this.height;
        //   更新摄像机的投影矩阵
        this.camera.updateProjectionMatrix();
    }
    public initRenderer():void {
        this.renderer = new THREE.WebGLRenderer(
            {
                 logarithmicDepthBuffer: true,
                  antialias: true,
            }
        )
         this.renderer.setSize(this.width, this.height);
         this.renderer.shadowMap.enabled = true;
         this.renderer.outputEncoding = THREE.sRGBEncoding;
         this.renderer.physicallyCorrectLights = true;
         this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
         this.renderer.toneMappingExposure = 0.75;
         this.renderer.sortObjects = true;
         this.domElement.appendChild(this.renderer.domElement) 
    }
    public addAxis(): void {
      
    let axis = new THREE.AxesHelper(5);
    this.scene.add(axis);
    }
    public initControl() {
    this.control = new OrbitControls(
      this.camera,
      this.renderer.domElement
    );
  }
    public init():void {
        this.initScene();
        this.initCamera();
        this.initRenderer();
        this.addAxis();
        this.initControl();
        //this.initEffect();
        this.update()
    } 

    public  initEffect():void{
    // 合成效果
    this.effectComposer = new EffectComposer(this.renderer);
    this.effectComposer.setSize(window.innerWidth, window.innerHeight);

    // 添加渲染通道
    const renderPass = new RenderPass(this.scene, this.camera);
    this.effectComposer.addPass(renderPass);

    // 点效果
    // const dotScreenPass = new DotScreenPass();
    // dotScreenPass.enabled = false;
    // effectComposer.addPass(dotScreenPass);

    // 抗锯齿
    // const smaaPass = new SMAAPass();
    // this.effectComposer.addPass(smaaPass);

    // // 发光效果
    // this.unrealBloomPass = new UnrealBloomPass();
    // this.effectComposer.addPass(this.unrealBloomPass);
  }
    public update(): void {
         this.control && this.control.update();
         this.renderer.render(this.scene, this.camera);
       //  this.effectComposer.render();
      // 每一帧去执行这个渲染方法
        requestAnimationFrame(this.update.bind(this));
    }
   public  hdrLoader(url:string):any {
    const hdrLoader = new RGBELoader();
    return new Promise((resolve, reject) => {
      hdrLoader.load(url, (hdr) => {
        resolve(hdr);
      });
    });
  }
  public setBg(url:string):void {
    this.hdrLoader(url).then((texture : THREE.Texture):void => {
      texture.mapping = THREE.EquirectangularReflectionMapping;
      texture.anisotropy = 16;
      texture.format = THREE.RGBAFormat;
      this.scene.background = texture;
      this.scene.environment = texture;
    });
  }
  public   gltfLoader(url:string):any {
    const gltfLoader = new GLTFLoader();
    const dracoLoader = new DRACOLoader();
    dracoLoader.setDecoderPath("./draco/gltf/");
    dracoLoader.setDecoderConfig({ type: "js" });
    dracoLoader.preload();
    gltfLoader.setDRACOLoader(dracoLoader);

    return new Promise((resolve, reject) => {
      gltfLoader.load(url, (gltf) => {
        resolve(gltf);
      });
    });
  }
    
}

在vue页面引入,调用就可以了

<template>
  <div id="contain"></div>
</template>

<script lang="ts" setup>
import * as THREE from "three";
import { onMounted } from "vue";
import World from "../three/index";

onMounted(() => {
  let world = new World("contain");
  world.setBg("./textures/1.hdr");
  world.gltfLoader("./model/RobotExpressive.glb").then((gltf: THREE.Group) => {
    world.scene.add(gltf.scene);
  });
});
</script>
 


<style scoped >
#contain {
  width: 1920px;
  height: 1080px;
  overflow: hidden;
}
</style>

页面渲染出来,three 初始化基本就搭建好了