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"
No comments yet. Be the first to share your thoughts.