将数据加载到OpenGL缓冲区

2017年04月14日 11:32 0 点赞 0 评论 更新于 2025-11-21 21:21

介绍

在OpenGL中,游戏角色的顶点、法线和UV坐标等数据会被加载到名为Buffer对象的OpenGL对象中。加载这些数据的方法有多种,本节将详细介绍所有可能的加载方式。

简单数据加载

首先,我们来加载一个简单的数组,该数组表示角色的顶点位置。具体步骤如下:创建一个单一的OpenGL缓冲区,将其绑定到GL_ARRAY_BUFFER,然后把顶点数据放入其中。数据加载完成后,将其链接到着色器属性位置。

代码1:简单数据加载

// Vertex data of character
float vertexData[250] = {1.0, 0.4, 0.9, 1.0, ....};

// 1. Create a buffer
GLuint myBuffer;
glGenBuffers(1, &myBuffer);

// 2. Bind a buffer
glBindBuffer(GL_ARRAY_BUFFER, myBuffer);

// 3. Load data in the buffer
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);

// 4. Get the location of the shader attribute called "position"
GLuint positionLocation = glGetAttribLocation(programObject, "position");

// 5. Get Location of uniforms called "modelViewProjectionMatrix"
GLuint modelViewProjectionUniformLocation = glGetUniformLocation(programObject, "modelViewProjectionMatrix");

// 6. Enable the attribute location
glEnableVertexAttribArray(positionLocation);

// 7. Link the buffer data to the shader attribute locations.
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid *)0);

代码解释

在加载数据之前,我们首先要创建一个缓冲区myBuffer(第1步)。接着,将该缓冲区绑定到绑定点GL_ARRAY_BUFFER(第2步)。绑定完成后,就可以将数据加载到myBuffer中,这是通过调用OpenGL函数glBufferData实现的(第3步)。

glBufferData函数需要三个关键参数:myBuffer的绑定点、需要加载的数据大小以及指向数据vertexData的指针。参数GL_STATIC_DRAW表示数据不会改变。

为了能在着色器中正确使用数据,我们需要获取着色器中引用的属性位置的存储位置(第4步)。获取位置后,将其启用(第6步)。启用属性位置后,就可以将属性位置与vertexData数组中的数据进行链接,这是通过调用glVertexAttribPointer函数完成的(第7步)。该函数的参数包括属性的位置、每个顶点的组成(在本例中是3,即x、y和z)、数据类型、是否进行归一化、步长大小以及指向数据第一个组成部分的指针。

加载交错数据

上述示例展示了如何加载表示3D模型位置的简单数据数组。但在实际应用中,我们可能还需要加载代表法线和UV坐标的数据。这些数据可以在characterData数组中交错存储,数组的前两个分量表示UV坐标,接下来的三个表示法线坐标,最后三个表示顶点位置。

代码2:加载交错数据

GLfloat characterData[192] = {
// Data layout for each line below is:
// UV1,UV2, Normal1, Normal2, Normal3, positionX, positionY, positionZ
0.00000, 1.00000, 0.00000, 0.00000, 1.00000, -0.50000, -0.50000, 0.50000,
0.00000, 0.00000, 0.00000, 0.00000, 1.00000, -0.50000, 0.50000, 0.50000,
...
};

// 1. Create a Vertex Buffer Object
GLuint myBuffer;
glGenBuffers(1, &myBuffer);

// 2. Bind the Vertex Buffer Object
glBindBuffer(GL_ARRAY_BUFFER, myBuffer);

// 3. Dump the data into the Buffer
glBufferData(GL_ARRAY_BUFFER, sizeof(characterData), characterData, GL_STATIC_DRAW);

// 4. Get the location of the shader attribute called "position"
GLuint positionLocation = glGetAttribLocation(shaderProgram, "position");

// 5. Get the location of the shader attribute called "normal"
GLuint normalLocation = glGetAttribLocation(shaderProgram, "normal");

// 6. Get the location of the shader attribute called "texCoord"
GLuint textureLocation = glGetAttribLocation(shaderProgram, "texCoord");

// 7. Get Location of the uniforms
GLuint modelViewProjectionUniformLocation = glGetUniformLocation(programObject, "modelViewProjectionMatrix");

// 8. Enable attribute locations
glEnableVertexAttribArray(positionLocation);
glEnableVertexAttribArray(normalLocation);
glEnableVertexAttribArray(textureLocation);

// 9. Link the buffer data to the shader attribute locations
glVertexAttribPointer(textureLocation, 2, GL_FLOAT, GL_FALSE, 32, BUFFER_OFFSET(0));
glVertexAttribPointer(normalLocation, 3, GL_FLOAT, GL_FALSE, 32, BUFFER_OFFSET(8));
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 32, BUFFER_OFFSET(20));

代码解释

此过程与代码1基本相同。第1 - 3步创建一个OpenGL缓冲区,并将数据加载到缓冲区中。第4、5和6步分别查询属性位置positionnormaltexCoord。第8步启用这些属性位置。

第9步将characterData数组中的数据与属性位置进行链接。glVertexAttribPointer函数的最后一个参数要求指向数组中组件的指针偏移量。例如,UV坐标的偏移量为0,因为它是数组中的第一个组件;表示法线坐标的偏移量是sizeof(float) * (elementPosition) = sizeof(float) * 2 = 8;顶点位置指针的偏移量设置为20,因为sizeof(float) * (elementPosition) = sizeof(float) * 5 = 20。步长设置为32,因为32是属性在数组中重复的间隔。

源代码

源代码 "Loading Interleaved data"。

使用glBufferSubData加载

我们也可以将顶点、法线和UV数据分别存储在不同的数组中,然后通过使用glBufferSubData函数将这些数据加载到同一个OpenGL缓冲区中。

代码3:使用glBufferSubData加载

float position[250] = {
1.0, 0.0, 1.0,
0.0, 1.0, 0.0,
0.5, 0.5, 1.0,
0.5, 0.0, 0.4,
....
};

float normal[250] = {
1.0, 0.0, 1.0,
0.0, 1.0, 0.0,
0.5, 0.5, 1.0,
0.5, 0.0, 0.4,
....
};

float uv[250] = {
1.0, 0.0,
1.0, 0.0,
1.0, 0.0,
...
};

// 1. Create a buffer
GLuint myBuffer;
glGenBuffers(1, &myBuffer);

// 2. Bind the buffer
glBindBuffer(GL_ARRAY_BUFFER, myBuffer);

// 3. Initialize the buffer with NULL data
glBufferData(GL_ARRAY_BUFFER, sizeof(position) + sizeof(normal) + sizeof(uv), NULL, GL_STATIC_DRAW);

// 4. Load position data
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(position), position);

// 5. Load normal data
glBufferSubData(GL_ARRAY_BUFFER, sizeof(position), sizeof(normal), normal);

// 6. Load uv data
glBufferSubData(GL_ARRAY_BUFFER, sizeof(position) + sizeof(normal), sizeof(uv), uv);

代码解释

该过程与前面的方法类似,首先创建一个OpenGL缓冲区并将其绑定(第1、2步)。但在调用glBufferData函数时,参数有所不同(第3步)。缓冲区的大小是所有三个数组的大小之和,数据的参数指针设置为NULL

接着,使用glBufferSubData函数分别将顶点、法线和UV数据加载到缓冲区的不同位置(第4、5、6步)。

通过上述三种方法,我们可以根据不同的需求将数据加载到OpenGL缓冲区中。

作者信息

孟子菇凉

孟子菇凉

共发布了 3994 篇文章