In previous articles, whether drawing graphics, drawing points or changing color values, all the contents are static.
In Web GL, the motion of graphics is divided into three types: translation, rotation and scaling.
Next, we will start with zero basis and learn more about how graphics move bit by bit.
First, let's understand the translation of the next figure from scratch
1. Graphic translation
First, let's look at how to realize the translation operation of graphics.
The operation of translation is to add the original coordinates of the drawing to the corresponding moving distance. First, let's look at the implementation of translation
const vertexShaderSource = "" + "attribute vec4 apos;" + // Define a coordinate "uniform float x;" + // Handle x-axis movement "uniform float y;" + // Handle y-axis movement "void main(){" + " gl_Position.x = apos.x + x;" + " gl_Position.y = apos.y + y;" + " gl_Position.z = 0.0;" + // z-axis fixed " gl_Position.w = 1.0;" + "}"; const fragmentShaderSource = "" + "void main(){" + " gl_FragColor = vec4(1.0,0.0,0.0,1.0);" + "}"; // initShader has been implemented many times, so I won't repeat it this time const program = initShader(gl,vertexShaderSource,fragmentShaderSource); const buffer = gl.createBuffer(); const data = new Float32Array([ 0.0,0.0, -0.5,-0.5, 0.5,-0.5, ]); gl.bindBuffer(gl.ARRAY_BUFFER,buffer); gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW); const aposlocation = gl.getAttribLocation(program,'apos'); const xlocation = gl.getUniformLocation(program,'x'); const ylocation = gl.getUniformLocation(program,'y'); gl.vertexAttribPointer(aposlocation,2,gl.FLOAT,false,0,0); gl.enableVertexAttribArray(aposlocation); let x = 0.0; let y = 0.0; function run () { gl.uniform1f(xlocation,x += 0.01); gl.uniform1f(ylocation,y += 0.01); gl.drawArrays(gl.TRIANGLES,0,3); // Use this method to achieve an animation requestAnimationFrame(run) } run()
Explanation:
- First declare a variable x and a variable y to handle the coordinates of the x-axis and y-axis. The uniform variable is used here because the translation operation affects all vertices on the graph.
- Through gl_Position.[xyzw] to set the values of x, y, z and w respectively. Used to change the position of the drawing.
- Use GL Uniform1f to assign values to x and y
- Use requestAnimationFrame to realize a jog animation. It is convenient to observe the effect.
- Other operations, buffer, drawing, assignment, activation,
As you can see, it's easy to understand how to handle graphic movement in this way, but because of a movement, we declare two uniform variables to implement it. And the xyz coordinates are set separately, which is very inconvenient.
Therefore, when dealing with webgl transformation (translation, scaling, rotation), matrix is usually used to realize it. Next, let's see how to use the matrix to realize the translation of graphics.
2. Translation matrix
Steps for deriving translation matrix:
- Obtain the drawing coordinates before and after translation (3D)
- Calculate the difference before and after translation
- Brought into translation matrix
- Working with drawing vertices
- Get the translated figure
2.1 derivation of translation matrix
First, let's look at a picture.
The meaning of this picture is that we move the orange triangle to the blue dotted triangle.
The three coordinates of the blue dotted triangle after moving are
- x' = x + x1
- y' = y + y1
- z' = z + z1
- w=1, homogeneous coordinate is 1
2.2 obtaining translation matrix
In Web GL, matrix is usually used to realize graphic transformation. Let's see how the matrix is represented.
On the left is the original coordinates before translation, and in the middle is a translation matrix. After multiplying the two, you can get a coordinate after translation.
Now let's see how the translation matrix is calculated
First, we get several equations from the matrix in the above picture. Multiply the columns on the left by the rows of the matrix to get the following formula
- ax + by + cz + w = x'
- ex + fy + gz + h = y'
- ix + jy + kz + l = z'
- mx + ny + oz + p = w'
Formula consolidation:
By combining the four equations in Section 1 and the four equations in Section 2, the following results can be obtained:
- ax + by + cz + w = x + x1 ': the left and right sides of the equation hold only when a = 1, b = c = 0, w = x1
- ex + fy + gz + h = y + y1 ': the left and right sides of the equation hold only when f = 1, e = g = 0, h = y1
- ix + jy + kz + l = z + z1 ': the left and right sides of the equation hold only when k = 1,i = j = 0, l = z1
- mx + ny + oz + p = 1 ': the left and right sides of the equation hold only when m = n = o = 0 and P = 1
Through the above equation, a translation matrix can be obtained:
| 1 0 0 x |
| 0 1 0 y |
| 0 0 1 z |
| 0 0 0 1 |
Then multiply the translation matrix with the original coordinates to get the translated coordinates.
3. Matrix actual combat
Let's see how to handle the translation of a graph using a matrix.
The first step is to create shader source code
const vertexShaderSource = "" + "attribute vec4 apos;" + "uniform mat4 mat;" + // Create a uniform variable to represent the translation matrix "void main(){" + " gl_Position = mat * apos;" + // The matrix is multiplied by the original coordinates "}"; const fragmentShaderSource = "" + "void main(){" + " gl_FragColor = vec4(1.0,0.0,0.0,1.0);" + "}";
The second step is to create a translation matrix
let Tx = 0.1; //Position of x coordinate let Ty = 0.1; //Position of y coordinate let Tz = 0.0; //Position of z coordinate let Tw = 1.0; //Difference const mat = new Float32Array([ 1.0,0.0,0.0,0.0, 0.0,1.0,0.0,0.0, 0.0,0.0,1.0,0.0, Tx,Ty,Tz,Tw, ]);
It can be seen here that the matrix used is different from the matrix derived by us. In the derived translation matrix, xyzw is located on the right side of the matrix, and now it is located at the bottom of the matrix. Why?
This is because in webgl, the use of matrix needs to be flipped once according to the diagonal line at the top left and bottom right. So the matrix used is xyzw at the bottom
Step 3: draw a triangle
const program = initShader(gl,vertexShaderSource,fragmentShaderSource); const aposlocation = gl.getAttribLocation(program,'apos'); const data = new Float32Array([ 0.0,0.0, -.3,-.3, .3,-.3 ]); const buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER,buffer); gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW); gl.vertexAttribPointer(aposlocation,2,gl.FLOAT,false,0,0); gl.enableVertexAttribArray(aposlocation); gl.drawArrays(gl.TRIANGLES,0,3); // The fifth step will be rewritten
The fourth step is to obtain the matrix variable and assign a value to the matrix
const matlocation = gl.getUniformLocation(program,'mat'); gl.uniformMatrix4fv(matlocation,false,mat);
Use GL here Uniformmatrix4fv to assign a value to the matrix.
Step 5: add jog animation
function run () { Tx += 0.01 Ty += 0.01 const mat = new Float32Array([ 1.0,0.0,0.0,0.0, 0.0,1.0,0.0,0.0, 0.0,0.0,1.0,0.0, Tx,Ty,Tz,Tw, ]); gl.uniformMatrix4fv(matlocation,false,mat); gl.drawArrays(gl.TRIANGLES,0,3); // Use this method to achieve an animation requestAnimationFrame(run) } run()
4. Complete code
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <canvas id="webgl" width="500" height="500"></canvas> <script> const gl = document.getElementById('webgl').getContext('webgl'); const vertexShaderSource = "" + "attribute vec4 apos;" + "uniform mat4 mat;" + "void main(){" + " gl_Position = mat * apos;" + "}"; const fragmentShaderSource = "" + "void main(){" + " gl_FragColor = vec4(1.0,0.0,0.0,1.0);" + "}"; const program = initShader(gl,vertexShaderSource,fragmentShaderSource); const aposlocation = gl.getAttribLocation(program,'apos'); const matlocation = gl.getUniformLocation(program,'mat'); const data = new Float32Array([ 0.0,0.0, -.3,-.3, .3,-.3 ]); const buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER,buffer); gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW); gl.vertexAttribPointer(aposlocation,2,gl.FLOAT,false,0,0); gl.enableVertexAttribArray(aposlocation); let Tx = 0.1; //Position of x coordinate let Ty = 0.1; //Position of y coordinate let Tz = 0.0; //Position of z coordinate let Tw = 1.0; //Difference function run () { Tx += 0.01 Ty += 0.01 const mat = new Float32Array([ 1.0,0.0,0.0,0.0, 0.0,1.0,0.0,0.0, 0.0,0.0,1.0,0.0, Tx,Ty,Tz,Tw, ]); gl.uniformMatrix4fv(matlocation,false,mat); gl.drawArrays(gl.TRIANGLES,0,3); // Use this method to achieve an animation requestAnimationFrame(run) } run() function initShader(gl,vertexShaderSource,fragmentShaderSource){ const vertexShader = gl.createShader(gl.VERTEX_SHADER); const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(vertexShader,vertexShaderSource); gl.shaderSource(fragmentShader,fragmentShaderSource); gl.compileShader(vertexShader); gl.compileShader(fragmentShader); const program = gl.createProgram(); gl.attachShader(program,vertexShader); gl.attachShader(program,fragmentShader) gl.linkProgram(program); gl.useProgram(program); return program; } </script> </body> </html>
So far, the graphic movement controlled by matrix has been completed.
That's all for today's sharing,
Bye~
More exciting content, customized gifts, book gifts, high salary job promotion, wechat search and focus on "Doupi fan"