如何使用渲染缓冲区和 glBlit 渲染到四个 Windows,使用 GLFW

How to use a renderbuffer and glBlit to render to four Windows, with GLFW

问题陈述

这应该是一个非常明显的答案,我可能在某处搞砸了一两行,但我似乎无法将三角形绘制到帧缓冲区。

我想要做的是将两个三角形连接到一个大型渲染缓冲区对象,并附加一个帧缓冲区,然后在四个 windows 中的一个中显示较大渲染的一部分 buffer/framebuffer 使用 glBlitFramebuffer。

我正在使用 init_FB() 定义要渲染的三角形以及渲染这些三角形的 RBO。我创建了一个渲染缓冲区对象和一个与渲染缓冲区对象关联的帧缓冲区对象。在 compute_FB() 函数中,我绑定了 RBO 的帧缓冲区,然后调用绘制到该帧缓冲区中。在绘制三角形之前,我将帧缓冲区清除为特定颜色,royalblue


到底发生了什么

在第一个 window 中,称为 window,出现的只是颜色 royalblue 由绘制到帧缓冲区的函数 (compute_FB()) 定义渲染缓冲区对象。但是,尽管我在 compute_FB() 末尾调用了 glDrawArrays(...) 函数,但仍绘制了 none 个三角形。

正在发生的事情的可能假设

我开始相信 RBO 需要自己的上下文才能成功呈现,但我不知道如何为 RBO 设置上下文。我认为上下文仅适用于 GLFW 中的 windows。


代码说明

我的尝试基于最初的 OpenGL 红皮书示例 01-三角形。在这个例子中,我编码了四个唯一的 windows 并希望最终将一个大的 RBO/framebuffer 复制到四个 windows 中的每一个 - 目前我只关注第一个显示。

我将 OpenGL4.5 与 GLFW 一起用于 windowing。


代码

//////////////////////////////////////////////////////////////////////////////
//
//  Triangles.cpp
//
//////////////////////////////////////////////////////////////////////////////

#include "vgl.h"
#include "LoadShaders.h"
#include <vector>

enum VAO_IDs { Triangles, NumVAOs };
enum Buffer_IDs { ArrayBuffer, NumBuffers };
enum Attrib_IDs { vPosition = 0 };

GLuint  VAOs[NumVAOs];
GLuint  Buffers[NumBuffers];

const GLuint  NumVertices = 6;

//////////////////////////////////////////////////
// Framebuffer Variables
//////////////////////////////////////////////////
enum {Color, NumRenderBuffers};
GLuint framebuffer, renderbuffer[NumRenderBuffers];
GLuint fbwidth = 3200;
GLuint fbheight = 600;


//----------------------------------------------------------------------------
//
// init
//
void init_FB( void )
{
    // Create an Empty RenderBuffer and Associated Framebuffer
    glCreateRenderbuffers(NumRenderBuffers, renderbuffer);
    glNamedRenderbufferStorage(renderbuffer[Color], GL_RGBA, fbwidth, fbheight);
    glGenFramebuffers(1, &framebuffer);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
    glNamedFramebufferRenderbuffer(framebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer[Color]);
    glEnable(GL_DEPTH_TEST);


    // Here's some info to initialize for the RBO
    // The framebuffer for the RBO has been bound (above) and ?SHOULD? be ready to draw to, right?
    glGenVertexArrays( NumVAOs, VAOs );
    glBindVertexArray( VAOs[Triangles] );

    GLfloat  vertices[NumVertices][2] = {
        { -1.00f, -1.00f }, { -1.00f,  0.40f }, {  0.00f, -1.00f },  // Triangle 1
        {  0.00f,  0.40f }, {  0.40f,  0.40f }, {  0.40f, -0.40f }   // Triangle 2
    };

    ShaderInfo  shaders[] =
        {
            { GL_VERTEX_SHADER, "media/shaders/triangles/triangles.vert" },
            { GL_FRAGMENT_SHADER, "media/shaders/triangles/triangles.frag" },
            { GL_NONE, NULL }
        };

    GLuint program = LoadShaders( shaders );
    glUseProgram( program );

    glVertexAttribPointer( vPosition, 2, GL_FLOAT,
                           GL_FALSE, 0, BUFFER_OFFSET(0) );
    glEnableVertexAttribArray( vPosition );
}


void init( void )
{
    // Create the standard window framebuffer for this window context
    // Basically, I want to give the window a framebuffer so that I can draw into it later in the 'draw' phase
    glCreateBuffers( NumBuffers, Buffers );
    glBindBuffer( GL_ARRAY_BUFFER, Buffers[ArrayBuffer] );

    static const float black[] = { 1.0f, 0.5f, 0.2f, 0.0f };

    // May as well clear it to a color that's visually separate from the color that it will be cleared to
    //      .. in the draw phase.
    glClearBufferfv(GL_COLOR, 0, black);
}


void init2( void )
{
    glGenVertexArrays( NumVAOs, VAOs );
    glBindVertexArray( VAOs[Triangles] );

    GLfloat  vertices[NumVertices][2] = {
        { -0.90f, -0.60f }, { -0.85f, -0.60f }, { -0.50f, -0.65f },  // Triangle 1
        {  0.90f, -0.85f }, {  0.90f,  0.90f }, { -0.85f,  0.90f }   // Triangle 2
    };

    glCreateBuffers( NumBuffers, Buffers );
    glBindBuffer( GL_ARRAY_BUFFER, Buffers[ArrayBuffer] );
    glBufferStorage( GL_ARRAY_BUFFER, sizeof(vertices), vertices, 0);

    ShaderInfo  shaders[] =
    {
        { GL_VERTEX_SHADER, "media/shaders/triangles/triangles.vert" },
        { GL_FRAGMENT_SHADER, "media/shaders/triangles/triangles.frag" },
        { GL_NONE, NULL }
    };

    GLuint program = LoadShaders( shaders );
    glUseProgram( program );

    glVertexAttribPointer( vPosition, 2, GL_FLOAT,
                           GL_FALSE, 0, BUFFER_OFFSET(0) );
    glEnableVertexAttribArray( vPosition );
}


void init3( void )
{
    glGenVertexArrays( NumVAOs, VAOs );
    glBindVertexArray( VAOs[Triangles] );

    GLfloat  vertices[NumVertices][2] = {
        { -0.90f, -0.90f }, { -0.90f,  0.90f }, {  0.00f, -0.90f },  // Triangle 1
        {  0.00f,  0.90f }, {  0.90f,  0.90f }, {  0.90f, -0.90f }   // Triangle 2
    };

    glCreateBuffers( NumBuffers, Buffers );
    glBindBuffer( GL_ARRAY_BUFFER, Buffers[ArrayBuffer] );
    glBufferStorage( GL_ARRAY_BUFFER, sizeof(vertices), vertices, 0);

    ShaderInfo  shaders[] =
    {
        { GL_VERTEX_SHADER, "media/shaders/triangles/triangles.vert" },
        { GL_FRAGMENT_SHADER, "media/shaders/triangles/triangles.frag" },
        { GL_NONE, NULL }
    };

    GLuint program = LoadShaders( shaders );
    glUseProgram( program );

    glVertexAttribPointer( vPosition, 2, GL_FLOAT,
                           GL_FALSE, 0, BUFFER_OFFSET(0) );
    glEnableVertexAttribArray( vPosition );
}


void init4( void )
{
    glGenVertexArrays( NumVAOs, VAOs );
    glBindVertexArray( VAOs[Triangles] );

    GLfloat  vertices[NumVertices][2] = {
        { -0.40f, -0.40f }, { -0.40f,  0.40f }, {  0.00f, -0.40f },  // Triangle 1
        {  0.00f,  0.40f }, {  0.40f,  0.40f }, {  0.40f, -0.40f }   // Triangle 2
    };

    glCreateBuffers( NumBuffers, Buffers );
    glBindBuffer( GL_ARRAY_BUFFER, Buffers[ArrayBuffer] );
    glBufferStorage( GL_ARRAY_BUFFER, sizeof(vertices), vertices, 0);

    ShaderInfo  shaders[] =
    {
        { GL_VERTEX_SHADER, "media/shaders/triangles/triangles.vert" },
        { GL_FRAGMENT_SHADER, "media/shaders/triangles/triangles.frag" },
        { GL_NONE, NULL }
    };

    GLuint program = LoadShaders( shaders );
    glUseProgram( program );

    glVertexAttribPointer( vPosition, 2, GL_FLOAT,
                           GL_FALSE, 0, BUFFER_OFFSET(0) );
    glEnableVertexAttribArray( vPosition );
}


//----------------------------------------------------------------------------
//
// display
//

void compute_FB()
{
    // Prepare to render into the framebuffer
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
    glViewport(0, 0, fbwidth, fbheight);

    // Clear before drawing. This shade of color comes through to the first window display
    static const float black[] = { 0.0f, 0.3f, 0.8f, 0.0f };

    glClearBufferfv(GL_COLOR, 0, black);

    // Try drawing the triangles... Nuthin
    glBindVertexArray( VAOs[Triangles] );
    glDrawArrays( GL_TRIANGLES, 0, NumVertices );
}

// Read from a section of the RBO/framebuffer
void display( void )
{
    static const float black[] = { 0.8f, 0.0f, 0.0f, 0.0f };

    glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer); // Set framebuffer to read from
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // set window to draw to
    glViewport(0, 0, 800, 600); // Probbaly not needed

    // Copy from READ framebuffer to DRAW framebuffer
    // QUESTION: Why isn't this copying to just a small corner of the window context's framebuffer?
    glBlitFramebuffer(0, 0, fbwidth, fbheight, 0, 0, 100, 200, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}

void display2( void )
{
    static const float black[] = { 0.0f, 0.3f, 0.4f, 0.0f };

    glClearBufferfv(GL_COLOR, 0, black);

    glBindVertexArray( VAOs[Triangles] );
    glDrawArrays( GL_TRIANGLES, 0, NumVertices );
}

void display3( void )
{
    static const float black[] = { 0.7f, 0.6f, 0.4f, 0.0f };

    glClearBufferfv(GL_COLOR, 0, black);

    glBindVertexArray( VAOs[Triangles] );
    glDrawArrays( GL_TRIANGLES, 0, NumVertices );
}

void display4( void )
{
    static const float black[] = { 0.2f, 0.3f, 0.7f, 0.0f };

    glClearBufferfv(GL_COLOR, 0, black);

    glBindVertexArray( VAOs[Triangles] );
    glDrawArrays( GL_TRIANGLES, 0, NumVertices );
}


//----------------------------------------------------------------------------
//
// main
//

#ifdef _WIN32
int CALLBACK WinMain(
  _In_ HINSTANCE hInstance,
  _In_ HINSTANCE hPrevInstance,
  _In_ LPSTR     lpCmdLine,
  _In_ int       nCmdShow
)
#else
int
main( int argc, char** argv )
#endif
{
    // Initialize GLFW
    glfwInit();


    //TODO Create Windows Class
    // Create Windows
    GLFWwindow* window = glfwCreateWindow(800, 600, "Triangles", NULL, NULL);
    GLFWwindow* window2 = glfwCreateWindow(800, 600, "Triangles2", NULL, NULL);
    GLFWwindow* window3 = glfwCreateWindow(800, 600, "Triangles3", NULL, NULL);
    GLFWwindow* window4 = glfwCreateWindow(800, 600, "Triangles4", NULL, NULL);



    // Initialize OpenGL
    gl3wInit();


    // Framebuffer Initialization
    init_FB();


    // Initialize Windows
    glfwMakeContextCurrent(window);
    init();
    glfwMakeContextCurrent(window2);
    init2();
    glfwMakeContextCurrent(window3);
    init3();
    glfwMakeContextCurrent(window4);
    init4();


    // Draw the Windows
    while (!glfwWindowShouldClose(window) && !glfwWindowShouldClose(window2) && !glfwWindowShouldClose(window3) && !glfwWindowShouldClose(window4))
    {
        glfwMakeContextCurrent(window);
        compute_FB();
        display();
        glfwSwapBuffers(window);
        glfwPollEvents();

        glfwMakeContextCurrent(window2);
        display2();
        glfwSwapBuffers(window2);
        glfwPollEvents();

        glfwMakeContextCurrent(window3);
        display3();
        glfwSwapBuffers(window3);
        glfwPollEvents();

        glfwMakeContextCurrent(window4);
        display4();
        glfwSwapBuffers(window4);
        glfwPollEvents();
    }


    // Destroy Windows
    glfwDestroyWindow(window);
    glfwDestroyWindow(window2);
    glfwDestroyWindow(window3);
    glfwDestroyWindow(window4);


    // Terminate GLFW Instance
    glfwTerminate();
}


编辑#2

感谢@Ripi2,我现在可以使用 glBlit 和渲染缓冲区了。不知何故,虽然我没有正确使用渲染缓冲区和不同的 FBO 中的一个或两个作为第二个 window。 注意 在这一点上,我还没有在第三个或第四个上实现 glBlit windows(尽管我会,一旦我可以成功地将渲染缓冲区和 glBlit 集成到第二个 window)

编辑 #2 代码

//////////////////////////////////////////////////////////////////////////////
//
//  Triangles.cpp
//
//////////////////////////////////////////////////////////////////////////////
#include <cstdio>
#include "vgl.h"
#include "LoadShaders.h"

enum VAO_IDs { Triangles, NumVAOs };
enum Buffer_IDs { ArrayBuffer, NumBuffers };
enum Attrib_IDs { vPosition = 0 };

GLuint  VAOs[NumVAOs];
GLuint  Buffers[NumBuffers];

const GLuint  NumVertices = 6;
////////////////////////////////////
//RBO variables
enum {Color=0, NumRenderBuffers=1, NumFBOs=4};
GLuint renderbuffer[NumRenderBuffers], fbos[NumFBOs];
GLuint buffwidth = 3200;
GLuint buffheight = 600;


//----------------------------------------------------------------------------
//
// init
//

void
init( void )
{
    glCreateRenderbuffers(NumRenderBuffers, renderbuffer);
    glNamedRenderbufferStorage(renderbuffer[Color], GL_RGBA, buffwidth, buffheight);
    glGenFramebuffers(1, &fbos[0]);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[0]);
    glNamedFramebufferRenderbuffer(fbos[0], GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer[Color]);

    glGenVertexArrays( NumVAOs, VAOs );
    glBindVertexArray( VAOs[Triangles] );

    GLfloat  vertices[NumVertices][2] = {
        { -0.90f, -0.90f }, { -0.90f,  0.90f }, {  0.00f, -0.90f },  // Triangle 1
        {  0.00f,  0.90f }, {  0.90f,  0.90f }, {  0.90f, -0.90f }   // Triangle 2
    };

    glCreateBuffers( NumBuffers, Buffers );
    glBindBuffer( GL_ARRAY_BUFFER, Buffers[ArrayBuffer] );
    glBufferStorage( GL_ARRAY_BUFFER, sizeof(vertices), vertices, 0);

    ShaderInfo  shaders[] =
    {
        { GL_VERTEX_SHADER, "media/shaders/triangles/triangles.vert" },
        { GL_FRAGMENT_SHADER, "media/shaders/triangles/triangles.frag" },
        { GL_NONE, NULL }
    };

    GLuint program = LoadShaders( shaders );
    glUseProgram( program );

    glVertexAttribPointer( vPosition, 2, GL_FLOAT,
                           GL_FALSE, 0, BUFFER_OFFSET(0) );
    glEnableVertexAttribArray( vPosition );
}


void
init2( void )
{
    glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer[Color]);
    glGenFramebuffers(1, &fbos[1]);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]);
    glNamedFramebufferRenderbuffer(fbos[1], GL_COLOR_ATTACHMENT1, GL_RENDERBUFFER, renderbuffer[Color]);

}


void
init3( void )
{
    glGenVertexArrays( NumVAOs, VAOs );
    glBindVertexArray( VAOs[Triangles] );

    GLfloat  vertices[NumVertices][2] = {
        { -0.90f, -0.90f }, { -0.90f,  0.90f }, {  0.00f, -0.90f },  // Triangle 1
        {  0.00f,  0.90f }, {  0.90f,  0.90f }, {  0.90f, -0.90f }   // Triangle 2
    };

    glCreateBuffers( NumBuffers, Buffers );
    glBindBuffer( GL_ARRAY_BUFFER, Buffers[ArrayBuffer] );
    glBufferStorage( GL_ARRAY_BUFFER, sizeof(vertices), vertices, 0);

    ShaderInfo  shaders[] =
    {
        { GL_VERTEX_SHADER, "media/shaders/triangles/triangles.vert" },
        { GL_FRAGMENT_SHADER, "media/shaders/triangles/triangles.frag" },
        { GL_NONE, NULL }
    };

    GLuint program = LoadShaders( shaders );
    glUseProgram( program );

    glVertexAttribPointer( vPosition, 2, GL_FLOAT,
                           GL_FALSE, 0, BUFFER_OFFSET(0) );
    glEnableVertexAttribArray( vPosition );
}


void
init4( void )
{
    glGenVertexArrays( NumVAOs, VAOs );
    glBindVertexArray( VAOs[Triangles] );

    GLfloat  vertices[NumVertices][2] = {
        { -0.40f, -0.40f }, { -0.40f,  0.40f }, {  0.00f, -0.40f },  // Triangle 1
        {  0.00f,  0.40f }, {  0.40f,  0.40f }, {  0.40f, -0.40f }   // Triangle 2
    };

    glCreateBuffers( NumBuffers, Buffers );
    glBindBuffer( GL_ARRAY_BUFFER, Buffers[ArrayBuffer] );
    glBufferStorage( GL_ARRAY_BUFFER, sizeof(vertices), vertices, 0);

    ShaderInfo  shaders[] =
    {
        { GL_VERTEX_SHADER, "media/shaders/triangles/triangles.vert" },
        { GL_FRAGMENT_SHADER, "media/shaders/triangles/triangles.frag" },
        { GL_NONE, NULL }
    };

    GLuint program = LoadShaders( shaders );
    glUseProgram( program );

    glVertexAttribPointer( vPosition, 2, GL_FLOAT,
                           GL_FALSE, 0, BUFFER_OFFSET(0) );
    glEnableVertexAttribArray( vPosition );
}


//----------------------------------------------------------------------------
//
// display
//

void
display( void )
{
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[0]);
    glViewport(0, 0, buffwidth, buffheight);
    static const float black[] = { 0.2f, 0.2f, 0.2f, 0.0f };
    static const float redish[] = { 0.6f, 0.4f, 0.3f, 0.0f };
    glClearBufferfv(GL_COLOR, 0, black);

    glBindVertexArray( VAOs[Triangles] );
    glDrawArrays( GL_TRIANGLES, 0, NumVertices );


    glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[0]);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
    glViewport(0, 0, 800, 600);
    glClearBufferfv(GL_COLOR, 0, redish);

    glBlitFramebuffer(0, 0, 800, 600, 0, 0, 800, 600, GL_COLOR_BUFFER_BIT, GL_NEAREST);

}

void
display2( void )
{
    glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[1]);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
    glViewport(0, 0, 800, 600);
    static const float redish[] = { 0.6f, 0.4f, 0.3f, 0.0f };
    glClearBufferfv(GL_COLOR, 0, redish);

    glBlitFramebuffer(buffwidth, 0, buffwidth+800, 600, 0, 0, 800, 600, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}

void
display3( void )
{
    static const float black[] = { 0.7f, 0.6f, 0.4f, 0.0f };

    glClearBufferfv(GL_COLOR, 0, black);

    glBindVertexArray( VAOs[Triangles] );
    glDrawArrays( GL_TRIANGLES, 0, NumVertices );
}

void
display4( void )
{
    static const float black[] = { 0.2f, 0.3f, 0.7f, 0.0f };

    glClearBufferfv(GL_COLOR, 0, black);

    glBindVertexArray( VAOs[Triangles] );
    glDrawArrays( GL_TRIANGLES, 0, NumVertices );
}


//----------------------------------------------------------------------------
//
// main
//

#ifdef _WIN32
int CALLBACK WinMain(
  _In_ HINSTANCE hInstance,
  _In_ HINSTANCE hPrevInstance,
  _In_ LPSTR     lpCmdLine,
  _In_ int       nCmdShow
)
#else
int
main( int argc, char** argv )
#endif
{
    // Initialize GLFW
    glfwInit();
    // Initialize OpenGL
    // Place it here before any OpenGL objects are needed, other OpenGL crashes
    //      ... in a "Segmentation fault (core dumped)" error
    gl3wInit();


    //TODO Create Windows Class
    // Create Windows
    GLFWwindow* window = glfwCreateWindow(800, 600, "Triangles", NULL, NULL);
    GLFWwindow* window2 = glfwCreateWindow(800, 600, "Triangles2", NULL, window);
    GLFWwindow* window3 = glfwCreateWindow(800, 600, "Triangles3", NULL, window);
    GLFWwindow* window4 = glfwCreateWindow(800, 600, "Triangles4", NULL, window);

    // Initialize Windows
    glfwMakeContextCurrent(window);
    init();
    glfwMakeContextCurrent(window2);
    init2();
    glfwMakeContextCurrent(window3);
    init3();
    glfwMakeContextCurrent(window4);
    init4();


    // Draw the Windows
    while (!glfwWindowShouldClose(window) && !glfwWindowShouldClose(window2) && !glfwWindowShouldClose(window3) && !glfwWindowShouldClose(window4))
    {
        glfwMakeContextCurrent(window);
        display();
        glfwSwapBuffers(window);
        glfwPollEvents();

        glfwMakeContextCurrent(window2);
        display2();
        glfwSwapBuffers(window2);
        glfwPollEvents();

        glfwMakeContextCurrent(window3);
        display3();
        glfwSwapBuffers(window3);
        glfwPollEvents();

        glfwMakeContextCurrent(window4);
        display4();
        glfwSwapBuffers(window4);
        glfwPollEvents();
    }


    // Destroy Windows
    glfwDestroyWindow(window);
    glfwDestroyWindow(window2);
    glfwDestroyWindow(window3);
    glfwDestroyWindow(window4);


    // Terminate GLFW Instance
    glfwTerminate();
}

您的初始化函数正在尝试在上下文之间共享状态。 GL 的上下文是所有 GL 状态的实例,如果 instructed to do so explicitly when initialized,则上下文仅共享数据。我建议仅使用一个 window 和 glviewport 对其进行分区。

也许你也犯了一个错误,没有让你的对象成为数组的数组,因为每个 glgen 都会覆盖它们。这不是 GL 的错误,而是 C 数组语法的错误。

我看到的一些问题:

您正在创建四个不同的 gl 上下文,每个 window 一个。您想要将 window_1 中渲染的帧缓冲区用于另一个 windows。为此,您需要做两件事:
1) 使用共享上下文,它可以在 glfwCreateWindow 调用时创建。
2) 共享 renderbuffer RB,而不是 FBO(FBO 与 VAO 一样,不能共享)。这意味着每个 window 需要一个不同的 FBO,并将相同的 RB 附加到每个 FBO。

在从缓冲区 (VBO) 绘制之前,它必须在 VAO 中 "associated" 到 glVertexAttribPointer。换句话说,每个 window 都需要自己的 VAO。
您没有请求 核心配置文件 上下文。这是通过 glfwWindowHintcontext hints glfwCreateWindow 之前实现的。不这样做可能会奏效,因为某些驱动程序会提供 兼容性配置文件 ;但可能不是,所以最好使用 Core Profile。核心配置文件 需要 VAO。

gl3wInit() 应在某些 gl-context 设置为当前后调用。我不知道 glfw 是否以某种方式使某些上下文成为当前,所以你不会收到错误。

您的 enum 使 NumVAOs = 1NumBuffers = 1。因此,glGenVertexArrays 和其他 glGenXXX 调用会覆盖最后的值。这样会乱画的

init() 你创建了一个 VBO,但没有设置它的大小。所以,glClearBufferfv 不知道必须有多少字节 "cleared".

Finally found the solution.

Notes

  • I want to thank Ripi2 for nudges in the right direction with documentation and initial review of code. I really appreciate the help and guidance.
  • The code posted in this answer is not sophisticated, nor is it intended to be, therefore this is obviously not production quality code. What I'm outlining here should be easy to read by anyone - be they beginner or advanced
  • As an less experienced OpenGL programmer, there are probably stylistic choices being made that are not optimal or best practices. I will update this answer to reflect any workable improvement to this code that still maintains an easy readability for newcomers to the concepts.
  • A more complex windowing system that I'd like to adopt would look something like this. The developer here has created a very robust windowing system. If I were to make a change, it would be to create a window class. But... that's an academic discussion and highly tangential to the answer at hand.

What was the Solution?

  1. Approaching context sharing. Again, thanks to Ripi2 for this. Originally, I was going to render a large framebuffer in a separate context, as in a fifth window, invisible window, and then perform glBlit copies to each active window. This may have worked, but upon guidance I was talked down to performing the actual rendering in the main window and then copying into the final three windows. This is faster because there are less operations
  2. Actually turning on Context Sharing with GLFW. At the end of each glfwCreateWindow(); command is a parameter in which to pass the context from another window. Since I was creating the renderbuffer in the initial init() for the first window, I had to set window for the final param for all three other windows, like so: GLFWwindow *window2 = glfwCreateWindow(width, height, title, NULL, window-whose-context-i-want-to-share);
  3. The difficult part, for me, was knowing exactly how to navigate the idea that a renderbuffer object can be shared, and so can its data, but you can only get access to it through an item that can't be shared across contexts. This leads to the idea of having one renderbuffer for all windows, and one framebuffer for each window. It turns out that actually implementing this was more simple than expected - but that was after hours of reading and research. Hopefully this answer will help you skip right to the heart of the matter.
  4. Each call to one of the init functions created a framebuffer and binds the renderbuffer to the current context.
  5. You have to rebind the renderbuffer to the current context, eventhough it's a shared resource. Not doing this will not load the rendered data from the first display() function. In other words, while the rendered data will still exist, you won't have access to it until you bind it in your current context... everytime you change to rendering into a new window.
  6. The REALLY important part, that I just had to figure out, was ensuring that I was setting the proper renderbuffer-target. In this case, what you'll notice in init2(const int) is the command that binds the current context's framebuffer to the shared renderbuffer, glNamedFramebufferRenderbuffer(...), must have the same renderbuffer-target as the original renderbuffer-target defined in init(): GL_COLOR_ATTACHMENT0. In hindsight, this should have been obvious, but the amount of documentation for multi-window rendering from a renderbuffer object was hard to find. Almost all others gave demonstrations using glTexture objects instead of renderbuffers. This GL_COLOR_ATTACHMENTi is like an internal address where multiple sets of rendered data can be stored... a very good hint for future projects.
  7. When binding the framebuffer for each context in the display2(const int) function, the important thing to set here was that the framebuffer was only going to READ from the renderbuffer, so use GL_READ_FRAMEBUFFER - because the data is already there and we're just using a framebuffer object as a way to get access to the data inside the renderbuffer. The FBO is the conduit, instead of the storage. Indeed, there is no way to access the data inside a renderbuffer without attaching an FBO first.
  8. Debugging - I started using various versions of the glGet function family in order to get information about the renderbuffer in different contexts, as well as the framebuffer. When the results from the gets matched my expectations, I knew I was on the right track. You'll see in display2(const int) that I left two lines in that get a renderbuffer parameter: the width. I left it in this code snippet for anyone else who might be interested in how this would work. WARNING printing from a display function is a bad idea for anything other than rudimentary debugging, because it will write the console once per cycle - and writing to stdout is ridiculously slow.

The Code

//////////////////////////////////////////////////////////////////////////////
//
//  Triangles.cpp
//
//////////////////////////////////////////////////////////////////////////////
#include <cstdio>
#include "vgl.h"
#include "LoadShaders.h"

enum VAO_IDs { Triangles, NumVAOs };
enum Buffer_IDs { ArrayBuffer, NumBuffers };
enum Attrib_IDs { vPosition = 0 };

GLuint  VAOs[NumVAOs];
GLuint  Buffers[NumBuffers];

const GLuint  NumVertices = 6;
////////////////////////////////////
//RBO variables
enum {Color=0, NumRenderBuffers=1, NumFBOs=4};
GLuint renderbuffer[NumRenderBuffers], fbos[NumFBOs];
GLuint buffwidth = 3200;
GLuint buffheight = 600;



//----------------------------------------------------------------------------
//
// init
//

void
init( void )
{
    glCreateRenderbuffers(NumRenderBuffers, renderbuffer);
    glNamedRenderbufferStorage(renderbuffer[Color], GL_RGBA, buffwidth, buffheight);
    glGenFramebuffers(1, &fbos[0]);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[0]);
    glNamedFramebufferRenderbuffer(fbos[0], GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer[Color]);
    printf("\"init()\", is RB valid: %i\n", glIsRenderbuffer(renderbuffer[Color]));
    printf("\"init()\", is FBO valid: %i\n", glCheckNamedFramebufferStatus(fbos[0], GL_DRAW_FRAMEBUFFER));

    glGenVertexArrays( NumVAOs, VAOs );
    glBindVertexArray( VAOs[Triangles] );

    GLfloat  vertices[NumVertices][2] = {
        { -0.90f, -0.90f }, { -0.90f,  0.90f }, {  0.00f, -0.90f },  // Triangle 1
        {  0.00f,  0.90f }, {  0.90f,  0.90f }, {  0.90f, -0.90f }   // Triangle 2
    };

    glCreateBuffers( NumBuffers, Buffers );
    glBindBuffer( GL_ARRAY_BUFFER, Buffers[ArrayBuffer] );
    glBufferStorage( GL_ARRAY_BUFFER, sizeof(vertices), vertices, 0);

    ShaderInfo  shaders[] =
    {
        { GL_VERTEX_SHADER, "media/shaders/triangles/triangles.vert" },
        { GL_FRAGMENT_SHADER, "media/shaders/triangles/triangles.frag" },
        { GL_NONE, NULL }
    };

    GLuint program = LoadShaders( shaders );
    glUseProgram( program );

    glVertexAttribPointer( vPosition, 2, GL_FLOAT,
                           GL_FALSE, 0, BUFFER_OFFSET(0) );
    glEnableVertexAttribArray( vPosition );
}

void
init2( const int idx_in )
{
    glGenFramebuffers(1, &fbos[idx_in]);
    glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[idx_in]);
    glEnable(GL_RENDERBUFFER);
    glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer[Color]);
    GLint tmpwidth = 0;
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &tmpwidth);

    glNamedFramebufferRenderbuffer(fbos[idx_in], GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer[Color]);
    printf("\"init2()\", RB width is: %i\n", tmpwidth);
    printf("\"init2()\", is RB valid: %i\n", glIsRenderbuffer(renderbuffer[Color]));
    printf("\"init2()\", is FBO valid: %i\n", glCheckNamedFramebufferStatus(fbos[idx_in], GL_DRAW_FRAMEBUFFER));

}

//----------------------------------------------------------------------------
//
// display
//

void
display( void )
{
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[0]);
    glViewport(0, 0, buffwidth, buffheight);
    static const float black[] = { 0.2f, 0.2f, 0.2f, 0.0f };
    static const float redish[] = { 0.6f, 0.4f, 0.3f, 0.0f };
    glClearBufferfv(GL_COLOR, 0, black);

    glBindVertexArray( VAOs[Triangles] );
    glDrawArrays( GL_TRIANGLES, 0, NumVertices );


    glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[0]);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
    glViewport(0, 0, 800, 600);
    glClearBufferfv(GL_COLOR, 0, redish);

    glBlitFramebuffer(0, 0, 800, 600, 0, 0, 800, 600, GL_COLOR_BUFFER_BIT, GL_NEAREST);

}

void
display2( const int idx_in )
{
    glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[idx_in]);

    GLint tmpwidth = 0;
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &tmpwidth);

    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
    glViewport(0, 0, 800, 600);


    glBlitFramebuffer(800 * idx_in - 1, 0, 800 * idx_in - 1 + 800, 600, 0, 0, 800, 600, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}


//----------------------------------------------------------------------------
//
// main
//

#ifdef _WIN32
int CALLBACK WinMain(
  _In_ HINSTANCE hInstance,
  _In_ HINSTANCE hPrevInstance,
  _In_ LPSTR     lpCmdLine,
  _In_ int       nCmdShow
)
#else
int
main( int argc, char** argv )
#endif
{
    // Initialize GLFW
    glfwInit();

    //TODO Create Windows Class
    // Create Window Hints
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
    // Create Windows
    GLFWwindow* window = glfwCreateWindow(800, 600, "Triangles", NULL, NULL);
    GLFWwindow* window2 = glfwCreateWindow(800, 600, "Triangles2", NULL, window);
    GLFWwindow* window3 = glfwCreateWindow(800, 600, "Triangles3", NULL, window);
    GLFWwindow* window4 = glfwCreateWindow(800, 600, "Triangles4", NULL, window);

    // Set window positions
    glfwSetWindowPos(window,  100, 100);
    glfwSetWindowPos(window2, 900, 100);
    glfwSetWindowPos(window3, 1700, 100);
    glfwSetWindowPos(window4, 2500, 100);

    // Initialize Windows
    glfwMakeContextCurrent(window);
    // Initialize gl3w - thanks @Ripi2 for the assist
    gl3wInit();
    init();
    glfwMakeContextCurrent(window2);
    init2(1);
    glfwMakeContextCurrent(window3);
    init2(2);
    glfwMakeContextCurrent(window4);
    init2(3);


    // Draw the Windows
    while (!glfwWindowShouldClose(window) && !glfwWindowShouldClose(window2) && !glfwWindowShouldClose(window3) && !glfwWindowShouldClose(window4))
    {
        glfwMakeContextCurrent(window);
        display();
        glfwSwapBuffers(window);
        glfwPollEvents();
        glFinish();

        glfwMakeContextCurrent(window2);
        display2(1);
        glfwSwapBuffers(window2);
        glfwPollEvents();
        glFinish();

        glfwMakeContextCurrent(window3);
        display2(2);
        glfwSwapBuffers(window3);
        glfwPollEvents();

        glfwMakeContextCurrent(window4);
        display2(3);
        glfwSwapBuffers(window4);
        glfwPollEvents();
    }


    // Destroy Windows
    glfwDestroyWindow(window);
    glfwDestroyWindow(window2);
    glfwDestroyWindow(window3);
    glfwDestroyWindow(window4);


    // Terminate GLFW Instance
    glfwTerminate();
}

The Final Render