具有独特着色器的 OpenGL 多次绘制调用会产生空白屏幕

Posted

技术标签:

【中文标题】具有独特着色器的 OpenGL 多次绘制调用会产生空白屏幕【英文标题】:OpenGL multiple draw calls with unique shaders gives blank screen 【发布时间】:2017-04-25 04:31:20 【问题描述】:

我正在尝试将两个不同的顶点集合相互叠加。现在,我的主循环在它自己时正确渲染一个,而另一个在它自己时正确渲染,但是当我调用我的两个绘图函数时,我看到一个空白窗口。为什么会发生这种情况?

第一个绘图调用使用一个着色器,而第二个绘图调用使用不同的着色器。我没有清除中间的屏幕。

如果它使代码更清晰,我的着色器程序将存储为类变量,加载后的纹理 ID 也会存储在我的程序中。

这是我的主循环:

while (true)

    // Clear the colorbuffer
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    drawModel1(); // This works when drawModel2() is commented out
    drawModel2(); // This works when drawModel1() is commented out

    // Unbind buffer
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    // Swap the screen buffers
    glfwSwapBuffers(_window);

我的drawModel1() 函数渲染点:

void drawModel1()

    // Use the image shader
    _img_shader.use();

    // Feed the position data to the shader
    glBindBuffer(GL_ARRAY_BUFFER, _img_pos_VBO);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    // Feed the color data to the shader
    glBindBuffer(GL_ARRAY_BUFFER, _img_color_VBO);
    glVertexAttribPointer(1, 3, GL_UNSIGNED_BYTE, GL_TRUE, 3 * sizeof(GLubyte), (GLvoid*)0);
    glEnableVertexAttribArray(1);

    // Set the projection matrix in the vertex shader
    GLuint projM = glGetUniformLocation(_img_shader.program(), "proj");
    glm::mat4 proj = _ndc * _persMat;
    glUniformMatrix4fv(projM, 1, GL_TRUE, glm::value_ptr(proj));

    // Set the view matrix in the vertex shader
    GLuint viewM = glGetUniformLocation(_img_shader.program(), "view");
    glUniformMatrix4fv(viewM, 1, GL_TRUE, glm::value_ptr(_viewMat));

    // Draw the points
    glBindVertexArray(_img_VAO);
    glDrawArrays(GL_POINTS, 0, _numImageVertices);

    // Disable attributes
    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);

而我的drawModel2() 函数呈现索引三角形:

void drawModel2()

    _model_shader.use();

    // Load the mesh texture
    GLuint texID = _loaded_textures.at(mesh.tex_file());
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texID);
    glUniform1i(glGetUniformLocation(_model_shader.program(), "texture_img"), 0);

    // Set the proj matrix in the vertex shader
    GLuint nvpmM = glGetUniformLocation(_model_shader.program(), "npvm");
    glm::mat4 npvm = _ndc * _persMat * _viewMat * mat;
    glUniformMatrix4fv(nvpmM, 1, GL_FALSE, glm::value_ptr(npvm));

    // Feed the position data to the shader
    glBindBuffer(GL_ARRAY_BUFFER, mesh.pos_VBO());
    GLuint pos_att = glGetAttribLocation(_model_shader.program(), "position");
    glEnableVertexAttribArray(pos_att);
    glVertexAttribPointer(pos_att, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);

    // Feed the texture coordinate data to the shader
    glBindBuffer(GL_ARRAY_BUFFER, mesh.tex_VBO());
    GLuint tex_coord_att = glGetAttribLocation(_model_shader.program(), "texCoords");
    glEnableVertexAttribArray(tex_coord_att);
    glVertexAttribPointer(tex_coord_att, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)0);

    // Draw mesh
    glBindVertexArray(mesh.VAO());
    glDrawElements(GL_TRIANGLES, mesh.numIndices(), GL_UNSIGNED_SHORT, (void*)0);

    // Disable attributes
    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);

    // Release resources
    glBindTexture(GL_TEXTURE_2D, 0);

【问题讨论】:

如果在绘制调用开始时而不是在对象被绘制之前绑定顶点数组会发生什么? @Xirema:成功了!怎么会? 【参考方案1】:

您需要在函数开始时绑定顶点数组,而不是在绘制调用本身之前。 Vertex Array 负责维护与给定 object[-type] 关联的状态,并且任何将设置状态的调用(如 glVertexAttribPointerglEnableVertexAttribArray)都将在该 Vertex Array 上维护。您对旧代码所做的基本上是为您的对象设置状态,然后切换到完全不同的 VAO,然后进行绘图,这意味着模型 1 正在使用模型 2 的绑定和设置,反之亦然。除非他们有相同的规则和设置,否则他们不太可能同时抽签。

顺便说一句,由于 VAO 的存储状态,在您的绘制调用中需要的唯一内容就是绘制调用本身,以及任何改变该帧的数据。因此,您需要考虑花一些时间重构您的代码,因为看起来大多数设置(如缓冲区绑定)不会逐帧更改。

【讨论】:

感谢您的解释!文档对于新手来说非常难以理解

以上是关于具有独特着色器的 OpenGL 多次绘制调用会产生空白屏幕的主要内容,如果未能解决你的问题,请参考以下文章

最佳实践:多次使用相同的着色器

用于片段着色器的 OpenGL GLSL 绑定采样器

openGL:带着色器的线条

基于顶点的openGL中具有不同颜色的三角形的平面着色

顶点着色器的绘制操作

如何避免使用着色器在 OpenGL 中消失线?