Search code examples
javascripttextureswebglopengl-es-2.0

How to pan a texture programatically using gl_Position


I am able to zoom a texture easily in this fiddle , but unable to pan properly.

Here's Code snippet for shaders :

var vertexShader = createVertexShader([
    'attribute vec3 attrVertexPos;',
    'attribute vec2 attrTextureCoord;',
    'varying highp vec2 vTextureCoord;',
    'uniform float zoomFactor;',
    'uniform vec2 panCoord;',
    'void main(void) {',
        '\tgl_Position = vec4(attrVertexPos.xy  + panCoord.xy, 0, zoomFactor);',
        '\tvTextureCoord = attrTextureCoord;',
    '}'
  ].join('\n'));

//create and compile Fragment Shader
var fragmentShader = createFragmentShader([
'#ifdef GL_FRAGMENT_PRECISION_HIGH',
    'precision highp float;',
'#else',
    'precision mediump float;',
'#endif',
    'varying highp vec2 vTextureCoord;',
    'uniform sampler2D uImage;',
    'void main(void) {',
        '\tgl_FragColor = texture2D(uImage, vTextureCoord);',
    '}'
   ].join('\n'));

Javascript code:

document.getElementById('canvas').onmousedown = function(e) {
if(startX === undefined) {
    startX = e.clientX;
    startY = e.clientY;
} else {
    startX = lastX;
    startY = lastY;
}
isMouseDown = true;
};
document.getElementById('canvas').onmouseup = function() {
    isMouseDown = false;
    previousTranslation[0] = xVal/100*zoomFactor;
    previousTranslation[1] = yVal/100*zoomFactor;
};
document.getElementById('canvas').onmouseout = function() {
    isMouseDown = false;
};
document.getElementById('canvas').onmousemove = function(e) {
    if (isMouseDown) {
        xVal = (lastX-startX)/canvas.width*100;
    yVal = (startY-lastY)/canvas.height*100;
    console.log(xVal/100*2.0 + ',' +yVal/100*2.0);
    gl.uniform2fv(pan, new Float32Array([previousTranslation[0] + xVal/100*zoomFactor, previousTranslation[1] + yVal/100*zoomFactor]));
    }
};

I think there is something that could be done on the javascript side as shader looks fine . Any suggestions?


Solution

  • The error happens because you try to write a vec4 into a float, which is not going to work out. The multiplication of a float with a vec4 results in a vec4, thus you have something like this:

    gl_Position.x = aVertexPosition.x * u_CosB - aVertexPosition.y * u_SinB
         float    =     float        *  vec4   -       float       * vec4
    

    Edit

    If this is really about panning (moving the object), then I don't exactly understand why there are sin and cos terms involved. In addition: Why is the x coordinate modified depending on the y coordinate? This would only be required if there is some rotation ongoing (which is not pan). In the end, the object is not visible at all, because you set the y-coordinate for all vertices to 0, which means they are collapsed to a line.

    So to just implement panning, the code could look somehow like this:

    //Contains offset in normalized device coordinates along x and y direction
    uniform vec2 pan;
    
    attribute vec4 aVertexPosition;
    
    void main()
    {
        gl_Position = vec4(aVertexPosition.xy + pan.xy, 0, 1.0);
    }
    

    Edit 2

    The way how you calculate the pan in onmousemove is wrong. You have to calculate the distance to the mouse down position (not sure what you are calculating now):

    document.getElementById('canvas').onmousemove = function(e) {
    if (isMouseDown) {
        var xVal = (lastX - e.clientX)/canvas.width;
        var yVal = (lastY - e.clientY)/canvas.height;
        gl.uniform2fv(pan, new Float32Array([xVal, yVal]));
        }
    };
    

    When you need it faster/slower multiply xVal and yVal with a constant factor. And it might be that you have to invert one of the directions when the pan goes in the wrong one.

    Edit 3

    The mousedown function now doesn't make much sense? Why should it behave differently after the first click? The only code there should be:

    document.getElementById('canvas').onmousedown = function(e) {
        startX = e.clientX;
        startY = e.clientY;
        isMouseDown = true;
    };
    

    Additionally, when calling the mouse-up function the second time, you override the value of previousTranslation instead of adding the current translation to it. Correct:

    document.getElementById('canvas').onmouseup = function() {
        isMouseDown = false;
    
        if (previousTranslation[0] == undefined)
        {
            previousTranslation[0] = 0.0;
            previousTranslation[1] = 0.0;
        }
    
        previousTranslation[0] += xVal/100*zoomFactor;
        previousTranslation[1] += yVal/100*zoomFactor;
    };