Tim
Tim

Reputation: 135

WebGL: Object moves according to current camera orientation

I created a simple scene with a cube moving parallel to the x-axis. Everything works as expected until I rotate the camera around the y-axis. Then the cube follows this rotation and moves parallel to the screen (x-axis in camera coordinates).

Again the initial setup:

Why does my camera movement affect the orientation of the cube?

Before camera rotation around y-axis

After camera rotation around y-axis

Here is some of the relevant code. I you would like to see more, don't hesitate to ask. I am using glMatrix for vector and matrix operations.

Main drawing routine:

// Clear the canvas before we start drawing on it.    
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

// Use the full window (minus border)
canvas.width = window.innerWidth - 16;
canvas.height = window.innerHeight - 16;

// Set viewport
gl.viewport(0, 0, canvas.width, canvas.height);

// Reset the perspective matrix
cam.aspectRatio = canvas.width / canvas.height;
mat4.perspective(perspectiveMatrix, cam.fovy, cam.aspectRatio, cam.nearPlane, cam.farPlane);

// Create the mvMatrix
mat4.lookAt(mvMatrix, cam.position, cam.poi, cam.up);

// Draw all objects
for (i = 0; i < ObjectStack.length; i++) {
    ObjectStack[i].draw();
}

Camera rotation:

// Rotation via yaw and pitch (FPS-style)
this.rotateYP = function (yaw, pitch) {

    // Rotation speed
    var rotSpeed = 0.5;
    yaw *= rotSpeed;
    pitch *= rotSpeed;

    // Update rotation
    var quatYaw = quat.create();
    quat.setAxisAngle(quatYaw, this.up, degToRad(yaw));
    var quatPitch = quat.create();
    quat.setAxisAngle(quatPitch, this.right, degToRad(pitch)); 
    var quatCombined = quat.create();
    quat.multiply(quatCombined, quatYaw, quatPitch);

    // Update camera vectors
    var tmp = vec3.create();
    vec3.subtract(tmp, this.poi, this.position);
    vec3.transformQuat(tmp, tmp, quatCombined);
    vec3.add(tmp, this.position, tmp);
    this.setPOI(tmp);
};

My setPOI() method (POI = point of interest):

this.setPOI = function (poi) {

    // Set new poi
    vec3.copy(this.poi, poi);

    // Set new view vector
    vec3.subtract(this.view, poi, this.position);
    vec3.normalize(this.view, this.view);

    // Set new right vector
    vec3.cross(this.right, this.view, [0.0, 1.0, 0.0]);
    vec3.normalize(this.right, this.right);

    // Set new up vector
    vec3.cross(this.up, this.right, this.view); 
    vec3.normalize(this.up, this.up);     
};

Object draw method for the cube:

this.draw = function () {

    // Save current mvMatrix
    mvPushMatrix();

    // Object movement
    mat4.translate(mvMatrix, mvMatrix, position);

    // Object rotation
    //mat4.mul(mvMatrix, mvMatrix, orientation);

    // Object scaling
    // ...

    // Set shader
    setShader();

    // Bind the necessary buffers
    gl.bindBuffer(gl.ARRAY_BUFFER, verticesBuffer);
    gl.vertexAttribPointer(positionAttribute, 3, gl.FLOAT, false, 0, 0);
    gl.bindBuffer(gl.ARRAY_BUFFER, normalsBuffer);
    gl.vertexAttribPointer(normalAttribute, 3, gl.FLOAT, false, 0, 0);
    gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
    gl.vertexAttribPointer(texCoordAttribute, 2, gl.FLOAT, false, 0, 0);
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vertexIndexBuffer);

    // Set active texture
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, cubeTexture);
    gl.uniform1i(gl.getUniformLocation(ShaderStack[shader], "uSampler"), 0);

    // Send the triangles to the graphics card for drawing
    gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);

    gl.bindTexture(gl.TEXTURE_2D, null);

    // Clean up the changed mvMatrix
    mvPopMatrix();
};

And finally the setShader() used above:

    function setShader() {

    var shaderProgram = ShaderStack[shader];
    gl.useProgram(shaderProgram);

    var pUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
    gl.uniformMatrix4fv(pUniform, false, perspectiveMatrix);

    var mvUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
    gl.uniformMatrix4fv(mvUniform, false, mvMatrix);

    var normalMatrix = mat4.create();
    mat4.invert(normalMatrix, mvMatrix);
    mat4.transpose(normalMatrix, normalMatrix);
    var nUniform = gl.getUniformLocation(shaderProgram, "uNormalMatrix");
    gl.uniformMatrix4fv(nUniform, false, normalMatrix);

    normalAttribute = gl.getAttribLocation(shaderProgram, "aVertexNormal");
    gl.enableVertexAttribArray(normalAttribute);

    positionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
    gl.enableVertexAttribArray(positionAttribute);

    texCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");
    gl.enableVertexAttribArray(texCoordAttribute);
};

Sorry for posting all this code here. If you have any idea, please let me know!

Upvotes: 1

Views: 2816

Answers (1)

redsoxfantom
redsoxfantom

Reputation: 956

I suspect you answered your question in your own question:

a simple scene with a cube moving parallel to the x-axis ... Then the cube follows this rotation and moves parallel to the screen (x-axis in camera coordinates).

Something like this happening leads me to believe that you applied the translation operation to your model-view matrix, not your model matrix, and from your code, I think I am right:

mat4.translate(mvMatrix, mvMatrix, position);

To fix this, you'll want to separate out your model and your view matrix, apply the translation to your model matrix, and then multiply the result by your view. Let me know how it goes!

If you're still confused by matrices, give this a read:

http://solarianprogrammer.com/2013/05/22/opengl-101-matrices-projection-view-model/

Upvotes: 3

Related Questions