Question
I’m trying to use Vue JS to display some very simple WebGL. I use a Vue method call inside my canvas element to start the function that renders the WebGL. It all works fine, except that the rendered image flashes on the screen in a flash, then disappears. The image is a simple triangle that’s supposed to just sit onthe screen once rendered. I can’t find a solution to it?
As I’m very novice, I might have made some obvious mistakes that are there the problem, there may be I’m guessing some standardised way to incorporate simple, basic WebGl into the HTML via Vue, but I’ve just not been able to find it?
What are the possible solutions? Unfortunately, I’m supposed to use just basic WebGL (not Three.js or anything), but via basic Vue JS.
I’ll explain the details, below.
Background
I’m fairly new to Vue JS, and very new to WebGL. I’m following this series of tutorials on YouTube for my first intro to WebGL, with the particular video linked there containing the code I followed for this problem.
The only, perhaps significantly, different thing to the vid I did was of course to use Vue JS as a UI framework, rather than writing plain HTML and Javascript files, then manually using node js to set up a server.
In the tutorial, he links via a tag in the html a separate .js file containing the WebGL code. In this script, Javascript just uses query selector to grab the canvas and its context, and assign it as the place to output the render.
Inside Vue JS, I wasn’t sure how to tell Vue to just run an external script that wasn’t a method or computed etc, so I put the WebGL code in a method, and output it to the screen by calling the method inside the canvas tag in the vue template:
<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. . . } } }
Now this actually works, and the little red triangle is actually rendered – except as soon as it’s rendered to the screen, it disappears.
My Thoughts As To Why
Here’s what I think happens: when the vue component first loads, it runs the runWebGL() method I’ve put there, creates and then displays the lil red triangle in the canvas. When the method has finished however, well it’s no longer running so everything in it is destroyed and there’s no WebGL to output.
If I put an alert function for example at the end of runWebGL() to prevent the function from automatically ending, you can see the triangle staying outputted to the screen until the user closes the alert.
What I’m assuming then is that when, as the guy has done in the video, you attach some script directly to the html via tags, be they local or externally referenced scripts, once they’re finished they don’t close. So the data etc in them is still available, even though they’ve been parsed to the end?
How do I solve this in Vue JS, then? Am I supposed to implement some way of keeping Vue JS methods ‘running’ even when the code in them has finished executing? Or is there some other, more standardised method for meshing WebGL and Vue JS?
I’m aware that there are some WebGL frameworks out there, and that there are some that try to blend Vue and WebGL, but unfortunately I’m not supposed to use them – I’ve got to get it as close to native VueJS and plain WebGL as I can.
Thanks very much indeed for your help!
The Javascript and WebGL code, which is identical to the one from the video, which I put inside runWebGL() is here:
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: // https://stackoverflow.com/questions/15823021/when-to-use-float32array-instead-of-array-in-javascript // 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: // https://stackoverflow.com/questions/15823021/when-to-use-float32array-instead-of-array-in-javascript // 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);
Advertisement
Answer
You need to learn more about 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/ ]