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:
- Local Coordinate is the starting Coordinate of the object;
- The next step is to change the local coordinates of the object to the world coordinates by
The model of matrix
The implementation. A model matrix is a transformation matrix that moves, scales, and rotates an object to the position or orientation it should be in. - 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 through
The view matrix
The implementation. - 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 a
Projection 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. - 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.