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

时间:2018-12-07 22:21:02

标签: c++ opengl-es glfw opengl-es-3.0

是否有某种方法可以使OpenGL ES 3.0向所有活动的(按glDrawBuffers())FBO颜色附件广播单输出片段着色器的值?

如果可能的话,我想保持我的着色器大致不变,并避免多个layout输出的重写要求:

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的Windows 10计算机上,我获得了蓝色的透明色,但第三个附件没有三角形(“未定义”):

bad output

第一个附件很好:

good output

1 个答案:

答案 0 :(得分:2)

No such functionality exists in the API

  

3)我们是否应该支持从gl_FragColor到所有gl_FragData [x]的广播    还是应该与gl_FragData [0]同义?

     

讨论:使用NV_draw_buffers,写入gl_FragColor会写入所有    启用的绘图缓冲区(即广播)。在OpenGL ES 3.0中使用时    ESSL 1.0,gl_FragColor等效于将单个输出写入    gl_FragData [0]和多个输出是不可能的。使用ESSL 3.0时,    只能使用用户定义的out变量。

     

如果支持广播,则某些实现可能必须替换    使用对所有可能的gl_FragData的复制写操作写入gl_FragColor    启用此扩展程序的位置。

     

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