1. Related articles:
    1. OpenGL first lesson — Name explanation
    2. OpenGL is introduced in lesson 2 – The common fixed storage shader

    3. OpenGL introduction third lesson — matrix transformation and coordinate system

    4. OpenGL entry lesson 4 — Depth

    5. OpenGL entry lesson 5 — front and back elimination

    6. OpenGL entry lesson 6 — Clipping and Blending

    7. OpenGL entry lesson 7 — Texture

    8. OpenGL is in class 8 — a supplementary case

    9. OpenGL ES que

    10. The application of GLKit

    11. GLSL met

    12. How to use custom shaders written in GLSL in iOS

    13. Precision qualifier in GLSL

    14. Use OpenGL to write a rotating stereo album for your girlfriend

In OpenGL, objects go through a series of coordinate transformations before being rendered to the screen, which sounds a little scary; But if you have some basic knowledge of linear algebra using matrix transformations, it’s actually not that hard. Even if you haven’t learned linear algebra, you’ll probably be able to learn OpenGL by just knowing some basic matrix operations. Now let’s learn a little bit about matrices.

Matrices are a very useful mathematical tool, a little difficult, but once you understand them, they become very useful. In order to understand matrix transformations, we need to understand vectors before we talk about matrices.

vector

The most basic definition of a vector is a direction. Or, more formally, a vector has a Direction and a Magnitude, also called intensity or length. You can think of vectors as instructions on a treasure map: “Take ten steps to the left, three to the north, then five to the right”; “Left” is the direction, and “10 steps” is the length of the vector. Since a vector represents a direction, where it starts doesn’t change its value. So the vector v and the vector w are going to be equal.

                           

A vector can be in any dimension, usually two or three, and a three dimensional vector is usually represented in a formula like this. In the figure below:

  


                                         

Because vectors are one direction, it is sometimes difficult to visualize them in positions. To make it more intuitive, we usually set the origin of this direction to (0, 0, 0) and then point in one direction, corresponding to a point, to make it a Position Vector. (You can also set the starting point to some other point and say: This Vector points from this point to another point.) For example, the position vector (3, 5) will start at (0, 0) and point to (3, 5).

Vectors and scalars

A Scalar is simply a number (or a vector with only one component). When adding/subtracting/multiplying/dividing a vector by a scalar, we can simply perform the operation on each component of the vector separately. For addition it would look like this:

                                              

The + can be +, -, · or ÷, where · is a multiplier sign. Note that – and ÷ cannot be reversed (scalar -/÷ vector) because the reverse operation is undefined.

Vector invert

Negate a vector reverses its direction. A vector that points northeast is going to point southwest when you take it backwards. We can reverse a vector by adding a negative sign to each component (or multiplying the vector by -1) :

                                       

Vector by subtracting

Vector addition can be defined as the addition of components, that is, each component of a vector plus the corresponding component of another vector; Just like adding and subtracting ordinary numbers, subtraction of a vector is equal to adding the opposite of the second vector:

    

Length of vector

We use the Pythagorean theorem to get the length of the vector:

                                                         

We can also extend this formula to three dimensions by adding Z squared. Another special type of Vector is called a Unit Vector. The unit vector has a special property that it has length 1. We can take each component of any vector and divide it by its length to get its unit vector.

Vector multiplication

Vector multiplication is divided into dot and cross products:

Dot product: The dot product of two vectors is equal to their number times their result times the cosine of the Angle between them. This might sound confusing, but let’s look at the formula.

                                   

So the Angle between them, let’s call it theta, what’s the use of that, and imagine what happens if you dot two unit vectors? Now the dot product only defines the Angle between two vectors. Remember the cosine of 90 degrees is 0 and the cosine of 0 degrees is 1. This makes it easy to test whether two vectors are Orthogonal or parallel using the dot product (Orthogonal means they are Orthogonal to each other).

Cosine theta.

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

                                

Unlike other operations, if you haven’t delved into linear algebra, you might find the cross product counter-intuitive, so just remember the formula (or not). Below you will see the cross product of two orthogonal vectors A and B:

                

matrix

A matrix is a rectangular array of mathematical expressions in which each Element is called an Element. Here is an example of a 2×3 matrix:

                                             

Matrices can be indexed by (I, j), where I is the row and j is the column, and that’s why the matrix above is called a 2 by 3 matrix.

Matrix addition and subtraction

The addition and subtraction between matrices and scalars are defined as follows:



The subtraction of matrices and scalars is also similar:



Addition and subtraction between matrices is the addition and subtraction of the corresponding elements of two matrices, so the general rule is similar to scalar operation, except that the operation can only be performed on the elements under the same index. This means that addition and subtraction are only defined for matrices of the same dimension. A 3 by 2 matrix and a 2 by 3 matrix (or a 3 by 3 matrix and a 4 by 4 matrix) cannot be added or subtracted. Let’s see how two 2 by 2 matrices add:



The same principle applies to subtraction:



The scalar times the matrix

Like adding and subtracting matrices and scalars, multiplication between matrices and scalars is the multiplication of each element of the matrix by that scalar. The following example shows the multiplication process:

 

Matrix multiplication

Multiplication between matrices is not necessarily complicated, but it is hard to get used to. Matrix multiplication basically means multiplying according to prescribed rules. Of course, there are some limitations to multiplying:

  • Two matrices can be multiplied only if the number of columns on the left is equal to the number of rows on the right;
  • Matrix multiplication does not follow Commutative, that is⋅ ⋅ B indicates B A;

Let’s take a look at an example of multiplying two 2-by-2 matrices:



And as you can see, matrix multiplication is very cumbersome and error-prone, but it doesn’t matter if it’s not enough, and we don’t want you to do it yourself in OpenGL, we have an API to do it, but I just want you to get a sense of how matrix multiplication works.

Matrix times vector

Now we know a lot about vectors. In OpenGL you can use vectors to represent positions, colors, and even texture coordinates. By analogy with a matrix, it’s just an N by 1 matrix, where N is the number of components of a vector (also called n-dimensional vectors). If you think about it, a vector is a sequence of numbers just like a matrix, but it has only one column. So how does this new definition help us? If we have an M by N matrix, we can multiply this matrix times our N by 1 vector, because the number of columns in this matrix is equal to the number of rows in this vector, so they can multiply.

But why do we care whether a matrix can be multiplied by a vector? Well, it just so happens that a lot of interesting 2D/3D transformations can be put into a matrix, and multiplying that matrix by our vector will Transform this vector. If you’re still a little confused, let’s look at some examples and you’ll get the point in no time.

Unit matrix

In OpenGL, we usually use a 4 by 4 transformation matrix for a couple of reasons, and one of the most important reasons 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 one for the left-hand corner line. As can be seen in the following, this transformation matrix leaves a vector completely unchanged:

   

You might wonder what’s the use of an untransformed transformation matrix? The identity matrix is usually the starting point for generating other transformation matrices, and if we dig into linear algebra, it’s also a very useful matrix for proving theorems and solving linear equations.

The zoom

A transformation matrix will be constructed to provide us with scaling. We know from the identity matrix that each diagonal entry is individually multiplied by the corresponding entry of the vector. What happens if we change 1 to 3? So in this case, we’re multiplying each member of the vector by 3, and we’re actually scaling the vector by 3. If we express the scaling variables as (S1,S2,S3) we can define a scaling matrix for any vector (x,y,z) :

          

Note that the fourth scaling vector is still 1, because scaling the W component in 3D space is meaningless. The W component has other uses.

The displacement

Displacement (Translation) is the process of adding another vector on the basis of the original vector to obtain a new vector in a different position, thus moving the original vector on the basis of the displacement vector. We’ve already talked about vector addition, so this shouldn’t be too unfamiliar. As with the scaling matrix, there are several special positions on the 4-by-4 matrix to perform specific operations, and for displacement they are the top three values in the fourth column. If we express the displacement vector as (Tx,Ty,Tz), we can define the displacement matrix as:

        

The displacement matrix, which allows us to move objects in three directions (x, y, and Z), is a very useful transformation matrix in our transformation toolbox.

rotating

To rotate in 3D space you need to define an Angle and a Rotation Axis. The object will rotate at a certain Angle along a given axis of rotation. When a 2D vector rotates in 3D space, we set the rotation axis to the Z axis.

Using trigonometry, you can transform a vector into a rotated vector given an Angle. This is usually achieved using a series of sines and cosines (commonly referred to as sines and cosines) in various clever combinations.

The rotation matrix is defined differently for each unit axis in 3D space, and the rotation Angle is denoted by θ :

Rotation along the X-axis:



Rotation along the y axis:



Rotation along the Z axis:



Using the rotation matrix we can rotate any position vector along an axis of unit rotation. You can also compose multiple matrices, for example, by first rotating along the X-axis and then rotating along the Y-axis.

You don’t have to worry too much about how rotations look complicated, how these rotations are computed, except to understand that rotations are also done by multiplying a particular matrix.

Combination of matrices

The real power of using matrices for transformations is that we can combine multiple transformations into one matrix based on the multiplication of matrices. Let’s see if we can generate a transformation matrix that combines multiple transformations. Suppose we have a vertex (x, y, z) and we want to scale it by a factor of 2 and then shift it by (1, 2, 3) units. We need a displacement and scaling matrix to do these transformations. The resulting transformation matrix looks like this:



Notice, when we multiply matrices we write the displacement first and then the scaling. Matrix multiplication is not commutative, which means that their order matters. 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!

Multiplying our vector by our final transformation matrix gives the following result:

           

Good job! The vector is scaled by 2, and then shifted by 1, 2, 3.

Coordinate systems in OpenGL

After the vertices are colored in OpenGL, our visible vertices are Normailzed Device Coordinate (NDC). So x,y, and z at each vertex should be direct from -1 to 1, otherwise they’re invisible to us.

A vertex passes through several important coordinates before being converted to a fragment:

  1. Local Space (or Object Space)
  2. World Space
  3. View Space (or Eye Space)
  4. Clip Space
  5. Screen Space

Transformation from one coordinate system to another requires the use of transformation matrices, the most important of which are Model, View and Projection matrices. The starting Coordinate of the vertex of the object is the Local Space, we’ll call it Local Coordinate, It’s going to be world Coordinate, View Coordinate, Clip Coordinate, and it’s going to end up as Screen Corrdinate.

Want to render 3 d graphics eventually to the 2 d screen of the device, in addition to using coordinate transformation model transformation and transformation to object to the camera coordinate system, projection transformation is needed to coordinate a cropping system, and then through the perspective transformation to the standardization of equipment division coordinate system (NDC), the viewport transform rendering to the 2 d screen, the following figure:



In the above figure, OpenGL only defines the clipping coordinate system, the normalized device coordinate system, and the screen coordinate system, while the local coordinate system (the object coordinate system), the world coordinate system, and the camera coordinate system are all customized coordinate systems for the convenience of user design. That is, model transformations, visual transformations, and projection transformations, which can be customized according to the needs of the developer, are done in the vertex shader. The other two perspective division and viewport transformations, which are performed automatically by OpenGL, are done in the post-vertex shader phase.

Each of the above transformations creates a transformation matrix, the model matrix, the observation matrix, and the projection matrix. When these matrices are combined, a vertex coordinate will be transformed to clipping coordinate according to the following procedure:



Note that the order of matrix operations is reversed (remember we need to read matrix multiplication from right to left). The last vertex should be assigned to gl_Position in the vertex shader, and OpenGL will automatically perform perspective division and cropping.

Let’s write a simple example of how to use matrix transformations to translate 3d images into 2d screens.

case

Let’s first look at the case effect and then talk about how to achieve it:

                                                            

In this case, we first get the model view matrix by first cross multiplying the translation matrix with the rotation matrix, then get the model view projection matrix by cross multiplying the model view matrix, which is often referred to as MVP, and then draw the graph with the plane shader. The specific code is as follows:

Registration of some initialization operations and callback functions for main:

int main(int argc, char* argv[]){   
 gltSetWorkingDirectory(argv[0]);   
 glutInit(&argc, argv);   
 glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_SINGLE);   
 glutInitWindowSize(800, 600);   
 glutCreateWindow("Matrix transformation Demo");   
 glutReshapeFunc(ChangeSize);   
 glutDisplayFunc(RenderScene);   
 GLenum error = glewInit();    
 if(GLEW_OK ! = error) { fprintf(stderr,"GLEW Error: %s\n",glewGetErrorString(error));      
   return 1;  
  }   
 setupRC();   
 glutMainLoop();   
 return 0;
}Copy the code

There are three methods that are important. The first is setupRC, which does some preparatory work before drawing:

void setupRC() {glClearColor(0.8, 0.8, 0.8, 1.0f); / / set the color screen clearing shaderManager InitializeStockShaders (); // initialize fixed pipeline glEnable(GL_DEPTH_TEST); GltMakeSphere (torusBatch, 0.4, 10, 20); // Form a ball glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // Set polygon fill mode}Copy the code

ChangeSize is called the first time the window is displayed or any other time the window changes. Mainly completed the setting of viewport and projection matrix, as follows:

void ChangeSize(int w, int h){
    if(h == 0) h = 1;
    glViewport(0, 0, w, h);   
     viewFrustum.SetPerspective(35, float(w)/float(h), 1, 1000); 
 }Copy the code

RenderScene (RenderScene, RenderScene, RenderScene, RenderScene)

Void RenderScene (void) {/ / clear screen, depth buffer glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // create static CStopWatch rotTimer based on time change; // Current time * 60sfloatYRot = rotTimer. GetElapsedSeconds () * 60.0 f; Rotate: rotate: mModelview: mModelViewProjection: M3DMatrix44f mTranslate, mRotate, mModelview, mModelViewProjection; M3DMatrix44f mTranslate, mModelview, mModelViewProjection; // Create a 4*4 matrixvariable and move the matrixmatrix44 2.5 units along the negative z-axis (mTranslate, 0.0f, 0.0f, -2.5f); M3dRotationMatrix44 (m3dRotationMatrix44(m3dDegToRad(yRot), 0.0f, 1.0f, 0.0f); M3dMatrixMultiply44 (mModelview, mTranslate, mRotate); // Add the result to mModerView by multiplying the matrix rotation matrix and the move matrix. // multiply the projection matrix by the model view matrix, and apply the change result to the mModelViewProjection matrix by matrix multiplication. * projection m3dMatrixMultiply44 = model (mModelViewProjection viewFrustum. GetProjectionMatrix (), mModelview); // Draw the color GLfloatVBlack [] = {0.0f, 0.0f, 0.0f, 1.0f}; // Submit the matrix and color through the flat shader. shaderManager.UseStockShader(GLT_SHADER_FLAT, mModelViewProjection, vBlack); // Start drawing torusbatch.draw (); // Swap buffers and immediately refresh glutSwapBuffers(); glutPostRedisplay(); }Copy the code

Here we first create the translation and rotation matrices by cross product: translation matrix X rotation matrix = model view matrix. Going from right to left is actually a rotation and translation, and the reason for rotation and translation was explained in the combination of matrices above.

Then the model view projection matrix (MVP) is obtained by cross multiplying the model view matrix by the projection matrix, so that the parameters required by the plane shader of the fixed pipeline are available, and then the relevant OpenGL API can be successfully drawn.