如何使用不同的 VAO、VBO 绘制 2 个或更多对象?

Posted

技术标签:

【中文标题】如何使用不同的 VAO、VBO 绘制 2 个或更多对象?【英文标题】:How to draw 2 or more objects with different VAO, VBO? 【发布时间】:2018-10-15 21:26:21 【问题描述】:

我想画立方体和球体,但 openGl 只画立方体。我制作了 2 个 VAO:第一个 VAO 包含 VBO(带有坐标立方体),其他 VAO 包含坐标球体。如果我只想绘制球体,它也不起作用。可能是因为不正确的 VBO 绑定而发生的......但我不完全了解如何处理它。

以下代码(制作球体):

GLuint sphere(float radius, int slices, int stacks) 
GLuint vbo;
int n = 2 * (slices + 1) * stacks;
int i = 0;
vec3 *points = new vec3[n];

for (float theta = -M_PI / 2; theta < M_PI / 2 - 0.0001; theta += M_PI / stacks) 
    for (float phi = -M_PI; phi <= M_PI + 0.0001; phi += 2 * M_PI / slices) 
        points[i++] = vec3(cos(theta) * sin(phi), -sin(theta), cos(theta) * cos(phi));
        points[i++] = vec3(cos(theta + M_PI / stacks) * sin(phi), -sin(theta + M_PI / stacks), cos(theta + M_PI / stacks) * cos(phi));
    


glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

return vbo;

Main(我在代码中删除了立方体坐标,因为它太长了):

glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "Laba5", nullptr, nullptr);
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, key_callback);
glfwSetMouseButtonCallback(window, mouse_button_callback);
glewExperimental = GL_TRUE;
glewInit();

glViewport(0, 0, WIDTH, HEIGHT);
glEnable(GL_DEPTH_TEST);

Shader ourShader("shader.vs", "shader.frag");
GLuint VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindVertexArray(0); // Unbind VAO
//-----------------------------------------------------------2
GLuint VAO2;
glGenVertexArrays(1, &VAO2);
glBindVertexArray(VAO2);
glBindBuffer(GL_ARRAY_BUFFER, sphere(1, 30, 30));
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindVertexArray(0); // Unbind VAO

while (!glfwWindowShouldClose(window))


    // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
    glfwPollEvents();

    glClearColor(0.32f, 0.31f, 0.30f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    ourShader.Use();

    mat4 view = mat4(1.0);
    mat4 projection = mat4(1.0);
    view = translate(view, vec3(1.0f, 1.0f, -4.8f));
    projection = perspective(45.0f, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
    GLint modelLoc = glGetUniformLocation(ourShader.Program, "model");
    GLint viewLoc = glGetUniformLocation(ourShader.Program, "view");
    GLint projLoc = glGetUniformLocation(ourShader.Program, "projection");

    glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
    glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
    glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

    if (lbutton_down)
    
        double cur_mx, cur_my;
        glfwGetCursorPos(window, &cur_mx, &cur_my);
        if (cur_mx != last_mx || cur_my != last_my)
        
            vec3 va = get_arcball_vector(last_mx, last_my);
            vec3 vb = get_arcball_vector(cur_mx, cur_my);
            float angle = acos(min(1.0f, dot(va, vb)));
            vec3 axis_in_camera_coord = cross(va, vb);
            mat3 camera2object = inverse(model);
            vec3 axis_in_object_coord = camera2object * axis_in_camera_coord;
            model = rotate(model, degrees(angle)*0.05f, axis_in_object_coord);
            last_mx = cur_mx;
            last_my = cur_my;
        
    

    glBindVertexArray(VAO);
    glDrawArrays(GL_TRIANGLES, 0, 36);
    glBindVertexArray(0);

    glBindVertexArray(VAO2);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 2 * 31 * 30);
    glBindVertexArray(0);

    glfwSwapBuffers(window);

glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteVertexArrays(1, &VAO2);
glfwTerminate();
return 0;

【问题讨论】:

我对球体坐标有疑问,但不适用于这个问题。感谢@derhass 的帮助,如果我不创建额外的缓冲区,它真的可以工作! 【参考方案1】:

您的 sphere 函数在内部创建一个新的 GL 缓冲区对象并返回其名称,因此:

GLuint sphereVBO = sphere(1, 30, 30);

您现在拥有包含该缓冲区名称的 sphereVBO

glGenVertexArrays(1, &VAO2);
glGenBuffers(1, &sphereVBO);

但是现在,您创建了一个新的缓冲区对象,并用新名称覆盖了sphereVBO。结果,您试图从一个空的缓冲区对象中进行绘制(没有调用 glBufferData 来实际为其创建数据存储),并且您完全失去了对球体数据的原始 VBO 的跟踪。

【讨论】:

我编辑代码删除glGenBuffers。我在 glBindBuffer 中调用方法 sphere(1, 30, 30) ,但效果不佳。 :(

以上是关于如何使用不同的 VAO、VBO 绘制 2 个或更多对象?的主要内容,如果未能解决你的问题,请参考以下文章

绘图时 VAO 和 VBO 崩溃

多个网格、多个 VBO、多个 VAO、OpenGL 4.1

OpenGL - 如何理解 VAO 与 VBO 之间的关系

OpenGL:一个 VBO 的多个 VAO

渲染一个包含两个 VBO 的 VAO

VAO VBO EBO