3

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!

user3648895
  • 395
  • 10
  • 20

1 Answers1

5

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).

Andon M. Coleman
  • 42,359
  • 2
  • 81
  • 106
  • 1
    I obviously missed it, but I was already checking (correctly?) and did not get any errors!.. Anyways, I edited the code (and the question), but nothing has changed?? – user3648895 Jun 06 '14 at 01:39
  • I am not sure why you are using a multisampled texture in the first place to be honest. Multisampled textures add additional hardware requirements (your hardware is adequate to support them, but you still do not need them). I would use a multisampled renderbuffer for the color buffer unless you actually want to use the *multisampled* color buffer in a shader. No matter what the case, you should be binding `texColorBuffer` to `GL_TEXTURE_2D_MULTISAMPLE`. – Andon M. Coleman Jun 06 '14 at 02:07
  • Likewise, you may be tempted to correct `glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);` to apply to `GL_TEXTURE_2D_MULTISAMPLE` while you are at it... but don't. Texture filtering is not possible on multisampled textures. In fact, no sampler parameters are applicable to multisampled textures, which is why `GL_TEXTURE_2D_MULTISAMPLE` is not a valid target for that function. When you fetch from a multisampled texture, you have to do so using `texelFetch (...)` in a shader, which bypasses all sampler state. – Andon M. Coleman Jun 06 '14 at 02:15
  • Changed it and now getting GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE error? – user3648895 Jun 06 '14 at 02:32
  • 1
    Yeah, now you are hitting the other cause of `GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE`: *"`GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE` is also returned if the value of `GL_TEXTURE_FIXED_SAMPLE_LOCATIONS` is not the same for all attached textures; or, if the attached images are a mix of renderbuffers and textures, the value of `GL_TEXTURE_FIXED_SAMPLE_LOCATIONS` is **not** `GL_TRUE` for all attached textures"*. You need to use `GL_TRUE` instead of `GL_FALSE` when you call `glTexImage2DMultisample (...)` ;) – Andon M. Coleman Jun 06 '14 at 02:36
  • It worked. " _fixedsamplelocations_: Specifies whether the image will use identical sample locations and the same number of samples for all texels in the image, and the sample locations will not depend on the internal format or size of the image" -> I don't understand what it means by sample locations... – user3648895 Jun 06 '14 at 02:55
  • You need to understand multisample rasterization to understand what a sample location is. But basically, if a color buffer stores 4 samples GL computes 4 sub-pixel samples from different locations in your pixel. Ordinarily, in single-sampled rasterization, GL only computes a single sample at the exact center of a pixel. I do not know how much you care about technical details, but [this could be an interesting read](http://mynameismjp.wordpress.com/2012/10/24/msaa-overview/) if you have the time. Pay attention to the 4th diagram in that article to get a general idea what I am talking about. – Andon M. Coleman Jun 06 '14 at 03:06
  • Any chance of getting a complete example of the corrected code? – Alec Jacobson Jun 19 '18 at 16:55