Access multiple FBO textures in GLSL to create a different texture

I created 4 textures and attached them to an FBO named fbo_texture0 - fbo_texture3. All of them were successfully created, as shown in the following screenshot:

Textures in the FBO image 1

Now I wanted to create the 5th texture obtained from previous textures (fbo_texture0 - fbo_texture3) using GLSL. For now, I just want to copy the first texture to the fifth texture. Unfortunately, here is what I got:

texture fail image 2

The question arises:

  • How can I access these fbo textures in GLSL?
  • How can I create a 5th texture? (or copy from the first texture to the fifth texture?)

Here is the full program code (if necessary):

#include <windows.h> #include <GL/glew.h> // Include the GLEW header file #include <GL/glut.h> // Include the GLUT header file #include <iostream> // Allow us to print to the console using namespace std; bool* keyStates = new bool[256]; // Create an array of boolean values of length 256 (0-255) unsigned int fbo; // The frame buffer object unsigned int fbo_depth; // The depth buffer for the frame buffer object unsigned int fbo_texture0; // The texture object to write our frame buffer object to unsigned int fbo_texture1; unsigned int fbo_texture2; unsigned int fbo_texture3; unsigned int fbo_texture4; GLhandleARB shaderProgram; GLhandleARB vertexShader; GLhandleARB fragmentShader; int window_width = 500; // The width of our window int window_height = 500; // The height of our window void initFrameBufferDepthBuffer(void) { glGenRenderbuffers(1, &fbo_depth); // Generate one render buffer and store the ID in fbo_depth glBindRenderbuffer(GL_RENDERBUFFER, fbo_depth); // Bind the fbo_depth render buffer glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, window_width, window_height); // Set the render buffer storage to be a depth component, with a width and height of the window glBindRenderbuffer(GL_RENDERBUFFER, 0); // Unbind the render buffer } void initFrameBufferTextures(void) { glGenTextures(1, &fbo_texture0); // Generate one ture glBindTexture(GL_TEXTURE_2D, fbo_texture0); // Bind the ture fbo_texture glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, window_width, window_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); // Create a standard ture with the width and height of our window glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); glGenTextures(1, &fbo_texture1); // Generate one ture glBindTexture(GL_TEXTURE_2D, fbo_texture1); // Bind the ture fbo_texture glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, window_width, window_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); // Create a standard ture with the width and height of our window glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); glGenTextures(1, &fbo_texture2); // Generate one ture glBindTexture(GL_TEXTURE_2D, fbo_texture2); // Bind the ture fbo_texture glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, window_width, window_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); // Create a standard ture with the width and height of our window glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); glGenTextures(1, &fbo_texture3); // Generate one ture glBindTexture(GL_TEXTURE_2D, fbo_texture3); // Bind the ture fbo_texture glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, window_width, window_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); // Create a standard ture with the width and height of our window glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); glGenTextures(1, &fbo_texture4); // Generate one ture glBindTexture(GL_TEXTURE_2D, fbo_texture4); // Bind the ture fbo_texture glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, window_width, window_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); // Create a standard ture with the width and height of our window glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); } void printInfoLog(GLhandleARB obj) { int infologLength = 0; int charsWritten = 0; char* infoLog; glGetObjectParameterivARB(obj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &infologLength); if (infologLength > 0) { infoLog = (char*)malloc(infologLength); glGetInfoLogARB(obj, infologLength, &charsWritten, infoLog); printf("%s\n",infoLog); free(infoLog); } } void initFrameBuffer(void) { initFrameBufferDepthBuffer(); // Initialize our frame buffer depth buffer initFrameBufferTextures(); // Initialize our frame buffer ture glGenFramebuffers(1, &fbo); // Generate one frame buffer and store the ID in fbo glBindFramebuffer(GL_FRAMEBUFFER, fbo); // Bind our frame buffer glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_texture0, 0);// Attach the ture fbo_texturen to the color buffer in our frame buffer glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, fbo_texture1, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, fbo_texture2, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, fbo_texture3, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT4, GL_TEXTURE_2D, fbo_texture4, 0); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo_depth); // Attach the depth buffer fbo_depth to our frame buffer GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); // Check that status of our generated frame buffer if (status != GL_FRAMEBUFFER_COMPLETE) // If the frame buffer does not report back as complete { cout << "Couldn't create frame buffer" << endl; // Output an error to the console exit(0); // Exit the application } glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind our frame buffer } void init(void) { //glEnable(GL_TEXTURE_2D); // Enable turing so we can bind our frame buffer ture glEnable(GL_DEPTH_TEST); // Enable depth testing initFrameBuffer(); // Create our frame buffer object } void keyOperations (void) { if (keyStates['a']) { // If the a key has been pressed // Perform 'a' key operations } } void renderTextures(void) { glBindFramebuffer(GL_FRAMEBUFFER, fbo); // Bind our frame buffer for rendering glPushAttrib(GL_VIEWPORT_BIT | GL_ENABLE_BIT); // Push our glEnable and glViewport states glViewport(0, 0, window_width, window_height); // Set the size of the frame buffer view port glDrawBuffer(GL_COLOR_ATTACHMENT0); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set the clear colour glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Clear the depth and colour buffers glLoadIdentity();// Reset the modelview matrix glTranslatef(0.0f, 0.0f, -5.0f); //Add ambient light GLfloat ambientColor[] = {0.2f, 0.2f, 0.2f, 1.0f}; //Color(0.2, 0.2, 0.2) glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor); //Add positioned light GLfloat lightColor0[] = {0.5f, 0.5f, 0.5f, 1.0f}; //Color (0.5, 0.5, 0.5) GLfloat lightPos0[] = {4.0f, 0.0f, 8.0f, 1.0f}; //Positioned at (4, 0, 8) glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor0); glLightfv(GL_LIGHT0, GL_POSITION, lightPos0); //Add directed light GLfloat lightColor1[] = {0.5f, 0.2f, 0.2f, 1.0f}; //Color (0.5, 0.2, 0.2) //Coming from the direction (-1, 0.5, 0.5) GLfloat lightPos1[] = {-1.0f, 0.5f, 0.5f, 0.0f}; glLightfv(GL_LIGHT1, GL_DIFFUSE, lightColor1); glLightfv(GL_LIGHT1, GL_POSITION, lightPos1); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_LIGHT1); glEnable(GL_DEPTH_TEST); glutSolidTeapot(2.0); glColor3f(0.1,0.2,0.7); glDrawBuffer(GL_COLOR_ATTACHMENT1); glClearColor(0.5f, 0.5f, 0.0f, 1.0f); // Set the clear colour glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Clear the depth and colour buffers glLoadIdentity();// Reset the modelview matrix glTranslatef(0.0f, 0.0f, -5.0f); glutSolidTorus(0.80, 1.6, 50, 100); glColorMaterial ( GL_FRONT_AND_BACK, GL_EMISSION ) ; glEnable ( GL_COLOR_MATERIAL ) ; glDrawBuffer(GL_COLOR_ATTACHMENT2); glClearColor(0.5f, 0.0f, 0.0f, 1.0f); // Set the clear colour glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Clear the depth and colour buffers glLoadIdentity();// Reset the modelview matrix glTranslatef(0.0f, 0.0f, -2.0f); glutSolidTetrahedron(); glColorMaterial ( GL_FRONT_AND_BACK, GL_EMISSION ) ; glEnable ( GL_COLOR_MATERIAL ) ; glDrawBuffer(GL_COLOR_ATTACHMENT3); glClearColor(0.5f, 0.0f, 0.3f, 1.0f); // Set the clear colour glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Clear the depth and colour buffers glLoadIdentity();// Reset the modelview matrix glTranslatef(0.0f, 0.0f, -2.0f); glutSolidOctahedron(); glColorMaterial ( GL_FRONT_AND_BACK, GL_EMISSION ) ; glEnable ( GL_COLOR_MATERIAL ) ; glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind our ture glActiveTexture(GL_TEXTURE0); //glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, fbo_texture0); glUniform1i(glGetUniformLocation(shaderProgram, "tex0"), 0); glActiveTexture(GL_TEXTURE1); //glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, fbo_texture1); glUniform1i(glGetUniformLocation(shaderProgram, "tex1"), 1); glActiveTexture(GL_TEXTURE2); //glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, fbo_texture2); glUniform1i(glGetUniformLocation(shaderProgram, "tex2"), 2); glActiveTexture(GL_TEXTURE3); //glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, fbo_texture3); glUniform1i(glGetUniformLocation(shaderProgram, "tex3"), 3); glPopAttrib(); // Restore our glEnable and glViewport states glutSwapBuffers(); } static char* textFileRead(const char *fileName) { char* text; if (fileName != NULL) { FILE *file = fopen(fileName, "rt"); if (file != NULL) { fseek(file, 0, SEEK_END); int count = ftell(file); rewind(file); if (count > 0) { text = (char*)malloc(sizeof(char) * (count + 1)); count = fread(text, sizeof(char), count, file); text[count] = '\0'; } fclose(file); } } return text; } void initShader(){ char* vsSource = textFileRead("./shader/multitexture.vs"); char* fsSource = textFileRead("./shader/multitexture.fs"); printf("%s\n",fsSource); vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader, 1, (const GLchar **)(&vsSource), NULL); glCompileShader(vertexShader); printInfoLog(vertexShader); fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, (const GLchar **)(&fsSource), NULL); glCompileShader(fragmentShader); printInfoLog(fragmentShader); delete [] vsSource; delete [] fsSource; shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertexShader); glAttachShader(shaderProgram, fragmentShader); glLinkProgram(shaderProgram); } void display (void) { keyOperations(); // Perform any key presses glUseProgram(0); renderTextures(); // Render our teapot scene into our frame buffer GLsync s = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); glUseProgram(shaderProgram); glClearColor(0.0f, 1.0f, 0.0f, 1.0f); // Clear the background of our window to red glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Clear the colour buffer (more buffers later on) glLoadIdentity(); // Load the Identity Matrix to reset our drawing locations glTranslatef(-4.7f, 1.0f, -4.0f); glWaitSync(s, 0, GL_TIMEOUT_IGNORED); glDeleteSync(s); glBindTexture(GL_TEXTURE_2D, fbo_texture0); // Bind our frame buffer ture glBegin(GL_QUADS); glColor4f(1, 1, 1, 1); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 0.0f); // The bottom left corner glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f); // The top left corner glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 0.0f); // The top right corner glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 0.0f); // The bottom right corner glEnd(); glBindTexture(GL_TEXTURE_2D, 0); // Unbind any tures glLoadIdentity(); glTranslatef(-2.5f, 1.0f, -4.0f); glBindTexture(GL_TEXTURE_2D, fbo_texture1); // Bind our frame buffer ture glBegin(GL_QUADS); glColor4f(1, 1, 1, 1); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 0.0f); // The bottom left corner glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f); // The top left corner glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 0.0f); // The top right corner glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 0.0f); // The bottom right corner glEnd(); glBindTexture(GL_TEXTURE_2D, 0); // Unbind any tures glLoadIdentity(); glTranslatef(-0.3f, 1.0f, -4.0f); glBindTexture(GL_TEXTURE_2D, fbo_texture2); // Bind our frame buffer ture glBegin(GL_QUADS); glColor4f(1, 1, 1, 1); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 0.0f); // The bottom left corner glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f); // The top left corner glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 0.0f); // The top right corner glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 0.0f); // The bottom right corner glEnd(); glBindTexture(GL_TEXTURE_2D, 0); // Unbind any tures glLoadIdentity(); glTranslatef(1.9f, 1.0f, -4.0f); glBindTexture(GL_TEXTURE_2D, fbo_texture3); // Bind our frame buffer ture glBegin(GL_QUADS); glColor4f(1, 1, 1, 1); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 0.0f); // The bottom left corner glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f); // The top left corner glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 0.0f); // The top right corner glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 0.0f); // The bottom right corner glEnd(); glBindTexture(GL_TEXTURE_2D, 0); // Unbind any tures glLoadIdentity(); glTranslatef(4.1f, 1.0f, -4.0f); glBindTexture(GL_TEXTURE_2D, fbo_texture4); // Bind our frame buffer ture glBegin(GL_QUADS); glColor4f(1, 1, 1, 1); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 0.0f); // The bottom left corner glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f); // The top left corner glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 0.0f); // The top right corner glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 0.0f); // The bottom right corner glEnd(); glBindTexture(GL_TEXTURE_2D, 0); // Unbind any tures glutSwapBuffers(); } void reshape (int width, int height) { glViewport(0, 0, (GLsizei)width, (GLsizei)height); // Set our viewport to the size of our window glMatrixMode(GL_PROJECTION); // Switch to the projection matrix so that we can manipulate how our scene is viewed glLoadIdentity(); // Reset the projection matrix to the identity matrix so that we don't get any artifacts (cleaning up) gluPerspective(60, (GLfloat)width / (GLfloat)height, 1.0, 100.0); // Set the Field of view angle (in degrees), the aspect ratio of our window, and the new and far planes glMatrixMode(GL_MODELVIEW); // Switch back to the model view matrix, so that we can start drawing shapes correctly } void keyPressed (unsigned char key, int x, int y) { keyStates[key] = true; // Set the state of the current key to pressed } void keyUp (unsigned char key, int x, int y) { keyStates[key] = false; // Set the state of the current key to not pressed } int main (int argc, char **argv) { glutInit(&argc, argv); // Initialize GLUT glutInitDisplayMode (GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA); // Set up a basic display buffer (only single buffered for now) glutInitWindowSize (1280, 500); // Set the width and height of the window glutInitWindowPosition (100, 100); // Set the position of the window glutCreateWindow ("OpenGL FBO"); // Set the title for the window if (GLEW_OK != glewInit()) { std::cout << "Couldn't initialize GLEW" << std::endl; exit(0); } initShader(); init(); glutDisplayFunc(display); // Tell GLUT to use the method "display" for rendering glutIdleFunc(display); // Tell GLUT to use the method "display" for rendering glutReshapeFunc(reshape); // Tell GLUT to use the method "reshape" for reshaping glutKeyboardFunc(keyPressed); // Tell GLUT to use the method "keyPressed" for key presses glutKeyboardUpFunc(keyUp); // Tell GLUT to use the method "keyUp" for key up events glutMainLoop(); // Enter GLUT main loop } 

Here is the vertex shader:

 void main(void) { gl_TexCoord[0] = gl_MultiTexCoord0; gl_Position = ftransform(); } 

And here is a fragment of a shader:

 uniform sampler2D tex0; uniform sampler2D tex1; uniform sampler2D tex2; uniform sampler2D tex3; void main(void) { gl_FragColor = texture2D(tex0, gl_TexCoord[0].st); } 

EDIT NO. 1

After changing the code suggested by @Damon (the code has also been edited), here is a screenshot of the result:

enter image description here image 3

Now I really don't know what the problem is. I tried changing the fragment shader to access a different texture, for example. gl_FragColor = texture2D(tex2, gl_TexCoord[0].st); but I still got the same display as above. So I think this is definitely not a model / projection problem.

EDIT No. 2

The problem is still unclear. However, I tried to give only one command glActiveTexture(GL_TEXTUREn); in the program and comment on another glActiveTexture command (without modifying the shader) and got the following result:

enter image description here Image 4 Enabled only glActiveTexture(GL_TEXTURE0); .

enter image description here Image 5 Enabled only glActiveTexture(GL_TEXTURE1); .

enter image description here Image 6 Only glActiveTexture(GL_TEXTURE2); activated.

enter image description here Image 7 glActiveTexture(GL_TEXTURE3); only glActiveTexture(GL_TEXTURE3); activated.

When at least 2 glActiveTexture(GL_TEXTUREn); , I got the same result as image 5. It made me think that the problem was actually.

+4
source share
2 answers

initShader compiles and initShader shader. It seems you also bind textures to texture units inside renderTextures in OK order (after decoupling FBO, which is important for synchronization). So far so good, but I can not find glUseProgram anywhere in the code. This would mean that the rendering returns to a fixed function that does not have any textures bound to that time.

The fragment shader is only read from tex0, so of course you would not expect to see tex1-tex3 anyway (but I assume this is only a minimal working code example).

Other than that, it looks fine from what I see after reading the code in 5 minutes.

(As a warning: init calls glEnable(GL_TEXTURE_2D) , this is not completely wrong, but useless once you use the shader, see this .)

+3
source

How about this:

 glBindFramebuffer(GL_FRAMEBUFFER, fbo); // Bind our frame buffer for rendering glDrawBuffer(GL_COLOR_ATTACHMENT4); glBindTexture(GL_TEXTURE_2D, fbo_texture0); // bind texture that is rendered in 0-th attachment glBegin(GL_QUADS); glColor4f(1, 1, 1, 1); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 0.0f); // The bottom left corner glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f); // The top left corner glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 0.0f); // The top right corner glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 0.0f); // The bottom right corner glEnd(); glBindFramebuffer(GL_FRAMEBUFFER, 0); 

You simply render the 4th attachment with the associated texture from the 0th attachment.

0
source

All Articles