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

28

我正在尝试为一个FBO启用多重采样和Alpha覆盖。对于默认的framebuffer,我只需要调用glEnable(GL_MULTISAMPLE)glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE)即可。然而,我无法在自己的FBO中实现相同的效果。

我的目标:将场景绘制到一个FBO中,以与上述属性绘制到默认framebuffer相同的方式。从那里,我希望能够使用图像作为未来通过着色器的传递的纹理。

这个有效:不使用多重采样/alpha-to-coverage,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维基,但它不完整(各种未完成的标题使其看起来不专业)。glGetError从不抱怨。我尝试过调整这个问题,但是我要么获得一个黑屏,要么是一堆垃圾像素。

主要问题:为了让FBO正常工作,我需要考虑/更改哪些方面(FBO创建、纹理、着色器),以实现多重采样和alpha-to-coverage?


你尝试过调用glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, defaultColorAttachment0,0);而不是glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, defaultColorAttachment0,0);吗? - Jherico
@Jherico 是的,我仍然得到一个黑屏。 - GraphicsMuncher
3个回答

30

为了使其正常工作并且与您的彩色缓冲区具有相同数量的采样,您需要为多重采样深度缓冲区分配空间。换句话说,您应该调用glRenderbufferStorageMultisample (...)而不是glRenderbufferStorage (...)

根据当前的分配方式,您的FBO应该无法通过完整性检查。调用glCheckFramebufferStatus (...)应该返回GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,因为您的深度缓冲区有1个采样,而颜色缓冲附件有4个采样。


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

多重采样纹理有一个特殊的采样器统一类型(例如sampler2DMS),您必须使用整数(非规范化)的纹素坐标和采样索引来显式获取纹理中的每个采样,使用texelFetch (...)。这也意味着它们无法进行过滤或mip-mapping。

在这种情况下,您可能不需要多重采样纹理,而是应该使用glBlitFramebuffer (...)对MSAA解析成一次采样的FBO。如果您这样做,便可以直接在着色器中读取抗锯齿结果,而不必获取每个采样并自行实现抗锯齿。


糟糕,我刚刚才发现这个问题。唉,算了。 - Jherico
1
是的!这非常有帮助。现在我已经让它工作了!我不得不创建另一个FBO来将多重采样图像复制到其中,但没关系。我将深度缓冲区与颜色缓冲区(多重采样,RGBA => 32)对齐。非常感谢。 - GraphicsMuncher

2
以下是与被接受的答案相配套的工作示例。这是从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
}

示例:

msaa 开启和关闭

感谢 LearnOpenGL 评论区的 Matt Stone 提供可用代码。


1

补充jackw11111的回答,我想在Python中测试这个示例代码。这是我对LearnOpenGL评论中Matt Stone的代码的近乎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()


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接