调用glDrawElements会中断某些GPU上的显示吗?

时间:2018-06-20 14:25:14

标签: c++ opengl glfw framebuffer

我正在与一些同事(OpenGL 3.3 + GLFW3和ImGui)一起在3D PBR引擎上工作,我们遇到了一个奇怪的错误:应用程序正常进入绘图循环,并且没有任何OpenGL错误(如使用glCheckError()),但屏幕完全空白。奇怪的是,这仅发生在使用Nvidia GeForce GTX 1060作为其图形卡的同事的计算机上(我的两个朋友使用的计算机完全相同)。

经过一些调查,结果发现此错误是由在主循环之前一次调用的函数触发的。长话短说,它使用几个着色器通过帧缓冲区将四边形渲染到纹理绑定。我将问题缩小为glDrawElements调用;在这些计算机上,除了注释掉该行以外,不进行任何其他操作可以固定显示(以显示垃圾显示纹理为代价,而不是绘制调用的预期结果),而其他所有计算机即使未注释该行也可以很好地运行应用程序

罪魁祸首功能如下:

/**
 * Returns a texture identifier filled with a precomputed irradiance map calculated
 * from a provided, already set up-environment map
 */
GLuint precomputeIrradianceMap(GLuint envMapId)
{
    std::cerr << "Precomputing irradiance map ... " << std::endl;

    GLuint irradianceVAO, irradianceVBO, irradianceMap,
        irradianceVertShader, irradianceFragShader, irradianceProgram, captureFBO,
        captureRBO;
    GLint aPos_location, env_map;

    glGenVertexArrays(1, &irradianceVAO);
    glBindVertexArray(irradianceVAO);
    glGenBuffers(1, &irradianceVBO);
    glBindBuffer(GL_ARRAY_BUFFER, irradianceVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(quad_vertices), quad_vertices, GL_STATIC_DRAW);

    glGenFramebuffers(1, &captureFBO);
    glGenRenderbuffers(1, &captureRBO);

    glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
    glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 320, 320);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, captureRBO);

    checkGLerror();

    glGenTextures(1, &irradianceMap);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, irradianceMap);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, 320, 320, 0, GL_RGBA, GL_FLOAT, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    glBindTexture(GL_TEXTURE_2D, envMapId);

    checkGLerror();

    irradianceVertShader = createShaderFromSource(GL_VERTEX_SHADER, "shaders/irradiance_vert.glsl");
    irradianceFragShader = createShaderFromSource(GL_FRAGMENT_SHADER, "shaders/irradiance_frag.glsl");

    irradianceProgram = glCreateProgram();
    glAttachShader(irradianceProgram, irradianceVertShader);
    glAttachShader(irradianceProgram, irradianceFragShader);
    printShaderLog(irradianceVertShader);
    printShaderLog(irradianceFragShader);
    glLinkProgram(irradianceProgram);

    glUseProgram(irradianceProgram);

    checkGLerror();

    env_map = glGetUniformLocation(irradianceProgram, "environmentMap");
    aPos_location = glGetAttribLocation(irradianceProgram, "aPos");
    glEnableVertexAttribArray(aPos_location);
    checkGLerror();
    glVertexAttribPointer(aPos_location, 3, GL_FLOAT, GL_FALSE, 0, (void *) 0);

    glUniform1i(env_map, 0);

    checkGLerror();

    glViewport(0, 0, 320, 320);
    glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);

    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, irradianceMap, 0);

    checkGLerror();

    glClearDepth(1.f);
    glClearColor(0.f, 0.f, 0.f, 1.f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    checkGLerror();

    // This is the culprit
    // glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, quad_indices);

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glBindRenderbuffer(GL_RENDERBUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    checkGLerror();

    // glDisableVertexAttribArray(aPos_location);
    glDeleteShader(irradianceVertShader);
    glDeleteShader(irradianceFragShader);
    glDeleteProgram(irradianceProgram);
    glDeleteFramebuffers(1, &captureFBO);
    glDeleteRenderbuffers(1, &captureRBO);
    glDeleteBuffers(1, &irradianceVBO);
    glDeleteVertexArrays(1, &irradianceVAO);

    checkGLerror();

    std::cerr << "... done " << std::endl;

    return irradianceMap;
}

您可以安全地将checkGLerror()替换为std::cerr << glCheckError() << std::endl。就像我之前说的那样,没有任何OpenGL错误,所有着色器都可以正常编译,而这仅在配备Nvidia GeForce GTX 1060的计算机上无法解决。

其余代码大部分是设置VAO,VBO等,并且渲染循环如下:

while (!glfwWindowShouldClose(window))
{
    glfwGetFramebufferSize(window, &display_w, &display_h);
    float newRatio = (float)display_w / display_h;
    if(ratio != newRatio)
    {
        ratio = newRatio;
        setAspectRatio(p, ratio);
        invP = p.inverse();
        glViewport(0, 0, display_w, display_h);
    }

    ImGui_ImplGlfwGL3_NewFrame();

    // Rendering + OpenGL rendering
    // Draw the skybox, then the model
    ImGui::Begin("Physical parameters", NULL, ImGuiWindowFlags_AlwaysAutoResize);
    ImGui::SliderFloat("Dielectric specularity", &ds, 0.f, 1.f, "%.3f");
    ImGui::SliderFloat("Light intensity", &l0, 0.f, 10.f, "%.2f");
    ImGui::Checkbox("Use irradiance map as skybox", &skyOrIrradiance);
    ImGui::Checkbox("Debug draw irradiance map", &debugDraw);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, skyOrIrradiance ? irradianceTexture : skybox_texture);
    ImGui::End();
    Matrix4f v = camera->getMatrix(), invV = v.inverse();

    // glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glBindVertexArray(vertex_array_objs[0]);
    glUseProgram(skybox_program);
    glUniformMatrix4fv(skybox_v_location, 1, GL_FALSE, (const GLfloat *) invV.data());
    glUniformMatrix4fv(skybox_p_location, 1, GL_FALSE, (const GLfloat *) invP.data());
    glDisable(GL_DEPTH_TEST);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, quad_indices);

    glBindVertexArray(vertex_array_objs[1]);
    glUseProgram(program);

    glUniformMatrix4fv(mv_location, 1, GL_FALSE, (const GLfloat *) v.data());
    glUniformMatrix4fv(p_location, 1, GL_FALSE, (const GLfloat *)p.data());
    glUniform1f(uDS_location, ds);
    glUniform1f(L0_location, l0);
    glActiveTexture(GL_TEXTURE0 + model.activeTexturesCount());
    glBindTexture(GL_TEXTURE_2D, irradianceTexture);
    glUniform1i(irradiance_location, model.activeTexturesCount());

    glEnable(GL_DEPTH_TEST);
    model.render();

    if(debugDraw)
    {
        displayTexture(irradianceTexture);
        displayTexture(skybox_texture, 0.f, -1.f);
    }

    camera->drawControls();
    // Draw basic interface
    basicInterface();

    ImGui::Render();
    ImGui_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData());
    glfwSwapBuffers(window);
    glfwPollEvents();
    camera->update(window);
}

model.render()如下:

void GltfModel::render() const
{
    for(unsigned int i = 0; i < _activeTextures.size(); i++)
    {
        if(_textureLocations[i] > -1)
        {
            int j = _activeTextures[i];
            glActiveTexture(GL_TEXTURE0 + i);
            glBindTexture(GL_TEXTURE_2D, _textureIds[j]);
            glUniform1i(_textureLocations[i], i);
        }
    }
    glBindBuffer(GL_ARRAY_BUFFER, _buffers[ARRAY_BUFFER]);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffers[ELEMENT_ARRAY_BUFFER]);
    glDrawElements(_drawingMode, _indicesCount, _indicesType, NULL);
}

多谢您的宝贵时间!

EDIT:将glClear(GL_COLOR_BUFFER_BIT)呼叫放在glfwSwapBuffers(window)之前,即使清除颜色已设置为浅蓝色,仍会显示白色屏幕,而罪魁祸首没有注释。注释罪魁祸首行的确显示为浅蓝色屏幕,因此这使我认为这是一个帧缓冲区问题,但我不能确定地说。

0 个答案:

没有答案