How to draw from a Multisampled buffer to default frame buffer - opengl

I am trying to render from a multisampled framebuffer to a default frame buffer.
first i enable multisampling with these commands.
glfwWindowHint(GLFW_SAMPLES, 4);
glEnable(GL_MULTISAMPLE);
Than i create a multisampled buffer
unsigned int fboMsaaId, rboDepthId;
unsigned int MsaaTexture;
// Generate texture
glGenTextures(1, &MsaaTexture);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, 800, 600, false);
// create a multisample buffer
glGenFramebuffers(1, &fboMsaaId);
glGenRenderbuffers(1, &rboDepthId);
glBindFramebuffer(GL_FRAMEBUFFER, fboMsaaId);
glBindRenderbuffer(GL_RENDERBUFFER, rboDepthId);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, 800, 600);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, MsaaTexture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,rboDepthId);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
In the render loop.
while (!glfwWindowShouldClose(window))
{
// input
// -----
processInput(window);
// render
// ------
glBindFramebuffer(GL_FRAMEBUFFER, fboMsaaId);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// draw our triangle
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboMsaaId);
glDrawBuffer(GL_BACK);
glBlitFramebuffer(0, 0, 800, 600, 0, 0, 800, 600, GL_COLOR_BUFFER_BIT, GL_LINEAR);
glfwSwapBuffers(window);
glfwPollEvents();
}
If i render into the default framebuffer i am able to see the drawn object but when i blit from multisampled frame buffer to default frame buffer nothing is drawn on the screen.

What you want to do is make it so that the default framebuffer is not multisampled. That is, you want to blit from a multisampled framebuffer to a non-multisampled framebuffer.
Yes, it is legal to blit between two multisampled framebuffers, but this is only legal if they have the same sample count. And while you did request a 4-sampled default framebuffer, the default framebuffer is not entirely under your control. Implementations get to play fast-and-loose as to how many samples a default framebuffer gets.
So it's best to avoid this possibility altogether and use a non-multisampled default framebuffer.

Related

Texture repeating over surface when rendered from FBO

I am trying to render a texture to a surface from a FBO but I am getting a repeat image effect like this: Repeating Image effect
I am not sure what is doing this. I am adapting the code from this tutorial: https://www.youtube.com/watch?v=21UsMuFTN0k which is in java to c++.
This is my code i use to setup to FBO:
GLuint frameBuffer;
glGenFramebuffers(1, &frameBuffer);
//generate name for frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
//create the framebuffer
glDrawBuffer(GL_COLOR_ATTACHMENT0);
//indicate that we will always render to color attachment 0
//texture setup
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 320, 180, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,texture, 0);
//depth buffer setup
GLuint depthBuffer;
glGenRenderbuffers(1, &depthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, 320, 180);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);
This section is used to render:
//water is two triangle joined together
Water test(texture);
render->addWater(&test);
while (!glfwWindowShouldClose(window))
{
// Set frame time
GLfloat currentFrame = (float)glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
gameController->update(deltaTime);
glBindTexture(GL_TEXTURE_2D, 0);//To make sure the texture isn't bound
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
glViewport(0, 0, 320, 180);
render->renderScene();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
render->renderScene();
render->renderWater();
glfwSwapBuffers(window);
}
If there is any other code that is needed, let me know.
The "repeat image" effect is caused, because you don't clear the framebuffer (color attachment texture and render buffer for the depth).
It is not sufficient to clear the drawing buffer. You have to clar the color plane and the depth buffer of the framebuffer too.
Bind the framebuffer, set the clear color (background of the texture) and clear the frame buffer. This causes that the each texel of the texture object is set to the color which you specify by glClearColor right before and the render buffer object (depthBuffer) is cleared (set to 1.0 - default value see glClearDepth).
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
After rendering to the texture, set the default framebuffer for rendering, set the background color and clear the buffer:
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

OpenGL depth buffer or depth test does not work when rendering with a shared context

I have a very specific OpenGL setup where the 3D geometry in the scene does not render. There is some depth-related state in the OpenGL context that illudes me.
This is in a production engine at work where we added shared context support with multiple window contexts. We are doing the rendering to a framebuffer object owned by the shared context, then blitting the color attachment renderbuffer to a window using a different context.
The clear color is showing in the resulting blit to the window, but not the 3D scene geometry itself, so we know the framebuffer and renderbuffer objects are at least partially correct.
To illustrate, I refactored a sample from the LearnOpenGL website to illustrate my bug. It shows up there as well, so I am clear this is something I am missing.
Here is the GitHub project where I made three commits to a working framebuffer sample so that it rendered to the framebuffer using a shared context and then blits the result: Framebuffer Shared Context Experiment
Here is most of the source code that produces the buggy result. I snipped a few sections that went unchanged.
// glfw dummy window creation
// --------------------
GLFWwindow* dummy = NULL;
#if USE_SHARED_CONTEXT
dummy = glfwCreateWindow(1, 1, "Dummy", NULL, NULL);
if (dummy == NULL)
{
std::cout << "Failed to create dummy GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(dummy);
#endif
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, dummy);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwShowWindow(window);
#if !USE_SHARED_CONTEXT
glfwMakeContextCurrent(window);
#endif
// <snip creation of shared resources>
// <snip creation of un-shared vertex array>
// framebuffer configuration
// -------------------------
unsigned int framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
#if 1
// create a color attachment render buffer
unsigned int Colorbuffer;
glGenRenderbuffers(1, &Colorbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, Colorbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, SCR_WIDTH, SCR_HEIGHT); // use a single renderbuffer object for both a depth AND stencil buffer.
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, Colorbuffer); // now actually attach it
#else
// 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);
#endif
// 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);
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// <snip timing and input>
#if USE_SHARED_CONTEXT
// use shared context because that is what is holding our framebuffer and vao.
// -----
glfwMakeContextCurrent(dummy);
#endif
// render
// ------
// bind to framebuffer and draw scene as we normally would to color texture
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glEnable(GL_DEPTH_TEST); // enable depth testing (is disabled for rendering screen-space quad)
// make sure we clear the framebuffer's content
glClearColor(1.0f, 0.1f, 0.1f, 1.0f);
glClearDepth(1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// following render is unchanged
shader.use();
glm::mat4 model;
glm::mat4 view = camera.GetViewMatrix();
glm::mat4 projection = glm::perspective(camera.Zoom, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
shader.setMat4("view", view);
shader.setMat4("projection", projection);
// cubes
glBindVertexArray(cubeVAO);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, cubeTexture);
model = glm::translate(model, glm::vec3(-1.0f, 0.0f, -1.0f));
shader.setMat4("model", model);
glDrawArrays(GL_TRIANGLES, 0, 36);
model = glm::mat4();
model = glm::translate(model, glm::vec3(2.0f, 0.0f, 0.0f));
shader.setMat4("model", model);
glDrawArrays(GL_TRIANGLES, 0, 36);
// floor
glBindVertexArray(planeVAO);
glBindTexture(GL_TEXTURE_2D, floorTexture);
shader.setMat4("model", glm::mat4());
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
#if 1
#if USE_SHARED_CONTEXT
// use window context for presentation via blit.
// -----
glfwMakeContextCurrent(window);
// temorary framebuffer for visible window since framebuffers are not shared
// -------------------------
unsigned int readFramebuffer;
glGenFramebuffers(1, &readFramebuffer);
glBindFramebuffer(GL_READ_FRAMEBUFFER, readFramebuffer);
glBindRenderbuffer(GL_RENDERBUFFER, Colorbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, SCR_WIDTH, SCR_HEIGHT);
glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, Colorbuffer);
#endif
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, SCR_WIDTH, SCR_HEIGHT, 0, 0, SCR_WIDTH, SCR_HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST);
#if USE_SHARED_CONTEXT
glDeleteFramebuffers(1, &readFramebuffer);
#endif
#else
// now bind back to default framebuffer and draw a quad plane with the attached framebuffer color texture
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDisable(GL_DEPTH_TEST); // disable depth test so screen-space quad isn't discarded due to depth test.
// clear all relevant buffers
glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // set clear color to white (not really necessery actually, since we won't be able to see behind the quad anyways)
glClear(GL_COLOR_BUFFER_BIT);
screenShader.use();
glBindVertexArray(quadVAO);
glBindTexture(GL_TEXTURE_2D, textureColorbuffer); // use the color attachment texture as the texture of the quad plane
glDrawArrays(GL_TRIANGLES, 0, 6);
#endif
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
// <snip epilog>
You can toggle the USE_SHARED_CONTEXT compile-time switch to remove the little bit of code that renders using a shared context.
I was missing a call to glViewport for the shared render context.
The value of the Viewport was defaulted to (0, 0) -> (width, height) for the context used by the visible window. The shared render context had been defaulted to (0, 0) -> (1, 1) because I used a width and height of 1 for the non-visible GLFW window.

OpenGL multiple Framebuffer chaining and post processing

I have three different framebuffer objects for three different steps:
GLuint fbo1; // Step1: draw initial scene
GLuint fbo2; // Step2: apply lens effect shader
GLuint fbo3; // Step3: final smoothing
I want to draw my initial scene as a texture attached to fbo1.
Then apply a lens-shader on that and save it as a texture in fbo2.
Then apply a smoothing-shader on fbo2 and save it to fbo3.
The code in my main render function, uses a few helper functions, and is as follows:
// Step1
useFrameBuffer(fbo1, fbo1); // src , dest
DrawInitialScene();
// Step2
LensShader.use(); // a glsl shader program class
useFrameBuffer(fbo1, fbo2); // src , dest
drawQuadOnFbo(fbo2Texture);
// Step3
SmoothingShader.use(); // a glsl shader program class
useFrameBuffer(fbo2, fbo3); // src , dest
drawQuadOnFbo(fbo3Texture);
// Final Copy Step to OpenGL default FBO
int width = self.frame.size.width;
int height = self.frame.size.height;
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo3);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); //destination
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
The helper function useFrameBuffer(src, dest), simply sets the source framebuffer to read from and destination write framebuffer:
void useFrameBuffer (GLuint fboSrc , GLuint fboDest)
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboSrc);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboDest);
GLenum drawbufs[] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, drawbufs);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glViewport(0, 0, width, height); // width, height are private to class
}
To apply the shader on the output of fbo1 and fbo2, I am drawing a quad with a texture associated to the respective frame-buffers:
void drawQuadOnFbo(GLuint fboTexture) {
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // We're not using stencil buffer now
glBlendFunc(GL_ONE, GL_ONE);
glBindVertexArray(quadVAO);
glDisable(GL_DEPTH_TEST);
glBindTexture(GL_TEXTURE_2D, fboTexture);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
}
The Problem:
The problem is that output appears to be a combination of Step1 and Step3 only. Looks like the Step2 is completely missed out. You can see in the final copy step, I am copying the contents of fbo3 to the final OpenGL default fbo using:
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo3);
If I change this to fbo2, then I do see the results of applying Step2 (but then I don't have Step3).
So it looks like that both fbo2 and fbo3 are connected directly to the output of fbo1, but not connected to each other.
How can I fix that ??
Other Helper Functions:
I initialised my Framebuffers at the start of the application, outside the main render function using the method below:
GLuint initFrameBuffer( int width, int height, GLenum textureTarget) {
// 1. Create FBO
GLuint fbo;
glGenFramebuffers(1, &fbo);
// 2. Bind it
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// FrameBuffer Requirements
// (i). atleast 1 buffer (color, depth, stencil)
// (ii). atleast 1 color-attachment
// (iii). all attachments should be complete
// (iv). same no. of samples for each buffer
// 3. Texture Attachment
GLuint quadTex; // texture to be associated to this FBO
glActiveTexture(textureTarget);
glGenTextures(1, &quadTex);
glBindTexture(GL_TEXTURE_2D, quadTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// attach it as COLOR-ATTACHMENT 0
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, quadTex, 0);
// 4. RenderBuffer attachement
GLuint rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// attach it
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
// 5. Set Targets for fragment shader output
GLenum drawbufs[] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, drawbufs);
// 6. Check that the FrameBuffer is complete
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE){
NSLog(#"ERROR::FRAMEBUFFER:: Framebuffer is not complete!");
return fboObj;
}
// 7. revert to Default
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return fbo;
}

Clearing FBO doesn't clear everything

I have code which is the equivalent of the following:
init code:
glCullFace(GL_FRONT_AND_BACK);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
//clear to pink
glClearColor(1.0f, 0.0f, 1.0f, 0.0f);
GLuint textureObject;
glGenTextures(1, &textureObject);
glBindTexture(GL_TEXTURE_2D, textureObject);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 512, 512, 0, GL_RGBA,
GL_UNSIGNED_BTYE, NULL);
GLuint fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
textureObject, 0);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
assert(glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER)
== GL_FRAMEBUFFER_COMPLETE);
Object obj = CreateAFullscreenQuad(textureObject);
draw code:
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
glViewport(0, 0, 512, 512);
//clear to cyan
GLfloat clear[] = {0, 1, 1, 1};
glClearBufferfv(GL_COLOR, 0, clear);
SetUpPerspectiveAndCameraMatrix();
DrawSomeStuff();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glViewport(0, 0, window.width, window.height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
SetUpPerspectiveAndCameraMatrix();
obj.Draw();
SwapBuffers();
glxinfo excerpt:
OpenGL vendor string: VMware, Inc.
OpenGL renderer string: Gallium 0.4 on llvmpipe (LLVM 3.4, 128 bits)
OpenGL version string: 2.1 Mesa 10.1.3
The problem is that the call to glClearBufferfv doesn't seem to clear the whole buffer. As you can see, it clears the triangular shape, but it doesn't touch the other areas. They are also not the glClearColor color (pink). If I use the regular glClear call, it does the same thing, but pink instead of cyan.
Turns out I created a feedback loop by drawing the fullscreen quad while the FBO was bound, drawing the contents of the buffer to itself. Apparently the effects of this end up being interspersed with the effects of glClear.

Cannot render to texture with multisampling

I ran the framebuffers example in this page -original code- (using glfw3 and glew in xcode 4.6 in osx 10.8), it worked fine, then I wanted to add multisampling (to avoid jagged edges on cube edges and on the floor, glfwWindowHint (GLFW_SAMPLES, 4) was enough when rendering directly to the back-buffer), found some answers directing to opengl.org, tried to use glTexImage2DMultisample but it displayed nothing (black screen). The framebuffer settings and rendering loop is:
// Create frame buffer
GLuint frameBuffer;
glGenFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
// Create texture to hold color buffer
GLuint texColorBuffer;
glGenTextures(1, &texColorBuffer);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texColorBuffer);
//glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGB, width, height, GL_FALSE);
/*
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
*/
//glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texColorBuffer, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, texColorBuffer, 0);
// Create Renderbuffer Object to hold depth and stencil buffers
GLuint rboDepthStencil;
glGenRenderbuffers(1, &rboDepthStencil);
glBindRenderbuffer(GL_RENDERBUFFER, rboDepthStencil);
//glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
glRenderbufferStorageMultisample (GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rboDepthStencil);
// ...
while (!window->shouldClose()) {
static int rot = 0;
// Bind our framebuffer and draw 3D scene (spinning cube)
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
auto err_res = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if(err_res != GL_FRAMEBUFFER_COMPLETE) {
ERR("Incomplete frameBuffer:%X!", err_res);
goto end;
}
glBindVertexArray(vaoCube);
glEnable(GL_DEPTH_TEST);
glUseProgram(sceneShaderProgram);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texKitten);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texPuppy);
// Clear the screen to white
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUniformMatrix4fv(uniModel, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(uniView, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(uniProj, 1, GL_FALSE, glm::value_ptr(proj));
// Draw cube
glEnable(GL_MULTISAMPLE);
glDrawArrays(GL_TRIANGLES, 0, 36);
glEnable(GL_STENCIL_TEST);
// Draw floor
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glStencilMask(0xFF);
glDepthMask(GL_FALSE);
glClear(GL_STENCIL_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 36, 6);
// Draw cube reflection
glStencilFunc(GL_EQUAL, 1, 0xFF);
glStencilMask(0x00);
glDepthMask(GL_TRUE);
model = glm::scale(glm::translate(model, glm::vec3(0, 0, -1)), glm::vec3(1, 1, -1));
glUniformMatrix4fv(uniModel, 1, GL_FALSE, glm::value_ptr(model));
glUniform3f(uniColor, 0.3f, 0.3f, 0.3f);
glDrawArrays(GL_TRIANGLES, 0, 36);
glUniform3f(uniColor, 1.0f, 1.0f, 1.0f);
glDisable(GL_STENCIL_TEST);
/*
// Bind default framebuffer and draw contents of our framebuffer
glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
glBindVertexArray(vaoQuad);
glDisable(GL_DEPTH_TEST);
glUseProgram(screenShaderProgram);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texColorBuffer);
glDrawArrays(GL_TRIANGLES, 0, 6);
*/
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // Make sure no FBO is set as the draw framebuffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBuffer); // Make sure your multisampled FBO is the read framebuffer
glDrawBuffer(GL_BACK); // Set the back buffer as the draw buffer
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
// Swap buffers
glfwSwapBuffers(window->getHandle());
glfwPollEvents();
}
glVersion: 3.2 NVIDIA-8.10.44 304.10.65f03
glRenderer: NVIDIA GeForce 9400M OpenGL Engine
The 'EXT' additions are probably unnecessary but I also tried to run without them before and the result was the same. What am I doing wrong?
EDIT: Now binding GL_TEXTURE_2D_MULTISAMPLE and getting GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE error!
If you checked your framebuffer object for completeness, you would probably have caught this by now... your depth/stencil buffer needs to be multisampled as well.
A framebuffer is considered multisample incomplete by both core and the EXT FBO extension if one attachment has a different number of samples than any other attachment. In your case, you have a color buffer attachment with 4 samples and a depth/stencil attachment with 1 sample.
Name
glCheckFramebufferStatus — check the completeness status of a framebuffer
Description
glCheckFramebufferStatus queries the completeness status of the framebuffer object currently bound to target. target must be GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER or GL_FRAMEBUFFER. GL_FRAMEBUFFER is equivalent to GL_DRAW_FRAMEBUFFER.
The return value is GL_FRAMEBUFFER_COMPLETE if the framebuffer bound to target is complete. Otherwise, the return value is determined as follows:
[...]
GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE is returned if the value of GL_RENDERBUFFER_SAMPLES is not the same for all attached renderbuffers; if the value of GL_TEXTURE_SAMPLES is the not same for all attached textures; or, if the attached images are a mix of renderbuffers and textures, the value of GL_RENDERBUFFER_SAMPLES does not match the value of GL_TEXTURE_SAMPLES.
To fix this, you need to allocate a multisampled depth/stencil attachment with 4 samples:
glRenderbufferStorageMultisample (GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, width, height);
By the way, since your implementation is >= 3.0, you do not need the EXT suffix on anything. All of the constants defined by the EXT extension are identical to ARB / core FBOs, but some of the EXT functions (such as glCheckFramebufferStatusEXT) have more restrictive behavior (requiring each attachment to have the same image dimensions, for instance).