1. The transform

In order to understand transformations, we need to review some basic mathematical background before we talk about matrices.

1.1 vector

A vector has a direction and a magnitude.

Vector scalar operation

Vector addition and subtraction

The corresponding intuitive representation is addition:To express subtraction intuitively:

The length of the vector

Just use the Pythagorean theorem

The vector dot product

The dot product of two vectors is also called the inner and quantitative product of vectors. The dot product operation of two vectors is the sum operation of the corresponding bits of the two vectors multiplied one by one

The dot product of two vectors is equal to their number times their result times the cosine of the Angle between them

Vector cross-product

The cross product is only defined in 3D space. It takes two non-parallel vectors as inputs to produce a third vector that is orthogonal to the two inputs. If the two inputs are also orthogonal, the cross product will produce three vectors that are orthogonal to each other. The following image shows what the cross product looks like in 3D space:

1.2 matrix

Scalar operation

The number by computing

Matrix multiplication

There are some limitations to multiplying:

1. Two matrices can be multiplied only if the number of columns on the left is the same as the number of rows on the right.

Merge-b ≠B⋅A. 2. Matrix multiplication does not comply with Commutative, i.e.

1.3 Matrices and Vectors

Unit matrix

In OpenGL, we usually use a 4 by 4 transformation matrix for a couple of reasons, the most important of which is that most vectors have four components. The simplest transformation Matrix we can think of is the Identity Matrix. The identity matrix is an N by N matrix that is zero except for the diagonal. As can be seen in the following, this transformation matrix leaves a vector completely unchanged:

Scaling matrix

To scale a vector is to scale its length

The displacement matrix

Displacement is the process of adding another vector to the original vector to get a new vector in a different position

Rotation matrix

Using the rotation matrix we can rotate our position vector along a unit axis. You can combine multiple matrices, for example, by first rotating along the x axis and then rotating along the y axis. But this can quickly lead to a problem — universal joint deadlocks. The real solution to avoiding gimbal deadlocks is to use Quaternion.

Combination of matrices

Matrix multiplication is not commutative, which means that their order is important. When matrices are multiplied, the matrix on the far right is the first one to multiply vectors, so you should read the multiplication from right to left. It is recommended that when you combine matrices, scale first, then rotate, and finally shift, otherwise they affect each other (negatively). For example, if you move and then scale, the displacement vector will also be scaled!

Coordinate system

All vertices need to be in different states before they can be converted to fragments. The coordinates of the object are converted to several transition coordinate systems. The important thing for us is that there are altogether 5 different coordinate systems.

  • Local Space (or Object Space)
  • World Space
  • View Space (or Eye Space)
  • Clip Space
  • Screen Space

Local coordinates are the coordinates of the object relative to the local origin, and are also the coordinates from which the object starts. The local coordinates are converted into world coordinates, which are used as a coordinate system of a larger space range. These coordinates are relative to the origin of the world. Next we convert the world coordinates to observation coordinates, which are the coordinates viewed from the perspective of the camera or observer. After processing the coordinates to the observation space, we need to project them to the clipping coordinates. Clipping coordinates are processed in the range of -1.0 to 1.0 and determine which vertices will appear on the screen. Finally, we need to convert clipping coordinates to screen coordinates. Finally, the converted coordinates will be sent to the raster, which will convert them into fragments.The most important matrices used are Model, View, and Projection

Model Matrix

The model matrix will translate the coordinates of the object from local coordinates to world coordinates, and the object will be translated, scaled, or rotated to put it in the position or direction it should be.

View Matrix

The observation matrix transforms the scene to the front of the camera through a series of translation and rotation combinations, and the observation space is the space observed from the camera’s perspective.

Projection Matrix

The projection matrix transforms the vertex coordinates from the Viewing space to the clipping space. All coordinates can fall within a given range, and any points outside this range should be clipped. The Viewing Box created by the projection matrix is called the Frustum.

The coordinates of a vertex will be converted to clipping coordinates according to the following procedure, multiplying each matrix from right to left:

3. Draw a cube

The shader code is:

// Vertex shader attribute vec4 a_position; uniform mat4 u_worldViewProjection; void main() { gl_Position = u_worldViewProjection * a_position; Void main() {gl_FragColor = vec4(1,1,1,1); }Copy the code

U_worldViewProjection is the matrix multiplication of observation matrix and projection matrix

First, define the vertices of the cube

// Private positions: number[] = [ -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, ]; Private indices: number[] = [0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 6, 6, 7, 4, 0, 4, 1, 5, 2, 6, 6, 3, 7, 7, 4, 4, 1, 5, 2, 6, 3, 7,];Copy the code

Indices indices are used to store vertex indexes. The advantage is to reduce the amount of vertex data and improve performance. You need to use the index policy to specify valid vertex data rather than adding all vertex data to avoid duplication.

// Get the variable declared in the shader let positionLoc = gl.getattribLocation (program, "a_position"); this.worldViewProjectionLoc = gl.getUniformLocation(program, "u_worldViewProjection"); // Cube vertex let positionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.positions), gl.STATIC_DRAW); gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(positionLoc); Gl.element_array_buffer let indicesBuffer = gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(this.indices), gl.STATIC_DRAW); / / each frame refresh window. RequestAnimationFrame (this. Render. Bind (this));Copy the code

Tell the browser window. RequestAnimationFrame () – you want to perform an animation, and required the browser until the next redraw calls the specified callback function to update the animation. This method takes as an argument a callback function that is executed before the browser’s next redraw.

Private render(clock: number) {// clock = clock / 1000; let gl = this.gl; Gl. ClearColor (0.0, 0.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // let filedOfView = 45 * math.pi / 180; let aspect = this.canvas.clientWidth / this.canvas.clientHeight; Let projection = threedmath. perspective(filedOfView, aspect, 0.01, 500); let radius = 5; let eye = [ Math.sin(clock) * radius, 1, Math.cos(clock) * radius, ]; let target = [0, 0, 0]; let up = [0, 1, 0]; // Let view = threedmath.lookat (eye, target, up); let worldViewProjection = ThreeDMath.multiplyMatrix(view, projection); gl.uniformMatrix4fv(this.worldViewProjectionLoc, false, worldViewProjection); // the first parameter is still the line to draw, the second parameter is the number of indexes to draw // The third parameter is the type of index value, we use Uint16, So the type is gl.unsigned_short // The last argument is offset indicating the start offset gl.drawelements (gl.lines, this.indices. Length, GL.unsigned_short, 0); requestAnimationFrame(this.render.bind(this)); }Copy the code

ThreeDMath is a library of mathematical function tools. You can do convenient matrix operations.

Threedmath.lookat is used to compute the view matrix

Eye: The euler coordinates of the observer’s position

Target: The point at which the object being observed is located

Up direction: a single viewpoint and line of sight does not uniquely determine the rendered figure (which may be up or down). Up direction is used to determine the vertical direction

Threedmath. perspective is used to calculate the projection matrix

public static perspective(angle: number, aspect: number, near: number, far: number): Number [] {let f = math.tan (math.pi * 0.5-0.5 * Angle); Let rangeInv = 1.0 / (near-far); return [ f / aspect, 0, 0, 0, 0, f, 0, 0, 0, 0, (near + far) * rangeInv, -1, 0, 0, near * far * rangeInv * 2, 0 ]; }Copy the code

The effect