Any complex 3D model consists of basic geometric elements: points, line segments, and polygon. With these elements, you can create a complex model. Therefore, this part is the basis for learning OpenGL programming.
1. Description and definition of basic elements
OpenGL elements are abstract geometric concepts rather than objects in the real world. Therefore, they must be described using mathematical models. All elements are described by a series of ordered vertex sets. To draw geometric elements in OpenGL, you must use the glbegain () and glend () functions. The parameters passed to the glbegain () function uniquely determine the type of geometric elements to be drawn, the ry element is defined in this function pair. The glend () function indicates the end of the vertex list. For example, the following code draws a polygon:
Glbegin (gl_polygon ); Glvertex2f (0.0, 0.0 ); Glvertex2f (0.0, 3.0 ); Glvertex2f (3.0, 3.0 ); Glvertex2f (4.0, 1.5 ); Glvertex2f (3.0, 0.0 ); Glend (); |
The glbegin (glenum mode) Mark describes the beginning of the vertex list of a geometric element. The mode parameter indicates the description type of the geometric element. The specific types are shown in table 1:
Type |
Description |
Gl_points |
Single vertex set |
Gl_lines |
Multiple groups of Dual-vertex Line Segments |
Gl_polygon |
Single simple fill Convex Polygon |
Gl_traingles |
Multiple groups of independent filled triangles |
Gl_quads |
Multiple groups of independently filled Quadrilateral |
Gl_line_strip |
Do not close the line |
Gl_line_loop |
Closed line |
Gl_traingle_strip |
Linear Continuous filling triangle string |
Gl_traingle_fan |
Slice continuous filling triangle string |
Gl_quad_strip |
Continuous filling of quadrilateral strings |
Table 1. geometric element types
For some geometric elements:
Figure 1. Some geometric elements |
The most important information between glbegin () and glend () is the vertex defined by the glvertex * () function, when necessary, you can also specify the color (only valid for the current or subsequent vertex), normal, texture coordinate, or other for each vertex, that is, call the relevant function:
Function |
Function meaning |
Glcolor *() |
Set current color |
Glindex *() |
Set the current color table |
Glnormal *() |
Set normal coordinates |
Glevalcoord *() |
Generate coordinates |
Glcalllist (), glcalllists () |
Display list |
Gltexcoord *() |
Set texture coordinates |
Gledgeflag *() |
Control boundary plotting |
Glmaterial *() |
Set Material |
Table 2. callable functions between glbegin () and glend ()
It should be pointed out that the points, lines, polygon, and other elements defined by OpenGL are not the same as general mathematical definitions, and there are some differences. One difference stems from the limitations of computer-based computing. All floating point calculation accuracy in OpenGL is limited, so there is a certain error in the coordinate values of points, lines, and polygon. Another difference stems from the limitations of bitmap display. In this way, the smallest display element is a pixel. Although the width of each pixel is small, they are much larger than the point or line width defined in mathematics. When OpenGL is used for computation, although a series of floating point values are used to define the vertex string, each vertex is still displayed in a single pixel, which is just an approximate fit.
Ii. Point)
Vertex is a vertex represented by a floating point value ). 3D coordinates (x, y, z) are used for processing all vertices in OpenGL internal computation. point defined by 2D coordinates (x, y) is 0 by default in OpenGL. Vertex coordinates can also be expressed by homogeneous coordinates (x, y, z, W). If W is not 0.0, the vertices in these homogeneous coordinates are three-dimensional spatial points (x/W, Y/W, Z/W). Generally, W is 1.0 by default.
You can use the glvertex {234} {sifd} [v] (type cords) function to define a vertex. For example:
Glvertex2f (2.0f, 3.0f); // two-dimensional coordinate defines vertex; |
The points defined in OpenGL can have different sizes. The function form is:
Void glpointsize (glfloat size ); |
The size parameter specifies the width (in pixels) of a vertex. The value must be greater than 0.0. The default value is 1.0.
3. Line)
In OpenGL, the line represents the line segment, which is connected by a series of vertices. Specifically, there are three types of independent line segments, strip, and closed strip, as shown in Figure 2:
Figure 2. Three link modes of a line segment |
OpenGL can specify the width of a line and draw different dotted lines, such as dotted lines and dotted lines. The function format is as follows:
1. Void gllinewidth (glfloat width );
Set the line width (in pixels ). The parameter width must be greater than 0.0. The default value is 1.0.
2. Void gllinestipple (glint factor, glushort pattern );
Set the current line to virtual point mode. The pattern parameter is a series of 16-bit binary numbers (0 or 1). It is repeatedly assigned to the specified line. From the low position, each binary represents a pixel, 1 indicates that a pixel is drawn with the current color (or the number specified by the proportional factor). 0 indicates that the current painting is not performed and only one pixel is moved (or the number specified by the proportional factor ). The factor parameter is a proportional factor. It is used to stretch the elements in pattern, that is, repeat 1 or move 0. For example, if factor is 2, it is drawn twice in a row when it hits 1, when 0 is met, two units are moved consecutively. The size range of factor is limited to 1 to 255.
Before creating a dotted line, you must start the dotted line mode, that is, call the gl_line_stipple function. When the function ends, call gldisable (gl_line_stipple) to disable it. The following code draws a dot line:
Void line2i (glint X1, glint Y1, glint X2, glint Y2) { Glbegin (gl_lines ); Glvertex2f (x1, Y1 ); Glvertex2f (X2, Y2 ); Glend (); } Gllinestipple (1, 0x1c47);/* dotted line */ Glenable (gl_line_stipple ); Glcolor3f (0.0, 1.0, 0.0 ); Line2i (1, 450,250,600,250 ); |
Polygon)
(1) convex and concave polygon.
OpenGL defines a polygon that is a closed area linked by a series of line segments. A polygon can be a plane polygon, that is, all vertices are on a plane or a space polygon. OpenGL specifies that the line segments in a polygon cannot be crossed, and there cannot be holes in the area, that is, the polygon must be a convex polygon (that is, the line between any non-adjacent two points of the polygon is located inside the polygon ), it cannot be a concave polygon, or it cannot be accepted by OpenGL functions. See Figure 3. Convex Polygon and concave polygon.
Figure 3. convex and concave polygon |
(2) Boundary flag issues.
In practice, some concave polygon are often drawn. The common solution is to divide them and use multiple triangles instead. Obviously, when drawing these triangles, some sides should not be drawn; otherwise, extra wireframes will appear inside the polygon. The solution provided by OpenGL is to control the creation of some edges by setting the edge flag command gledgeflag (), while other edges are not drawn, which is also called the boundary line or non-boundary line. The command is defined as follows;
Void gledgeflag (glboolean flag ); Void gledgeflag (pglboolean PFLAG ); |
(3) polygon rendering mode.
Polygon draw modes include: full filling, contour point, contour, pattern filling, and specified front and back. The following describes the corresponding OpenGL functions.
1) polygon Mode settings. Its function is:
Void glpolygonmode (glenum face, glenum mode ); |
The face parameter is gl_front, gl_back, or gl_front_and_back. The mode parameter is gl_point, gl_line, or gl_fill, indicating the contour point polygon, contour polygon, or fully-filled polygon respectively. In OpenGL, polygon can be divided into front and back sides to operate on both sides. By default, OpenGL draws the front and back sides of a polygon in the same way and changes the drawing state, you must call the polygonmode () function,
2) set the pattern-filled polygon. Its function is:
Void glpolygonstipple (const glubyte * mask ); |
The mask parameter is a pointer to a 32x32 bitmap. Similar to creating a dotted line, when a person is 1, nothing is painted. Note: Before calling this function, you must first start gl_polygon_stipple; disable it with gldisable (gl_polygon_stipple) when not in use. An example of polygon extension drawing is given below:
Void callback display (void) { /* Fill mode definition (32x32 )*/ Glubyte pattern [] = { 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x80, 0x03, 0xff, 0xff, 0xc0, 0x07, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xff, 0xf8, 0x0f, 0xff, 0xff, 0xf0, 0x07, 0xff, 0xff, 0xe0, 0x03, 0xff, 0xff, 0xc0, 0x01, 0xff, 0xff, 0x80, 0x00, 0xff, 0xff, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x01, 0x80, 0x00 }; Glclear (gl_color_buffer_bit ); /* Draw a triangle filled with a specified pattern */ Glcolor3f (0.9, 0.86, 0.4 ); Glpolygonstipple (pattern ); Glbegin (gl_triangles ); Glvertex2i (310,310 ); Glvertex2i (220,80 ); Glvertex2i (405, 80 ); Glend (); Gldisable (gl_polygon_stipple ); Glflush (); } |
(3) Specify the front and back sides of a polygon.
Its function is:
Void glfrontface (glenum mode ); |
Under normal circumstances, the front and back sides of the polygon in OpenGL are determined by the order of the drawn polygon vertices. The area drawn counterclockwise is the front of the polygon. However, use this function in OpenGL to customize the front of a polygon. The mode parameter of this function specifies the front direction. It can be cl_ccw and cl_cw, respectively specifying the clockwise and clockwise directions of the polygon.
Iv. Calculation and designation of normal vectors
Normal vectors are an important attribute of geometric elements. The normal vector of the geometric object is the unit vector of the vertical and curved plane surface. It defines the spatial direction of the geometric object, especially the direction relative to the light source, determines how much light is acceptable at this point.
OpenGL itself does not provide functions for calculating normal vectors (the task for calculating normal vectors is done by the programmer), but it provides functions that grant Normal directions to the current vertex.
(1) Calculation Method of plane normal direction.
In a plane, there are two intersecting line segments. Assume that one of them is vector W, the other is vector V, and the normal direction of the plane is N, then the normal direction of the plane is equal to the cross product of two vectors (following the right hand rule), that is, n = wxv. For example, if the three vertices of a triangle plane are P0, P1, and P2, and the corresponding vectors are W and V, the method of calculating the normal direction of the Triangle Plane is shown in the following code:
Void getnormal (glfloat GX [3], glfloat Gy [3], Glfloat GZ [3], glfloat * ddnv) { Glfloat w0, W1, W2, V0, V1, V2, NR, NX, NY, NZ; W0 = GX [0]-Gx [1]; W1 = Gy [0]-gy [1]; W2 = GZ [0]-GZ [1]; V0 = GX [2]-Gx [1]; V1 = Gy [2]-gy [1]; v2 = GZ [2]-GZ [1]; Nx = (W1 * v2-w2 * V1); ny = (W2 * v0-w0 * V2); NZ = (W0 * v1-w1 * V0 ); Nr = SQRT (nx * Nx + ny * ny + NZ * NZ); // vector unitization. Ddnv [0] = NX/NR; ddnv [1] = NY/NR; ddnv [2] = nz/NR; } |
The output parameter of the above function is the pointer ddnv, which points to the three components of the forward direction, and the process has been normalized to the Unit.
(2) Calculation of the surface method vector.
There are many ways to calculate the normal direction of each vertex of a surface, such as the method of finding a partial direction based on the function expression. However, in most cases, the polygon in OpenGL is not built by the surface equation, but by the model array. At this time, the method for obtaining the normal vector is to divide the surface into multiple small polygon, then, select the three adjacent vertices V1, V2, and V3 on the polygon (of course, the three vertices cannot be in the same straight line), and obtain the method based on the plane normal vector.
(3) Definition of normal vectors.
The vector definition function of OpenGL is as follows:
Void glnormal3 {bsifd} (type NX, type NY, type NZ ); Void glnormal3 {bsifd} V (const type * V ); |
The first method is used for non-vector form definition, that is, the normal three component values NX, NY, and NZ are given in the function, and the second method is used for vector form definition, set V to a pointer pointing to three elements, for example, V [3] = {NX, NY, NZ }.
5. display the list
(1) define the display list.
In the preceding example, function commands are provided instantaneously. OpenGL instantly executes the corresponding commands. This drawing method is called the instant or instantaneous mode ). The OpenGL display list is composed of a group of pre-stored OpenGL function statements that are left for future calls. when the list is called, the function statements listed in the table are executed in sequence. The display list can be used in the following scenarios:
1) matrix operations
Most matrix operations require OpenGL to calculate the inverse matrix. Both the matrix and its inverse matrix can be saved in the display list.
2) grating bitmap and Image
The grating data defined by the program is not necessarily an ideal format suitable for hardware processing. When compiling and organizing a display list, OpenGL may convert the data into data acceptable to the hardware, which can effectively improve the speed of Bitmap painting.
3) light, material, and lighting model
When you use a complex lighting environment to draw a scenario, the material calculation may be slow. If you place the material definition in the display list, you do not have to re-calculate the material every time you change the material, so you can draw illumination scenarios faster.
4) Texture
Because the hardware texture format may be different from the OpenGL format, if the texture definition is placed in the display list, the format can be converted during compilation and display of the list, rather than during execution, this greatly improves the efficiency.
5) polygon pattern filling mode, you can put the defined pattern in the display list.
OpenGL allows you to create a display list in the format of glbegin () and glend (). The corresponding functions are as follows:
Void glnewlist (gluint list, glenum mode ); Void glendlist (void ); |
The glnewlist () function indicates the beginning of a display list. The subsequent OpenGL function is stored in the display list until the end table function is called ). The list parameter in the glnewlist () function is a positive integer that indicates a unique display list. The possible values of the mode parameter are gl_compile and gl_compile_and_execute; if you want to save function statements in the list only but not execute them, use gl_compile. If you want to save the function statements in the list to a table and execute them one time in an instantaneous manner, use gl_compile_and_execute.
Note: not all OpenGL functions can be stored in the display list and executed through the display list. Generally, function statements used to pass parameters or return values cannot be stored in the display list, because this table may be called outside the scope of the parameter; if such functions are called when the display list is defined, they are executed in an instantaneous manner and not saved in the display list. Sometimes an error occurs when you call the display list function. The following lists OpenGL functions that cannot be saved to the display list:
Gldeletelists () glisenable () Glfeedbackbuffer () glislist () Glfinish () glpixelstore () Glgenlists () glrendermode () Glget * () glselectbuffer () |
After the display list is created, you can call the function that executes the display list to execute it. The function allows multiple times to run the same display list in the program. It can also be used in combination with other functions in an instantaneous manner. The list of executed functions is displayed as follows:
Void glcalllist (gluint list );
The list parameter specifies the display list to be executed. The function statements in the list are executed sequentially according to the order in which they are stored. If the list is not defined, nothing will happen.
(2) Manage the display list
In practical applications, the glgenlist () function is generally called to create multiple display lists, which can avoid accidental deletion and generate a list of unused displays. In addition, you can also use the gldeletelists () function to delete a display list in one or more ranges.
1) gluint glgenlist (glsizei range)
This function assigns a range of adjacent unused display list indexes. This function returns a positive integer index value, which is the first value of a group of consecutive null indexes. The returned indexes are empty and occupied. These indexes will not be returned when you call this function later. If the specified number of indexes cannot be met or the range is 0, the function returns 0.
2) glboolean glislist (gluint List)
This function shows whether the list is occupied. If the index list is occupied, the function returns true; otherwise, the function returns faulse.
3) void gldeletelists (gluint list, glsizei range)
This function deletes a set of continuous display lists, that is, from the display list indicated by the parameter list, the display lists of range are deleted, and the deleted indexes are valid again.
(3) multi-level display list
The establishment of a multi-level display list is to call another display list in one display list, that is, to call glcalllist () between the glnewlist () function and gldlist (). Multi-Level Display lists are useful for constructing objects composed of multiple components, especially when some components need to be reused. To avoid infinite recursion, the nested depth of the display list is up to 64 (maybe higher, depending on different OpenGL implementations). Of course, you can also call the glgetintegerv () function () to obtain the maximum nested depth value. OpenGL also allows you to use a display list that contains several low-level display lists to simulate the creation of an editable display list.
The following code uses list nesting to display a triangle:
Glnewlist (1, gl_compile ); Glvertex3fv (V1 ); Glendlist (); Glnewlist (2, gl_compile ); Glvertex3fv (V2 ); Glendlist ();Glnewlist (3, gl_compile ); Glvertex3fv (V3 ); Glendlist (); Glnewlist (4, gl_compile ); Glbegin (gl_polygon ); Glcalllist (1 ); Glcalllist (2 ); Glcalllist (3 ); Glend (); Glendlist (); |