I am trying to implement the multi render target effect of open gl 3 using a single FBO with multiple color attachment points. I am attaching 4 textures(loaded with images) to 4 color attachment points of the FBO. After this ia m trying to blit from the framebuffer. However, i cannot see any output. Could anyone point out the error in my code.
This is an example to demonstrate Multiple Render Targets and framebuffer blits.
First, we will render a quad that outputs four textures
per fragment using MRTs.
Then, we will copy the four color buffers into four screen quadrants
using framebuffer blits.
code :
#include <EGL/egl.h>
#include <GLES3/gl3.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#include "MultiRenderTarget.h"
#include "imageload.h"
#define mMAXFC 500
extern EGLDisplay sEGLDisplay;
extern EGLSurface sEGLSurface;
static const char* samplerNames1[4]=
{
"tex1",
"tex2",
"tex3",
"tex4",
};
struct texImage
char *fileName;
GLenum imgFormat;
const char* nameOfImageIneternalformat;
char *bmp_texpath[100] = {
(char*)"./assets/Raw1.bmp",
(char*)"./assets/Raw2.bmp",
(char*)"./assets/Raw3.bmp",
(char*)"./assets/Raw4.bmp",
GLfloat texcoords_bmp1[] =
0, 0,
0, 1,
1, 1,
1, 0
int width=res.h_res,height=res.v_res;
GLuint VSObject,FSObject;
GLuint ProgramObject;
GLuint mfbo;
GLuint colorTexId[4];
GLsizei textureWidth =res.h_res;
GLsizei textureHeight = res.v_res;
int MRT_bDone = 0;
int mframeCount = 0;
ImageData img[5] ;
void MRT_signalHandler (int sig)
switch (sig)
case SIGINT:
case SIGTERM:
GTS_INFO("Ctrl+c pressed\n");
MRT_bDone = 1;
default:
break;
}
const GLenum attachments[4] =
GL_COLOR_ATTACHMENT0,
GL_COLOR_ATTACHMENT1,
GL_COLOR_ATTACHMENT2,
GL_COLOR_ATTACHMENT3
int InitFBO ( )
int i;
GLint defaultFramebuffer = 0;
glGetIntegerv ( GL_FRAMEBUFFER_BINDING, &defaultFramebuffer );
glGenFramebuffers ( 1, &mfbo );
glBindFramebuffer ( GL_FRAMEBUFFER, mfbo );
glGenTextures ( 4, &colorTexId[0] );
for (i = 0; i < 4; ++i)
GetImageFromBMP(&img[index],bmp_texpath[index]);
if(img->imagedata != NULL)
GL_CHECK(glActiveTexture(GL_TEXTURE0 + i));
glBindTexture ( GL_TEXTURE_2D, colorTexId[i] );
glTexImage2D ( GL_TEXTURE_2D, 0, GL_RGBA,
img->OriginalImageWidth, img->OriginalImageHeight,
0, GL_RGBA, GL_UNSIGNED_BYTE, img[i].imagedata );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glFramebufferTexture2D ( GL_DRAW_FRAMEBUFFER, attachments[i],
GL_TEXTURE_2D, colorTexId[i], 0 );
glDrawBuffers ( 4, attachments );
if ( GL_FRAMEBUFFER_COMPLETE != glCheckFramebufferStatus ( GL_FRAMEBUFFER ) )
return false;
// Restore the original framebuffer
glBindFramebuffer ( GL_FRAMEBUFFER, defaultFramebuffer );
return true;
GLfloat vVertices[] = { -1.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
void DrawGeometry ( )
// Set the viewport
glViewport ( 0, 0, width,height );
// Clear the color buffer
glClear ( GL_COLOR_BUFFER_BIT );
glUseProgram ( ProgramObject );
//glDrawBuffers(4,attachments);
for(i=0; i<4; i++)
glDrawBuffers(1,attachments[i]);
//for(i=0; i<4; i++)
//{
GL_CHECK(glBindTexture(GL_TEXTURE_2D,colorTexId[i]));
//}
glVertexAttribPointer ( 0, 3, GL_FLOAT,
GL_FALSE, 3 * sizeof ( GLfloat ), vVertices );
GL_CHECK(glGenBuffers(1, texbuf));
GL_CHECK(glBindBuffer(GL_ARRAY_BUFFER, texbuf));
GL_CHECK(glBufferData(GL_ARRAY_BUFFER, sizeof(texcoords_bmp1), texcoords_bmp1, GL_STATIC_DRAW));
GLuint textureCoordinateLocation1 = GL_CHECK(glGetAttribLocation (uiProgramObject, "attributeTextureCoordinate1"));
GL_CHECK(glEnableVertexAttribArray(textureCoordinateLocation1));
GL_CHECK(glVertexAttribPointer(textureCoordinateLocation1, 2, GL_FLOAT, GL_FALSE, 0, 0));
GL_CHECK(glBindTexture(GL_TEXTURE_2D, colorTexId[i]));
GL_CHECK(glUniform1i(glGetUniformLocation(ProgramObject, samplerNames1[i]),i));
// Load the vertex position
glEnableVertexAttribArray ( 0 );
// Draw a quad
glDrawElements ( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices );
///
// Copy MRT output buffers to screen
//
void BlitTextures ( )
int index= 0;
glBindFramebuffer ( GL_READ_FRAMEBUFFER, mfbo );
while( mframeCount < mMAXFC && !MRT_bDone){
// Copy the output red buffer to lower left quadrant
glReadBuffer ( attachments[(index+0)%4] );
glBlitFramebuffer ( 0, 0, textureWidth, textureHeight,
0, 0, width/2, height/2,
GL_COLOR_BUFFER_BIT, GL_LINEAR );
// Copy the output green buffer to lower right quadrant
glReadBuffer ( attachments[(index+1)%4] );
width/2, 0, width, height/2,
// Copy the output blue buffer to upper left quadrant
glReadBuffer ( attachments[(index+2)%4] );
width/2, height/2, width, height,
// Copy the output gray buffer to upper right quadrant
glReadBuffer ( attachments[(index+3)%4] );
0, height/2, width/2, height,
GTS_INFO("Frame Count ----> %d\n",mframeCount + 1);
eglSwapBuffers(sEGLDisplay,sEGLSurface);
mframeCount ++;
usleep(200000);
index+=1;
if(index>=4)
index=0;
// Render to MRTs and screen
GLuint vb;
void Draw ( )
static const GLfloat buffer_data[] = {
0.0f, 1.0f, 0.0f,
//GLuint texID = glGetUniformLocation(quad_programID, "renderedTexture1");
// FIRST: use MRTs to output four colors to four buffers
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
//glDrawBuffers ( 4, attachments );
//glDrawBuffers ( 1, attachments[0] );
//for triangle
/* glGenBuffers(1, &vb);
glBindBuffer(GL_ARRAY_BUFFER, vb);
glBufferData(GL_ARRAY_BUFFER, sizeof(buffer_data), buffer_data, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer( 0,3,GL_FLOAT,GL_FALSE,0,(void*)0);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDrawBuffers ( 1, attachments[1] );
GLuint texID1 = glGetUniformLocation(quad_programID, "renderedTexture2");
//for square
GLfloat vertices[] = {-1, -1, 0,
-1, 1, 0,
1, 1, 0,
1, -1, 0};
GLubyte indices[] = {0,1,2,
0,2,3};
glGenBuffers(1, &vb1);
glBindBuffer(GL_ARRAY_BUFFER, vb1);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0,3, GL_FLOAT,GL_FALSE 0, (void*)0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, indices);
*/
DrawGeometry ( );
// Restore the default framebuffer
glBindFramebuffer ( GL_DRAW_FRAMEBUFFER, defaultFramebuffer );
BlitTextures ( );
int MultiRenderTarget ()
signal (SIGINT, MRT_signalHandler);
signal (SIGTERM, MRT_signalHandler);
if(processShader(&VSObject, (char *)VERT_FILE_IR, GL_VERTEX_SHADER) == EXIT_FAILURE)
return EXIT_FAILURE;
if(processShader(&FSObject, (char *)FRAG_FILE_IR, GL_FRAGMENT_SHADER) == EXIT_FAILURE)
ProgramObject = GL_CHECK(glCreateProgram());
GL_CHECK(glAttachShader(ProgramObject, VSObject));
GL_CHECK(glAttachShader(ProgramObject, FSObject));
GL_CHECK(glLinkProgram(ProgramObject));
InitFBO ();
glClearColor ( 1.0f, 1.0f, 1.0f, 0.0f );
Draw();
// ShutDown();
GL_CHECK(glUseProgram(0));
GL_CHECK(glDeleteShader(VSObject));
GL_CHECK(glDeleteShader(FSObject));
GL_CHECK(glDeleteProgram(ProgramObject));
MRT_bDone = 0;
return 0;
Vertex Shader:
#version 300 es
layout(location = 0) in vec4 a_position;
in vec2 attributeTextureCoordinate1;
out vec2 varyingTextureCoordinate1;
void main()
varyingTextureCoordinate1 = attributeTextureCoordinate1;
gl_Position = a_position;
fragment Shader:
precision mediump float;
uniform sampler2D tex1;
uniform sampler2D tex2;
uniform sampler2D tex3;
uniform sampler2D tex4;
in vec2 varyingTextureCoordinate1;
layout(location = 0) out vec4 fragData0;
layout(location = 1) out vec4 fragData1;
layout(location = 2) out vec4 fragData2;
layout(location = 3) out vec4 fragData3;
fragData0 = texture(tex1,varyingTextureCoordinate1);
fragData1 = texture(tex2,varyingTextureCoordinate1);
fragData2 = texture(tex3,varyingTextureCoordinate1);
fragData3 = texture(tex4,varyingTextureCoordinate1);
I found your code a little hard to follow due to the large sections and loops commented out, but the first thing I notice is the odd behaviour of your DrawGeometries method, which seems to have a for loop whereby each iteration apparently binds a single texture, a single buffer and a single attribute then commits one draw, whereas I'd imagine what you wanted to do was bind your various attributes textures and buffers and then draw once.
I think perhaps it would help you to start a little smaller, get this working with a single render to texture, then step it up to a pair of render targets, then see about putting it in indexed loops.