27

我正在尝试为 FBO 启用多重采样和 alpha-to-coverage。使用默认的帧缓冲区,我所要做的就是调用glEnable(GL_MULTISAMPLE)glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE). 但是,我无法使用我自己的 FBO 达到相同的效果。

我的目标:将场景绘制到 FBO,就像将场景绘制到具有上述属性的默认帧缓冲区一样。从那里我希望能够将图像用作未来通过着色器的纹理。

这有效:用于制作没有多重采样/alpha 覆盖、1 个颜色附件、1 个深度附件的 FBO 的代码:

// Generate the color attachment
glGenTextures(1,&defaultColorAttachment0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,defaultColorAttachment0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,screenWidth,screenHeight,0,GL_RGBA,GL_UNSIGNED_BYTE,NULL);

// Bind the texture to the FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, defaultColorAttachment0,0);

// Generate the depth attachment
glGenRenderbuffers(1,&defaultDepthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, defaultDepthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, screenWidth, screenHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, defaultDepthBuffer);

这行不通。尝试制作多采样 FBO 的代码:

// Generate the color attachment
glGenTextures(1,&defaultColorAttachment0);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, defaultColorAttachment0);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA, screenWidth, screenHeight, GL_FALSE);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, defaultColorAttachment0,0);

// Generate the depth attachment
glGenRenderbuffers(1,&defaultDepthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, defaultDepthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, screenWidth, screenHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, defaultDepthBuffer);

我已经尝试过浏览 OpenGL wiki,尽管它不完整(各种未完成的标题使它看起来不专业)。glGetError从不抱怨。我已经尝试过解决这个问题,但我要么得到黑屏,要么得到一个满是垃圾像素的屏幕。

主要问题:为了让多重采样和 alpha-to-coverage 与 FBO 一起工作,我需要考虑/更改哪些内容以及在何处(FBO 创建、纹理、着色器)?

4

2 回答 2

28

您需要分配一个多重采样深度缓冲区才能正常工作,并为其提供与颜色缓冲区相同数量的样本。换句话说,你应该打电话glRenderbufferStorageMultisample (...)而不是glRenderbufferStorage (...).

您的 FBO 应该没有按照现在的分配方式进行完整性检查。调用glCheckFramebufferStatus (...)应该返回GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,因为您的深度缓冲区正好有 1 个样本,而您的颜色缓冲区附件有4 个


由于您还在此 FBO 中使用了多采样纹理附件,因此您应该了解在 GLSL 着色器中采样单采样纹理与多采样纹理之间的差异。

多重采样纹理具有特殊的采样器统一类型(例如 sampler2DMS),您必须通过其整数(非归一化)纹理坐标和样本索引使用texelFetch (...). 这也意味着它们不能被过滤或映射。

在这种情况下,您可能不想要多采样纹理,您可能希望使用glBlitFramebuffer (...)MSAA 解析为单采样 FBO。如果您这样做,您可以在着色器中读取抗锯齿结果,而不必获取每个样本并自己实现抗锯齿。

于 2013-12-10T08:12:32.810 回答
0

这是一个与接受的答案一起使用的工作示例。这是 LearnopenGL 教程中三角形示例的修改示例,用于将 MSAA 自定义帧缓冲区绘制到四边形,然后绘制到默认帧缓冲区(屏幕):

#include <iostream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>

const char *vertexShaderSource = "#version 330 core\n"
    "layout (location = 0) in vec3 aPos;\n"
    "void main()\n"
    "{\n"
    "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
    "}\0";
const char *fragmentShaderSource = "#version 330 core\n"
    "out vec4 FragColor;\n"
    "void main()\n"
    "{\n"
    "   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
    "}\n\0";

const char *postProcessvertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec2 position;\n"             
"layout (location = 1) in vec2 inTexCoord;\n"

"out vec2 texCoord;\n"
"void main(){\n"
"    texCoord = inTexCoord;\n"
"    gl_Position = vec4(position.x, position.y, 0.0f, 1.0f);\n"
"}\n\0";

const char *postProcessFragmentShaderSource = "#version 330 core\n"
"out vec4 fragmentColor;\n"
"in vec2 texCoord;\n"
"//notice the sampler\n"
"uniform sampler2DMS screencapture;\n"
"uniform int viewport_width;\n"
"uniform int viewport_height;\n"

"void main(){\n"
"   //texelFetch requires a vec of ints for indexing (since we're indexing pixel locations)\n"
"   //texture coords is range [0, 1], we need range [0, viewport_dim].\n"
"   //texture coords are essentially a percentage, so we can multiply text coords by total size \n"
"   ivec2 vpCoords = ivec2(viewport_width, viewport_height);\n"
"   vpCoords.x = int(vpCoords.x * texCoord.x); \n"
"   vpCoords.y = int(vpCoords.y * texCoord.y);\n"
"   //do a simple average since this is just a demo\n"
"   vec4 sample1 = texelFetch(screencapture, vpCoords, 0);\n"
"   vec4 sample2 = texelFetch(screencapture, vpCoords, 1);\n"
"   vec4 sample3 = texelFetch(screencapture, vpCoords, 2);\n"
"   vec4 sample4 = texelFetch(screencapture, vpCoords, 3);\n"
"   fragmentColor = vec4(sample1 + sample2 + sample3 + sample4) / 4.0f;\n"
"}\n\0";

int main()
{
    int width = 800;
    int height = 600;
    
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    GLFWwindow* window = glfwCreateWindow(width, height, "OpenglContext", nullptr, nullptr);
    if (!window)
    {
        std::cerr << "failed to create window" << std::endl;
        exit(-1);
    }
    glfwMakeContextCurrent(window);

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cerr << "failed to initialize glad with processes " << std::endl;
        exit(-1);
    }

    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    int samples = 4;
    float quadVerts[] = {
        -1.0, -1.0,     0.0, 0.0,
        -1.0, 1.0,      0.0, 1.0,
        1.0, -1.0,      1.0, 0.0,

        1.0, -1.0,      1.0, 0.0,
        -1.0, 1.0,      0.0, 1.0,
        1.0, 1.0,       1.0, 1.0
    };

    GLuint postVAO;
    glGenVertexArrays(1, &postVAO);
    glBindVertexArray(postVAO);

    GLuint postVBO;
    glGenBuffers(1, &postVBO);
    glBindBuffer(GL_ARRAY_BUFFER, postVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(quadVerts), quadVerts, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), reinterpret_cast<void*>(0));
    glEnableVertexAttribArray(0);

    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), reinterpret_cast<void*>(2 * sizeof(float)));
    glEnableVertexAttribArray(1);

    glBindVertexArray(0);


    GLuint msaaFB;
    glGenFramebuffers(1, &msaaFB);
    glBindFramebuffer(GL_FRAMEBUFFER, msaaFB); //bind both read/write to the target framebuffer

    GLuint texMutiSampleColor;
    glGenTextures(1, &texMutiSampleColor);
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texMutiSampleColor);
    glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, width, height, GL_TRUE);
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, texMutiSampleColor, 0);

    glBindFramebuffer(GL_FRAMEBUFFER, 0);


    // vertex shader
    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);
    // check for shader compile errors

    // fragment shader
    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);
    // check for shader compile errors

    // link shaders
    unsigned int shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    // check for linking errors

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);


    //postprocess vertex shader
    unsigned int postProcessVertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(postProcessVertexShader, 1, &postProcessvertexShaderSource, NULL);
    glCompileShader(postProcessVertexShader);

    // postprocess fragment shader
    unsigned int postProcessFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(postProcessFragmentShader, 1, &postProcessFragmentShaderSource, NULL);
    glCompileShader(postProcessFragmentShader);
    // check for shader compile errors

    // link shaders
    unsigned int postProcessShaderProgram = glCreateProgram();
    glAttachShader(postProcessShaderProgram, postProcessVertexShader);
    glAttachShader(postProcessShaderProgram, postProcessFragmentShader);
    glLinkProgram(postProcessShaderProgram);
    // check for linking errors

    glDeleteShader(postProcessVertexShader);
    glDeleteShader(postProcessFragmentShader);

    glUseProgram(postProcessShaderProgram);
    glUniform1i(glGetUniformLocation(postProcessShaderProgram, "screencapture"), 0); 
    glUniform1i(glGetUniformLocation(postProcessShaderProgram, "viewport_width"), width); 
    glUniform1i(glGetUniformLocation(postProcessShaderProgram, "viewport_height"), height); 

    float vertices[] = {
        -0.5f, -0.5f, 0.0f,
         0.5f, -0.5f, 0.0f,
         0.0f,  0.5f, 0.0f 
    }; 

    unsigned int VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0); 
    glBindVertexArray(0); 

    bool use_msaa = true;

    while (!glfwWindowShouldClose(window))
    {

        if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        {
            glfwSetWindowShouldClose(window, true);
        }

        if (glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS)
            use_msaa = true;
        if (glfwGetKey(window, GLFW_KEY_T) == GLFW_PRESS)
            use_msaa = false;     

        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        if (use_msaa) {
            glBindFramebuffer(GL_FRAMEBUFFER, msaaFB);
        }

        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // draw our first triangle
        glUseProgram(shaderProgram);
        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 3);

        if (use_msaa) {
            glBindFramebuffer(GL_FRAMEBUFFER, 0);
            glUseProgram(postProcessShaderProgram);
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texMutiSampleColor);
            glBindVertexArray(postVAO);
            glDrawArrays(GL_TRIANGLES, 0, 6);
        }

        glfwSwapBuffers(window);
        glfwPollEvents();

    }
    glfwTerminate();
    // cleanup
}

感谢 LearnOpenGL 评论部分的 Matt Stone 提供工作代码。

于 2021-02-03T08:53:32.590 回答