Vbo如何绘制原语

Posted

技术标签:

【中文标题】Vbo如何绘制原语【英文标题】:Vbo how to draw primitivies 【发布时间】:2013-10-24 02:16:23 【问题描述】:

您好,我正在尝试使用本教程学习 OpenGL http://www.opengl-tutorial.org/beginners-tutorials/tutorial-2-the-first-triangle/ 我会说本教程不适合初学者。他们展示了很多适用于 1 个三角形的代码,但没有更多示例。 Atm 我正在尝试编写绘制函数。线条或矩形,我有问题,因为这对我来说很难。 我想写可重用的函数。但我不明白 VBO :/ 我想编写将从主循环执行的函数 draw。

class lines
    public: lines()
    
    static void draw(GLuint ve)

        float vertices[] = -0.5f, -0.5f, 0.5f, 0.5f;
        unsigned int indices[] = 0, 1;

        glEnableClientState(GL_VERTEX_ARRAY);
        glVertexPointer(2, GL_FLOAT, 0, vertices);
        glDrawElements(GL_LINES, 2, GL_UNSIGNED_INT, indices);
    
;


    // Ensure we can capture the escape key being pressed below
glfwEnable( GLFW_STICKY_KEYS );

// Dark blue background
glClearColor(0.0f, 0.0f, 0.4f, 0.0f);

GLuint VertexArrayID;
glGenVertexArrays(1, &VertexArrayID);
glBindVertexArray(VertexArrayID);

// Create and compile our GLSL program from the shaders
GLuint programID = LoadShaders( "SimpleVertexShader.vertexshader", "SimpleFragmentShader.fragmentshader" );


static const GLfloat g_vertex_buffer_data[] =  
    -0.8f, -1.0f,0.0f,
    0.8f,  -1.0f, 0.0f,
    -0.8f,   1.0f, 0.0f,
    -0.8f, 1.0f, 0.0f,
    0.8f, 1.0f, 0.0f,
    0.8, -1.0f, 0.0f,
;

GLuint vertexbuffer;
glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

do

    // Clear the screen
    glClear( GL_COLOR_BUFFER_BIT );

    // Use our shader
    glUseProgram(programID);

    // 1rst attribute buffer : vertices
    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
    glVertexAttribPointer(
        0,                  // attribute 0. No particular reason for 0, but must match the layout in the shader.
        3,                  // size
        GL_FLOAT,           // type
        GL_FALSE,           // normalized?
        0,                  // stride
        (void*)0            // array buffer offset
    );

    glDrawArrays(GL_TRIANGLES, 0, 6); 

    glDisableVertexAttribArray(0);
    lines::draw(vertexbuffer);
    // Swap buffers
    glfwSwapBuffers();

 // Check if the ESC key was pressed or the window was closed
while( glfwGetKey( GLFW_KEY_ESC ) != GLFW_PRESS &&
       glfwGetWindowParam( GLFW_OPENED ) );

【问题讨论】:

这里混合了顶点数组、VBO 和 VAO。您能否更具体地说明您遇到的问题? 【参考方案1】:

要绘制图元,您需要给 OpenGL 一个顶点列表以及连接它们的模式(例如,形成直线、三角形等)。最简单的方法是立即模式 (glBegin/glVertex3f/glEnd)。立即模式非常慢,通过“顶点数组”一次传递所有顶点要快得多(不是顶点数组对象(VAO),它们是不同的),它们按顺序排列在一维数组中,就像你的@ 987654331@。顶点数组是指将指向主内存中数组的指针传递给glVertexPointer。这更快,但每次绘制时都会将整个数组发送到 GPU。顶点缓冲区对象 (VBO) 允许您发送数组并将其存储在 GPU 内存中。使用 VBO,只需要发送一次绘图调用,并且数据已经在 GPU 上,因此无需传输,而且速度更快。 VAO 对 glVertexPointer 参数进行分组以加快绑定速度,glDraw*Indirect 允许使用 GPU 内存中已有的参数进行绘图,但我将这些留待以后使用。

大多数教程都是从硬编码顶点开始的,但对于最终应用程序来说这当然是不切实际的。您想要存储任意几何图形的许多位。类似于您的lines 的网格类很常见。关键是网格有一个顶点列表。您可以添加索引数组来重新引用现有顶点以形成图元,从而节省内存和重复顶点的计算。

但我现在先从立即模式渲染开始 - 出错的可能性较小。

让你开始,

struct vec3f 
    float x, y, z;
    vec3f(float nx, float ny, float nz) : x(nx), y(ny), z(nz) 
;

class Mesh 
    std::vector<vec3f> vertices;
public:
    void add(float x, float y, float z)
    
        vertices.push_back(vec3f(x, y, z));
    
    void draw()
    
        glBegin(GL_LINE_STRIP);
        for (size_t i = 0; i < vertices.size(); ++i)
            glVertex3f(vertices[i].x, vertices[i].y, vertices[i].z);
        glEnd();
    
;

并使用它,

Mesh square;
...
//Lots of add() and push_back is slow. Much faster to use
//dynamic allocation yourself and copy data in bigger blocks,
//but this will do for an example.
square.add(-1,-1,0);
square.add(-1,1,0);
square.add(1,1,0);
square.add(1,-1,0);
square.add(-1,-1,0); //these could also be read from a file
...
line.draw();

例如,这个想法允许您拥有一个全局std::list&lt;Mesh&gt; lines;。您可以在初始化时、从文件中或在运行时向其添加行。然后你的绘图代码只需要在每个元素上调用 draw。稍后你可以扩展这个想法以支持三角形(例如,使GL_LINE_STRIP 成为primitive 成员)。稍后您可以添加索引、法线(在这里您需要使用gl*Pointer 函数的stride 参数查找交错顶点/法线数据)、颜色/材质等。

关于使用 VBO 的问题。 glGenBuffers 会给你一个唯一的句柄来引用你的 VBO - 只是一个整数。当您修改或使用该 VBO 时,您需要绑定它。

glBufferData(GL_ARRAY_BUFFER... 对当前绑定的GL_ARRAY_BUFFER(如果尚未)进行实际初始化/调整大小,并在数据参数为非 NULL 时传输数据。

glVertexAttribPointer 将假定您在主内存中为上面提到的“顶点数组”传递一个数组,除非您glBindBuffer(GL_ARRAY_BUFFER, vbo),在这种情况下,最后一个参数将成为该 VBO 的偏移量。在你打电话给glVertexAttribPointer之后。

所以,

1. glGenBuffers
2. glBufferData (while the buffer is bound)

这是初始化处理。开始画画……

3. glVertexAttribPointer (while the buffer is bound)

然后

4. glDrawArrays

4. glDrawElements (while an element array buffer is bound, containing the order in which to draw the vertices)

所以要扩展网格类,您至少需要一个GLuint vertexBuffer;。也许添加一个函数upload() 来生成句柄并缓冲当前数据。然后您可以调用vertices.clear()(实际上,对于std::vector 使用this)假设您不再需要主内存中的顶点数据。然后改变draw函数绑定vbo,调用glVertexPointer,unbind和glDrawArrays(从numVertices成员vertices.size()现在可能为零)。

这里有一些相关的帖子:

The best way to use VBOs What are some best practices for OpenGL coding (esp. w.r.t. object orientation)? Most basic working vbo example 来自您的教程,tutorial-9-vbo-indexing SimpleVBO.c http://www.opengl.org/wiki/VBO_-_just_examples

【讨论】:

以上是关于Vbo如何绘制原语的主要内容,如果未能解决你的问题,请参考以下文章

如何序列化包含指向原语的指针的类?

如何在Java中对原语进行等效的通过引用传递

Kotlin中的并发原语

如何在 AST 解析器中将类型解析为原语

如何使用 win32 从多线程上下文初始化线程原语?

EF 时间原语类型