不使用VBO的方式
把顶点坐标和纹理坐标分开定义
把顶点坐标和纹理坐标分开定义,glVertexAttribPointer会把他们的对应关系处理好,使得顶点数据的每一个坐标和纹理坐标一一对应起来。
void glVertexAttribPointer(
GLuint index,
GLint size,
GLenum type,
GLboolean normalized,
GLsizei stride,
const void * pointer);
glVertexAttribPointer定义了OpenGL该如何解释顶点数据
- 第一个参数指定我们要配置的顶点属性(例如在顶点着色器中使用
layout(location = 0)
) - 第二个参数指定顶点属性的大小
- 第三个参数指定数据的类型
- 第四个参数定义是否希望数据被标准化(Normalize)
- 第五个参数叫做步长(Stride)
- 最后一个offset,表示位置数据在缓冲中起始位置的偏移量(Offset)
glEnableVertexAttrib Array以顶点属性位置值作为参数,启用顶点属性,顶点属性默认是禁用的。
// 定义顶点数据
float vertices[] = {
-0.5f, 0.5f, 1.0, //0左上
0.5f, 0.5f, 1.0, //1右上
-0.5f, -0.5f, 1.0, //2左下
0.5f, -0.5f, 1.0, //3右下
};
// 这个纹理翻转和和顶点坐标对应起来显示的图像就是正的,如果和顶点坐标次序一样,
// 那么图像是反过来的,所以把纹理坐标的下面两个点放到第一行和第二行和顶点对应
// 参考下面的手绘图可以理解
float textureCoords[] = {
0, 0, //0左下放到左上
1, 0, //1右下放到右上
0, 1, //2左上放到左下
1, 1, //3右上放到右下
};
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), vertices);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), textureCoords);
glEnableVertexAttribArray(1);
//绘制部分
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
把texture坐标和顶点坐标放到一个数组里面
// 定义顶点数据(这样定义正Z字顶点次序)
float vertices[] = {
-1.0f, 1.0f, 1.0, 0, 0, //0左上
1.0f, 1.0f, 1.0, 1, 0, //1右上
-1.0f, -1.0f, 1.0, 0, 1, //2左下
1.0f, -1.0f, 1.0, 1, 1, //3右下
};
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * 4, &vertices[0]);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * 4, &vertices[3]);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
//绘制部分
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
交叉定义顶点坐标和纹理坐标在同一个buffer
// 交叉(Interleave)属性,前三列是顶点坐标后两列是纹理坐标
float vertices[] = {
-1.0f, 1.0f, 1.0, 0, 0, //0左上
-1.0f, -1.0f, 1.0, 0, 1, //1左下
1.0f, 1.0f, 1.0, 1, 0, //2右上
1.0f, -1.0f, 1.0, 1, 1, //3右下
};
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * 4, &vertices[0]);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * 4, &vertices[3]);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
//绘制部分
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
分批处理顶点属性
一个vbo的情况
使用glVertexAttribPointer函数可以指定缓冲内容的顶点数组的属性的布局(Layout)。
我们可以做的是把每种类型的属性的所有向量数据批量保存在一个布局,而不是交叉布局。与交叉布局123123123123不同,我们采取批量方式111122223333。当从文件加载顶点数据时你通常获取一个位置数组,一个法线数组和一个纹理坐标数组。需要花点力气才能把它们结合为交叉数据。使用glBufferSubData可以简单的实现分批处理方式。
一个顶点数据,一个纹理数据,通过glBufferSubData放到一个vbo里面,然后通过glVertexAttribPointer分别指定每个location的属性,通过绑定VAO绘制。
- glBufferData先分配总的内存大小
- glBufferSubData依次存入vertices和texCoords数据
- glVertexAttribPointer分别设置顶点坐标和纹理坐标的数据格式 关键代码如下:
float vertices[] = {
-1.0f, 1.0f, 1.0, //0左上
1.0f, 1.0f, 1.0, //1右上
-1.0f, -1.0f, 1.0, //2左下
1.0f, -1.0f, 1.0 //3右下
};
float texCoords[] = {
0, 0, //0左下放到左上
1, 0, //1右下放到右上
0, 1, //2左上放到左下
1, 1 //3右上放到右下
};
size_t vSize = sizeof(vertices);
size_t tSize = sizeof(texCoords);
GLuint vbo, vao;
glGenBuffers(1, &vbo);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, vSize+tSize, nullptr, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, vSize, &vertices);
glBufferSubData(GL_ARRAY_BUFFER, vSize, tSize, &texCoords);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), (GLvoid*)(sizeof(vertices)));
/* 最后一个offset的参数的写法是不行的 */
// glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), vertices);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//绘制部分
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
glBindVertexArray(0);
两个vbo的情况
使用两个VBO,先生成VAO,然后再分别绑定vertexVbo,textureVbo,并且glBufferData,glVertexAttribPointer指定数据格式。绘制的时候只需要绑定这个VAO即可。
关键代码如下:
float vertices[] = {
-1.0f, 1.0f, 1.0, //0左上
1.0f, 1.0f, 1.0, //1右上
-1.0f, -1.0f, 1.0, //2左下
1.0f, -1.0f, 1.0 //3右下
};
float texCoords[] = {
0, 0, //0左下放到左上
1, 0, //1右下放到右上
0, 1, //2左上放到左下
1, 1 //3右上放到右下
};
GLuint vertexVbo, textureVbo, vao;
glGenBuffers(1, &vertexVbo);
glGenBuffers(1, &textureVbo);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vertexVbo);
/* 下面这段代码是在刚开始用glBufferData的时候犯的错误,第一个参数是GL_ARRAY_BUFFER
不是绑定的vbo,这个错写成vbo后,编译执行都没有问题,就是画不出来,最后废了好大劲,通
过后面的glGetError输出才发现是这个参数错了。 */
// glBufferData(vertexVbo, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
/* glEnableVertexAttribArray放在glBindBuffer和glBufferData之后也没有问题 */
glEnableVertexAttribArray(0);
/* 下面这两种写法都可以,最后一个参数不对的话绘不出来 */
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
// glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), 0);
/* glGetError还是能方便的发现问题,前面参数写错的时候,输出是1280,然后才发现问题所在 */
std::cout << glGetError() << std::endl;
glBindBuffer(GL_ARRAY_BUFFER, textureVbo);
/* 同上,第一个参数是GL_ARRAY_BUFFER,而不是vbo id */
// glBufferData(textureVbo, sizeof(texCoords), texCoords, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(texCoords), texCoords, GL_STATIC_DRAW);
/* glVertexAttribPointer之前绑定的是先前定义的textureVbo对象,顶点属性1现在会链接到它的顶点数据。 */
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
// glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), 0);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//绘制部分
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);