Three.js的2d和3d渲染器的使用

66 阅读2分钟

a1007662a5a5c09c58ca1d9676355391.png

vue组件部分

<template>
  <div class="home">
    <div id="container"></div>
    <div id="loading">
      <div>加载中...</div>
      <div id="progress"><div id="progress-bar"></div></div>
      <div id="progress-text">0%</div>
    </div>
  </div>
</template>

js部分

<script>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";

import {
  CSS2DRenderer,
  CSS2DObject,
} from "three/examples/jsm/renderers/CSS2DRenderer";
import {
  CSS3DRenderer,
  CSS3DObject,
} from "three/examples/jsm/renderers/CSS3DRenderer";

export default {
  name: "HomeView",
  components: {},
  data() {
    return {
      scene: null,
      camera: null,
      renderer: null,
      controls: null,
      container: null,
      css2DRenderer: null,
      css3DRenderer: null,
    };
  },
  mounted() {
    this.$nextTick(() => {
      this.init();
    });
  },
  methods: {
    init() {
      // const clock = new THREE.Clock();

      // 创建场景
      const scene = new THREE.Scene();
      scene.background = new THREE.Color(0x000000);
      this.scene = scene;

      // 创建相机
      const camera = new THREE.PerspectiveCamera(
        45,
        window.innerWidth / window.innerHeight,
        0.1,
        10000
      );
      camera.position.set(0, 300, 1000);
      this.camera = camera;

      const container = document.getElementById("container");
      const renderer = new THREE.WebGLRenderer({
        antialias: true,
        logarithmicDepthBuffer: true,
      });
      renderer.setPixelRatio(window.devicePixelRatio);
      renderer.setSize(window.innerWidth, window.innerHeight);
      // renderer.shadowMap.enabled = true;
      // renderer.shadowMap.type = THREE.VSMShadowMap;
      // renderer.outputEncoding = THREE.sRGBEncoding;
      // renderer.toneMapping = THREE.ACESFilmicToneMapping;
      container.appendChild(renderer.domElement);
      this.renderer = renderer;
      this.container = container;

      // 坐标轴
      let axes = new THREE.AxesHelper(500);
      scene.add(axes);

      // 创建轨道控制器
      const controls = new OrbitControls(camera, renderer.domElement);
      // 设置控制器阻尼,让控制器更有真实效果
      controls.enableDamping = true;
      controls.dampingFactor = 0.05;
      controls.minPolarAngle = Math.PI / 4; // 垂直旋转最小角度(54度)
      controls.maxPolarAngle = Math.PI / 2.2; // 垂直旋转最大角度(126度)
      this.controls = controls;

      // 加载天空盒
      this.loadSkyBox();
      // 加载模型
      this.loadModel();
      // 渲染循环
      this.animate();

      // 初始化2d渲染器
      this.initCSS2DRenderer();
      // 初始化3d渲染器
      this.initCSS3DRenderer();

      // 缩放时重置页面
      window.addEventListener("resize", this.onWindowResize);
    },
    onWindowResize() {
      this.camera.aspect = window.innerWidth / window.innerHeight;
      this.camera.updateProjectionMatrix();
      this.renderer.setSize(window.innerWidth, window.innerHeight);
      // 设置渲染器的像素比
      this.renderer.setPixelRatio(window.devicePixelRatio);
    },
    // 渲染循环
    animate() {
      this.controls.update();
      this.renderer.render(this.scene, this.camera);
      requestAnimationFrame(this.animate);
      this.css2DRenderer && this.css2DRenderer.render(this.scene, this.camera);
      this.css3DRenderer && this.css3DRenderer.render(this.scene, this.camera);
    },
    // 加载天空盒
    loadSkyBox() {
      const hdrLoader = new RGBELoader();
      hdrLoader.load("./textures/023.hdr", (texture) => {
        // texture.mapping = THREE.EquirectangularReflectionMapping;
        // texture.anisotropy = 16;
        // texture.format = THREE.RGBAFormat;
        this.scene.background = texture;
        this.scene.environment = texture;
        this.scene.environment.mapping = THREE.EquirectangularReflectionMapping;
      });
    },
    // 加载gltf模型
    loadModel() {
      const loadingManager = new THREE.LoadingManager( () => {
            // 加载完成
            console.log('所有资源加载完成');
            document.getElementById('loading').style.display = 'none';

            for (let i = 0; i < 10; i++) {
              this.add2DElementToScene(new THREE.Vector3((i - 5) * 100, 100, -300));
              this.add3DElementToScene(new THREE.Vector3((i - 5) * 100, 100, 300));
            }

            // this.add2DElementToScene();
            // this.add3DElementToScene();

        }, (item, loaded, total) => {
              // 加载进度更新
              const progress = (loaded / total) * 100;
              document.getElementById('progress-bar').style.width = `${progress}%`;
              document.getElementById('progress-text').textContent = `${Math.round(progress)}%`;
              console.log(`加载进度: ${Math.round(progress)}%`);
        }, (url) => {
            // 加载错误
            console.error('资源加载失败:', url);
        }
      );

      let loader = new GLTFLoader(loadingManager);
      loader.load("./model/nsk.glb", (gltf) => {
        // gltf.scene.position.set(0, 1.25, 0);

        // 把模型旋转45度
        // model.rotation.y = -Math.PI / 4;
        // gltf.scene.rotation.x = -Math.PI / 2;
        // model.rotation.z = Math.PI / 6;

        this.scene.add(gltf.scene);
      
        console.log('gltf----', );
      });
    },
    // 初始化CSS2DRenderer
    initCSS2DRenderer() {
      // 创建 CSS2DRenderer
      const css2DRenderer = new CSS2DRenderer();
      css2DRenderer.setSize(window.innerWidth, window.innerHeight);
      css2DRenderer.domElement.style.position = "absolute";
      css2DRenderer.domElement.style.top = 0;
      css2DRenderer.domElement.style.pointerEvents = "none"; // 让渲染器不阻挡鼠标事件

      css2DRenderer.render(this.scene, this.camera);

      document.body.appendChild(css2DRenderer.domElement);
      this.css2DRenderer = css2DRenderer;
    },
    // 添加元素到场景中
    add2DElementToScene(position = new THREE.Vector3(0, 100, -300)) {
     // 创建dom元素
      const div = this.createDomElement();
      // 创建 CSS2DObject
      let labelObject = new CSS2DObject(div);
      // labelObject.position.set(0, 200, -300);
      labelObject.position.copy(position)
      this.scene.add(labelObject);
      // 设置标签所在的层,这里设置为0层
      labelObject.layers.set(0);
    },
    

    // CSS3DRenderer
    initCSS3DRenderer() {
      let css3DRenderer = new CSS3DRenderer();
      css3DRenderer.setSize(window.innerWidth, window.innerHeight);
      css3DRenderer.render(this.scene, this.camera);
      css3DRenderer.domElement.style.position = "absolute";
      css3DRenderer.domElement.style.top = 0;
      css3DRenderer.domElement.style.pointerEvents = "none";
      document.body.appendChild(css3DRenderer.domElement);
      this.css3DRenderer = css3DRenderer;
    },

     // 添加元素到场景中
    add3DElementToScene(position = new THREE.Vector3(0, 100, 300)) {
     // 创建dom元素
      const div = this.createDomElement();
      let css3DObject = new CSS3DObject(div);
      css3DObject.scale.set(0.5, 0.5, 0.5);
      // css3DObject.position.set(0, 200, 300);
      css3DObject.position.copy(position)
      this.scene.add(css3DObject);
      css3DObject.layers.set(0);
    },

    // 创建dom元素
    createDomElement() {
      // 创建 HTML 元素
      const div = document.createElement("div");
      div.className = "label";

      // div.style.width = '70px';
      // div.style.height = '200px';
      div.style.backgroundColor = "rgba(0,0,0,0.5)";
      div.style.border = "2px solid #000";
      div.style.borderRadius = "10px";
      div.style.color = "#fff";

      // 换行
      div.style.whiteSpace = "pre-line";
      div.style.wordWrap = "break-word";
      // div.style.textAlign = 'center';

      const img = document.createElement("img");
      img.src = `./img/map/1.png`;
      img.style.width = "61px";
      img.style.height = "136px";
      div.appendChild(img);

      // div.innerHTML += '设备名称2';

      return div;
    },

    // 销毁
    destroy() {
      this.controls.dispose();
      this.renderer.dispose();
      this.css2DRenderer.dispose();
      this.css3DRenderer.dispose();
      window.removeEventListener("resize", this.onWindowResize);
      this.container.removeChild(this.renderer.domElement);
      this.container.removeChild(this.css2DRenderer.domElement);
      this.container.removeChild(this.css3DRenderer.domElement);
    },
  },
};
</script>

css部分

<style>
#container {
  width: 100vw;
  height: 100vh;
}
#loading {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: white;
  font-family: Arial, sans-serif;
  background: rgba(0, 0, 0, 0.7);
  padding: 20px;
  border-radius: 10px;
}
#progress {
  width: 200px;
  height: 20px;
  background: #333;
  margin-top: 10px;
  border-radius: 10px;
  overflow: hidden;
}
#progress-bar {
  height: 100%;
  width: 0%;
  background: #4caf50;
  transition: width 0.3s;
}
</style>