preface

As for the animation content of graphics, I have shared and completed two chapters, namely webGL transformation: In-depth graphic translation and WebGL transformation: In-depth Graphic rotation.

In the past study, we will find a problem, there is no basic knowledge, even if can learn, will forget quickly. Therefore, in the previous article, I not only introduced how to realize the translation and rotation of graph through matrix, but also analyzed in detail how to obtain the desired matrix through mathematical derivation.

I also hope that through the derivation and matrix association memory, you can remember and understand the transformation of graphics more deeply.

Today we are going to share the last transformation content: graphic scaling 🤔

Without further ado, let’s look at the first one.

1. Scale matrix

1.1 Get the coordinate expression

So here we need a little example of how to get a scaling matrix, okay

Description:

In the figure above, the blue triangle is extended to the dotted orange line by scaling.

By observing the coordinate changes of the triangle at points A and A ‘, we can obtain the following equation:

  • X prime is equal to Tx times x
  • Y prime is Ty times y
  • Z prime is equal to Tz times z

1.2 View the mapping formula between point A and point A ‘

From the figure above, we can obtain the correlation calculation relation between point A and point A ‘

ax + by + cz + w = x'
ex + fy + gz + h = y'
ix + jy + kz + l = z'
mx + ny + oz + p = w'
Copy the code

Ps: We’ve written about this formula many times. If you don’t understand it, please read the previous article

1.3 Obtain the scaling matrix

By combining the formulas obtained in step 1 and Step 2, the following equation can be obtained:

  • Ax + by + cz + w = Tx * x: only if A = Tx, b = c = w = 0The left and right sides of this equation are true
  • Ex + fy + gz + h = Ty * y: Only if f = Ty, e = g = h = 0The left and right sides of this equation are true
  • ix + jy + kz + l = Tx * zOnly when:k = Tz, i = j = l = 0The left and right sides of this equation are true
  • mx + ny + oz + p = 1Only when:m = n = o = 0, p = 1The left and right sides of this equation are true

After processing, the rotation matrix can be obtained as:

| Tx 0 0 0 |

| 0 Ty 0 0 |

| 0 0 Tz 0 |

| | 0 0 0 1

And you can see that this matrix is either row major order or column major order. They’re all equal, and a matrix that’s symmetric along the main diagonal is called a symmetric matrix

Practice 2.

1. Draw a triangle (rotten code ~~)
const vertexShaderSource = "" +
      "attribute vec4 apos;" +
      "void main(){" +
      " gl_Position = apos;" +
      "}";
const fragmentShaderSource = "" +
      "void main(){" +
      "Gl_FragColor = vec4 (0.0, 0.0, 1.0, 1.0);" +
      "}";

const program = initShader(gl,vertexShaderSource,fragmentShaderSource);
const aposLocation = gl.getAttribLocation(program,'apos');

const data =  new Float32Array([
  0.0.0.3,
  -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 second step is to comment out the method
Copy the code
2. Add matrix variables and drawing methods

Because matrices affect all vertices, we use data of uniform type.

const vertexShaderSource = "" +
      "attribute vec4 apos;" +
      "uniform mat4 mat;" +  // Add the matrix variable
      "void main(){" +
      " gl_Position = mat * apos;" +
      "}";

const matLocation = gl.getUniformLocation(program,'mat');

let Tx = 0;    // Initial x scaling
let Ty = 0;    // Initial y scaling
let Tz = 1;		 // the z-axis is fixed to 1
let step = 0.06; // The amount of change each time
let status = true
function run () {
  // Set the maximum volume to 2 times
  if (Tx >= 2) {
    status = false
  }
  if (Tx <= 0) {
    status = true
  }
  if (status) {
    Tx += step;
    Ty += step;
  } else {
    Tx -= step;
    Ty -= step;
  }

  // Initialize a rotation matrix.
  const mat = new Float32Array([
    Tx,  0.0.0.0.0.0.0.0,  Ty, 0.0.0.0.0.0.0.0,  Tz, 0.0.0.0.0.0.0.0.1.0,]);// Assign the rotation matrix to the shader
  gl.uniformMatrix4fv(matLocation,false,mat);
  // Draw a new triangle
  gl.drawArrays(gl.TRIANGLES,0.3);

  // Use this method to implement an animation
  requestAnimationFrame(run)
}
run()
Copy the code
3. Effect demonstration

3. Summary

After the sharing of three articles, we have obtained three kinds of matrix of graph transformation. Here we summarize:

1. Translation matrix

| 1 0 0 x |

| 0 1 0 y |

| 0 0 1 z |

| | 0 0 0 1

2. Rotation matrix

| cos (beta) sin (beta) | 0 0

| – sin (beta) cos (beta) | 0 0

1 0 | | 0 0

| | 0 0 0 1

3. Scale matrix

| Tx 0 0 0 |

| 0 Ty 0 0 |

| 0 0 Tz 0 |

| | 0 0 0 1

Ps: Rotation matrix We only derive the rotation matrix about the z axis. The matrices around the x axis and the y axis can also be derived by yourself.

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 (0.0, 0.0, 1.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.3,
    -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;    // Initial x scaling
  let Ty = 0;    // Initial y scaling
  let Tz = 1;		 // the z-axis is fixed to 1
  let step = 0.06; // The amount of change each time
  let status = true
  function run () {
    // Set the maximum volume to 2 times
    if (Tx >= 2) {
      status = false
    }
    if (Tx <= 0) {
      status = true
    }
    if (status) {
      Tx += step;
      Ty += step;
    } else {
      Tx -= step;
      Ty -= step;
    }

    // Initialize a rotation matrix.
    const mat = new Float32Array([
      Tx,  0.0.0.0.0.0.0.0,  Ty, 0.0.0.0.0.0.0.0,  Tz, 0.0.0.0.0.0.0.0.1.0,]);// Assign the rotation matrix to the shader
    gl.uniformMatrix4fv(matLocation,false,mat);
    // Draw a new triangle
    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