曾经有一个老师在教我们的时候,给我们说了一句很实在的话,当你接触一个新的领域的时候,什么最压人?名词最压人,随便一个专有名词都够你吃一壶的。事实上确实如此。
学习OpenGL首先,先要弄明白的三个名词就是VAO,VBO和IBO。我们学习一个东西的时候,往往被过多,过详细的数据,淹没真正核心的那几句话,这可能也是不同人学习能力不一样的原因把,我应该属于学习能力比较弱的那种,总是要一边叮嘱自己,关键那几句话在哪?一边看东西,才能勉强找到关键那几句话。
本篇不是教程,不会说明这三个玩意的来龙去脉以及用法,只是在你已具备相应基础的前提下,加深一下认知。
VAO:顶点数组对象,Vertex Array Object
VBO:顶点缓存对象,Vertex Buffer Object,VBO
IBO:顶点索引对象,Element Buffer Object,EBO或Index Buffer Object,IBO
借用一下LearnOpenGL网站上的图。
最让我迷惑的不是IBO,而是VAO的作用,其实这VAO和IBO一样,都是为了“复用”。而VBO关键作用在于“打包”上传。
从上图看来,VAO,其实是VBO的约束器,解读器。它管理一类VBO的配置。VAO1,只用到一个属性,VAO2则有两个属性,位置和颜色。同时还要用AttributePointer去描述VBO。
1、VAO
顶点数组对象:
使用VAO的好处——复用属性设置。
1、VAO是可复用的
2、VAO可以绑定一系列顶点属性
3、VAO只能和匹配的VBO绑定,比如你VAO只描述了位置,而VBO包含了顶点和颜色信息,他们不能绑定,否则数据就错乱了
4、VAO解绑后可以绑定给其他匹配的VBO,这就是“复用”。
2、VBO
顶点缓存对象:
使用VBO的好处——批量数据传输。
具体来讲:使用VBO好处是可以一次发送大量顶点到显卡,而不是每次只发送一个顶点。
这里额外思考一下,既然IO是相当费时的,那么如果内存和显卡可以异步传输数据是不是能节省掉一部分时间?比如先传A数据,然后渲染A的同时传B的树呢?这应该就是多线程渲染做的事情,并不是真的多线程渲染,而是把渲染和IO交错以节省时间。当然如果瓶颈不是IO,而是渲染本事,那多线程渲染也无力回天了。
3、IBO
先对来讲IBO是最好理解的,假定一个四边形由两个三角形组成,共六个顶点,实际上有两对顶点共享,所以只有四个顶点,IBO用来告诉你,如何复用这些顶点。
比如0,1,3是一个三角形,1,2,3是另一个三角形。
这就是IBO的复用。
依据以上总结,在回头来看这段源代码,就思路就相对清晰了。
//声明顶点数据,实际应用的时候,应该是从FBX等文件里面读取的。 //很少自己生成 float vertices[] = { -0.5f, -0.5f, 0.0f, // left 0.5f, -0.5f, 0.0f, // right 0.0f, 0.5f, 0.0f // top }; //创建顶点数组对象和顶点缓存对象 unsigned int VBO, VAO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); //绑定,或者说“选用”这个顶点缓存对象。 glBindVertexArray(VAO); //绑定VBO对象,并拷贝数据到VBO //注意数据不是拷贝给VAO的,VAO里面不存顶点数据 //GL_STATIC_DRAW,这是不是Unity区分Static和non-static物件的原因? glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //描述顶点,并把它在0号位置激活,一个VAO可以有多个描述对象。 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); //相当于解除绑定。注意是解绑,不是删除。 glBindBuffer(GL_ARRAY_BUFFER, 0); //简单来讲,按照绑定的反顺序进行解绑就对了。先解绑VBO,再解绑VAO glBindVertexArray(0); //到此位置,该送入GPU的数据以及都“送”进去了 //开始进入渲染主循环 while (!glfwWindowShouldClose(window)) { //处理输入 processInput(window); //清屏 glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); //选择shader,这里不是本篇要讲的内容,略过 glUseProgram(shaderProgram); //因为没有其他数据,所以不用绑定解绑也没事,这么做是为了规范。 //思考:当VBO有多个的时候,这里要怎么做? glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLES, 0, 3); glBindVertexArray(0); glfwSwapBuffers(window); glfwPollEvents(); } //这里才是删除缓存 glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO);
版权声明:本文为CSDN博主「幼发拉底河上的无头女尸」的原创文章,
遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/WangHaoDiablo/article/details/104407245