Search code examples
javascriptwebglglsles

Webgl - dynamic mouse position


I am playing around with webgl copied from this page. I want to use mouse to control the light position.

If I just send the static value from js to fragment shader it will work:

mouseLocation = gl.getUniformLocation(program, "mouse");
gl.uniform2f(mouseLocation, 0,0);

I have a function to update the mouse position:

function updateMousePosition(e){
    console.log(e.pageX);
    mousex = e.pageX;
    mousey = e.pageY;
}

which is called when mouse move:

canvas.addEventListener( 'mousemove', updateMousePosition, false );

then send the mouse position to fragment shader every frame:

gl.uniform2f(mouseLocation, mousex,mousey);

After that I will get the mouse position from fragment shader:

uniform vec2 mouse;

void main(void) {
    //mp - mouse position
    vec2 mp = vec2(mouse.x / resolution.x, 1.0 - mouse.y / resolution.y);

    //lp depend on mp
    vec3 lp = vec3(mp.x, mp.y, -1.0);

    //other calculation ...
}

it did not work. But when I send down static value from console.log in updateMousePosition(e):

gl.uniform2f(mouseLocation, 679, 590);//number taken from console.log

it work.

I am not sure what is not working but I suspect is the format or type of the number, js is not strictly type but glsl is.

How can I resolve this?


Solution

  • Finaly decided to put this answer (edited).

    As Gman commented, there is apprently nothing wrong with your code. Technically, this should work. However you pointed out a real tricky aspect of the WebGL/Javascript couple compared to OpenGL: The typage. And due to how Javascript variables works, you easily generate strange bugs.

    Imagine this absurde situation:

    var x = 55;
    var y = 128;
    
    // some code here...
    
    x = "blah"; // oups !
    
    // some code here...
    
    gl.uniform2f(mouseLocation, x, y);
    

    With C, C++ or other strong typed language, such absurdity is almost impossible to do (this is possible through pointers, but this is another subject), because the compiler forbid it stricly and throw an error. But in javascript, there is no problem, and in this case, the x number variable becomes an string variable, and when passed to WebGL, it become NaN. If you don't have correctly tracked the x variable, you will search long time why this does not work.

    All WebGL functions only accepts typed data in the last stage, because last stage is the GPU, and GPU is low-level. That is, even if you pass a pure Javascript Number variable to a WebGL function, this Javascript variable WILL BE converted to typed data before. But the fact is that Javascript will never throw an error about wrong variable types: It converts it (cast) to typed data, and even if what you attempt to do is absurd, it will never warn you, it will transform non-convertible data into 'NaN`.

    Fortunately, to avoid any ambiguity, WebGL comes with a bunch of new Javascript objects: the Typed Arrays: https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/TypedArray

    With typed Arrays, you can know within your javascript code what data is stored, and so what data will be sent to the GPU before you send it. That is, you can use typed array to convert (cast) Javascript variables into known typed data before it is sent to GPU.

    var mousePos = new Float32Array(2);
    
    function updateMousePosition(e){
      mousePos[0] = e.pageX;
      mousePos[1] = e.pageY;
    }
    
    // do something
    
    console.log(mousePos);
    gl.uniform2fv(mouseLocation, mousePos);
    

    In the above example, the variables e.pageX and e.pageY are directly converted into WebGL compatible typed data. That way, if you now print the mousePos variable using console.log you will see what will be really sent, as it, to WebGL. For debug purpose, you can put the console.log just before the call to gl.uniform2fv to ensure nothing wrong happened "out of view".

    In this example, if any data was wrongly converted, you will see something like NaN, NaN in your output log, so you know that you have to search somewhere a mistake in your code.