OpenGL Note 15 vertex data

Source: Internet
Author: User

All the contents of this lecture are packed in a cube, hehe.
Hehe, draw a cube, simple ah, we learned the first lesson of the second lesson, will already.
Don't worry, the cube is simple, but here's just an example of a cube to illustrate how OpenGL is improving in drawing.
Start with a primitive approach
A cube has six faces, each face is a square, well, draw six squares on it.

Glbegin (gl_quads);
glvertex3f (...);
glvertex3f (...);
glvertex3f (...);
glvertex3f (...);

// ...
Glend ();



To draw six squares, we specify four vertices for each square, and finally we need to specify 6*4=24 vertices. But we know that a cube actually has only eight vertices in total, and to specify 24 times, it means that each vertex is actually reused three times, which is not a good phenomenon. At the very least, repeating cumbersome code like the above is easy to make mistakes. Slightly careless, even if the same vertex may be assigned to a different vertex.
If we define an array, put eight vertices in an array, and then use pointers each time the vertices are specified, instead of using direct data, this avoids the possibility of a large amount of data being taken into account when specifying vertices, thus reducing the likelihood of code errors.

Save eight vertices of a cube into an array
static const Glfloat Vertex_list[][3] = {
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
// ...
};
When specifying vertices, use pointers instead of directly using specific data
Glbegin (gl_quads);
GLVERTEX3FV (Vertex_list[0]);
GLVERTEX3FV (vertex_list[2]);
GLVERTEX3FV (Vertex_list[3]);
GLVERTEX3FV (vertex_list[1]);

// ...
Glend ();



After the change, the code is much longer, but it's really easy to read. It's easy to see that the No. 0, 2, 3, 1, four vertices form a square.
With a little observation you can see that we use a lot of GLVERTEX3FV functions, in fact each sentence is only the vertex ordinal is not the same, so we can then define an ordinal array, all the serial number is also put in. This makes the code much simpler.

Save eight vertices of a cube into an array
static const Glfloat Vertex_list[][3] = {
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
};

Save the ordinal of the vertex you want to use in an array
static const Glint Index_list[][4] = {
0, 2, 3, 1,
0, 4, 6, 2,
0, 1, 5, 4,
4, 5, 7, 6,
1, 3, 7, 5,
2, 6, 7, 3,
};

int I, J;

The code is simple when you draw it.
Glbegin (gl_quads);
for (i=0; i<6; ++i)//have six faces, loop six times
for (j=0; j<4; ++j)//four vertices per face, four cycles
GLVERTEX3FV (Vertex_list[index_list[i][j]);
Glend ();



In this way, we get a more mature version of the drawing cube. Its data and program code are basically separate, all vertices are placed in an array, using the ordinal of the vertex into another array, and the use of these two arrays to draw the cube code is very simple.
The following image can help you understand the number of vertices.


Right on our face, in counter-clockwise order, back to our face, then in a clockwise order, so that the above index_list array.
Why do you have to follow the clockwise counterclockwise rule? Because doing so ensures that no matter what the angle of view, see is "positive", not the back. In the calculation of light, the front and back of the treatment may be different, in addition, the elimination of the back only to draw the front, can improve the efficiency of the program operation. (for front, back, and culling, see lesson three, drawing some details on geometry)
For example, call the following code before you draw:

Glfrontface (GL_CCW);
Glcullface (Gl_back);
Glenable (Gl_cull_face);
Glpolygonmode (Gl_front_and_back, gl_line);


The drawing is only the front, and only the edges are displayed, not populated.
Effect

Vertex array
(Hint: the vertex array is the functionality provided by OpenGL 1.1)
In the previous method, we separated the data from the code and looked like a cube could be drawn with just eight vertices. But in fact, the loop still executes the 6*4=24 times, that is to say, although the structure of the code is clear, but the efficiency of the program, or the original method is the same.
Reducing the number of calls to functions is one of the ways to improve operational efficiency. So we thought about the display list. The code that draws the cube is loaded into a display list, which can be called only after the display list is invoked.
This looks good, but the display list has one drawback, which is that once established, it cannot be changed. If we are not going to draw a cube, but a person who can walk around, as the characters move around, the position of the limbs changes, there is little way to put all the content into a display list. You must use a separate display list for each action, which can cause a large number of display list management difficulties.
Vertex arrays are a way to solve this problem. When using a vertex array, as in the previous method, save all vertices with an array and save the number of vertices with an array. But at the end of the drawing, instead of writing the circular statement one by one, the specified vertex is notified OpenGL, the "array of saved vertices" and "the array where the vertex numbers are saved", where OpenGL automatically finds vertices and draws them.
The following code illustrates how a vertex array is used:

Glenableclientstate (Gl_vertex_array);
Glvertexpointer (3, gl_float, 0, vertex_list);
Gldrawelements (Gl_quads, Gl_unsigned_int, index_list);



which
Glenableclientstate (Gl_vertex_array); Indicates that a vertex array is enabled.
Glvertexpointer (3, gl_float, 0, vertex_list); Specifies the position of the vertex array, 3 means that each vertex is composed of three quantities (x, y, z), and gl_float indicates that each quantity is a value of type glfloat. The third parameter, 0, is described later in the "Stride parameter". The last vertex_list indicates the actual position of the array.
Gldrawelements (Gl_quads, Gl_unsigned_int, index_list); Finds the corresponding vertex based on the ordinal in the ordinal array, and finishes drawing. Gl_quads represents a quadrilateral, 24 means a total of 24 vertices, and gl_unsigned_int indicates that each ordinal in an ordinal array is a Gluint type value, and index_list indicates the actual position of the ordinal array.
The above three lines of code replace the original loop. As you can see, the original glbegin/glend is no longer needed, and you do not need to call the Glvertex* series function to specify vertices, so you can significantly reduce the number of function calls. In addition, the contents of the array can be modified at any time, more flexible than the display list.

A little more detailed explanation.
Vertex arrays are actually multiple arrays, vertex coordinates, texture coordinates, normal vectors, vertex colors, and so on, each of the vertices ' properties can be assigned an array and then accessed using a uniform ordinal number. For example, the sequence number 3 means that the 3rd element of the color array is taken as a color, the 3rd element of the texture coordinate array is taken as the texture coordinate, the 3rd element of the normal vector array is taken as the normal vector, and the 3rd element of the vertex coordinate array is taken as the vertex coordinate. Combine all the data and finally get a vertex.
Each array can be turned on and off separately with Glenableclientstate/gldisableclientstate.
Glenableclientstate (Gl_vertex_array);
Glenableclientstate (Gl_color_array);
Glenableclientstate (Gl_normal_array);
Glenableclientstate (Gl_texture_coord_array);
Use the following function to specify the position of the array:
Glvertexpointer
Glcolorpointer
Glnormalpointer
Gltexcoordpointer

Why not use the original glenable/gldisable function, but specifically specify a glenableclientstate/gldisableclientstate function? This has to do with OpenGL's working mechanism. OpenGL at design time, think that the whole OpenGL system can be divided into two parts, part of the client, it is responsible for sending OpenGL command. Part of the service side is responsible for receiving OpenGL commands and performing the appropriate operations. For a personal computer, the CPU, memory and other hardware, as well as the user-written OpenGL program as a client, and OpenGL drivers, display devices, and so on as the server.
In general, all states are stored on the server side, making it easy for OpenGL to use. For example, if textures are enabled, the server often needs to know this state when drawing, and the client OpenGL program we write needs to know this state only rarely. So it is more advantageous to put this state on the server side.
But the state of the vertex array is different. We specify vertices, which are actually sending vertex data from the client to the server. Whether to enable vertex arrays, just control how vertex data is sent. The service side receives the vertex data regardless of how the vertex data is specified (you can use glbegin/glend/glvertex* directly, or you can use a vertex array). Therefore, the server does not need to know whether the vertex array is open. Therefore, it is reasonable for the state of the vertex array to be placed on the client.
To indicate the difference between the server state and the client state, the state of the server is glenable/gldisable, and the client state is glenableclientstate/gldisableclientstate.
The STRIDE parameter.
Vertex arrays do not require that all data be stored continuously. If the data is not continuously stored, specify the interval between the data.
For example, we use a struct to hold the data in the vertex. Note that each vertex has extra data in addition to the coordinates (here is a value of type int).

typedef struct __POINT__ {
Glfloat Position[3];
int id;
} point;
Point vertex_list[] = {
-0.5f, -0.5f, -0.5f, 1,
0.5f, -0.5f, -0.5f, 2,
-0.5f, 0.5f, -0.5f, 3,
0.5f, 0.5f, -0.5f, 4,
-0.5f, -0.5f, 0.5f, 5,
0.5f, -0.5f, 0.5f, 6,
-0.5f, 0.5f, 0.5f, 7,
0.5f, 0.5f, 0.5f, 8,
};
Static Glint index_list[][4] = {
0, 2, 3, 1,
0, 4, 6, 2,
0, 1, 5, 4,
4, 5, 7, 6,
1, 3, 7, 5,
2, 6, 7, 3,
};
Glenableclientstate (Gl_vertex_array);
Glvertexpointer (3, gl_float, sizeof (point), vertex_list);
Gldrawelements (Gl_quads, Gl_unsigned_int, index_list);



Note the last three lines of code, and you can see that almost all the places are the same as the original, only the third parameter of the Glvertexpointer function is different. This parameter is stride, which represents "the number of bytes separated from the beginning of one data to the beginning of the next." This is set to sizeof (point) just fine. If set to 0, the data is tightly arranged, and for 3 glfloat, when the data is tightly arranged, stride is actually 3*4=12.
Mixed arrays. If you need to use both a color array, an array of vertex coordinates, an array of texture coordinates, and so on, there is a way to mix all of the data together and assign it to the same number of groups. This is the mixed array.

Glfloat arr_c3f_v3f[] = {
1, 0, 0, 0, 1, 0,
0, 1, 0, 1, 0, 0,
0, 0, 1,-1, 0, 0,
};
Gluint index_list[] = {0, 1, 2};
Glinterleavedarrays (gl_c3f_v3f, 0, arr_c3f_v3f);
Gldrawelements (Gl_triangles, 3, Gl_unsigned_int, index_list);



Glinterleavedarrays, you can set a mixed array. This function automatically calls Glvertexpointer, Glcolorpointer and other functions, and automatically turns on or disables the associated array.
The first parameter of a function represents the type of the mixed array. For example gl_c3f_v3f: Three floating-point numbers as color, three floating-point numbers as vertex coordinates. There can also be other formats, such as GL_V2F, gl_v3f, gl_c4ub_v2f, gl_c4ub_v3f, gl_c3f_v3f, gl_n3f_v3f, gl_c4f_n3f_v3f, gl_t2f_v3f, GL_T4F_V4F, gl_t2f_c4ub_v3f, gl_t2f_c3f_v3f, gl_t2f_n3f_v3f, gl_t2f_c4f_n3f_v3f, gl_t4f_c4f_n3f_v4f and so on. where t represents the texture coordinates, c is the color, n is the normal vector, and V represents the vertex coordinates.
Again, the difference between a vertex array and a display list. Both can significantly reduce the number of calls to the function, but there are advantages.
For vertex arrays, vertex data is stored in memory, which is stored on the client. Each time you draw, you need to send all the vertex data from the client (memory) to the server (display device) and then handle it. For a display list, the vertex data is placed in the display list, and the display list itself is stored on the server side, so the data will not be sent repeatedly.
For vertex arrays, because vertex data is placed in memory, it can be modified at any time, and each time it is drawn, the contents of the current array are sent and drawn as vertex data. For the display list, the data is already stored in the server segment and cannot be removed, so it cannot be modified.
In other words, the display list can avoid repeated sending of data, the efficiency is higher; Although the vertex array will send data repeatedly, it is more flexible because the data can be modified at any time.
Vertex Buffer Object
(Tip: The vertex buffer object is a feature provided by OpenGL 1.5, but it is an ARB extension before becoming a standard and can be used with the gl_arb_vertex_buffer_object extension.) As already mentioned, the function name of the ARB extension ends with the letter arb, the constant name ends with the letter _arb, and the standard function, the constant, removes the ARB typeface. Many OpenGL implementations support both the standard and ARB extended versions of the vertex buffer object. We're talking about the arb extension here, because most PCs currently support ARB extensions, but a few graphics cards only support OpenGL 1.4 and cannot use the standard version. )
Before we say that vertex arrays and display lists have pros and cons when drawing cubes, is there a way to bring their advantages together and reduce the drawbacks as much as possible? The vertex buffer object was born to solve this problem. Its data is stored on the server side, while also allowing the client to modify flexibly, taking into account the operational efficiency and flexibility.
Vertex buffer objects have a lot in common with texture objects. First, assign a buffer object number, then specify the data for the corresponding numbered buffer object, and you can modify the data in it at any time. The following table can help to understand the analogy.

Texture object Vertex Buffer Object
Allocation number Glgentextures Glgenbuffersarb
Binding (specified as the currently used object) Glbindtexture Glbindbufferarb
Specify Data glteximage* Glbufferdataarb
modifying Data gltexsubimage* Glbuffersubdataarb



Vertex data and sequence numbers each use a different buffer. Specifically, the vertex data is placed in a buffer of type Gl_array_buffer_arb, and the ordinal data is placed in a buffer of type Gl_element_array_buffer_arb.
The specific situation can be explained by the following code:

Static Gluint Vertex_buffer;
Static Gluint Index_buffer;

Assigns a buffer and assigns vertex data to it
Glgenbuffersarb (1, &vertex_buffer);
Glbindbufferarb (Gl_array_buffer_arb, Vertex_buffer);
Glbufferdataarb (Gl_array_buffer_arb,
sizeof (vertex_list), vertex_list, Gl_static_draw_arb);

Assigns a buffer and assigns ordinal data to it
Glgenbuffersarb (1, &index_buffer);
Glbindbufferarb (Gl_element_array_buffer_arb, Index_buffer);
Glbufferdataarb (Gl_element_array_buffer_arb,
sizeof (index_list), index_list, Gl_static_draw_arb);



When you specify buffer data, the last parameter is a hint about performance. Altogether there are Stream_draw, Stream_read, Stream_copy, Static_draw, Static_read, Static_copy, Dynamic_draw, DYNAMIC_READ, DYNAMIC_ Copy these nine kinds. Each represents the frequency and purpose of use, and OpenGL performs some degree of performance optimization based on these hints.
(Hints are just hints, not hard rules.) That is, even if Stream_draw is used, it tells OpenGL that the buffer data will not be modified after it is specified, but can still be modified in the future, but there may be a significant performance cost when modifying it.

When using Glbindbufferarb, various OpenGL functions that use pointers as parameters change behavior.
In GLCOLOR3FV, for example, this function usually takes a pointer as a parameter and extracts a contiguous number of three floating-point numbers from the position pointed to by the pointer as the current color.
With Glbindbufferarb, however, this function no longer takes data from the position indicated by the pointer. The function converts the pointer to an integer, assuming that the result of the conversion is k, the data is taken from the nth byte of the current buffer. In particular, if we write GLCOLOR3FV (null), since NULL is usually zero after converting to an integer, the data is taken from the No. 0 byte of the buffer, that is, the data is taken from the first position of the buffer.
In this way, the original writing

Glvertexpointer (3, gl_float, 0, vertex_list);
Gldrawelements (Gl_quads, Gl_unsigned_int, index_list);


After using the buffer object, it becomes the

Glvertexpointer (3, gl_float, 0, NULL);
Gldrawelements (Gl_quads, Gl_unsigned_int, NULL);

The following is the complete code that uses the vertex buffer object:

Static Glfloat vertex_list[][3] = {
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
};

Static Glint index_list[][4] = {
0, 2, 3, 1,
0, 4, 6, 2,
0, 1, 5, 4,
4, 5, 7, 6,
1, 3, 7, 5,
2, 6, 7, 3,
};

if (glee_arb_vertex_buffer_object) {
If the vertex buffer object is supported
static int isfirstcall = 1;
Static Gluint Vertex_buffer;
Static Gluint Index_buffer;
if (Isfirstcall) {
First Call, initialize buffer
Isfirstcall = 0;

Assigns a buffer and assigns vertex data to it
Glgenbuffersarb (1, &vertex_buffer);
Glbindbufferarb (Gl_array_buffer_arb, Vertex_buffer);
Glbufferdataarb (Gl_array_buffer_arb,
sizeof (vertex_list), vertex_list, Gl_static_draw_arb);

Assigns a buffer and assigns ordinal data to it
Glgenbuffersarb (1, &index_buffer);
Glbindbufferarb (Gl_element_array_buffer_arb, Index_buffer);
Glbufferdataarb (Gl_element_array_buffer_arb,
sizeof (index_list), index_list, Gl_static_draw_arb);
}
Glbindbufferarb (Gl_array_buffer_arb, Vertex_buffer);
Glbindbufferarb (Gl_element_array_buffer_arb, Index_buffer);

The actual use is very similar to the vertex array, except that the actual array is not specified when the array is specified and NULL is specified instead
Glenableclientstate (Gl_vertex_array);
Glvertexpointer (3, gl_float, 0, NULL);
Gldrawelements (Gl_quads, Gl_unsigned_int, NULL);
} else {
Vertex buffer objects are not supported
Working with vertex arrays
Glenableclientstate (Gl_vertex_array);
Glvertexpointer (3, gl_float, 0, vertex_list);
Gldrawelements (Gl_quads, Gl_unsigned_int, index_list);
}

You can assign multiple buffer objects, vertex coordinates, color, texture coordinates, and so on, and you can use a single buffer for each.
Each buffer can have different performance hints, such as when drawing a moving character, vertex coordinate data often changes, but normal vector, texture coordinates and so on will not change, can give different performance hints, to improve performance.
Summary

This lesson starts with drawing a cube and describes the processing of OpenGL in each version of the drawing.
When drawing objects, the data should be stored separately, try not to write around like glvertex3f (1.0f, 0.0f, 1.0f) such code. By storing vertex coordinates and vertex ordinals in a separate array, you can make the code easier to draw.
All commands for drawing objects can be loaded into a display list, which avoids repeated data transfer. However, because once the display list is established, it cannot be modified, so the flexibility is poor.
OpenGL version 1.1, provides a vertex array. It can specify the location of the data, the position of the vertex ordinal, thus effectively reducing the number of function calls, to achieve the purpose of improving efficiency. But it does not avoid duplication of data transmission, so efficiency needs to be further improved.
OpenGL version 1.5, which provides a vertex buffer object. It combines the advantages of displaying lists and vertex arrays, with both operational efficiency and flexibility, and is a good choice for drawing objects. If OpenGL 1.5 is not supported on the system, you can also check whether extended Gl_arb_vertex_buffer_object is supported.

OpenGL Note 15 vertex data

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.