Vue JS - 如何让 WebGL 通过 Vue 呈现而不消失?

Vue JS - How do I get WebGL to render via Vue without disappearing?

问题

我正在尝试使用 Vue JS 来显示一些非常简单的 WebGL。我在 canvas 元素内使用 Vue 方法调用来启动呈现 WebGL 的函数。一切正常,除了渲染图像在屏幕上一闪而过,然后消失。该图像是一个简单的三角形,渲染后应该就位于屏幕上。我找不到解决办法?

因为我是新手,所以我可能犯了一些明显的错误,这就是问题所在,我猜可能是通过 Vue 将简单、基本的 WebGl 合并到 HTML 中的一些标准化方法, 但我一直找不到它?

有哪些可能的解决方案?不幸的是,我应该只使用基本的 WebGL(不是 Three.js 或任何东西),而是通过基本的 Vue JS。

我会在下面详细说明。

背景


我对 Vue JS 很陌生,对 WebGL 也很陌生。我正在关注 YouTube 上的 this 系列教程,这是我对 WebGL 的第一次介绍,其中链接的特定视频包含我针对此问题所遵循的代码。

与我制作的视频唯一可能有显着不同的地方当然是使用 Vue JS 作为 UI 框架,而不是编写普通的 HTML 和 Javascript 文件,然后手动使用node js搭建服务器。

在教程中,他通过 html 中的一个标签链接了一个单独的包含 WebGL 代码的 .js 文件。在此脚本中,Javascript 仅使用查询选择器获取 canvas 及其上下文,并将其指定为输出渲染的位置。

在 Vue JS 内部,我不确定如何告诉 Vue 只是 运行 一个不是方法或计算等的外部脚本,所以我将 WebGL 代码放在一个方法中,然后输出通过调用 vue 模板中 canvas 标签内的方法将其显示到屏幕上:

<template>
<div id="app">

<canvas> {{runWebGL()}} </canvas>

</div>
</template>
export default{

methods: {

 runWebGL(){
   . 
   .
   //All the WebGLCode from the video - which for brevity I've pasted right at the bottom.
   .
   .
 }

}

}

现在这真的有效了,小红色三角形也被渲染了——除了它一渲染到屏幕上,它就消失了。


我的想法为什么

这是我认为会发生的事情:当 vue 组件首次加载时,它 运行 是我放在那里的 运行WebGL() 方法,创建然后显示 lil 红色三角形canvas。但是,当该方法完成时,它不再是 运行ning,因此其中的所有内容都被销毁并且没有 WebGL 可输出。

如果我在 运行WebGL() 的末尾放置一个警报功能以防止该功能自动结束,您可以看到三角形一直输出到屏幕,直到用户关闭警报。

然后我假设的是,正如那个人在视频中所做的那样,您通过标签将一些脚本直接附加到 html,无论是本地脚本还是外部引用的脚本,一旦它们'重新完成他们不关闭。所以其中的数据等仍然可用,即使它们已经被解析到最后?

那我该如何在 Vue JS 中解决这个问题呢?我是否应该实现某种方式来保持 Vue JS 方法 'running' 即使其中的代码已完成执行?或者是否有其他更标准化的方法来网格化 WebGL 和 Vue JS?

我知道有一些 WebGL 框架,并且有一些试图混合 Vue 和 WebGL,但不幸的是我不应该使用它们 - 我必须得到它作为尽可能接近原生 VueJS 和纯 WebGL。

非常感谢您的帮助!


Javascript 和 WebGL 代码,与我放在 运行WebGL() 中的视频中的代码相同:

const canvas = document.querySelector(`canvas`); 
const gl = canvas.getContext(`webgl`); 
if (!gl) { throw new Error(`webGL not supported`); } 
/* ***OUTLINE**** */ // load vertexData into Buffer // create vertex SharedArrayBuffer // create program // attach shaders to program // enable vertex attributes // draw 
console.log(`Starting...`); 
// create a triangle, x,y,z. WebGL uses OpenGL uses Float32 Arrays. // Typically don't use Float32 arrays in JS as per: // 
// changing these points will alter where the corners of your triangle are. max is 1, min is -1 
const vertexData = new Float32Array([ 0, 1, 0, 1, -1, 0, -1, -1, 0, ]); const buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 
// bind this buffer as the current buffer; as an array of vertices 
gl.bufferData(gl.ARRAY_BUFFER, vertexData, gl.STATIC_DRAW); 
// STATIC_DRAW if not rewriting (optomise) common alternative is DYNAMIC_DRAW // Creates the vertex-shader; a mini program that runs on the GPU // this uses the GL shading language (not JS), hence the typed return for the function (ie 'void') // gl_position is the output of the shader; an array of 4 components from the buffer // vec3 indicates the three parts of x,y,z; this is converted to a vec4 by adding "1" to 'position' 
const vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, ` attribute vec3 position; void main() { gl_Position = vec4(position, 1); } `); gl.compileShader(vertexShader); 
// create a fragment-shader // a fragment shader shades the pixels between vertices // the vec4 is in format RGB-A // try changing some of these numbers and see what happens // eg vec4(0,1,0,.5) gives a transparent green triangle 
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, ` void main(){ gl_FragColor = vec4(1, 0, 0, 1); } `); gl.compileShader(fragmentShader); 
// create a program that links the two shaders (vertex and fragment) // to the actual vertices 
const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); 
// load the attributes. // Currently we just have a single attribute `position` created with the vertex shader // Attributes are disabled by default - so we have to enable it 
const positionLocation = gl.getAttribLocation(program, `position`); gl.enableVertexAttribArray(positionLocation); 
// how webgl should retrieve data from the currently bound buffer // what is being retrieved? positionLocation // how many elements to read at a time? 3 (x,y,z) // what type are the elements? floating point numbers (from Float32Array) // remaining arguments are not used ('normalise' is an advanced optomisation, not needed here) 
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0); 
// create an excutable program on the graphics card // drawArrays, 2nd argument is which vertex to start at, how many to draw? (in this case there are 3 vertices all with 3 attributes (x,y,z)) 
gl.useProgram(program); 


gl.drawArrays(gl.TRIANGLES, 0, 3);const canvas = document.querySelector(`canvas`); 
const gl = canvas.getContext(`webgl`); 
if (!gl) { throw new Error(`webGL not supported`); } 
/* ***OUTLINE**** */ // load vertexData into Buffer // create vertex SharedArrayBuffer // create program // attach shaders to program // enable vertex attributes // draw 
console.log(`Starting...`); 
// create a triangle, x,y,z. WebGL uses OpenGL uses Float32 Arrays. // Typically don't use Float32 arrays in JS as per: // 
// changing these points will alter where the corners of your triangle are. max is 1, min is -1 
const vertexData = new Float32Array([ 0, 1, 0, 1, -1, 0, -1, -1, 0, ]); const buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 
// bind this buffer as the current buffer; as an array of vertices 
gl.bufferData(gl.ARRAY_BUFFER, vertexData, gl.STATIC_DRAW); 
// STATIC_DRAW if not rewriting (optomise) common alternative is DYNAMIC_DRAW // Creates the vertex-shader; a mini program that runs on the GPU // this uses the GL shading language (not JS), hence the typed return for the function (ie 'void') // gl_position is the output of the shader; an array of 4 components from the buffer // vec3 indicates the three parts of x,y,z; this is converted to a vec4 by adding "1" to 'position' 
const vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, ` attribute vec3 position; void main() { gl_Position = vec4(position, 1); } `); gl.compileShader(vertexShader); 
// create a fragment-shader // a fragment shader shades the pixels between vertices // the vec4 is in format RGB-A // try changing some of these numbers and see what happens // eg vec4(0,1,0,.5) gives a transparent green triangle 
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, ` void main(){ gl_FragColor = vec4(1, 0, 0, 1); } `); gl.compileShader(fragmentShader); 
// create a program that links the two shaders (vertex and fragment) // to the actual vertices 
const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); 
// load the attributes. // Currently we just have a single attribute `position` created with the vertex shader // Attributes are disabled by default - so we have to enable it 
const positionLocation = gl.getAttribLocation(program, `position`); gl.enableVertexAttribArray(positionLocation); 
// how webgl should retrieve data from the currently bound buffer // what is being retrieved? positionLocation // how many elements to read at a time? 3 (x,y,z) // what type are the elements? floating point numbers (from Float32Array) // remaining arguments are not used ('normalise' is an advanced optomisation, not needed here) 
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0); 
// create an excutable program on the graphics card // drawArrays, 2nd argument is which vertex to start at, how many to draw? (in this case there are 3 vertices all with 3 attributes (x,y,z)) 
gl.useProgram(program); 


gl.drawArrays(gl.TRIANGLES, 0, 3);

您需要进一步了解 the life cycle of the vue-instance. For example try the mounted hook

<template>
<div id="app">
  <canvas></canvas>
</div>
</template>

<script>
export default{
  ...
  mounted () {
    this.runWebGL()
  },
  methods: {
    runWebGL(){
      ...
    }
  }
}
</script>

[https://jsfiddle.net/stdob__/fyojs1vn/]

stdon 的回答对于让 WebGL 自动启动并在首次加载 Vue 应用程序时保持在那里非常有效。谢谢!

此外,我最终想要做的是让 WebGL 不是 自动启动,而是在单击按钮时打开。让它自动启动应该是我开发的更简单的起点。

然后我自己试验了一下,只有一个按钮在单击时激活“运行WebGL”方法 - 因此不需要将 {{运行WebGL()}} 放入canvas 标记以自动调用该方法 - 它成功了!

但是 stdon 关于使用 mount 的回答是我最初提出的问题的正确答案,值得点赞。谢谢!


我的新设置:

请注意,stdon 的答案中的代码用于在加载 Vue 应用程序时自动将 WebGL 设置为 运行。下面我的是我使用的按钮设置。

HTML

<template>
<div id="app">

<button @click="runWebGL"> Start WebGL </button>
<canvas></canvas>

</div>
</template>

Javascript

<script>

export default {
      .
      .
   methods: {

   runWebGL(){

     //The Javascript & WebGL code from my original question
    
     }
   }
    .
    .

}

</script>