简体   繁体   English

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

[英]Broadcast fragment-shader output to all FBO color attachments?

Is there some way to get OpenGL ES 3.0 to broadcast the value of a single-output fragment shader to all active (as per glDrawBuffers() ) FBO color attachments? 是否有某种方法可以使OpenGL ES 3.0向所有活动的FBO颜色附件广播单输出片段着色器的值(根据glDrawBuffers() )?

If possible I'd like to keep my shaders more-or-less as-is and avoid the re-write required by multiple layout 'd outputs: 如果可能的话,我想保持我的着色器大致不变,并避免多个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 );
}

...or an output array: ...或输出数组:

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 );
}

Here's the program I'm using for testing, it (tries) to draw a red triangle to all four FBO attachments & then blits the 3rd attachment to the default framebuffer: 这是我用于测试的程序,它(尝试)为所有四个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;
}

On this Windows 10 machine using a recent-ish ANGLE build I get the blue clear color but no triangle ("undefined") for the 3rd attachment: 在这台使用最新的ANGLE版本的Windows 10计算机上,我获得了蓝色透明色,但是第三个附件没有三角形(“未定义”):

输出不良

The first attachment is fine: 第一个附件很好:

好的输出

No such functionality exists in the API : API中不存在此类功能

3) Should we support broadcast from gl_FragColor to all gl_FragData[x] or should it be synonymous with gl_FragData[0]? 3)我们应该支持从gl_FragColor到所有gl_FragData [x]的广播,还是应该与gl_FragData [0]同义?

DISCUSSION: With NV_draw_buffers, writing to gl_FragColor writes to all the enabled draw buffers (ie broadcast). 讨论:使用NV_draw_buffers,写入gl_FragColor会写入所有已启用的绘制缓冲区(即广播)。 In OpenGL ES 3.0 when using ESSL 1.0, gl_FragColor is equivalent to writing a single output to gl_FragData[0] and multiple outputs are not possible. 在使用ESSL 1.0的OpenGL ES 3.0中,gl_FragColor等效于将单个输出写入gl_FragData [0],并且不可能有多个输出。 When using ESSL 3.0, only user-defined out variables may be used. 使用ESSL 3.0时,只能使用用户定义的out变量。

If broadcast is supported, some implementations may have to replace writes to gl_FragColor with replicated writes to all possible gl_FragData locations when this extension is enabled. 如果支持广播,则启用此扩展后,某些实现可能必须用对所有可能的gl_FragData位置的复制写操作替换对gl_FragColor的写操作。

RESOLVED: Writes to gl_FragColor are broadcast to all enabled color buffers. 已解决:对gl_FragColor的写入将广播到所有启用的颜色缓冲区。 ES 3.0 using ESSL 1.0 doesn't support broadcast because ESSL 1.0 was not extended to have multiple color outputs (but that is what this extension adds). 使用ESSL 1.0的ES 3.0不支持广播,因为未将ESSL 1.0扩展为具有多个颜色输出(但这就是此扩展添加的内容)。 ESSL 3.0 doesn't support the broadcast because it doesn't have the gl_FragColor variable at all, and only has user- defined out variables. ESSL 3.0不支持广播,因为它根本不具有gl_FragColor变量,而仅具有用户定义的变量。 This extension extends ESSL 1.0 to have multiple color outputs. 此扩展将ESSL 1.0扩展为具有多个颜色输出。 Broadcasting from gl_FragColor to all enabled color buffers is the most consistent with existing draw buffer extensions to date (both NV_draw_buffers and desktop GL). 从gl_FragColor到所有启用的颜色缓冲区的广播与迄今为止的现有绘图缓冲区扩展(NV_draw_buffers和桌面GL)最一致。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM