如何在OpenGL FBO中使用多重采样

kqhtkvqz  于 2022-09-26  发布在  其他
关注(0)|答案(3)|浏览(148)

我正在尝试为FBO启用多重采样和Alpha到覆盖。使用默认的帧缓冲区,我所要做的就是调用glEnable(GL_MULTISAMPLE)glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE)。然而,我无法用我自己的FBO达到同样的效果。

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

这行得通:制作无多采样/Alpha-to-Coverage的FBO代码,1个颜色附件,1个深度附件:

// 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维基,尽管它不完整(各种未完成的标题让它看起来不专业)。glGetError从不抱怨。我试着摆弄这个,但我要么得到了一个黑屏,要么得到了一个充满垃圾像素的屏幕。

**主要问题:**我需要考虑/更改哪些内容以及在哪里(FBO创建、纹理、着色器)才能使用FBO进行多重采样和Alpha到覆盖?

9w11ddsr

9w11ddsr1#

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

你的FBO应该没有通过现在分配的完整性检查。对glCheckFramebufferStatus (...)的调用应该返回GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,因为您的深度缓冲区具有*正好1样本,而您的颜色缓冲区附件具有4

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

多采样纹理具有特殊的采样器统一类型(例如sampler2DMS),您必须使用texelFetch (...)通过其整数(非规格化)纹理像素坐标和采样索引显式获取纹理中的每个采样。这也意味着它们不能被过滤或MIPMap。

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

cl25kdpy

cl25kdpy2#

这里有一个符合公认答案的工作例子。这是LearnOpenGL教程中三角形示例的修改示例,用于将MSAA自定义帧缓冲区绘制到四边形,然后绘制到默认帧缓冲区(屏幕):


# include <iostream>

# include <glad/glad.h>

# include <GLFW/glfw3.h>

const char *vertexShaderSource = "#version 330 coren"
    "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 coren"
    "out vec4 FragColor;n"
    "void main()n"
    "{n"
    "   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);n"
    "}n0";

const char *postProcessvertexShaderSource = "#version 330 coren"
"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"
"}n0";

const char *postProcessFragmentShaderSource = "#version 330 coren"
"out vec4 fragmentColor;n"
"in vec2 texCoord;n"
"//notice the samplern"
"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 demon"
"   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"
"}n0";

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提供的工作代码。

bgtovc5b

bgtovc5b3#

除了jackw11111的回答之外,我还想用python测试这个样例代码。随信附上的是我在LearnOpenGL的评论中显然是由Matt Stone翻译成Python代码的近1:1翻译。在Ubuntu和MacOS上进行了测试。


## Not needed for python.

## #include <glad/glad.h>

# Setup might be something like:

# python3 -m venv venv_msaa

# source venv_msaa/bin/activate

# pip install PyOpenGL glfw numpy

# Note: On a MacOS hidpi screen, the results will vary.

import ctypes
import numpy as np

import glfw
from OpenGL.GL import *

VERTEX_SHADER_SOURCE = """#version 330 core
    layout (location = 0) in vec3 aPos;

    void main()
    {
       gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
    }
"""

FRAGMENT_SHADER_SOURCE = """#version 330 core
    out vec4 FragColor;

    void main()
    {
       FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
    }
"""

POSTPROCESS_VERTEX_SHADER_SOURCE = """#version 330 core
    layout (location = 0) in vec2 position;
    layout (location = 1) in vec2 inTexCoord;
    out vec2 texCoord;

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

POSTPROCESS_FRAGMENT_SHADER_SOURCE = """#version 330 core
    out vec4 fragmentColor;
    in vec2 texCoord;
    // notice the sampler
    uniform sampler2DMS screencapture;
    uniform int viewport_width;
    uniform int viewport_height;

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

def main():
    width = 800
    height = 600

    glfw.init()
    glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
    glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
    glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
    glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, GL_TRUE)

    window = glfw.create_window(width, height, "OpenglContext", None, None)
    if not window:
        print("failed to create window")
        sys.exit(-1)
    glfw.make_context_current(window);

## Not needed for python.

## if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))

## {

## std::cerr << "failed to initialize glad with processes " << std::endl;

## exit(-1);

## }

    glfw.set_input_mode(window, glfw.CURSOR, glfw.CURSOR_DISABLED)

    samples = 4
    quadVerts = np.array([
        -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
    ], dtype=np.float32)

    postVAO = glGenVertexArrays(1)
    glBindVertexArray(postVAO)

    sizeof_float = ctypes.sizeof(ctypes.c_float) # Complicated way of saying 4
    postVBO = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, postVBO)
    glBufferData(GL_ARRAY_BUFFER, quadVerts.nbytes, quadVerts.ctypes._as_parameter_, GL_STATIC_DRAW)

    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof_float, ctypes.c_void_p(0))
    glEnableVertexAttribArray(0)

    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof_float, ctypes.c_void_p(2 * sizeof_float))
    glEnableVertexAttribArray(1)

    glBindVertexArray(0)

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

    texMutiSampleColor = glGenTextures(1)
    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
    vertexShader = glCreateShader(GL_VERTEX_SHADER)
    glShaderSource(vertexShader, VERTEX_SHADER_SOURCE)
    glCompileShader(vertexShader)
    # check for shader compile errors

    # fragment shader
    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER)
    glShaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE)
    glCompileShader(fragmentShader)
    # check for shader compile errors

    # link shaders
    shaderProgram = glCreateProgram()
    glAttachShader(shaderProgram, vertexShader)
    glAttachShader(shaderProgram, fragmentShader)
    glLinkProgram(shaderProgram)
    # check for linking errors

    glDeleteShader(vertexShader)
    glDeleteShader(fragmentShader)

    #postprocess vertex shader
    postProcessVertexShader = glCreateShader(GL_VERTEX_SHADER)
    glShaderSource(postProcessVertexShader, POSTPROCESS_VERTEX_SHADER_SOURCE)
    glCompileShader(postProcessVertexShader)
    # check for shader compile errors

    # postprocess fragment shader
    postProcessFragmentShader = glCreateShader(GL_FRAGMENT_SHADER)
    glShaderSource(postProcessFragmentShader, POSTPROCESS_FRAGMENT_SHADER_SOURCE)
    glCompileShader(postProcessFragmentShader)
    # check for shader compile errors

    # link shaders
    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) 

    vertices = np.array([
        -0.5, -0.5, 0.0,
         0.5, -0.5, 0.0,
         0.0,  0.5, 0.0 
    ], dtype=np.float32)

    VAO = glGenVertexArrays(1)
    VBO = glGenBuffers(1)
    glBindVertexArray(VAO)
    glBindBuffer(GL_ARRAY_BUFFER, VBO)
    glBufferData(GL_ARRAY_BUFFER, vertices.nbytes, vertices.ctypes._as_parameter_, GL_STATIC_DRAW)
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof_float, ctypes.c_void_p(0))
    glEnableVertexAttribArray(0)
    glBindBuffer(GL_ARRAY_BUFFER, 0) 
    glBindVertexArray(0)

    use_msaa = True

    while not glfw.window_should_close(window):

        if glfw.get_key(window, glfw.KEY_ESCAPE) == glfw.PRESS:
            glfw.set_window_should_close(window, True)

        if glfw.get_key(window, glfw.KEY_R) == glfw.PRESS:
            use_msaa = True
        if glfw.get_key(window, glfw.KEY_T) == glfw.PRESS:
            use_msaa = False

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

        if use_msaa:
            glBindFramebuffer(GL_FRAMEBUFFER, msaaFB)

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

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

        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)
            glBindVertexArray(0)

        glfw.swap_buffers(window)
        glfw.poll_events()

    glfw.terminate()
    # cleanup

if __name__ == "__main__":
    main()

相关问题