
Face clipping
Try to imagine a cube and calculate the maximum number of its faces, which you can see from any direction. If your imagination is not too vivid, then, most likely, you will come to the conclusion that this number is 3. From whatever point or direction you look at the cube, you can never see more than three faces. So why spend the computing power on drawing the remaining three faces, if they are not visible? If we could discard their processing in some way, we would save more than half of the fragment shader executions!
It says “more than half” because in certain situations only two, or even one facet of a cube are visible. In such cases, more than 50% will be saved.
The idea is good, but a new task appears: it is necessary to determine which face is not visible from the position of the observer.
Imagine any closed volumetric shape: each of its faces has two sides. And one of these parties will be turned to the observer, and the other from him. What if we draw only the faces facing the observer?
It is in this process that the essence of the procedure for cutting faces lies. OpenGL checks all facets for orientation, allowing only those facing the observer to be rendered, and at the same time cutting off those that are turned from him, which ultimately saves a lot of calls to the fragment shader (which is very expensive!). We just need to somehow convey to OpenGL information about which facets are considered to be faces and which are not. OpenGL uses a resourceful solution to determine the orientation of faces: analysis of the traversal order in the list of vertex data.
Bypass order
When we specify a set of vertices for a triangle, we define them in a specific traversal order: either clockwise (clockwise, CW) or counterclockwise (counter-clockwise, CCW). Each triangle consists of three vertices and we set them in the order of detour, defined relative to the center of the triangle.
As can be seen from the diagram, we first set vertex 1, and then we have a choice: to set vertex 2 or 3, thereby determining the order of traversing a triangle. We give the code for clarity:
float vertices[] = {
Each set of three vertices defining a triangle, as a result, contains data on the order of its traversal. When rendering the primitives you have prepared, OpenGL uses this information to determine whether the next triangle is facial or not. By default, triangles with counterclockwise order are considered frontal.
When specifying a traversal in your set of vertices, you must represent the triangle as if you are looking directly at it, and you specify the vertices in counterclockwise order. The most interesting thing in this imaginary process is that the direct processing of the traversal order occurs at the rasterization stage, i.e. after running the vertex shader. This means that the vertices are indeed transformed to the point of view of the observer.
It turns out that all the vertices of the triangles, which the observer looks at, are really set in the same order as we walked around them. But at the same time the vertices of the triangles on the other side of the cube are now displayed in such a way that their traversal order has become inverted. As a result, the triangles in front of the observer are considered to be facial, and the far triangles are considered to be non-face. Look at the image for better visibility:

In the list of vertices, we defined both triangles in counterclockwise order (the near triangle is specified as 1-2-3, and the back one is also defined as 1-2-3 (if the observer looked at his front side)). However, from this position, the order of the description of the far triangle 1-2-3 is observed given in a clockwise direction. Despite the fact that the vertices of the far triangle were set with the counterclockwise order, when rendering it became clockwise. And this behavior is just necessary for successful cutting off invisible faces.
Rejection of faces
At the beginning of the lesson, it was noted that OpenGL can cut off triangles if they are displayed as non-face. And now, knowing about the method of specifying the traversal order, we can begin to use the clipping function, which is disabled by default in the library.
The vertex data for the cube from past lessons was not formed with the counterclockwise order requirements, so you need a new array of vertices that lies
here . Practice - try to mentally check the order of traversal of each triangle.
You can enable the clipping function as follows:
glEnable(GL_CULL_FACE);
From this point on, all non-face faces will be dropped (try looking inside the cube and make sure that the surfaces of the inner faces are dropped). As a result, we save more than 50% of the passes of processing fragments, but only for such closed figures as a cube. In the previous lesson, we would have to turn off the clipping of faces for objects depicting grass, since they must have both front and non-face faces drawn.
OpenGL allows us to configure exactly which side of the face we would like to drop. Suddenly we need to reject the face edge? To do this, you can use the following call:
glCullFace(GL_FRONT);
The function has three possible parameters:
•
GL_BACK : Rejects only non-faces.
•
GL_FRONT :
Drops only the front edges.
•
GL_FRONT_AND_BACK :
Rejects both faces.
The default is
GL_BACK .
In addition to choosing a face for dropping, it would be nice to be able to specify which order of traversal in a triangle will determine the front face. To do this, use the following function:
glFrontFace(GL_CCW);
The default is
GL_CCW , implying a counterclockwise crawl. The second possible value is
GL_CW , which sets the clockwise round.
As a simple experiment, you can set the clipping of non-face faces and the clockwise traversal order as specifying the front faces, instead of the standard counterclockwise traversal:
glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glFrontFace(GL_CW);
As a result, only the rear faces of the cube will remain visible:
Conversely, this effect can be achieved by cutting off the front faces, but using the counterclockwise bypass as a defining front edge:
glEnable(GL_CULL_FACE); glCullFace(GL_FRONT); glFrontFace(GL_CW);
Summing up, we can say that the mechanism for cutting off edges is an excellent tool for increasing productivity, which requires little effort to work. In addition, you only need to keep track of to which objects in the scene it is beneficial to apply clipping, and for which clipping is unacceptable.
Exercises
Try to override the data in the list of vertices so that each triangle is now defined in clockwise order? Display the scene when defining the face triangles as described clockwise.
The solution is
here .
PPS : We have a
telegram-konf to coordinate transfers. If there is a desire to fit into the cycle, then you are welcome!