Skip to main content

Basic Transformations and Animation_WebGL Notes 10

Free2016-01-06#JS#WebGL#webgl transform#webgl translate#webgl rotate#webgl scale#webgl animation#webgl animate#webgl平移旋转缩放#webgl动画

Unlike canvas drawing which has convenient transformation APIs, WebGL requires self-implementation

Preface

In [previous notes](/articles/glsl-es(opengl-es 着色器语言)-webgl 笔记 9/), we comprehensively understood GLSL ES syntax, must-master WebGL basics are basically all here (meaning APIs are gone..), following content is basically all mathematics, and transformation is just the most superficial part

I. Mathematical Principles of Transformation

Can't explain clearly in one or two sentences, for example translate is adding a delta to each component of coordinates of every point in image, point (0, 3, 2) moves 2 unit lengths along y-axis positive direction, result is (0, 5, 2). Principle is this simple, can't explain clearly because involves formula derivation and matrix representation, as follows:

x' = x + deltaX;      how? why?      |1 0 0 tx|
y' = y + deltaY;    ------------>    |0 1 0 ty|
z' = z + deltaZ;                     |0 0 1 tz|
                                     |0 0 0 1 |

Why does 3D coordinates need 4x4 matrix to represent translate transformation? What benefit does doing this have? What about rotate and scale?

If interested can explore yourself (search), complete process is formula derivation->homogeneous coordinates->matrix multiplication->transformation matrix->CTM, after understanding CTM, completely understand mathematical principles of transformation

P.S. Too mathematical things don't want to write temporarily, but will write sooner or later. So won't expand transformation matrix here, talk about it later

II. WebGL Transformation Matrix

Don't all transformation matrices look the same? No no no, in previous notes said, matrices in GLSL ES are column-major, for example translate matrix must be like this:

Matrix4.prototype.setTranslate = function(x, y, z) {
    var e = this.elements;
    e[0] = 1;  e[4] = 0;  e[8]  = 0;  e[12] = x;
    e[1] = 0;  e[5] = 1;  e[9]  = 0;  e[13] = y;
    e[2] = 0;  e[6] = 0;  e[10] = 1;  e[14] = z;
    e[3] = 0;  e[7] = 0;  e[11] = 0;  e[15] = 1;
    return this;
};

P.S. Above code excerpted from "WebGL Programming Guide" source code cuon-matrix.js, API style is very handy to use, so.. will use directly from now on

Note: WebGL and OpenGL same, matrix elements are stored in column-major order, actually CSS transform: matrix(a,b,c,d,e,f) is also column-major, but in linear algebra generally habitually use row-major

III. Matrix Library

A simple matrix library is necessary, at least must support 3 basic transformations and matrix multiplication, of course best to have a set of friendly (comfortable to use) APIs

"WebGL Programming Guide" provided matrix library cuon-matrix.js is not bad, small and only provides most basic functionality, if feel not good to use can go find other matrix libraries, but don't recommend using Three.js, because functionality is many and complete, not conducive to our in-depth understanding of WebGL

Following examples will use cuon-matrix.js, specific usage as follows:

// Create 4x4 matrix, default fill as identity matrix
var m4 = new Matrix4();
// m4.elements === [
// 1, 0, 0, 0,
// 0, 1, 0, 0,
// 0, 0, 1, 0,
// 0, 0, 0, 1]

// Fill as translation matrix, parameters are deltaX, deltaY, deltaZ
m4.setTranslate(0.25, 0.25, 0.0);
// m4.elements === [
// 1, 0, 0, 0,
// 0, 1, 0, 0,
// 0, 0, 1, 0,
// 0.25, 0.25, 0, 1]

// Rotate on basis of translation
m4.rotate(30, 0, 0, 1);    // Counterclockwise 30 degrees, (0, 0, 1) is rotation axis z-axis
// m4.elements === [
// 0.8660253882408142, 0.5, 0, 0,
// -0.5, 0.8660253882408142, 0, 0,
// 0, 0, 1, 0,
// 0.25, 0.25, 0, 1]

// ...
// setXXX is fill
// xxx is calculate CTM, i.e. m4 = m4 x new Matrix4().setXXX

Also have very convenient and practical functions, for example:

// Copy
var newM4 = new Matrix4(oldM4);
// Matrix multiplication, product placed in m4_1
m4_1.multiply(m4_2);
// Matrix transpose
m4.transpose();

IV. Basic Transformations

Transformation is doing same operation on coordinates of every point on image, because fragments are generated according to vertices, so we only need to do operations on vertices, meaning only need to modify vertex shader, as follows:

// Vertex shader source program
var vsSrc = 'attribute vec4 a_Position;' +
    'uniform mat4 u_transformMatrix;' +
    'void main() {' +
    'gl_Position = u_transformMatrix * a_Position;' +   // Set coordinates
    // A little trick, to display triangle before transformation
    'if (u_transformMatrix == mat4(0.0)) {gl_Position = a_Position;}' +
'}';

We declared matrix type uniform variable u_transformMatrix, used to accept transformation matrix, next need to pass value in

First need to have a transformation matrix, make one now:

// Transformation
var transformMatrix = new Matrix4();
// First translate
transformMatrix.setTranslate(0.25, 0.25, 0.0);  // Translate to upper right
// Then rotate
transformMatrix.rotate(30, 0, 0, 1);    // Counterclockwise 30 degrees, (0, 0, 1) is rotation axis z-axis
// Then scale
transformMatrix.scale(0.5, 0.5, 1);     // x, y scale half, z unchanged

This worked out a very complex transformation matrix, then find way to assign to uniform variable, as follows:

// Pass transformation matrix to vertex shader
var u_transformMatrix = gl.getUniformLocation(glUtil.program, 'u_transformMatrix');
gl.uniformMatrix4fv(u_transformMatrix, false, transformMatrix.elements);

Assigning value to mat type shader variable uses gl.uniformMatrix4fv, parameter meanings as follows:

gl.uniformMatrix4fv(location, transpose, array)
---
transpose   In WebGL can only be false, indicates whether matrix is transpose matrix, WebGL doesn't provide matrix transpose method
array       Typed array, 4x4 matrix stored in column-major order

Finally draw it out, completed

V. Animation

Animation is visual effect produced by constantly erasing and redrawing, for example rotation is drawn through rotation angle constantly incrementing, to guarantee smoothness need to guarantee angle uniformly increments. So key to implementing animation becomes guaranteeing uniform change, while simple setInterval won't work, so need to add time control, specifically as follows:

// Transformation
var angle = 0;
var ROTATE_SPEED = 60;  // 60 degrees/second
var transformMatrix = new Matrix4();
var now;
var lastTime;
var delta = 0;
var u_transformMatrix = gl.getUniformLocation(glUtil.program, 'u_transformMatrix');
// draw
function draw() {
    now = Date.now();
    delta = (now - lastTime) * ROTATE_SPEED / 1000;
    // Angle increment
    angle += delta;
    angle %= 360;
    // console.log(angle);
    // Rotate
    transformMatrix.setRotate(angle, 0.0, 0.0, 1.0);
    // Pass rotation matrix to vertex shader
    gl.uniformMatrix4fv(u_transformMatrix, false, transformMatrix.elements);
    // clear
    gl.clear(gl.COLOR_BUFFER_BIT);
    // Draw transformed triangle
    gl.drawArrays(gl.TRIANGLES, 0, arrVtx.length / 2);

    // Record time
    lastTime = now;

    // Delayed recursion
    window.requestAnimationFrame(draw);
}
// Animation
window.requestAnimationFrame(draw);
lastTime = Date.now();

Rotation speed is degrees/second, how many degrees rotated at next draw is calculated according to time, this guarantees rotation angle changes uniformly

Note: requestAnimationFrame similar to setTimeout/setInterval, difference is requestAnimationFrame only takes effect when tab page is in active state, different from setTimeout/setInterval, performance also higher

P.S. In this example of course can't see animation stops when tab page not active, because we calculate angle according to time, originally stopped for a period in middle, but when recovering activation rotated a large angle, caught up with stopped part in middle, css3 animation internal principle is also like this:

CSS3 animation when Tab switches back, animation performance doesn't pause; through Chrome frames tool testing found, after Tab switches, calculation rendering drawing all stop, when Tab switches back seems through built-in JS calculated animation position to achieve redraw, creating feeling animation doesn't pause

P.S. Excerpted from CSS3 Animation So Strong, What Use Is requestAnimationFrame? « Zhang Xinxu-Xin Space-Xin Life, for more information about requestAnimationFrame can view this article, very valuable

VI. DEMO

Complete examples containing above code, please view:

Can see matrix values after each transformation step in console

VII. Summary

Transformation matrix also called model matrix, mvp indispensable in 3D is (Model Matrix, View Matrix, Projection Matrix)

Up to this point, WebGL 2D is finished, think carefully seems didn't learn much, are most APIs on 3D side? No, most commonly used APIs we've all used, remaining except a little bit useful just some very advanced functions (like dynamic textures), 3D scenes almost all "rubbed" out using matrices, no usable APIs left, no magical methods like Camera.setPosition(0, 0, 3)

What is perspective, is matrix, what is lighting, matrix, what about shadows, matrix, what about fog, said all are matrices...

References

  • "WebGL Programming Guide"

Comments

No comments yet. Be the first to share your thoughts.

Leave a comment