最近正在使用Qt的QOpenGLWidget來學習opengl,前期進展十分順利,直到我遇到了framebuffer這一章節
framebuffer的大致使用方式如下:
// 創建FBO
unsigned int framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
// create a color attachment texture
unsigned int textureColorbuffer;
glGenTextures(1, &textureColorbuffer);
glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureColorbuffer, 0);
// create a renderbuffer object for depth and stencil attachment (we won't be sampling these)
unsigned int rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, SCR_WIDTH, SCR_HEIGHT); // use a single renderbuffer object for both a depth AND stencil buffer.
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); // now actually attach it
// now that we actually created the framebuffer and added all attachments we want to check if it is actually complete now
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// 第一處理階段(Pass)
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 我們現在不使用模板緩沖
glEnable(GL_DEPTH_TEST);
DrawScene();
// 第二處理階段
glBindFramebuffer(GL_FRAMEBUFFER, 0); // 返回默認
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
screenShader.use();
glBindVertexArray(quadVAO);
glDisable(GL_DEPTH_TEST);
glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
glDrawArrays(GL_TRIANGLES, 0, 6);
程序很簡單,但是當我運行之后,發現屏幕全白,即對應glClearColor(1.0f, 1.0f, 1.0f, 1.0f)
,為了驗證我這個猜想,我進行了以下步驟:
- 屏幕全白是不是真的對應到glClearColor這個函數。修改清屏顏色,發現運行后的屏幕顏色確實和其一一對應。
- 有沒有真的把三角形繪制上去。把
glDrawArrays(GL_TRIANGLES, 0, 6)
刪除后,發現並不影響屏幕全白這一結果,因此問題應該出現在glClearColor(1.0f, 1.0f, 1.0f, 1.0f)
之前。 - 刪除第二處理階段的這幾行代碼,發現屏幕變黑,對應
glClearColor(0.1f, 0.1f, 0.1f, 1.0f)
,再把glBindFramebuffer(GL_FRAMEBUFFER, framebuffer)
注釋掉,讓物體不繪制在幀緩沖內,而是直接繪制到當前屏幕上,嘿,沒問題!
那……問題應該是出現在幀緩沖綁定上?我們是否真的綁定到了幀緩沖區呢?
stackoverflow搜索一波:
OpenGL Qt : problem using framebuffers for bloom effect
OpenGL does not render to screen by calling glBindFramebuffer(GL_FRAMEBUFFER,0)
Easiest way for offscreen rendering with QOpenGLWidget
果真是緩沖區綁定的問題!當我們執行glBindFramebuffer(GL_FRAMEBUFFER, 0)
時,我們並沒有返回到Qt默認的幀緩沖,應該先調用QOpenGLContext::defaultFramebufferObject()
這個函數得到Qt默認的幀緩沖,
Call this to get the default framebuffer object for the current surface.
On some platforms (for instance, iOS) the default framebuffer object depends on the surface being rendered to, and might be different from 0. Thus, instead of calling glBindFramebuffer(0), you should call glBindFramebuffer(ctx->defaultFramebufferObject()) if you want your application to work across different Qt platforms.
If you use the glBindFramebuffer() in QOpenGLFunctions you do not have to worry about this, as it automatically binds the current context's defaultFramebufferObject() when 0 is passed.
Note: Widgets that render via framebuffer objects, like QOpenGLWidget and QQuickWidget, will override the value returned from this function when painting is active, because at that time the correct "default" framebuffer is the widget's associated backing framebuffer, not the platform-specific one belonging to the top-level window's surface. This ensures the expected behavior for this function and other classes relying on it (for example, QOpenGLFramebufferObject::bindDefault() or QOpenGLFramebufferObject::release()).
See also QOpenGLFramebufferObject.
即需要這么改:
glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
修改完之后,終於把這個問題解決啦!
當然,我們可以使用qt封裝好的QOpenGLFramebufferObject類,這樣代碼更加簡潔:
// 創建FBO
m_FBO = std::make_unique<QOpenGLFramebufferObject>(this->size(), QOpenGLFramebufferObject::CombinedDepthStencil);
// 第一處理階段(Pass)
m_FBO->bind();
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 我們現在不使用模板緩沖
glEnable(GL_DEPTH_TEST);
DrawScene();
// 第二處理階段
m_FBO->release();
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
screenShader.use();
glBindVertexArray(quadVAO);
glDisable(GL_DEPTH_TEST);
glBindTexture(GL_TEXTURE_2D, m_FBO->texture();
glDrawArrays(GL_TRIANGLES, 0, 6);
我使用QOpenGLWidget學習opengl的代碼放在github上了,歡迎訪問~ miyanyan/learnopengl-qt