1.VBO(頂點緩衝對象 Vertex Buffer Objects ,VBO)
VBO:頂點緩衝對象,是在顯卡存儲空間中開闢出的一塊內存緩存區,用於存儲頂點的各類屬性信息,如頂點座標,頂點向量,頂點顏色數據等。在渲染時,可以直接從VBO取出頂點的各類屬性數據,由於VBO在顯存而不是在內存中,不需要從CPU傳輸數據,處理效率更高。
可以開闢很多個VBO,每個VBO在OpenGL中有它的唯一標識ID,這個ID對應這具體的VBO的顯存地址,通過這個ID可以對特定的VBO內的數據進行存取操作
(1)創建VBO 開闢顯存空間並分配VBO的ID
GLuint vboid;
glGenBuffers(1, &vboid);
原型: void glGenBuffers(GLsizei n, GLuint * buffers);
n:指定要生成的緩衝區對象名稱的數量
buffers:指定存儲生成的緩衝區對象名稱的數組
(2)創建的VBO可用來保存不同類型的頂點數據,創建之後需要通過分配的ID綁定製定的VBO,對於同一類型的頂點數據一次只能綁定一個VBO。綁定操作通過glBindBuffer來實現;
glBindBuffer(GL_ARRAY_BUFFER, vboId);
原型: void glBindBuffer(GLenum target, GLuint buffer);
target:表示目標緩衝類型
buffer:緩衝對象名字
target分類:
GL_ARRAY_BUFFER,
GL_ATOMIC_COUNTER_BUFFER,
GL_COPY_READ_BUFFER,
GL_COPY_WRITE_BUFFER,
GL_DRAW_INDIRECT_BUFFER,
GL_DISPATCH_INDIRECT_BUFFER,
GL_ELEMENT_ARRAY_BUFFER,
GL_PIXEL_PACK_BUFFER,
GL_PIXEL_UNPACK_BUFFER,
GL_QUERY_BUFFER,
GL_SHADER_STORAGE_BUFFER,
GL_TEXTURE_BUFFER,
GL_TRANSFORM_FEEDBACK_BUFFER,
GL_UNIFORM_BUFFER
注:以上類型,根據不同的OpenGL版本使用,我們今天用的是GL_ARRAY_BUFFER,用於頂點數組綁定,其他類型具體什麼用法,在這不做多解釋,想要更多瞭解的同學請移步OpenGL WIKI文檔,會有更詳細的解釋https://www.khronos.org/opengl/wiki/Main_Page
(3)調用glBufferData把用戶的數據傳輸到當前綁定的顯存緩衝區
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
原型:void glBufferData(GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage);
targe:VBO類型同上
size:VBO的大小
data:制定需要傳遞的數據,可以爲空
usage:制定VBO的存儲方式
usage類型主要是:
GL_STREAM_DRAW,
GL_STREAM_READ,
GL_STREAM_COPY,
GL_STATIC_DRAW,
GL_STATIC_READ,
GL_STATIC_COPY,
GL_DYNAMIC_DRAW,
GL_DYNAMIC_READ,
GL_DYNAMIC_COPY
STREAM:數據存儲內容將修改一次,最多使用幾次;
STATIC:數據存儲內容將被修改一次並多次使用;
DYNAMIC:數據存儲內容將被反覆修改和多次使用;
DRAW:應用程序修改數據存儲內容,並將其用作GL繪圖和圖像規範命令的源;
READ:通過從GL讀取數據來修改數據存儲內容,並在應用程序查詢時返回該數據;
COPY:通過從GL中讀取數據來修改數據存儲內容,並將其用作GL繪圖和圖像規範命令的源;
在這我們使用GL_STATIC_DRAW,其他的用法,可參考OpenGL WIKI文檔
(4)調用glVertexAttribPointer 告訴OpenGL怎麼解釋這些頂點數據
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
原型:
void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer);
void glVertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid * pointer);
void glVertexAttribLPointer(GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid * pointer);
index:指定頂點屬性位置,與頂點着色器中layout(location = 0)對應
size:頂點屬性大小
type:參數指定數據類型
stride:步長,指定在連續的頂點屬性之間的間隔
pointer:我們的位置數據在緩衝區起始位置的偏移量
2.VAO(頂點數組對象 Vertex Array Object ,VAO)
VAO:保存了所有頂點數據屬性的狀態結合,它存儲了頂點數據的格式以及頂點數據所要的VBO的引用
(1)VAO的創建和配置
GLuint vaoId;
glGenVertexArrays(1, &vaoId);
glBindVertexArray(vaoId);
執行VAO綁定之後,後面你的所有VBO配置都是這個VAO對象的一部分,可以說VBO是對頂點屬性信息的綁定,VAO是對多個VBO綁定
(2)OpenGL中所有的圖形都是通過分解成三角形的方式進行繪製,glDrawArrays函數把模型繪製出來,它使用當前激活的着色器,當前VAO對象中的VBO頂點數據和屬性配置來繪製圖形
void glDrawArrays(GLenum mode, GLint first, GLsizei count);
mode:基本圖元類型包括GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES, GL_LINE_STRIP_ADJACENCY, GL_LINES_ADJACENCY, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_TRIANGLES, GL_TRIANGLE_STRIP_ADJACENCY, GL_TRIANGLES_ADJACENCY , GL_PATCHES
first:指定緩存中的哪一位開始繪製,一般定義爲0
count:指定繪製的頂點數量
3.EBO(索引緩衝對象,Element Buffer Object,EBO)
EBO:索引緩衝對象EBO相當於OpenGL中頂點數組的概念,爲了解決同一個頂點多次重複調用的問題,可以減少內存浪費,提高效率。需要用重複的頂點的時候,通過頂點的位置索引來調用頂點。
EBO中存儲的內容就是位置的索引,EBO跟VBO很像,也是顯存中的一部分內存緩衝器
(1)創建EBO並綁定,用glBufferData,以GL_ELEMENT_ARRAY_BUFFER作爲參數,把索引存儲到EBO中
GLuint EBO;
glGenBuffers(1,&EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
(2)當用EBO綁定頂點索引的方式繪製模型時,需要使用glDrawElements
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
原型:void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid * indices);
mode:基本圖元類型,GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES, GL_LINE_STRIP_ADJACENCY, GL_LINES_ADJACENCY, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_TRIANGLES, GL_TRIANGLE_STRIP_ADJACENCY, GL_TRIANGLES_ADJACENCY ,GL_PATCHES
count:繪製的頂點個數
type:頂點數據類型,必須是GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, , GL_UNSIGNED_INT
indices:可選的EBO中偏移量設定
4.應用:
1.用VBO_VAO畫正方形
#include <GL/glew.h>
#include<gl\glut.h>
void MyInit();
void reshape(int w, int h);
void display();
GLuint vboId;
GLuint vaoId;
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(512, 512);
glutCreateWindow("VBO_VAO_RECTANGLE");
glewInit();
MyInit();
glutReshapeFunc(&reshape);
glutDisplayFunc(&display);
glutMainLoop();
return 0;
}
void MyInit()
{
glClearColor(0.0,0.0, 0.0,0.0);
const GLfloat vertices[] = {
-0.5f,-0.5f,0.0f,1.0f,
0.5f,-0.5f,0.0f,1.0f,
0.5f,0.5f,0.0f,1.0f,
-0.5f,0.5f,0.0f,1.0f,
};
//創建VAO對象
glGenVertexArrays(1, &vaoId);
glBindVertexArray(vaoId);
//創建VBO對象
glGenBuffers(1, &vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
//傳入VBO數據
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//解除VBO綁定
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void reshape(int w, int h)
{
glViewport(0, 0,(GLsizei)w, (GLsizei)h);
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
//綁定VBO
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glEnableVertexAttribArray(0);
//解釋頂點數據方式
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
//繪製模型
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(0);
glutSwapBuffers();
}
我們來看下效果
2.VEO畫正方形
#include <GL/glew.h>
#include<gl\glut.h>
#include<iostream>
void MyInit();
void reshape(int w, int h);
void display();
GLuint vboId;
GLuint vaoId;
GLuint eboId;
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(512, 512);
glutCreateWindow("EBO_VBO_VAO_RECTANGLE");
glewInit();
MyInit();
glutReshapeFunc(&reshape);
glutDisplayFunc(&display);
glutMainLoop();
return 0;
}
void MyInit()
{
glClearColor(0.0, 0.0, 0.0, 0.0);
const GLfloat vertices[] = {
-0.5f,-0.5f,0.0f,1.0f,
0.5f,-0.5f,0.0f,1.0f,
0.5f,0.5f,0.0f,1.0f,
-0.5f,0.5f,0.0f,1.0f,
};
GLshort indices[] = {
0, 1, 3, // 第一個三角形
1, 2, 3 // 第二個三角形
};
//創建VAO對象
glGenVertexArrays(1, &vaoId);
glBindVertexArray(vaoId);
//創建VBO對象
glGenBuffers(1, &vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
//傳入VBO數據
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//創建EBO對象
glGenBuffers(1, &eboId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboId);
//傳入EBO對象
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
//解釋頂點數據方式
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
//解除VAO綁定
glBindVertexArray(0);
//解除VBO綁定
glBindBuffer(GL_ARRAY_BUFFER, 0);
//解除EBO綁定
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
void reshape(int w, int h)
{
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(vaoId);
//繪製模型
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, NULL);
glutSwapBuffers();
}
看下效果