本文主要解决以下两个问题:
1、如何使用元素缓存对象来显示一个矩形?
2、如何用不同的方式显示两个三角形?
首先,对之前一章中显示三角形的方式搞了一些科学研究_:
前一章中,指定顶点属性的格式是用这样的方式:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
3sizeof(float)表示了数组两个元素之间的跨度,而函数说明上写着,如果元素是紧密排列的,也可以使用0来做参数,达到一样的效果。因吹斯汀,马上把3 sizeof(float)改成0。编译运行,果然如此。
再来!第二个参数3表示了每个顶点需要更新的分量数目,现在用的是三维顶点格式,但最后一维置成了0。那么是不是可以使用二维的顶点格式,将第二个参数设置为2,第五个参数设置为0,把顶点数组中的z坐标去掉呢?理论可行,马上动手修改。嗯,果然显示效果还是一样的。
一、元素缓存对象(Element Buffer Object,简称EBO)
如果你学过DX的话,那么你一定知道顶点索引这东西,用来标识顶点在顶点数组中的位置的。元素缓存对象也是起的同样的作用,在数组中标识出位置,使用位置来绘制图形,避免了大量存储重复数据。举个例子,想象一下如何在OpenGL中显示一个矩形?使用三角形图元的话,就需要将矩形分为两个三角形,这两个三角形拥有两个相同的顶点。但是,这样一来,我的顶点数组中就需要6个顶点数据,这样太浪费了,3D显示本来就非常耗资源,再弄点冗余的数据无异于雪上加霜。于是,元素缓存对象横空出世,解决了冗余数据的问题。
使用EBO的方式和使用VBO的方式一样,先生成一个唯一ID,然后绑定到OpenGL,再然后将定义好的索引数组保存到EBO中去,最后进行绘制。好,理清思路,开始动手。
1、定义顶点数组和索引数组
4个顶点,6个索引,像这样:
float rectVertices[] = {
0.5f, 0.5f, 0.0f, //右上角
0.5f, -0.5f, 0.0f, //右下角
-0.5f, -0.5f, 0.0f, //左下角
-0.5f, 0.5f, 0.0f //左上角
};
unsigned int indices[] = {
0, 1, 3, //第一个三角形
1, 2, 3 //第二个三角形
};
2、获取唯一ID
还是用glGenBuffers函数,像这样
unsignedintEBO;
glGenBuffers(1, &EBO);
3、绑定到OpenGL
绑定VBO用的参数是GL_ARRAY_BUFFER,绑定EBO需要的是GL_ELEMENT_ARRAY_BUFFER,像这样:
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
4、复制索引数据到EBO中去
还是使用glBufferData,不过复制的时候需要指明是EBO,所以,要用GL_ELEMENT_ARRAY_BUFFER参数,像这样:
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
5、绘制
绘制VBO使用glDrawArrays函数,绘制EBO就要使用glDrawElements函数,像这样:
glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0);
声明要绘制的东西是三角形(GL_TRIANGLES),索引数量是6个(6),索引的数据类型是无符号整型数(GL_UNSIGNED_INT),索引已经在EBO中,没有外部的位置指示(0)。
搞出来之后,应该是这个样子滴:
做一下总结:
可以看到,VAO中保存的是绘制所需要的所有的数据,包括VBO和EBO,所以在绘制的时候只需要绑定响应的VAO就可以绘制相应的东西了。而想要将数据保存到VAO里,就必须把它绑定到当前的OpenGL环境,绑定之后,所有的操作就会保存到当前的VAO中,这也就是为什么总是会看到那么多的绑定函数的原因了。
从OpenGL环境中获取的VAO和VBO的值都是从1开始的,这就有问题了,0去哪里了?不过转念一想也是,OpenGL不需要任何的输入就可以运行起来,说明它本身就已经有一个完整的环境在里面了,这个环境里的VAO和VBO的编号应该就是0了(大胆假设)。
二、用不同的方式显示两个三角形
1、显示两个三角形
这太简单了,只要再顶点数组中定义3个顶点,然后绘制的时候将顶点数量改成6就可以了。
效果如下:
2、使用不同的VAO和VBO绘制两个三角形
在创建一个VAO和VBO,然后用前面一样的方式来分配ID,复制数据。复制数据的时候通过制定大小和起始偏移的方式来使用同一个数组中的数据。
第一个VBO这样复制数据:glBufferData(GL_ARRAY_BUFFER, sizeof(vertices) / 2/*sizeof(rectVertices)*/, vertices/*rectVertices*/, GL_STATIC_DRAW);
第二个VBO就这样复制:glBufferData(GL_ARRAY_BUFFER, sizeof(vertices) / 2, vertices + 9, GL_STATIC_DRAW);
使用的是同一个数组。编译运行,效果跟上面一毛一样。
3、绘制两个不同颜色的三角形
再弄一个片元着色器,编译进另一个着色器程序之中,使用一个着色器程序绘制之后马上选用另一个着色器再绘制一个。
先搞一个黄色的片元着色器:
const char* fragmentShaderSource1 = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 1.0f, 0.1f, 1.0f);\n"
"}\0"; //千万别忘记这个\0
再编译这个着色器:
int fragmentShader1 = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader1, 1, &fragmentShaderSource1, NULL);
glCompileShader(fragmentShader1);
再放到着色器程序中:
int shaderProgram1 = glCreateProgram();
glAttachShader(shaderProgram1, vertexShader);
glAttachShader(shaderProgram1, fragmentShader1);
glLinkProgram(shaderProgram1);
最后显示:
glUseProgram(shaderProgram1);
glBindVertexArray(VAO1);
glDrawArrays(GL_TRIANGLES, 0, 3);
嗯,效果完美:
参考资料:
1、www.learningopengl.com(推荐学习站点)