代码之家  ›  专栏  ›  技术社区  ›  genpfault

是否将片段着色器输出广播到所有FBO颜色附件?

  •  1
  • genpfault  · 技术社区  · 7 年前

    有没有办法让OpenGL ES 3.0将单个输出片段着色器的值广播给所有活动对象(根据 glDrawBuffers() )FBO颜色附件?

    如果可能的话,我希望我的着色器或多或少保持原样,并避免多个着色器需要重新写入 layout “d”产出:

    layout( location = 0 ) out vec4 out_color0;
    layout( location = 1 ) out vec4 out_color1;
    layout( location = 2 ) out vec4 out_color2;
    layout( location = 3 ) out vec4 out_color3;
    void main()
    {
        out_color0 = vec4( 1.0, 0.2, 0.0, 1.0 );
        out_color1 = vec4( 1.0, 0.2, 0.0, 1.0 );
        out_color2 = vec4( 1.0, 0.2, 0.0, 1.0 );
        out_color3 = vec4( 1.0, 0.2, 0.0, 1.0 );
    }
    

    out vec4 out_color[4];
    void main()
    {
        out_color[0] = vec4( 1.0, 0.2, 0.0, 1.0 );
        out_color[1] = vec4( 1.0, 0.2, 0.0, 1.0 );
        out_color[2] = vec4( 1.0, 0.2, 0.0, 1.0 );
        out_color[3] = vec4( 1.0, 0.2, 0.0, 1.0 );
    }
    

    这是我用于测试的程序,它(尝试)为所有四个FBO附件绘制一个红色三角形&然后,将第三个附件点放到默认帧缓冲区:

    #include <glad/glad.h>
    #define GLFW_INCLUDE_NONE
    #include <GLFW/glfw3.h>
    
    #include <cstdlib>
    #include <cstdarg>
    #include <iostream>
    #include <vector>
    
    struct Program
    {
        static GLuint Load( const char* shader, ... )
        {
            const GLuint prog = glCreateProgram();
            va_list args;
            va_start( args, shader );
            while( shader )
            {
                AttachShader( prog, va_arg( args, GLenum ), shader );
                shader = va_arg( args, const char* );
            }
            va_end( args );
            glLinkProgram( prog );
            CheckStatus( prog );
            return prog;
        }
    
    private:
        static void CheckStatus( GLuint obj )
        {
            GLint status = GL_FALSE;
            if( glIsShader(obj) ) glGetShaderiv( obj, GL_COMPILE_STATUS, &status );
            if( glIsProgram(obj) ) glGetProgramiv( obj, GL_LINK_STATUS, &status );
            if( status == GL_TRUE ) return;
            GLchar log[ 1 << 15 ] = { 0 };
            if( glIsShader(obj) ) glGetShaderInfoLog( obj, sizeof(log), NULL, log );
            if( glIsProgram(obj) ) glGetProgramInfoLog( obj, sizeof(log), NULL, log );
            std::cerr << log << std::endl;
            std::exit( EXIT_FAILURE );
        }
    
        static void AttachShader( GLuint program, GLenum type, const char* src )
        {
            const GLuint shader = glCreateShader( type );
            glShaderSource( shader, 1, &src, NULL );
            glCompileShader( shader );
            CheckStatus( shader );
            glAttachShader( program, shader );
            glDeleteShader( shader );
        }
    };
    
    const char* vert = 1 + R"GLSL(
    #version 300 es
    void main()
    {
        const vec2 verts[3] = vec2[3]
        (
            vec2( -0.5, -0.5 ),
            vec2(  0.5, -0.5 ),
            vec2(  0.0,  0.5 )
        );
        gl_Position = vec4( verts[ gl_VertexID ], 0.0, 1.0 );
    }
    )GLSL";
    
    const char* frag = 1 + R"GLSL(
    #version 300 es
    precision mediump float;
    out vec4 out_color;
    void main()
    {
        out_color = vec4( 1.0, 0.2, 0.0, 1.0 );
    }
    )GLSL";
    
    int main( int argc, char** argv )
    {
        glfwSetErrorCallback( []( int err, const char* desc )
        {
            std::cerr << "GLFW error: " << desc << std::endl;
        } );
    
        if( !glfwInit() )
            return EXIT_FAILURE;
    
        glfwWindowHint( GLFW_CLIENT_API, GLFW_OPENGL_ES_API );
        glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 3 );
        glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 0 );
        glfwWindowHint( GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API );
        GLFWwindow* window = glfwCreateWindow( 640, 480, "GLFW", NULL, NULL );
        if( nullptr == window )
            return EXIT_FAILURE;
    
        glfwMakeContextCurrent( window );
        glfwSwapInterval( 1 );
        gladLoadGLES2Loader( (GLADloadproc)glfwGetProcAddress );
    
        const GLuint prog = Program::Load( vert, GL_VERTEX_SHADER, frag, GL_FRAGMENT_SHADER, NULL );
        glUseProgram( prog );
    
        // init framebuffer attachments
        std::vector< GLuint > textures( 4, 0 );
        glGenTextures( 4, textures.data() );
        for( size_t i = 0; i < textures.size(); ++ i )
        {
            glBindTexture( GL_TEXTURE_2D, textures[i] );
            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, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
        }
        GLuint rbDepth = 0;
        glGenRenderbuffers(1, &rbDepth );
        glBindRenderbuffer( GL_RENDERBUFFER, rbDepth );
        glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 32, 32 );
    
        // init FBO
        GLuint fbo = 0;
        glGenFramebuffers( 1, &fbo );
        glBindFramebuffer( GL_FRAMEBUFFER, fbo );
        for( size_t i = 0; i < textures.size(); ++ i )
        {
            glFramebufferTexture2D( GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, textures[i], 0 );
        }
        glFramebufferRenderbuffer( GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbDepth );
        if( GL_FRAMEBUFFER_COMPLETE != glCheckFramebufferStatus( GL_FRAMEBUFFER ) )
        {
            std::cerr << "Incomplete framebuffer" << std::endl;
            std::exit( EXIT_FAILURE );
        }
    
        while( !glfwWindowShouldClose( window ) )
        {
            glfwPollEvents();
    
            // render to FBO
            glBindFramebuffer( GL_FRAMEBUFFER, fbo );
            GLenum bufs[] =
            {
                GL_COLOR_ATTACHMENT0 + 0,
                GL_COLOR_ATTACHMENT0 + 1,
                GL_COLOR_ATTACHMENT0 + 2,
                GL_COLOR_ATTACHMENT0 + 3,
            };
            glDrawBuffers( 4, bufs );
            glViewport( 0, 0, 32, 32 );
            glClearColor( 0.0f, 0.6f, 1.0f, 1.f );
            glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
            glDrawArrays( GL_TRIANGLES, 0, 3 );
    
            // switch back to default framebuffer & clear it with non-black color
            glBindFramebuffer( GL_FRAMEBUFFER, 0 );
            GLenum defaultBuf = GL_BACK;
            glDrawBuffers( 1, &defaultBuf );
            glClearColor( 1.0f, 0.0f, 1.0f, 1.f );
            glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    
            // blit a color attachment to the default framebuffer
            glBindFramebuffer( GL_READ_FRAMEBUFFER, fbo );
            glReadBuffer( GL_COLOR_ATTACHMENT0 + 2 );
            glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 );
            glBlitFramebuffer( 0, 0, 32, 32, 0, 0, 640, 480, GL_COLOR_BUFFER_BIT, GL_LINEAR );
    
            glfwSwapBuffers( window );
        }
    
        glfwMakeContextCurrent( NULL );
        glfwDestroyWindow( window );
    
        glfwTerminate();
        return EXIT_SUCCESS;
    }
    

    ANGLE build I为第三个附件获得蓝色透明颜色,但没有三角形(“未定义”):

    bad output

    第一个附件很好:

    good output

    1 回复  |  直到 7 年前
        1
  •  2
  •   genpfault    7 年前

    No such functionality exists in the API :

    或者它应该是gl_FragData[0]的同义词?

    已启用的绘图缓冲区(即广播)。在OpenGL ES 3.0中使用 ESSL 1.0,gl_FragColor相当于将单个输出写入 只能使用用户定义的out变量。

    如果支持广播,则可能需要替换某些实现 写入gl_FragColor,并复制写入所有可能的gl_FragData 启用此扩展时的位置。

    已解决:对gl_FragColor的写入将广播到所有启用的颜色 缓冲区。使用ESSL 1.0的ES 3.0不支持广播,因为 ESSL 1.0未扩展为具有多个颜色输出(但实际上是 此扩展添加的内容)。 它根本没有gl_FragColor变量,只有user- 定义了变量。 颜色输出。从gl_FragColor到所有启用颜色的广播 缓冲区与现有的绘图缓冲区扩展最为一致 日期(NV\U绘图缓冲区和桌面总账)。