• LearnOpenGL学习笔记(三)——VBO,VAO,EBO理解


            在opengl中所有的数据都要放在显存中,我们通过一定的手段去管理它,既要提供地方存放它,还要提供方法去正确地提取它们,去使用它们,opengl通过VAO,VBO,EBO这些手段来解决这些问题。 (一)VBO(Vertex Buffer Objects,顶点缓冲对象)介绍: 首先我们要明白VBO是一种管理手段,它的中文名中“缓冲”,就决定了它是管理存储的一种手段(methon),总的来 说,为什么要设置这个VBO,是应该从着色器程序说起。 着色器的第一个顶点着色器接受到我们数据流传入的顶点数据后,它会把这些数据放到GPU的显存上面(等同内存) 接下来向OpenGL中配置如何去解释这些内存,未来如何发给显卡处理。接下来着色器才会按照我们编的代码做我们想做 事情。 我们设立VBO的原因就是要管理这些内存,这样做有很多好处比如:1.我们可以往一个VBO管理的内存中放很多顶点 的数据,这样我们就可以一下子把这些数据都从cpu的数据流上送到GPU(显卡)的显存上,这样节省时间,因为从CPU往 GPU的过程中,要经过很多“关卡”,送一“筐”和送一“卡车”是没有区别的。 (二)声明一个VBO如下:

    unsigned int VBO;//声明一个句柄
    glGenBuffers(1, &VBO);//1是一个缓冲ID,用Gen生成一个缓冲区。
    glBindBuffer(GL_ARRAY_BUFFER, VBO);  //用这个函数将这个缓冲区变成一个GL_ARRAY_BUFFER,这是顶点缓冲类型的意思。
    //注意这是一个绑定函数,现在所有输入GL_ARRAY_BUFFER的数据,都会直接被送入现在这个VBO,而不是其他的VBO2,VBO3之类的什么,如果
    我们不要用这个VBO,想换一个VBO2,我们必须先解绑这个VBO。
    //注意我们不会去直接用VBO,因为我们的数据必须全部输入到目标缓冲GL_ARRAY_BUFFER里,这样表明我们输入的是顶点属性,而不是别的
    什么。
    //这就相当于我们可以输入的缓冲对象类型,已经被设定好了,我们只能往这些地方输入数据,位于它们后面缓冲区,可以不断更换用来实现
    不同目的。

    (三)往VBO里面放入数据:

         接下来这个函数是很重要的,它是将数据从cpu的流中提取出来,放入显存中的VBO管理的区域里。
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    //首先是缓冲类型声明,GL_ARRAY_BUFFER表明数据将被传入刚才绑定这个类型的缓冲区里,第二个是数据流的大小,第三个是数据流名字,
    第四个是希望显卡如何管理给定的数据,要告诉显卡这些数据未来会不会被改变。要是希望经常改变的话,显卡会把数据放在能够高速写入
    的内存部分,这样节省资源。
    GL_STATIC_DRAW :数据不会或几乎不会改变。
    GL_DYNAMIC_DRAW:数据会被改变很多。
    GL_STREAM_DRAW :数据每次绘制时都会改变。
    
    
    //到这一步,我们的数据流已经没有用了,放在内存上的缓冲区的坐标数据,将被传入显卡上的着色器,被用来使用。
    接下来就是着色器的任务了。如何去解释数据流,将一条连续的float流数据解读为三维的顶点数据(就是将数字变成具有几何意义的坐标点)
    这都是着色器的任务了。

           在解释什么是VAO之前,我们先要解释一下着色器的一个任务,正是着色器的这个任务,导致我们必须使用VAO这种方式来设置。

    复制顶点数组到缓冲中供OpenGL使用
    glBindBuffer(GL_ARRAY_BUFFER, VBO); 
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 
    // 1. 设置顶点属性指针 
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); 
    glEnableVertexAttribArray(0); 
    // 2. 当我们渲染一个物体时要使用着色器程序 
    glUseProgram(shaderProgram); 
    // 3. 绘制物体 
    someOpenGLFunctionThatDrawsOurTriangle();
    //这就是我们渲染一个物体的步骤,其中最重要的就是1.将数据从cpu数据流传入CPU的VBO缓冲区管理的显存里2.将数据流正确的解释成三维
    坐标。
    //这里使用了一个属于叫做顶点属性指针,他就是用来解释该如何去解释数据流的,使用不同的属性指针可以得到不同的解释。

    (四)设立VAO,顶点数组对象(Vertex Array Object):

       一个VAO中包含两个关键东西,glVertexAttribPointer函数绑定的VBO和顶点属性配置(也就是数据放在哪里
    ,和如何读取数据)。此外还有glEnableVertexAttribArray和glDisableVertexAttribArray这两个函数用来启
    用顶点属性,和取消顶点属性链接。
       这样就造成了一个你绑定一个VAO就可以知道,去哪里读,用什么方式去解读。
    
    
    这样你在绑定一个VAO的情况下,便可以绘制这个物体,只要它们的读取方式(顶点属性配置)一样。
    注意把顶点数组复制到缓冲中供OpenGL使用,是在VBO里面布置的,
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    //举个例子,你想绘制一个边长3cm的立方体,就绑定这个VAO,OPENGL这个状态机下一刻就会绘制出来这个
    //如果你想绘制一个长方体,就解绑之前哪个VAO,绑定VAO1,就下一刻绘制出长方体了。

      (五)设立EBO,索引缓冲对象(Element Buffer Object):

          索引缓冲对象EBO,不是我们必须设置的东西,我们必须设置VAO,来告诉OpenGL如何读取,将数据放入哪里。但我们不必一定
    要有EBO。我们之所以发明EBO是用来简化我们的程序的渲染步骤的,它是用来告诉我们如何去绘制的。
          这要从我们3d世界说起,电脑里面的所有3d立体图形,无论是球形,还是不规则图形,它们都是由三角形构成的,也就是说
    都是由三个点构成一个面,无数个面构成一个立体图形。这样在不规则立体图形里,必然由许多的点是共用的,我们为了能在输入
    数据的时候少输入一些点(传输数据很花时间的),就要想办法将这些共用的点利用起来,只输入一次,这就是EBO的发明原因。
    在操作时,我们先输入所有点的数据(VBO,和设置顶点属性指针),再设置将哪些点绘制成面(EBO),这就形成了正规的3d立体图形。
    float vertices[] = { 
           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开始!  
             0, 1, 3, // 第一个三角形 
             1, 2, 3 // 第二个三角形 
    };
    我们也是用数据流来表明,哪些点构成一个面。
    接下来的操作似曾相识,将数据索引复制到缓冲里。
    unsigned int EBO; 
    glGenBuffers(1, &EBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); 
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    
    在渲染的时候,因为调用了EBO,所以不能用绘制VAO的函数glDrawArrays,要使用兼容glDrawElements,用法是类似的。
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); 
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    //6的意思是一共绘制六个顶点。0是一个偏移量,这里填0就好了
    
    在绘制的时候,EBO和VBO都可以看作缓存,它们都被VAO管理着,所以实际渲染中,我们只需要绑定VAO,就可以了。
    
    

                                              ,并且将一条连续的float流数据解读为三维的顶点数据 (就是将数字变成具有几何意义的坐标点)。    

  • 相关阅读:
    富文本的一般处理方式,document.getElementById('富文本的ID').contentWindow.document.body.innerHTML = '%s'" %(content)
    本地搭建Jenkins
    if __name__ == '__main__'是什么意思?如何理解?看到一个很有用的解答
    关于执行webdriver.Chrome; 报错WebDriverException: Message: unknown error: Element is not clickable at point (1085, 103)
    【转载】学习总结——接口测试基础
    fiddler使用——配置抓取https,出现提示“禁用解密”“单击配置”
    算法(二叉树-矩阵-堆排序)
    算法(递归)---写的很乱
    ES6高级技巧(二)
    ES6高级使用技巧(reduce,filter篇)
  • 原文地址:https://www.cnblogs.com/zobol/p/10716333.html
Copyright © 2020-2023  润新知