vue3使用threejs渲染报错

threejs yekong

vue3 vite项目开发中,渲染threejs时,会报下面的错误

TypeError: 'get' on proxy: property 'modelViewMatrix' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected '#<Matrix4>' but got '#<Matrix4>')

原因

考虑到Three.js对象的复杂性,以及Vue可能在处理这些对象时出现问题,我建议将与Three.js相关的所有对象和变量移出Vue数据对象。这样,它们就不会被Vue的响应式系统追踪。

修改前的代码

<template>
  <div className="canvasGLTFBody">
    <div ref='canvasGLTF' className="canvasGLTF" id="canvasGLTF"></div>
    <div id="LoadingInfo" className="LoadingInfo"></div>
  </div>
</template>

<script>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import img from './assets/lun.png';

export default {
  name: 'GLTFCanvas',
  data() {
    return {
      renderer: null,
      camera: null,
      width: 0,
      height: 0,
      scene: null,
      mesh: null
    }
  },
  mounted() {
    const viewElem = document.body;
    this.$nextTick(this.init);
    const resizeObserver = new ResizeObserver(this.handleResize);
    resizeObserver.observe(viewElem);
  },
  methods: {
    async init() {
      this.width = this.$refs.canvasGLTF.offsetWidth;
      this.height = this.$refs.canvasGLTF.offsetHeight;
      
      // Create scene, camera, renderer, controls
      this.createScene();
      this.createCamera();
      this.createRenderer();
      this.createControls();

      // Load texture and start animation
      await this.loadTexture();
      this.animate();

      this.$refs.canvasGLTF.appendChild(this.renderer.domElement);
    },
    createScene() {
      this.scene = new THREE.Scene();
    },
    createCamera() {
      this.camera = new THREE.PerspectiveCamera(75, this.width / this.height, 0.1, 1000);
      this.camera.position.set(-0.003236905604211045, -0.5120776906761103, 0.2721449110235568);
      this.scene.add(this.camera);
    },
    createRenderer() {
      this.renderer = new THREE.WebGLRenderer({
        logarithmicDepthBuffer: true,
        antialias: true,
      });

      this.renderer.setSize(this.width, this.height);
      this.renderer.shadowMap.enabled = true;
      this.renderer.physicallyCorrectLights = true;
      this.renderer.setClearColor(0xcccccc, 1);
      this.renderer.autoClear = false;
      this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
      this.renderer.outputEncoding = THREE.sRGBEncoding;
      this.renderer.sortObjects = true;
      this.renderer.logarithmicDepthBuffer = true;
      this.renderer.setPixelRatio(window.devicePixelRatio);
    },
    createControls() {
      const controls = new OrbitControls(this.camera, this.renderer.domElement);
      controls.enableDamping = true;
    },
    async loadTexture() {
      const loader = new THREE.TextureLoader();
      const texture = await loader.loadAsync(img);
      const material = new THREE.MeshBasicMaterial({
        map: texture,
        transparent: true,
        depthWrite: false,
        opacity: 0.5
      });

      const geometry = new THREE.PlaneGeometry(1, 1);
      this.mesh = new THREE.Mesh(geometry, material);
      this.scene.add(this.mesh);
    },
    animate() {
      requestAnimationFrame(this.animate);

      if (this.mesh) {
        this.mesh.rotation.z += 0.005;
      }

      this.renderer.render(this.scene, this.camera);
    },
    handleResize() {
      this.width = this.$refs.canvasGLTF.offsetWidth;
      this.height = this.$refs.canvasGLTF.offsetHeight;

      this.camera.aspect = this.width / this.height;
      this.camera.updateProjectionMatrix();
      this.renderer.setSize(this.width, this.height);
      this.renderer.setPixelRatio(window.devicePixelRatio);
    }
  }
}
</script>

修改后的代码

<template>
  <div className="canvasGLTFBody">
    <div ref='canvasGLTF' className="canvasGLTF" id="canvasGLTF"></div>
    <div id="LoadingInfo" className="LoadingInfo"></div>
  </div>
</template>

<script>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import img from './assets/lun.png';

let renderer = null;
let camera = null;
let width = 0;
let height = 0;
let scene = null;
let mesh = null;

export default {
  name: 'GLTFCanvas',
  mounted() {
    const viewElem = document.body;
    this.$nextTick(this.init);
    const resizeObserver = new ResizeObserver(this.handleResize);
    resizeObserver.observe(viewElem);
  },
  methods: {
    async init() {
      const canvasGLTF = this.$refs.canvasGLTF;
      width = canvasGLTF.offsetWidth;
      height = canvasGLTF.offsetHeight;
      
      // Create scene, camera, renderer, controls
      this.createScene();
      this.createCamera();
      this.createRenderer();
      this.createControls();

      // Load texture and start animation
      await this.loadTexture();
      this.animate();

      canvasGLTF.appendChild(renderer.domElement);
    },
    createScene() {
      scene = new THREE.Scene();
    },
    createCamera() {
      camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
      camera.position.set(-0.003236905604211045, -0.5120776906761103, 0.2721449110235568);
      scene.add(camera);
    },
    createRenderer() {
      renderer = new THREE.WebGLRenderer({
        logarithmicDepthBuffer: true,
        antialias: true,
      });

      renderer.setSize(width, height);
      renderer.shadowMap.enabled = true;
      renderer.physicallyCorrectLights = true;
      renderer.setClearColor(0xcccccc, 1);
      renderer.autoClear = false;
      renderer.toneMapping = THREE.ACESFilmicToneMapping;
      renderer.outputEncoding = THREE.sRGBEncoding;
      renderer.sortObjects = true;
      renderer.logarithmicDepthBuffer = true;
      renderer.setPixelRatio(window.devicePixelRatio);
    },
    createControls() {
      const controls = new OrbitControls(camera, renderer.domElement);
      controls.enableDamping = true;
    },
    async loadTexture() {
      const loader = new THREE.TextureLoader();
      const texture = await loader.loadAsync(img);
      const material = new THREE.MeshBasicMaterial({
        map: texture,
        transparent: true,
        depthWrite: false,
        opacity: 0.5
      });

      const geometry = new THREE.PlaneGeometry(1, 1);
      mesh = new THREE.Mesh(geometry, material);
      scene.add(mesh);
    },
    animate() {
      requestAnimationFrame(this.animate);

      if (mesh) {
        mesh.rotation.z += 0.005;
      }

      renderer.render(scene, camera);
    },
    handleResize() {
      const canvasGLTF = this.$refs.canvasGLTF;
      width = canvasGLTF.offsetWidth;
      height = canvasGLTF.offsetHeight;

      camera.aspect = width / height;
      camera.updateProjectionMatrix();
      renderer.setSize(width, height);
      renderer.setPixelRatio(window.devicePixelRatio);
    }
  }
}
</script>

以上的代码中,所有与Three.js相关的变量现在都被定义为脚本的全局变量,而不是Vue组件的数据。这样,它们就不会被Vue的响应式系统跟踪,并且可以避免可能出现的问题。

喜欢