In previous articles, everything was static, whether it was drawing shapes, drawing points, or changing color values.

In WebGL, the motion of graphics can be divided into three types: translation, rotation and scaling.

Next, we’ll take a closer look at how graphics move, starting from the ground up.

So let’s start from zero and think about the translation of the graph

1. Graph translation

First, let’s see how to translate a graph.

The operation of translation is to add the original coordinates of the graph to the corresponding movement distance. Let’s first look at the implementation of translation

const vertexShaderSource = "" +
      "attribute vec4 apos;" + // Define a coordinate
      "uniform float x;" + // Handle the X-axis movement
      "uniform float y;" + // Handle the 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 and will not be covered here
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 implement an animation
  requestAnimationFrame(run)
}
run()
Copy the code

Explanation:

  • Let’s start by declaring a variablexAnd variablesyIs used to deal with x and y coordinates.This is what we’re using hereuniformVariable, because translation affects all vertices on the graph.
  • throughgl_Position.[xyzw]To set them separatelyX, y, z, wThe value of the. Used to change graphic position.
  • usegl.uniform1fTo assign x and y
  • userequestAnimationFrameImplement a slow animation. Convenient to observe the effect.
  • Other operations, buffer, draw, assign, activate,

As you can see, it makes sense to handle graphical movements this way, but for one movement, we declare two Uniform variables to implement it. And separately set xyz coordinates, very inconvenient.

So, when dealing with WebGL transformations (translation, scaling, rotation), matrices are often used. Now let’s see how we can use matrices to translate shapes.

2. Translation matrix

Steps for deriving the translation matrix:

  • Get graph coordinates before and after translation (3D)
  • Calculate the difference before and after the translation
  • Plug in the translation matrix
  • Processing graph vertices
  • Get the shifted graph

2.1 Derivation of translation matrix

First let’s look at a picture.

The point of this picture is that we have moved the orange triangle to the blue dotted triangle.

The three coordinates of the blue dotted triangle are

  • X prime is equal to x plus x1
  • y' = y + y1
  • z' = z + z1
  • w=1The homogeneous coordinate is 1

2.2 Obtaining the translation matrix

In WebGL, matrices are commonly used to transform graphics. So let’s see what the matrix looks like.

On the left is the original coordinate before the translation, and in the middle is a translation matrix, and by multiplying the two, you get a translated coordinate.

Now how do we compute the shift matrix

First of all, let’s get some equations from the matrix in the picture above. Multiply the rows of the matrix by the columns on the left, and you 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 combination:

Combining the four equations from Section 1 with the four equations from Section 2 gives the following result:

  • ax + by + cz + w = x + x1'Only when:A is equal to 1, b is equal to c is equal to 0, w is equal to x1The left and right sides of this equation are true
  • ex + fy + gz + h = y + y1'Only when:f = 1, e = g = 0, h = y1The left and right sides of this equation are true
  • ix + jy + kz + l = z + z1'Only when:k = 1,i = j = 0, l = z1The left and right sides of this equation are true
  • mx + ny + oz + p = 1'Only when:m = n = o = 0, p = 1The left and right sides of this equation are true

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

And then you multiply the translation matrix by the original coordinate, and you get the translated coordinate.

3. Matrix practice

So let’s see how we can do this with matrices.

The first step is to create shader source code
const vertexShaderSource = "" +
      "attribute vec4 apos;" +
      "uniform mat4 mat;" + // Create a UNIFORM variable that represents the shift matrix
      "void main(){" +
      " gl_Position = mat * apos;" + // Matrix multiplied by original coordinates
      "}";
const fragmentShaderSource = "" +
      "void main(){" +
      "Gl_FragColor = vec4 (1.0, 0.0, 0.0, 1.0);" +
      "}";
Copy the code
The second step is to create the translation matrix
let Tx = 0.1;    // The position of the x coordinate
let Ty = 0.1;    // The position of the y coordinate
let Tz = 0.0;    // The z position
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,
]);
Copy the code

Here you can see that the matrix used is not quite the same as the matrix we derived. In the translation matrix derived xyZW is on the right side of the matrix, now it is at the bottom of the matrix. Why is that?

This is because in WebGL, the use of a matrix requires a flip diagonally from the top left to the bottom right. So the matrix used, xyzw, is 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); // It will be rewritten in step 5
Copy the code
Step 4: Get the matrix variables and assign values to the matrix
const matlocation = gl.getUniformLocation(program,'mat');
gl.uniformMatrix4fv(matlocation,false,mat);
Copy the code

Gl. uniformMatrix4fv is used to assign the matrix.

Step 5, add slow 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 implement an animation
  requestAnimationFrame(run)
}
run()
Copy the code

4. Complete code

<! DOCTYPEhtml>
<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;    // The position of the x coordinate
  let Ty = 0.1;    // The position of the y coordinate
  let Tz = 0.0;    // The z position
  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 implement 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>
Copy the code

At this point, the matrix control graph movement is complete.

That’s all for today’s sharing,

Bye~


Data platform front-end team, responsible for the research and development of many big data-related products in the company. We maintain a very strong enthusiasm in front-end technology, in addition to data products related research and development, in data visualization, mass data processing optimization, Web Excel, WebIDE, private deployment, engineering tools have a lot of exploration and accumulation, welcome to submit resume!