在 vscode 扩展中使用 Three.js 渲染

Rendering with Three.js in vscode-extension

我正在创建一个 vscode 扩展,我想在其中可视化 3D 网格。在我获得 HTML 内容的函数下方:

function getWebviewContent(context: vscode.ExtensionContext, panel: vscode.WebviewPanel) {
      const threejs =  vscode.Uri.file(context.extensionPath+"/js/three.js");  
      const threejsUri = panel.webview.asWebviewUri(threejs);
      const geometryjs =  vscode.Uri.file(context.extensionPath+"/js/geometry.js");  
      const geometryjsUri = panel.webview.asWebviewUri(geometryjs);
      return `
      <!DOCTYPE html>
      <html>
        <head>
          <meta charset="utf-8">
          <title>My first three.js app</title>
          <style>
            body { margin: 0; }
          </style>
        </head>
        <body>
          <script src=${threejsUri}></script>
          <script src=${geometryjsUri}></script>
        </body>
      </html>
      `
}

现在如果我的geometry.js

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );

camera.position.z = 5;

function animate() {
    requestAnimationFrame( animate );

    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;

    renderer.render( scene, camera );
};
animate();

它按预期工作。但是如果我用它做一个class:

import { Scene, PerspectiveCamera, WebGLRenderer, BoxGeometry, MeshBasicMaterial, Mesh, requestAnimationFrame } from 'three';
class Viewer {
    constructor() {
        this.scene = new Scene();
        this.camera = new PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
    
        this.renderer = new WebGLRenderer();
        this.renderer.setSize( window.innerWidth, window.innerHeight );
        document.body.appendChild( renderer.domElement );
    
        this.geometry = new BoxGeometry();
        this.material = new MeshBasicMaterial( { color: 0x00ff00 } );
        this.cube = new Mesh( this.geometry, this.material );
        this.scene.add( cube );
    
        this.camera.position.z = 5;
    }
    
    animate() {
        requestAnimationFrame( this.animate );
    
        this.cube.rotation.x += 0.01;
        this.cube.rotation.y += 0.01;
    
        this.renderer.render( this.scene, this.camera );
    }
}

let viewer = new Viewer();
viewer.animate();

HTML 页面是空的。我在这里做错了什么?我依赖于使用 classes 的外部包进行渲染,因此我需要了解如何在此处正确创建和使用这些 classes。

编辑:我已经把geometry.js改成了typescript,但是还是不行

import { Scene, PerspectiveCamera, WebGLRenderer, BoxGeometry, MeshBasicMaterial, Mesh } from 'three';

export class Viewer {
    private scene!: Scene;
    private camera: PerspectiveCamera;
    private renderer: WebGLRenderer;
    private geometry: BoxGeometry;
    private material: MeshBasicMaterial;
    private cube: Mesh;

    constructor(document: Document) {
        this.scene = new Scene();
        this.camera = new PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
    
        this.renderer = new WebGLRenderer();
        this.renderer.setSize( window.innerWidth, window.innerHeight );
        document.body.appendChild( this.renderer.domElement );

        this.geometry = new BoxGeometry();
        this.material = new MeshBasicMaterial( { color: 0x00ff00 } );
        this.cube = new Mesh( this.geometry, this.material );
        this.scene.add( this.cube );
    
        this.camera.position.z = 5;
        this.animate();
    }
    
    public animate() {
        requestAnimationFrame( this.animate );
    
        this.cube.rotation.x += 0.01;
        this.cube.rotation.y += 0.01;
    
        this.renderer.render( this.scene, this.camera );
    }
}

new Viewer(document);

编辑 2: 奇怪的是,这样的东西可行

function init() {
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

    const renderer = new THREE.WebGLRenderer();
    renderer.setSize( window.innerWidth, window.innerHeight );
    document.body.appendChild( renderer.domElement );

    const geometry = new THREE.BoxGeometry();
    const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
    const cube = new THREE.Mesh( geometry, material );
    scene.add( cube );

    camera.position.z = 5;

    function animate() {
        requestAnimationFrame( animate );
    
        cube.rotation.x += 0.01;
        cube.rotation.y += 0.01;
    
        renderer.render( scene, camera );
    };

    animate();
}

window.addEventListener('message', (event) => {
    init();
});

我不明白这里发生了什么。为什么 animate() 函数必须在 init() 中?在一个class中,这不是通过成员使用this实现的吗?

编辑 3:

class Viewer {
    constructor() {
        this.init();
    }

    init() {
        this.scene = new THREE.Scene();
        this.camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
    
        this.renderer = new THREE.WebGLRenderer();
        this.renderer.setSize( window.innerWidth, window.innerHeight );
        document.body.appendChild( renderer.domElement );
    
        var geometry = new THREE.BoxGeometry();
        var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
        this.cube = new THREE.Mesh( geometry, material );
        this.scene.add( cube );
    
        this.camera.position.z = 5;
    
        this.boundAnimate = this.animate.bind(this);
        this.animate(); // this.boundAnimate();
    }
    
    animate() {
        requestAnimationFrame(this.boundAnimate);
    
        this.cube.rotation.x += 0.01;
        this.cube.rotation.y += 0.01;
    
        this.renderer.render( this.scene, this.camera );
    }
}

let viewer = new Viewer();

requestAnimationFrame 尝试在 window 对象 (this === window) 的上下文中调用提供的函数。

当您以功能方式 运行 您的代码(您的原始和编辑 2 方法)时,一些对象的作用域是这样的,您仍然可以在 animate 函数中访问它们。但是当你使用 class 和 requestAnimationFrame 调用 this.animate 时,它会在第一次工作,但第二次你的 this 变成 window 并且你的程序已经飞离 rails.

为了纠正这个问题,我通常将函数绑定到上下文以防止覆盖。您也可以直接 call 它并提供您要使用的上下文。

使用函数绑定

class MyClass{
  constructor(){
    this.boundAnimate = this.animate.bind(this);
  }

  animate(){
    requestAnimationFrame(this.boundAnimate);
  }
};

使用call

class MyClass{
  animate(){
    window.prototype.requestAnimationFrame.call(this, this.animate);
  }
};