在 vscode-extension 中使用 Three.js 进行渲染

发布于 2025-01-11 04:00:24 字数 6432 浏览 0 评论 0原文

我正在创建一个 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();

它会按预期工作。但是如果我用它创建一个类:

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 页面是空的。我在这里做错了什么?我依赖使用类的外部包进行渲染,因此我需要了解如何在此处正确创建和使用这些类。

编辑:我已将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()内部?在类中,这不就是通过使用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();

I am creating a vscode extension where I want to visualize 3D meshes. Below the function where I get the HTML content:

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>
      `
}

Now if my geometry.js is

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

it works as expected. But if I make a class out of it:

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

the HTML page is empty. What am I doing wrong here? I depend on an external package for rendering which uses classes, so I need to understand how to properly create and use these classes here.

Edit: I have changed geometry.js to typescript, but still no luck

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

Edit 2: Oddly, something like this works

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

I don't understand what's happening here. Why does the animate() function has to be inside init()? In a class, isn't this achieved by using this for the members?

Edit 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();

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

寒冷纷飞旳雪 2025-01-18 04:00:25

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

当您以函数方式(原始方法和 edit 2 方法)运行代码时,某些对象会受到作用域限制,以便您仍然可以在 animate 函数内访问它们。但是,当您使用该类时,requestAnimationFrame 调用this.animate,它第一次会工作,但第二次您的this 变为 < code>window 并且你的程序已经脱离了轨道。

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

使用函数绑定

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

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

使用 call

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

requestAnimationFrame tries to call the provided function under the context of the window object (this === window).

When you run your code in a functional way (your original and edit 2 methods), some of the objects are scoped such that you still have access to them inside the animate function. But when you use the class, and requestAnimationFrame calls this.animate, it will work the first time, but the second time your this becomes window and your program has flown off the rails.

To correct this, I usually bind the function to a context to prevent an override. You can also call it directly and provide the context you want to use.

Using function binding

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

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

Using call

class MyClass{
  animate(){
    window.prototype.requestAnimationFrame.call(this, this.animate);
  }
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文