OpenGL simple image drawing basic process

The basic process of OpenGL simple graph drawing is shown in the following figure

int main(int argc, char* argv[])
{
    gltSetWorkingDirectory(argv[0]);
    glutInit(&argc, argv);
    // Apply for a color cache, a depth cache, a double cache, and a template cache
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    // Set the size of window
    glutInitWindowSize(800.600);
    // Create the window name
    glutCreateWindow("GL_POINTS");
    
    
    // Register the callback function (change the size)
    glutReshapeFunc(ChangeSize);
    // The function that is called when a space is clicked
    glutKeyboardFunc(KeyPressFunc);
    // Special key function (up, down, left, right)
    glutSpecialFunc(SpecialKeys);
    // Display function
    glutDisplayFunc(RenderScene);
    
    // Determine if you can initialize the Glew library to ensure that your project can use the OpenGL framework properly
    GLenum err = glewInit();
    if(GLEW_OK ! = err) {fprintf(stderr."GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }
    
    / / to draw
    SetupRC();
    
    //runloop runs the loop
    glutMainLoop();
    return 0;
}
Copy the code

Glut initialization

OpenGL is a cross-platform specification that does not provide an implementation of display Windows for operating systems. It only gives the lowest level of specification, and how the window is displayed depends on the operating system. We learned that OpenGL needs a window system to display various rendering and transformation effects, so we need a tool to implement this window. Glut is a windowview-independent toolkit for writing OpenGL programs. It implements a simple windowindow application programming interface (API) for OpenGL.

The main work glut initializer does is shown in the figure below

To learn more about glut, click on glut

    glutInit(&argc, argv);
    // Apply for a color cache, a depth cache, a double cache, and a template cache
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    // Set the size of window
    glutInitWindowSize(800.600);
    // Create window and name it
    glutCreateWindow("GL_POINTS");
Copy the code

Register the callback function

Registering callback functions here requires actual usage to register. In this case, the callback function shown below is registered:

    // Register the callback function (change the size)
    glutReshapeFunc(ChangeSize);
    // The function that is called when a space is clicked
    glutKeyboardFunc(KeyPressFunc);
    // Special key function (up, down, left, right)
    glutSpecialFunc(SpecialKeys);
    // Display function
    glutDisplayFunc(RenderScene);
Copy the code

Register the window size change callback function

We use void glutReshapeFunc (void (* func) (int width, int height)) to register a callback function when the window size changes. Custom callback functions are executed when the display window changes. In this case, the callback is void ChangeSize(int w, int h).

void ChangeSize(int w, int h)
{
    glViewport(0.0, w, h);
    // Create the projection matrix and load it into the projection matrix stack
    viewFrustum.SetPerspective(35.0 f.float(w) / float(h), 1.0 f.500.0 f);
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    
    // Call top load cell matrix
    modelViewMatrix.LoadIdentity();
}
Copy the code

Set the viewport function, when the display window changes, need to reset the viewport, use the glViewport function to set the new viewport:

void glViewport(GLint x,        // The X value of the lower left coordinate of the viewport is 0 by default
                GLint y,        // The Y value of the lower left coordinate of the viewport is 0 by default
                GLsizei width,  // Viewport width
                GLsizei height) // Viewport height
Copy the code

, pass in the X and Y values (0, 0 by default) and the new width and height for the lower-left coordinates of the viewport.


When the display window changes, you need to re-create a new projection matrix based on the new viewport. Setting up a new matrix using the SetPerspective method is essentially defining a flat-frustum. ViewFrustum defines a GLFrustum object, which is a perspective projection object.

SetPerspective( float fFov,       // Observer Angle
                float fAspect,    / / aspect ratio
                float fNear,      // The minimum distance between observer and viewport
                float fFar)       // The maximum distance between observer and viewport
Copy the code

After setting the projection matrix, load it onto the projection matrix stack. Use the LoadMatrix method. ProjectionMatrix is a matrix stack object.

LoadMatrix(const M3DMatrix44f mMatrix) 
Copy the code

Finally, the cell matrix is loaded onto the model view matrix stack, while the graph has not passed vertices and there is no model view matrix. ModelViewMatrix is a matrix stack object.

LoadIdentity(void)
Copy the code

Registers callback functions that respond to keyboard events

We use void glutKeyboardFunc(void (*func)(unsigned char key, int x, int y)) to register a callback function in response to keyboard events. After the user presses the specified key, the corresponding effect is triggered. In this case, the callback is void KeyPressFunc(unsigned char key, int x, int y).

void KeyPressFunc(unsigned char key, int x, int y)
{
    if(key == 32)
    {
        nStep++;
        
        if(nStep > 6)
            nStep = 0;
    }
    
    switch(nStep)
    {
        case 0:
            glutSetWindowTitle("GL_POINTS");
            break;
        case 1:
            glutSetWindowTitle("GL_LINES");
            break;
        case 2:
            glutSetWindowTitle("GL_LINE_STRIP");
            break;
        case 3:
            glutSetWindowTitle("GL_LINE_LOOP");
            break;
        case 4:
            glutSetWindowTitle("GL_TRIANGLES");
            break;
        case 5:
            glutSetWindowTitle("GL_TRIANGLE_STRIP");
            break;
        case 6:
            glutSetWindowTitle("GL_TRIANGLE_FAN");
            break;
    }
    
    glutPostRedisplay();
}
Copy the code

In this method, the number of times the user clicks the space bar and changes the name of the window. The most important is the glutPostRedisplay method. He sends a redisplay message to the main loop to redraw the graph.

Registers the callback function for a particular key position on the keyboard

We use the void glutSpecialFunc(void (*func)(int key, int x, int y)) function to register a callback function in response to a special keyboard event. After the user presses the specified key, the corresponding effect is triggered. The callback function in this case is void SpecialKeys(int key, int x, int y).

void SpecialKeys(int key, int x, int y)
{
    if(key == GLUT_KEY_UP)
        // Rotate around a specified X,Y, and Z axis.
        objectFrame.RotateWorld(m3dDegToRad(5.0 f), 1.0 f.0.0 f.0.0 f);
    
    if(key == GLUT_KEY_DOWN)
        objectFrame.RotateWorld(m3dDegToRad(5.0 f), 1.0 f.0.0 f.0.0 f);
    
    if(key == GLUT_KEY_LEFT)
        objectFrame.RotateWorld(m3dDegToRad(5.0 f), 0.0 f.1.0 f.0.0 f);
    
    if(key == GLUT_KEY_RIGHT)
        objectFrame.RotateWorld(m3dDegToRad(5.0 f), 0.0 f.1.0 f.0.0 f);
    
    glutPostRedisplay();
}
Copy the code

In this method, the user presses the up, down, left, and right arrow keys on the keyboard to transform the image’s position in the world coordinate system. ObjectFrame is a GLFrame object. This object holds where the object is now, where it will be moved, and in what direction it will be moved. The most important is the glutPostRedisplay method. He sends a redisplay message to the main loop to redraw the graph.

Register Register the current window display callback function (draw)

void RenderScene(void)
{
    // Clear the window with current clearing color
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
   
    / / pressure stack
    modelViewMatrix.PushMatrix();
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    
    // Matrix multiplied by matrix at the top of the stack, the result of the multiplication is then stored at the top of the stack
    modelViewMatrix.MultMatrix(mCamera);
    
    M3DMatrix44f mObjectFrame;
    // Just use the GetMatrix function to get the value at the top of the matrix stack. This function can be overridden twice. Used to use GLShaderManager. Or get the vertex copy of the top matrix
    objectFrame.GetMatrix(mObjectFrame);
    
    // Matrix multiplied by matrix at the top of the stack, the result of the multiplication is then stored at the top of the stack
    modelViewMatrix.MultMatrix(mObjectFrame);
    
    /* Uniform values in GLShaderManager -- Plane shader arguments 1: plane shader arguments 2: Run of geometry transform to specify a 4 * 4 transformation matrix -- transformPipeline. GetModelViewProjectionMatrix () to obtain GetMatrix function can get the top 3: the value of the parameter matrix stack color (black) * /
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    
    switch(nStep) {
        case 0:
            // Set the size of the point
            glPointSize(4.0 f);
            pointBatch.Draw();
            glPointSize(1.0 f);
            break;
        case 1:
            // Set the width of the line
            glLineWidth(2.0 f);
            lineBatch.Draw();
            glLineWidth(1.0 f);
            break;
        case 2:
            glLineWidth(2.0 f);
            lineStripBatch.Draw();
            glLineWidth(1.0 f);
            break;
        case 3:
            glLineWidth(2.0 f);
            lineLoopBatch.Draw();
            glLineWidth(1.0 f);
            break;
        case 4:
            DrawWireFramedBatch(&triangleBatch);
            break;
        case 5:
            DrawWireFramedBatch(&triangleStripBatch);
            break;
        case 6:
            DrawWireFramedBatch(&triangleFanBatch);
            break;
    }
    
    // Revert to the previous model view matrix (identity matrix)
    modelViewMatrix.PopMatrix();
    
    // Perform buffer swap
    glutSwapBuffers();
}
Copy the code

After reading the code, let’s first look at the change of the top data in the modelViewMatrix in this code, as shown below:

We learned about coordinate changes in OpenGL earlier, as shown below:

  1. Local Coordinate is the starting Coordinate of the object;
  2. The next step is to change the local coordinates of the object to the world coordinates byThe model of matrixThe implementation. A model matrix is a transformation matrix that moves, scales, and rotates an object to the position or orientation it should be in.
  3. The object’s world coordinates are then converted to observer coordinates, so that each coordinate is viewed from the camera’s or observer’s point of view. The process is throughThe view matrixThe implementation.
  4. Once the coordinates reach the observation space, we need to project them to the clipping coordinates. Clipping coordinates are processed to the range of -1.0 to 1.0 and determine which vertices will appear on the screen. To transform vertex coordinates from observation to clipping space, we need to define aProjection matrix, which specifies the coordinates of a range, such as -1000 to 1000 on each dimension. The projection matrix then transforms the coordinates within this specified range to the range of normalized device coordinates (-1.0, 1.0). All coordinates outside the range are not mapped to the range between -1.0 and 1.0, so they are cropped out.
  5. Finally, we Transform the clipping coordinates to screen coordinates, using a process called Viewport Transform. Viewport transform transforms coordinates in the range -1.0 to 1.0 to the range defined by the glViewport function. The final coordinates will be sent to the raster and converted into fragments.

Setting drawing Parameters

void SetupRC(a)
{
    // Grey background
    glClearColor(0.7 f.0.7 f.0.7 f.1.0 f );
    shaderManager.InitializeStockShaders();
    glEnable(GL_DEPTH_TEST);
    // Set up the transform pipeline to use two matrix stacks
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    cameraFrame.MoveForward(15.0 f);
    
    /* Common function: void GLBatch::Begin(GLenum primitive,GLuint nVerts,GLuint nTextureUnits = 0); Parameter 1: using primitive parameters of 2:3: vertices parameters texture coordinates (optional) / / is responsible for the vertex coordinates void GLBatch: : CopyVertexData3f (GLFloat * vNorms); Void GLBatch::End(void); // Data replication is complete. * /
    // Define some points, triangular shapes.
   
    GLfloat vCoast[9] = {
        3.3.0.0.3.0.3.0.0
        
    };
    
    // Use the dot form
    pointBatch.Begin(GL_POINTS, 3);
    pointBatch.CopyVertexData3f(vCoast);
    pointBatch.End();
    
    // Through the line form
    lineBatch.Begin(GL_LINES, 3);
    lineBatch.CopyVertexData3f(vCoast);
    lineBatch.End();
    
    // Through the line segment form
    lineStripBatch.Begin(GL_LINE_STRIP, 3);
    lineStripBatch.CopyVertexData3f(vCoast);
    lineStripBatch.End();
    
    // Through the line loop form
    lineLoopBatch.Begin(GL_LINE_LOOP, 3);
    lineLoopBatch.CopyVertexData3f(vCoast);
    lineLoopBatch.End();
    
// Create a pyramid with triangles
    GLfloat vPyramid[12] [3] = {
        2.0 f.0.0 f.2.0 f.2.0 f.0.0 f.2.0 f.0.0 f.4.0 f.0.0 f.2.0 f.0.0 f.2.0 f.2.0 f.0.0 f.2.0 f.0.0 f.4.0 f.0.0 f.2.0 f.0.0 f.2.0 f.2.0 f.0.0 f.2.0 f.0.0 f.4.0 f.0.0 f.2.0 f.0.0 f.2.0 f.2.0 f.0.0 f.2.0 f.0.0 f.4.0 f.0.0 f
        
    };

// GLfloat vPyramid[12][3] = {
/ / 4.0 f, f, 0.0-4.0 f,
/ f / 4.0, 0.0 f to 4.0 f,
0.0 f/f / 0.0, 8.0, f,
//
/ f / 4.0, 0.0 f to 4.0 f,
4.0 f/f / 4.0, 0.0, f,
0.0 f/f / 0.0, 8.0, f,
//
4.0 f/f / 4.0, 0.0, f,
/ / 4.0 f, f 0.0, 4.0 f,
0.0 f/f / 0.0, 8.0, f,
//
/ / 4.0 f, f 0.0, 4.0 f,
/ / 4.0 f, f, 0.0-4.0 f,
0.0 f/f / 0.0, 8.0, f};
    
    Use GL_TRIANGLES to define a new triangle for every three vertices
    triangleBatch.Begin(GL_TRIANGLES, 12);
    triangleBatch.CopyVertexData3f(vPyramid);
    triangleBatch.End();
    
    
    // The triangle sector is hexagonal
    GLfloat vPoints[100] [3];    
    int nVerts = 0;
    / / radius
    GLfloat r = 3.0 f;
    // origin (x,y,z) = (0,0,0);
    vPoints[nVerts][0] = 0.0 f;
    vPoints[nVerts][1] = 0.0 f;
    vPoints[nVerts][2] = 0.0 f;
    
    
    //M3D_2PI means 2Pi, just like a circle. Draw a circular
    for(GLfloat angle = 0; angle < M3D_2PI; angle += M3D_2PI / 6.0 f) {
        
        // Array subscript increment (each increment represents one vertex)
        nVerts++;
        PI /* arc length is equal to the radius times the Angle, which is in radians, not in the usual Angle system and since we know what the cosine of theta is, the Angle is equal to the arccosine of theta, we can just take an inverse trigonometric function */
        // cos(Angle) * radius
        vPoints[nVerts][0] = float(cos(angle)) * r;
        // sine (Angle) * radius
        vPoints[nVerts][1] = float(sin(angle)) * r;
        // The coordinates of z
        vPoints[nVerts][2] = 0.5 f;
    }
    
    // Draw a total of 7 vertices in front of the end sector (including the center of the circle)
    // Add a closed endpoint
    // Add demo: mask 177-180 lines of code and change the drawing node to 7. The triangle fan cannot be closed.
    nVerts++;
    vPoints[nVerts][0] = r;
    vPoints[nVerts][1] = 0;
    vPoints[nVerts][2] = 0.0 f;
    
    / / load!
    //GL_TRIANGLE_FAN fanned out around a circle center, sharing a set of triangles with adjacent vertices
    triangleFanBatch.Begin(GL_TRIANGLE_FAN, 8);
    triangleFanBatch.CopyVertexData3f(vPoints);
    triangleFanBatch.End();
    
    // A triangular strip, a small ring or cylindrical segment
    // Vertex subscript
    int iCounter = 0;
    / / radius
    GLfloat radius = 3.0 f;
    // From 0 degrees to 360 degrees, with a step length of 0.3 radians
    for(GLfloat angle = 0.0 f; angle <= (2.0 f*M3D_PI); angle += 0.3 f)
    {
        // Maybe the vertex of the circle is X,Y
        GLfloat x = radius * sin(angle);
        GLfloat y = radius * cos(angle);
        
        // Draw 2 triangles (they have the same x and y vertices, but different z points)
        vPoints[iCounter][0] = x;
        vPoints[iCounter][1] = y;
        vPoints[iCounter][2] = 0.5;
        iCounter++;
        
        vPoints[iCounter][0] = x;
        vPoints[iCounter][1] = y;
        vPoints[iCounter][2] = 0.5;
        iCounter++;
    }
    
    // Close the loop
    printf("Number of vertices with triangle: %d\n",iCounter);
    // End the loop, generating 2 triangles in the loop position
    vPoints[iCounter][0] = vPoints[0] [0];
    vPoints[iCounter][1] = vPoints[0] [1];
    vPoints[iCounter][2] = 0.5;
    iCounter++;
    
    vPoints[iCounter][0] = vPoints[1] [0];
    vPoints[iCounter][1] = vPoints[1] [1];
    vPoints[iCounter][2] = 0.5;
    iCounter++;
    
    // GL_TRIANGLE_STRIP A group of triangles that share vertices on a strip
    triangleStripBatch.Begin(GL_TRIANGLE_STRIP, iCounter);
    triangleStripBatch.CopyVertexData3f(vPoints);
    triangleStripBatch.End();
}
Copy the code

The important thing to note here is that you choose different connections when drawing different shapes. See OpenGL basic primitives

Open the main loop

This is equivalent to entering a while loop, in which the program continuously checks the user’s input and shows what function to call back based on the user’s input.

The above is my OpenGL basic metagraph drawing case analysis, if there are fallacies, please leave a message. If you need source code, please leave a message.