Use OpenGL VBO Expansion - Nehe Tutorial

zhaozj2021-02-16  55

Use VBO extension

The original article http://nehe.gamedev.net Lesson 45.

Translation: XheartBlue Pan Li Liang Stanly Lee 2004-4-18

Homepage: http://gamehunter.3322.net/xpertsoft/

MSN / Email: XheartBlue@etangc.om

One of the biggest goals of any 3D app is speed, you need to limit the actual rendering of triangles from beginning to end, whether you are sorting, culling, or hierarchical detail (LOD). If other methods are invalid, you want to simply improve the speed of the polygon, usually use OpenGL to provide optimization methods, the vertex array is a better way. Plus the most recent expansion named Vertex Buffer Object, which greatly improves the application's FPS. ARB_VERTEX_BUFFER's extension work and vertex arrays are very similar, except, ARB_VERTX_Buffer loads data to high performance memory of the graphics card. It greatly reduces the rendering time. Of course, this extension is relying on newer hardware, not all graphics cards are supported, so we must use some techniques to balance.

In this tutorial, we will:

1: Load data from the height figure.

2: Use the vertex array more efficient to submit grid data to OpenGL.

3: Load the data into high-performance memory via VBO (Vertex_Buffer_Object).

Now let's define the parameters of several applications.

#define mesh_resolution 4.0f // Variety each vertex corresponds to several pixels

#define mesh_heightscale 1.0F // Mesh Height Scale

// # Define no_vbos // If defined, forced not using VBO

The two parameters starting are defined for high graphs. The first defines the resolution of each pixel in the high graph. The latter is set to load the scale of the vertical direction in the vertical direction. The third constant, if you are defined, it will be forced to use VBO.

Next, we will define the constant, data type, and function pointers of VBO extensions.

// VBO extension definition from Glext.h

#define GL_Array_buffer_arb 0x8892

#define gl_static_draw_arb 0x88E4

TypeDef void (Apientry * PfnglbindBuffRBProc) (GLENUM TARGET, GLUINT BUFFER);

TypeDef void (Apientry * PfngldeleteBuffrsarbproc) (Glsizei N, Const Gluint * Buffers);

TypeDef void (Apientry * PfngnGlgenBuffersarbproc) (Glsizei N, Gluint * Buffers);

TypeDef void (Apientry * PfnglbufferDataArbproc) (Glenum Target, int size, glenum usage);

// VBO extension function pointer

PfnglgenBuffersarbProc GlgenBuffersarb = NULL; // VBO Name Generating Function

PfnglbindBuffRBProc glbindbuffrarb = null; // VBO binding function

Pfnglbufferdataarbproc glbufferdataarb = null; // VBO Data Load Function PfngLDeleteBuffersarbProc GLDELETEBUFFERSARB = NULL; // VBO Delete Function

I just contain the things you need for this demo program. If you need more other extensions, I suggest you download the latest Glext.h file in http://www.opengl.org and use the definition inside (this will make your program more than a certain extent Aesthetic). We will inquire into those functions we will use.

Now let's define basic mathematics objects, plus our own grid, all of which are a very simple design of this demo, I suggest that you have developed a math library.

Class CVert // Vertex

{

PUBLIC:

Float X; // x Component

Float y; // y company

Float z; // z company

}

TypeDef Cvert CVEC; // Defines CVEC and CVERT.

Class ctexcoord // texture coordinate class

{

PUBLIC:

Float u; // u Component

Float v; // v Component

}

Class Cmesh

{

PUBLIC:

// Grid data

INT m_nvertexcount; // Number of vertices

CVERT * m_pvertices; // vertex data

CTEXCOORD * m_PTEXCOORDS; // Texture coordinates

Unsigned int m_ntextureid; // Texture ID ID

// VBO's name

Unsigned int m_nvbovertices; // Vertex VBO's name

Unsigned int m_nvbotexcoords; // Texture coordinate VBO name

// Temporary data

AUX_RGBIMAGEREC * m_PTextureImage; // Data of high graph

PUBLIC:

Cmesh (); // Mesh Constructor

~ Cmesh (); // Mesh Deconstructor

// High graph load function

Bool Loadheightmap (Char * Szpath, Float Flheightscale, float flresolution);

// Get a height of a point

Float pTheight (int NX, int NY);

// Bind the VBO object.

Void Buildvbos ();

}

Most of the code is self-commented. Please note that I put the vertices and textures. This is not required, and I will give it to explain this.

Now let's define global variables. The first is that VBO is not supported flag variable, which will be set in the initialization code. Next is our grid object. And angle of rotating around Y axis. The variable used to calculate the FPS. I decided to write an extent that the FPS-based thing to display this code is optimized.

BOOL g_fvbosupported = false; // arb_vertex_buffer_Object is supported?

CMESH * g_pmesh = null; // Grid data

FLOAT G_FLYROT = 0.0f; // Rotate

INT g_nfps = 0, g_nframes = 0; // FPS and FPS counters

DWORD g_dwlastfps = 0; // Finally calculate the time of FPS

Let's directly the definition of the CMESH function, starting with LoadHeightmap. For those who have not been exposed to Heightmap, I can understand Heightmap, a HeightMap is a two-dimensional data group, usually an image, which specifies the height of the terrain grid in a vertical. There are many ways to achieve a high graph, but there is almost no perfect. My implementation method is to read data from a 3 channel (24bit) bitmap, using the illuminance algorithm to determine the height defined by the data, so regardless of the color image you use, the result is the same. So you can use color images to define a height map. Personal suggestion of four channels of images. Such as TGA, etc.. We can use its alpha channel to represent the height. However, just for this tutorial, I think a simple Bitmap is still the most appropriate. first of all. We determine that a high graph does not exist. if it exists. We use the GLAUX image load routine, which may be more useful to write your own image load routine, but this has exceeded the scope of this tutorial.

Bool Cmesh :: Loadheightmap (Char * Szpath, Float Flheightscale, Float Flresolution)

{

// Error-Checking

File * ftest = fopen (szpath, "r"); // Open the Image

IF (! ftest) // Make Sure It Was Found

Return False; // if not, the file is missing

Fclose (ftest); // done with the handle

// load texture data

m_ptextureiMage = auxdibimageload (szpath); // Utilize Glaux's Bitmap Load Routine

Now, things have become interesting. First of all, I want to point out that my high graph will generate three vertices for each triangle. The vertex is not shared with other triangles, I will explain it later why I do this, but you need to know this before the code.

I start calculating the total number of vertices in grid. The algorithm is like this: (Terrain Width / Resolution) * 3 Vertices in A Triangle * 2 Triangles In A Square). Then allocate the data and then populate the data.

// generate Vertex Field

m_nvertexcount = (int) (m_ptextureImage-> sizex * m_ptextureImage-> sizey * 6 / (flresolution * flresolution);

m_pvertices = new cVEC [m_nvertexcount]; // allocate Vertex Data

m_ptexcoords = new ctexcoord [m_nvertexcount]; // allocate tex coord data

INT NX, NZ, NTRI, NINDEX = 0; // Create Variables

Float Flx, FLZ;

For (NZ = 0; NZ sizey; nz = (int) flresolution)

{

For (Nx = 0; NX sizex; nx = (int) flresolution) {

For (ntri = 0; NTRI <6; NTRI )

{

// Using this Quick Hack, Figure the x, z position of the point

FLX = (float) NX (ntri == 1 || ntri == 2 || ntri == 5)? flresolution: 0.0f);

FLZ = (FLOAT) NZ ((ntri == 2 || ntri == 4 || ntri == 5)? flresolution: 0.0f);

// set the data, using ptheight to obtain the y y value

m_pvertices [nindex] .x = flx - (m_ptextureImage-> sizex / 2);

M_pvertices [nindex] .y = ptheight ((int) FLX, (int) FLZ) * FlHeightscale;

M_pvertices [nindex] .z = flz - (m_ptextureImage-> sizey / 2);

// stretch the texture across the entire mesh

M_PTEXCOORDS [NINDEX] .u = flx / m_ptextureImage-> sizex;

M_PTEXCOORDS [NINDEX] .V = flz / m_ptextureImage-> sizey;

// increment ou Index

NINDEX ;

}

}

}

Finally, the texture of the high graph will be loaded into OpenGL. Then release the backup of our texture data. This is similar to the previous tutorial.

// load the texture Into OpenGL

GlGentextures (1, & m_ntextureid); // Get An Open ID

GlbindTexture (GL_Texture_2D, M_NTexture); // Bind The Texture

GLTexImage2D (GL_Texture_2D, 0, 3,

M_PTextureImage-> SIZEX, M_PTEXTUREIMAGE-> SIZEY, 0, GL_RGB,

GL_unsigned_byte, m_ptextureImage-> data);

GLTEXPARAMETERI (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

Gltexparameteri (GL_Texture_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

// free the texture data

IF (M_PTextureImage)

{

IF (M_PTextureImage-> DATA)

Free (M_PTextureImage-> Data);

Free (m_ptextureImage);

}

Return True;

}

PTHEight This function is relatively simple. It calculates the height of the data that needs to be queried, and detects, and then calculates the height, the calculation of the brightness value is also very simple. You can see it, so I will not talk more.

FLOAT CMESH :: PTHEIGHT (int NX, int NY) {

// Calculate The Position in The Texture, Careful Not to overflow

INT NPOS = ((NX% M_PTextureImage-> SIZEX) ((NY% M_PTextureImage-> Sizey) * m_ptextureImage-> sizex) * 3;

FLOAT FLR = (FLOAT) m_ptextureImage-> data [npos]; // get the red company

Float flg = (float) m_ptextureImage-> data [npos 1]; // Get the green component

Float flb = (float) m_ptextureImage-> data [npos 2]; // Get the blue component

Return (0.299F * FLR 0.587F * FLG 0.114F * FLB); // Calculate The Height Using The Luminance Algorithm

}

Long live, now is the time to tell the vertex array and VBOS. What is Vertex Arrays, which is such a system, you can tell OpenGL, where is your vertex data, then divided into subsequent sequences to render, only a few function calls are required. The result of doing this is to reduce the call of the function (Glvertex, et al.), Increase the speed of the program. What is VBOS? Vertex Buffer Object uses high-speed graphics memory instead of ordinary system RAM memory. It not only reduces the memory operation of each frame, but also reduces the transmission between the data card and the CPU, in my experiment, VBO greatly increases the frame rate, rather than improving a little bit.

Now let's create a Vertex Buffer Objects. In fact, there are two methods to do, one of which is a method called "mapping". I think the easiest way is also the best way. The process is as follows: First use GLGENBUFFERSARB to generate a "name" that available VBO, substantially, one name is a ID number, OpenGL uses this ID to associate your data. Because a number does not necessarily represent an effective VBO name. Then, we bind VBO objects through the GlbindBuffRB function and activate it. Finally, we load the data into the graphics card, which can be implemented through glBufferDataArB, passing the data pointer and size into in, GlbufferDataAarb will copy your data to the graphics memory, which means that we don't have to maintain this data, So we can delete it (data in memory)

Void cmesh :: buildvbos ()

{

// generate and bind the Vertex Buffer

GLGENBUFFERSARB (1, & M_nvbovertices); // Get a Valid Name

GLBINDBUFFERARB (GL_ARRAY_BUFFER_ARB, M_NVBOVERTICES); // Bind The Buffer

// load the data

GlbufferDataArb (GL_Array_buffer_arb,

m_nvertexcount * 3 * sizeof (float), m_pvertices, gl_static_draw_arb;

// generate and bind the texture coordinate buffer

Glgenbuffersarb (1, & m_nvbotexcoords); // Get a Valid Name

GLBINDBUFFERARB (GL_ARRAY_BUFFER_ARB, M_NVBOTEXCOORDS); // Bind The Buffer

// load the data

GlbufferDataArb (GL_Array_buffer_arb,

m_nvertexcount * 2 * sizeof (float),

M_PTEXCOORDS, GL_STATIC_DRAW_ARB);

// Our Copy of The Data IS No Longer Necessary, IT IS Safe In The Graphics Card

Delete [] m_pvertices; m_pvertices = null;

DELETE [] M_PTEXCOORDS; M_PTEXCOORDS = NULL;

}

All right. It is time to initialize. We allocate memory and load data, then we test GL_ARB_VERTEX_Buffer_Object is not support, if support, we get a pointer to all VBO extended functions through the WGlgetProcadDress function, and then build our VBO object. note. If VBO is not supported, we will keep data like usually, please also note that the forced shutdown VBOS mentioned earlier.

// load the mesh data

g_pmesh = new cmesh (); // instantiateur mesh

IF (! g_pmesh-> loadingheightmap ("terrain.bmp", // load ur hothtmap

Mesh_heightscale, Mesh_Resolution))))

{

MessageBox (Null, "Error Loading Heightmap", "Error", MB_OK;

Return False;

}

// Check for VBOS Supported

#ifndef no_vbos

g_fvbosupported = ISEXtensionsionsUpported ("GL_ARB_VERTEX_Buffer_Object");

IF (g_fvbosupported)

{

// Get Pointers to the GL Functions

GlgenBuffersarb = (PfnglgenBuffersarbproc) WGLGetProcaddress ("GlgenBuffersarb");

GlbindBuffRB = (PfnglbindBuffRBProc) WGLGetProcaddress ("GlbindBufferB");

GlbufferDataArb = (pfnglbufferdataarbproc) WGLGetProcaddress ("GlbufferDataArb");

GLDELETEBUFFERSARB = (PfngldeleteBuffersarbproc) WGlgetProcaddress ("GldeleteBuffersarb);

// load Vertex Data Into the Graphics Card Memory

g_pmesh-> buildvbos (); // build the vbos}

#ELSE / * NO_VBOS * /

g_fvbosupported = false;

#ENDIF

ISEXtensionsionSupported is a function that can be obtained from OpenGL.org, and the change in my function is: It is clearer with my vulgar point of view.

Bool ISEXtensionsionSupported (Char * SztargeTextension)

{

Const unsigned char * pszextensions = null;

Const UNSIGNED Char * Pszstart;

Unsigned char * pszwhere, * pszterminator;

// Extension Names SHOULD NOT HAVE SPACES

Pszwhere = (unsigned char *) strchr (sztargetextension, '');

IF (pszwhere || * sztargetextension == '/ 0')

Return False;

// Get Extensions String

Pszextensions = Glgetstring (GL_EXTENSIONS);

// search the extensions String for an exact copy

Pszstart = pszextensions;

For (;;)

{

Pszwhere = (unsigned char *) strstr (const char *) pszstart, sztargetextension;

IF (! pszwhere)

Break;

PSZTERMINATOR = PSZwhere Strlen (SztargeTextension);

IF (pszwhere == pszstart || * (pszwhere - 1) == ')

IF (* pszterminator == '|| * pszterminator ==' / 0 ')

Return True;

Pszstart = pszterminator;

}

Return False;

}

This function is relatively simple. Some people use strstr to search for sub-strings, but obvious OpenGL.org does not trust this function, I don't agree with them.

Basically all do it. What we have to do is to render the data.

Void Draw (void)

{

GLCLEAR (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_bit); // Clear Screen and Depth Buffer

GLLoadIdentity (); // reset the modelview matrix

// Get FPS

IF (gettickcount () - g_dwlastfps> = 1000) // When A Second Has Passed ...

{

g_dwlastfps = gettickcount (); // Update Our Time Variable

g_nfps = g_nframes; // save the fps

g_nframes = 0; // reset the fps counter

CHAR SZTITLE [256] = {0}; // Build The Title Stringsprintf (Sztitle, "Lesson 45: Nehe & Paul Frazee's VBO Tut -% D Triangles,% D fps",

g_pmesh-> m_nvertexcount / 3, g_nfps);

IF (g_fvbosupported) // incrude a notice about VBOS

Strcat (Sztitle, ", Using VBOS");

Else

STRCAT (SZTITLE, ", NOT USING VBOS");

SetWindowText (g_window-> hwnd, sztitle); // set the title

}

g_nframes ; // increment OUR FPS Counter

//Move the camera

GLTranslateF (0.0F, -220.0F, 0.0F); // Move Above the Terrain

GLROTATEF (10.0F, 1.0F, 0.0F, 0.0F); // Look Down Slightly

GLROTATEF (G_FlyRot, 0.0F, 1.0F, 0.0F); // Rotate The Camera

Quite simple, every second, we have saved the value of the frame counter as the value of the FPS. Then clear the frame counter. Then, we move Camera on Terrain (if you change the high graph, maybe you need to adjust him), and do some rotation, g_flyrot is incremented by each Update call.

To use vertex arrays (and VBOS), you need to tell OpenGL what kind of data you want to provide, so the first step is to open the client's GL_VERTEX_ARRAY and GL_TEXTURE_COORD_ARRAY two status, then I will come to set our data pointer, I want to Unless you have multiple Mesh objects, you don't have to do this when you render each frame. But this relationship is not big, so I don't think it is a big problem.

To set a pointer for a specific data type, you need to use the corresponding function - GlvertExPointer and GLTEXCOORDPOINTER, which is very simple in our example, the total number of variables in the vertex (a vertex has 2, one texture coordinates have 2 ), Data type (FLOAT), Stride (if the vertex is not stored in a continuous data structure), and the pointer of the data, you can also use GlinterleaveDarrays, and put all data Save in a large buffer, but I chose to separate the data, so that better demonstrates how to use multiple VBOS.

Speaking of VBOS. It is not too different, the only difference is the supplied data pointer. After we bind a VBO, you can set the data pointer to null. please look below:

// set Pointers to Our Data

IF (g_fvbosupported)

{

GLBINDBUFFERARB (GL_Array_buffer_arb, g_pmesh-> m_nvbovertices);

GlvertexPointer (3, GL_FLOAT, 0, (Char *) NULL); // SET The Vertex Pointer to the Vertex BufferglbindBuffRB (GL_Array_buffer_arb, g_pmesh-> m_nvbotexcoords);

Gltexcoordpointer (2, GL_FLOAT, 0, (Char *) NULL); // set the texcoord pointer to the texcoord buffer

Else

{

GlvertExPointer (3, GL_FLOAT, 0, G_PMESH-> m_pvertices); // set the vertex pointer to our Vertex Data

GLTEXCOORDPOINTER (2, gl_float, 0, g_pmesh-> m_ptexcoords); // set the vertex pointer to our texcoord data

}

Rendering is very easy.

// render

GLDRAWARRAYS (GL_TRIANGLES, 0, G_PMESH-> m_nvertexcount); // Draw All of the Triangles AtCe

Here we use GLDRAWARRAYS to give the data to OpenGL.GLDRAWARRAYS to detect which client status is activated, then use its pointer to render. We tell OpenGL geometric primitives, starting from that index, and how many vertices are rendered. There are many other ways to submit data to render. Such as GlaRrayElement, but this is the fastest way. You will notice that GLDRAWARRAYS is not between Glbegin and Glend. Because there is no need.

GLDRAWARRAYS is why I choose to share the vertices between triangles. Because sharing is impossible, the best way to optimize memory usage is to use Triangle Strips, but it is not the range of this tutorial. Maybe you still realize that you want to specify a vector for each vertex, which means you have to use the vector, each with a companion vector, if you calculate the vector to improve the visual authenticity of the rendering results for each vertex.

Now that we have to do is to close the vertex array, here, we will end.

// disable pointers

GLDISABLECLIENTATATE (GL_VERTEX_ARRAY); // Disable Vertex Arrays

GLDISABLECLIENTSTATE (GL_TEXTURE_COORD_ARRAY); // Disable Texture Coord Arrays

}

转载请注明原文地址:https://www.9cbs.com/read-21581.html

New Post(0)