OpenGL ES /OpenGL ES /OpenGL ES /OpenGL ES /OpenGL ES Image rendering implementation and rendering problems OpenGL/OpenGL ES introduction: basic transformation – beginning vector/matrix OpenGL/OpenGL ES introduction: texture exploration – common API parsing OpenGL/OpenGL ES introduction: OpenGL/OpenGL ES: Vertex shaders and slice shaders (OpenGL transition OpenGL ES) Introduction to OpenGL/OpenGL ES: GLKit usage and case studies

The realization of image rendering

Let’s start with a doughnut rendered with a flat shader

Code implementation:

  • mainFunction, program entry. soOpenGLProcessing graphics, images are all chained form, as well as basedOpenGLThe packaged image processing framework is also chained programming
gltSetWorkingDirectory(argv[0]); glutInit(&argc, argv); / / initialize window glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL); glutInitWindowSize(800, 600); glutCreateWindow("ZB"); // glutReshapeFunc(ChangeSize); glutSpecialFunc(SpecialKeys); glutDisplayFunc(RenderScene); GLenum err = glewInit();if(GLEW_OK ! = err) { fprintf(stderr,"GLEW Error:%s\n", glewGetErrorString(err));
        return1; } // active trigger, ready to work SetupRC(); GlutMainLoop (); // An infinite loop that handles Windows and user input to the operating systemreturn 0;
Copy the code
  • changeSizethroughglutReshapeFuncRegister as a remodeling function that is called to resize the window/viewport when the window is first created or the screen size changes
// The height cannot be 0if(h == 0) { h = 1; } // Set the viewport to the window size glViewport(0, 0, w, h); / / create the projection matrix and loading it in the projection matrix stack viewFrustum. SetPerspective (35,float(w)/float(h), 1, 1000); projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix()); / / initialize the rendering pipeline transformPipeline SetMatrixStacks (modelViweMatix projectionMatrix);Copy the code
  • SetupRCSettings need to render graphics related vertex data, color values, etc., manually inmainA function call
// 1. Set background glClearColor(0.3, 0.3, 0.3, 1); / / 2. Initialize shader manager shaderManager. InitializeStockShaders (); Viewframe. MoveForward(5.0); // 3. /** void gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloatminorRadius, GLint numMajor, GLint numMinor); Parameter 1: GLTriangleBatch Container help class parameter 2: outer edge radius parameter 3: inner edge radius parameter 4, 5: the number of subdivided cells of primary and secondary radii */ gltMakeTorus(torusBatch, 1, 0.3, 88, 33); GlPointSize (4.0);Copy the code
  • RenderScenethroughglutDisplayFuncRegister as a rendering function. This function is called when the screen changes or the developer renders actively, and is used to implement the data -> rendering process
/ / 1. The clear window and depth buffer glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); / / 2. The camera model matrix, the matrix into pressure stack - storage a state modelViweMatix PushMatrix (viewFrame); // 3. Set the drawing color GLfloatvRed[] = {1, 0, 0, 1}; / / 4. Using flat shader shaderManager. UseStockShader (GLT_SHADER_FLAT, transformPipeline GetModelViewProjectionMatrix (), vRed); // 5. Draw torusbatch.draw (); Modelviwematix.popmatrix (); GlutSwapBuffers ();Copy the code

At this point, a compilation run should produce the image shown above. Using a flat shader. Pretty low.

Here’s a cool wave of operations based on this.

There is a function registered in main, SpecialKeys, which, as the name suggests, controls up, down, left, and right keys

// 1if(key == GLUT_KEY_UP) {// 2. Adjust observer position according to direction // parameter 1: Viewframe.rotateworld (m3dDegToRad(-5), 1, 0, 0); }if (key == GLUT_KEY_DOWN) {
        viewFrame.RotateWorld(m3dDegToRad(5), 1, 0, 0);
    }
    if (key == GLUT_KEY_LEFT) {
        viewFrame.RotateWorld(m3dDegToRad(-5), 0, 1, 0);
    }
    if(key == GLUT_KEY_RIGHT) { viewFrame.RotateWorld(m3dDegToRad(5), 0, 1, 0); } // 3. Refresh glutPostRedisplay();Copy the code

Look at the implementation

In a more realistic move, we use the default light shader

/ / 1. The clear window and depth buffer glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); / / 2. Pressing the camera matrix into the model of matrix modelViweMatix. PushMatrix (viewFrame); // 3. Set the drawing color GLfloatvRed[] = {1, 0, 0, 1}; / / 4. Using flat shader / / shaderManager UseStockShader (GLT_SHADER_FLAT, transformPipeline GetModelViewProjectionMatrix (), vRed); GLT_SHADER_DEFAULT_LIGHT // Parameter 1: GLT_SHADER_DEFAULT_LIGHT // Parameter 2: model view matrix // Parameter 3: projection matrix // Parameter 4: Basic color value shaderManager. UseStockShader (GLT_SHADER_DEFAULT_LIGHT, transformPipeline GetModelViewMatrix (), transformPipeline.GetProjectionMatrix(), vRed); // 5. Draw torusbatch.draw (); // 6. Restore modelViwematix.popmatrix (); GlutSwapBuffers ();Copy the code

The renderings are as follows:

As you can see, we had a problem with our rendering.

Problem analysis

When using the default light shader, the illuminated side is the original color due to the illumination, while the backlit side is dark and invisible. In fact, it is easy to understand that the sun shines on the earth, the sunny side is day, and the backlit side is night.

When drawing a 3D scene, we need to decide which parts are visible to the observer and which parts are not, and what parts are not visible should be discarded as soon as possible. For example, behind an opaque wall, there should be no render, a condition called hidden face elimination

Here’s a solution to this problem.

A solution to the problem

Oil painting algorithm

First draw objects far away from the observer in the scene, and then draw objects near, as shown below

The order of drawing is red, yellow and gray, so that the order of rendering can solve the problem of hidden surface elimination.

But there will be some bad problems along with it

  • The efficiency is very low, and the overlapping part will be rendered for many times, which wastes resources
  • For some existing scenes that cannot distinguish the sequence of distance and distance, this method cannot be used to solve the problem, as shown in the following figure

Front and back culling

The first thing you need to figure out is that any plane has 2 sides, front/back, which means you can only see one side at a time.

A cube graph, viewed from any direction, can see up to 3 faces, meaning that the other invisible faces, we do not need to draw it, if we can somehow discard this part of the data, OpenGL rendering performance can be improved by 50%.

Yes, OpenGL can distinguish between heads and tails by analyzing the order of vertex data

Front/back division

  • Face: Triangular faces in the order of counterclockwise vertex links
  • Back side: Triangular faces joined in order by clockwise vertices

Heads and tails in a cube

Analysis:

  • The order of the left triangle vertices is: 1->2->3; The vertices of the right triangle are in order: 1->2->3
  • When the observer is on the right, the right triangle is counterclockwise for heads, and the left triangle is clockwise for tails
  • When the observer is on the left, the triangle on the left is counterclockwise for the front and the triangle on the right is clockwise for the back

Summary: The front and back sides are determined by the order in which the vertices of the triangle are defined and by the direction of the observer. The front and back sides change as the Angle of the observer changes

The relevant code

// Enable surface culling (default backside culling) void glEnable(GL_CULL_FACE); Void glDisable(GL_CULL_FACE); Void glCullFace(GLenum mode); The mode parameter is GL_FRONT, GL_BACK, or GL_FRONT_AND_BACK. The default value is GL_BACK. // The user can also specify the front void glFrontFace(GLenum mode). The mode parameter is: GL_CW, GL_CCW, default GL_CCW // remove the positive implementation glCullFace(GL_BACK); glFrontFace(GL_CW); Or glCullface (GL_FRONT);Copy the code

Concrete code implementation

/ / 1. The clear window and depth buffer glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Enable front and back culling glEnable(GL_CULL_FACE); glFrontFace(GL_CCW); glCullFace(GL_BACK); / / 2. Pressing the camera matrix into the model of matrix modelViweMatix. PushMatrix (viewFrame); // 3. Set the drawing color GLfloatvRed[] = {1, 0, 0, 1}; // The following code is the same as aboveCopy the code

The realization effect is shown as follows:

As you can see, the previous problem has been solved, but there is another embarrassing problem. The donut appears to have a large gap, as those of you who are familiar with graphic rendering will know, this is a depth problem.

The depth of the

The depth is how far that pixel is from the camera in the 3D world, which is the Z value. A depth buffer is an area of memory dedicated to storing the depth value Z for each pixel (drawn on the screen). The larger Z is, the farther away it is from the screen.

So why do we need a deep buffer? When the depth test is not practical, if we draw a relatively close object first and draw a distant object, the distant bitmap will cover the close object because of the later drawing. With the depth buffer, the order in which objects are drawn is not so important. That’s the problem that caused the big gap up there.

In fact, Whenever a depth buffer exists, OpenGL writes the depth value of the pixel to the buffer, unless glDepthMask(GL_FALSE) is called to disallow writing.

Depth test depth buffer and color buffer are corresponding. The color buffer stores the color information of the pixel, while the depth buffer stores the depth information of the pixel. When deciding whether to draw an object surface, first compare the depth value of the pixel corresponding to the surface with the value in the current depth buffer. If the value is greater than the depth buffer, discard the part; otherwise, use the depth value and color value corresponding to the pixel to update the depth buffer and color buffer respectively. This process is called depth testing.

The relevant code

// Enable the depth test glEnable(GL_DEPTH_TEST); // Clear the color buffer and depth buffer glClearColor(0, 0, 0, 1) before drawing the scene; glClear(GL_COLOR_BUFFER_BIT | GL_GEPTH_BUFFER_BIT);Copy the code

Clear depth buffer the default value is 1.0, indicating the maximum depth value, which ranges from (0,1). The smaller it is, the closer it is to the observer, but the farther it is from the observer.

The following is a judgment about depth testing

Void glDepthFunc(GLEnum mode); Void glDepthMask(GKBool value); Value: GL_TURE Enable write GL_FALSE disable write

The final result is as follows:

ZFighting flicker problems

Why the ZFighting flicker problem

After the depth test is enabled, OpenGL will not draw the shaded part of the model, which makes the reality more realistic. However, due to the precision limitation of the depth buffer, OpenGL may fail to correctly judge the depth values of the two models when the depths are almost the same, which will lead to unpredictable results of the depth test. The reality of the phenomenon will flicker.

The solution

  • Step 1: EnablePolygon OffsetWay to solve

    The interval between the depth values can be interpreted as a slight increase in the depth values of the cube before the depth test is performed, so that the depth values of the two overlapping figures can be distinguished.
// Enable Polygon Offset glEnable(GL_POLYGON_OFFSET_FILL); Parameter list: GL_POLYGON_OFFSET_POINT corresponds to the rasterization mode: GL_POINT GL_POLYGON_OFFSET_LINE corresponds to the rasterization mode: GL_LINE GL_POLYGON_OFFSET_FILL corresponds to the rasterization mode: GL_LINE GL_POLYGON_OFFSET_FILL corresponds to the rasterization mode: GL_FILLCopy the code
  • Step 2: Specify the offset

    • throughglPolygon OffsetTo specify.glPolygon OffsetTwo parameters are required:factor , units.
    • eachFragmentWill increase the offset as shown below:

      Offset = ( m * factor ) + ( r * units);

      m: The maximum slope of the depth of a polygon. Understand that the more parallel a polygon is to the near clipping surface, the closer m is to 0.

      r: the minimum discernible difference that can be generated in the depth value of the window coordinate systemOpenGLA platform – specified constant.
    • One greater than 0OffsetIt’s going to push the model further away from you, and the corresponding one is less than zeroOffsetIt pulls the model closer
    • In general, you simply assign -1.0 and -1 toglPolygon OffsetBasically enough to meet the needs.
  • Step 3: Close Polygon Offset

glDisable(GL_POLYGON_OFFSET_FILL);
Copy the code

OK, that’s it, we’ve rendered the donut perfectly. Some of the problems encountered above will also have to be solved.