How to fix this Render To Texture for my project?

A minimal steps for others to reproduce the problem

  • Go to that GitHub link and download the file render-to-texture.zip (there is a text “Sorry” but the Download button is on top right). I don’t know how to upload more than 100 files on GitHub, that’s why it’s a .zip file.
  • Extract the .zip file somewhere, and open the main folder “render-to-texture” with VSCode, there is a “CMakeLists.txt” in it.
  • Select a Kit (GCC 12.2.0 x86_64-w64-mingw32 or Visual Studio Build Tools 2017 Release - amd64).
  • Select the launch target “test_render_to_texture”.
  • Compile and run run the project named “test_render_to_texture”.

If you cannot compile the project (or want to switch to other compiler), then close VSCode and delete the folder named “build” and try again the previous steps.

Very important note to avoid being stuck

Very white quad probably means that there is an error in the GLSL code. If the GLSL compilation seems weird, like the result is always white even if you are sure it should have other color like green-yellow-red then there’s probably an error in the GLSL code but you didn’t see the error message, it’s shown in the terminal. Colorizing that error will require a lot of codes, that’s why it’s not colorized. This is a minimal project.

The reason of this thread

There is another project named “tutorial14_render_to_texture” (Tutorial 14 : Render To Texture) and I learned from it. When I compare it line by line with my project (test_render_to_texture) then it seems ok, but mine doesn’t work,
the reason the 2 projects are very different is mine is very close to my real project, and when it’s fixed, the real project will be instantly fixed. I was stuck for a very long time with my real project, that’s why I make this thread. Please help.

The problem and goal

When you run the project named “test_render_to_texture” then everything is just blue, the quad doesn’t show, that quad can be white (error) or black or something else but it should be a quad with triangle as a rendered texture as a goal result, the reason I make addition +0.15 to the texture color is to avoid confusion on the quad with the scene background.

There are 3 important settings on top of “test_render_to_texture.cpp”, you can change these values between 0 and 1 for each line.

SimpleFragmentShader.fragmentshader

#version 330 core

uniform sampler2D map;

layout (location = 0) in vec3 inNormal;
layout (location = 1) in vec2 inUV;

out vec3 outFragColor;

void main() {
	#if 0
		outFragColor = vec3(inUV.x, inUV.y, 0);
	#else
		vec4 c = texture(map, inUV);
		outFragColor = 0.15 + 0.85 * vec3(c);
	#endif
}

A few functions in test_render_to_texture.cpp

GLuint Shader::gl_useProgram(const PVMStruct &PVM) {
    GLuint programID = gl_getProgramID();
    glUseProgram(programID);
    GLuint projection_id = glGetUniformLocation(programID, "ubo_projection");
    GLuint view_id       = glGetUniformLocation(programID, "ubo_view");
    GLuint model_id      = glGetUniformLocation(programID, "ubo_model");
    if (projection_id != -1) { glUniformMatrix4fv(projection_id, 1, GL_FALSE, &PVM.projection[0][0]); }
    if (view_id       != -1) { glUniformMatrix4fv(view_id      , 1, GL_FALSE, &PVM.view[0][0]); }
    if (model_id      != -1) { glUniformMatrix4fv(model_id     , 1, GL_FALSE, &PVM.model[0][0]); }
    return programID;
}

void Shader::gl_beginAttributes_pos() {
    EMC_VERIFY(_gl.numAttribs == 0);
    _gl.numAttribs = 1;
    for (int i = 0; i < _gl.numAttribs; i++) {
        glEnableVertexAttribArray(i);
        int stride = (3) * sizeof(GLfloat);
        switch (i) {
        case 0:
            // position
            glVertexAttribPointer(
                i,        // index
                3,        // size
                GL_FLOAT, // type
                GL_FALSE, // normalized?
                stride,   // stride
                (void*)0  // array buffer offset
            );
            break;
        }
    }
}

void Shader::gl_beginAttributes_pos_nor_uv() {
    EMC_VERIFY(_gl.numAttribs == 0);
    _gl.numAttribs = 3;
    for (int attrib = 0; attrib < _gl.numAttribs; attrib++) {
        glEnableVertexAttribArray(attrib);
        int stride = (3 + 3 + 2) * sizeof(GLfloat);
        switch (attrib) {
        case 0:
            // position
            glVertexAttribPointer(
                attrib,   // index
                3,        // size
                GL_FLOAT, // type
                GL_FALSE, // normalized?
                stride,   // stride
                (void*)0  // array buffer offset
            );
            break;
        case 1:
            // normal
            glVertexAttribPointer(
                attrib,                      // index
                3,                           // size
                GL_FLOAT,                    // type
                GL_FALSE,                    // normalized?
                stride,                      // stride
                (void*)(3 * sizeof(GLfloat)) // array buffer offset
            );
            break;
        case 2:
            // texCoord
            glVertexAttribPointer(
                attrib,                            // index
                2,                                 // size
                GL_FLOAT,                          // type
                GL_FALSE,                          // normalized?
                stride,                            // stride
                (void*)((3 + 3) * sizeof(GLfloat)) // array buffer offset
            );
            break;
        }
    }
}

void Shader::gl_endAttributes() {
    EMC_VERIFY(_gl.numAttribs != 0);
    for (int i = 0; i < _gl.numAttribs; i++) {
        glDisableVertexAttribArray(i);
    }
    _gl.numAttribs = 0;
}

Highlight important things in “test_render_to_texture/test_render_to_texture.cpp”

AppBase *appBase = new AppBase();
RenderToTexture rtt;
rtt.init(appBase, false, 1024, 1024);
rtt.makeData();
GLuint rtt_texID = glGetUniformLocation(rtt._shader->_gl.programID, "renderedTexture");

// Dark blue background
glClearColor(0.0f, 0.0f, 0.4f, 0.0f);

GLuint VertexArrayID;
glGenVertexArrays(1, &VertexArrayID);
glBindVertexArray(VertexArrayID);

// Create and compile our GLSL program from the shaders
Shader *shader = new Shader(
    appBase,
    engineDataDir(_PROJECT_NAME) + "/shaders/SimpleVertexShader.vertexshader",
    engineDataDir(_PROJECT_NAME) + "/shaders/SimpleFragmentShader.fragmentshader"
);

// A quad
static const GLfloat g_vertex_buffer_data[] = { 
  // Position         Normal          UV
    -0.9f,-0.9f,0.0f, 0.0f,0.0f,1.0f, 0.0f,0.0f,
     0.9f,-0.9f,0.0f, 0.0f,0.0f,1.0f, 1.0f,0.0f,
    -0.9f, 0.9f,0.0f, 0.0f,0.0f,1.0f, 0.0f,1.0f,

     0.9f,-0.9f,0.0f, 0.0f,0.0f,1.0f, 1.0f,0.0f,
     0.9f, 0.9f,0.0f, 0.0f,0.0f,1.0f, 1.0f,1.0f,
    -0.9f, 0.9f,0.0f, 0.0f,0.0f,1.0f, 0.0f,1.0f,
};

GLuint vertexbuffer;
glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

70% of “test_render_to_texture.cpp”

#define DEBUG_RENDER_TO_TEXTURE           1 // It should be 1.
#define DEBUG_RTT_ON_DEFAULT_FRAME_BUFFER 0 // It should be 0.
#define DEBUG_RENDER_SCENE                1 // It should be 1.

...

struct PVMStruct {
    glm::mat4 projection;
    glm::mat4 view;
    glm::mat4 model;
};

class_memoryBlock (Shader) {
    friend class LightData;
public: // TODO: private not public
    AppBase *_appBase = nullptr;

#ifdef USE_OPENGL
    struct {
        GLuint programID  = -1;
        int    numAttribs = 0;
    } _gl;
#endif

public:
    Shader(AppBase *appBase, const string &vertexFilePath, const string &fragmentFilePath);
    ~Shader();
    AppBase *getAppBase() { return _appBase; }; // Needed by the glTF importer for example.
#ifdef USE_OPENGL
    GLuint gl_getProgramID() { EMC_VERIFY(_gl.programID != -1); return _gl.programID; } // Must be public and not placed in _gl because used by glTF import function outside class.
    GLuint gl_useProgram(const PVMStruct &PVM);
    void gl_beginAttributes_pos();
    void gl_beginAttributes_pos_col();
    void gl_beginAttributes_pos_nor_uv();
    void gl_endAttributes();
#endif
};

Shader::Shader(AppBase *appBase, const string &vertexFilePath, const string &fragmentFilePath) {
    _appBase = appBase;
    //Context *ctx = appBase->getPrimaryContext_s(); [EDIT]
    //ctx->makeCurrent();                            [EDIT]

    if (g_renderingApi == RENDERINGAPI_OPENGL) {
        #ifdef USE_OPENGL
            // Referenced from [Basic OpenGL > "common/shader.cpp"](https://www.opengl-tutorial.org/beginners-tutorials/)

            // Create the shaders
            GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
            GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);

            // Read the Vertex Shader code from the file
            std::string VertexShaderCode;
            std::ifstream VertexShaderStream(vertexFilePath.c_str(), std::ios::in);
            if (VertexShaderStream.is_open()) {
                std::stringstream sstr;
                sstr << VertexShaderStream.rdbuf();
                VertexShaderCode = sstr.str();
                VertexShaderStream.close();
            } 
            else {
                throw "Impossible to open the 'vertexFilePath'. Are you in the right directory ? Don't forget to read the FAQ !";
                return;
            }

            // Read the Fragment Shader code from the file
            std::string FragmentShaderCode;
            std::ifstream FragmentShaderStream(fragmentFilePath.c_str(), std::ios::in);
            if (FragmentShaderStream.is_open()) {
                std::stringstream sstr;
                sstr << FragmentShaderStream.rdbuf();
                FragmentShaderCode = sstr.str();
                FragmentShaderStream.close();
            }

            GLint Result = GL_FALSE;
            int InfoLogLength;


            // Compile Vertex Shader
            printf("Compiling shader : %s\n", vertexFilePath.c_str());
            char const * VertexSourcePointer = VertexShaderCode.c_str();
            glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);
            glCompileShader(VertexShaderID);

            // Check Vertex Shader
            glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
            glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
            if (InfoLogLength > 0) {
                std::vector<char> VertexShaderErrorMessage(InfoLogLength+1);
                GLchar *str = &VertexShaderErrorMessage[0];
                glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, str);
                if (strcasecmp(str, "") != 0) {
                    //pushConsoleTextColor(ConsoleTextColor_Red); [EDIT]
                        printf("%s\n", str);
                    //popConsoleTextColor(); [EDIT]
                }
            }


            // Compile Fragment Shader
            printf("Compiling shader : %s\n", fragmentFilePath.c_str());
            char const * FragmentSourcePointer = FragmentShaderCode.c_str();
            glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL);
            glCompileShader(FragmentShaderID);

            // Check Fragment Shader
            glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
            glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
            if (InfoLogLength > 0) {
                std::vector<char> FragmentShaderErrorMessage(InfoLogLength+1);
                GLchar *str = &FragmentShaderErrorMessage[0];
                glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, str);
                if (strcasecmp(str, "") != 0) {
                    //pushConsoleTextColor(ConsoleTextColor_Red); [EDIT]
                        printf("%s\n", str);
                    //popConsoleTextColor(); [EDIT]
                }
            }


            // Link the program
            printf("Linking program\n");
            _gl.programID = glCreateProgram();
            glAttachShader(_gl.programID, VertexShaderID);
            glAttachShader(_gl.programID, FragmentShaderID);
            glLinkProgram(_gl.programID);

            // Check the program
            glGetProgramiv(_gl.programID, GL_LINK_STATUS, &Result);
            glGetProgramiv(_gl.programID, GL_INFO_LOG_LENGTH, &InfoLogLength);
            if (InfoLogLength > 0) {
                std::vector<char> ProgramErrorMessage(InfoLogLength+1);
                GLchar *str = &ProgramErrorMessage[0];
                glGetProgramInfoLog(_gl.programID, InfoLogLength, NULL, str);
                if (strcasecmp(str, "") != 0) {
                    //pushConsoleTextColor(ConsoleTextColor_Red); [EDIT]
                        printf("%s\n", str);
                    //popConsoleTextColor(); [EDIT]
                }
            }

            
            glDetachShader(_gl.programID, VertexShaderID);
            glDetachShader(_gl.programID, FragmentShaderID);
            
            glDeleteShader(VertexShaderID);
            glDeleteShader(FragmentShaderID);
        #endif
    }
    else if (g_renderingApi == RENDERINGAPI_VULKAN) {
        #ifdef USE_VULKAN
            // TODO
        #endif
    }
    else if (g_renderingApi == RENDERINGAPI_DIRECTX12) {
        #ifdef USE_DIRECTX
            // TODO
        #endif
    }
}

Shader::~Shader() {
    if (g_renderingApi == RENDERINGAPI_OPENGL) {
        #ifdef USE_OPENGL
			glDeleteProgram(_gl.programID); _gl.programID = -1;
        #endif
    }
    else if (g_renderingApi == RENDERINGAPI_VULKAN) {
        #ifdef USE_VULKAN
            // TODO
        #endif
    }
    else if (g_renderingApi == RENDERINGAPI_DIRECTX12) {
        #ifdef USE_DIRECTX
            // TODO
        #endif
    }
}

GLuint Shader::gl_useProgram(const PVMStruct &PVM) {
    GLuint programID = gl_getProgramID();
    glUseProgram(programID);
    GLuint projection_id = glGetUniformLocation(programID, "ubo_projection");
    GLuint view_id       = glGetUniformLocation(programID, "ubo_view");
    GLuint model_id      = glGetUniformLocation(programID, "ubo_model");
    if (projection_id != -1) { glUniformMatrix4fv(projection_id, 1, GL_FALSE, &PVM.projection[0][0]); }
    if (view_id       != -1) { glUniformMatrix4fv(view_id      , 1, GL_FALSE, &PVM.view[0][0]); }
    if (model_id      != -1) { glUniformMatrix4fv(model_id     , 1, GL_FALSE, &PVM.model[0][0]); }
    return programID;
}

void Shader::gl_beginAttributes_pos() {
    EMC_VERIFY(_gl.numAttribs == 0);
    _gl.numAttribs = 1;
    for (int i = 0; i < _gl.numAttribs; i++) {
        glEnableVertexAttribArray(i);
        int stride = (3) * sizeof(GLfloat);
        switch (i) {
        case 0:
            // position
            glVertexAttribPointer(
                i,        // index
                3,        // size
                GL_FLOAT, // type
                GL_FALSE, // normalized?
                stride,   // stride
                (void*)0  // array buffer offset
            );
            break;
        }
    }
}

void Shader::gl_beginAttributes_pos_nor_uv() {
    EMC_VERIFY(_gl.numAttribs == 0);
    _gl.numAttribs = 3;
    for (int attrib = 0; attrib < _gl.numAttribs; attrib++) {
        glEnableVertexAttribArray(attrib);
        int stride = (3 + 3 + 2) * sizeof(GLfloat);
        switch (attrib) {
        case 0:
            // position
            glVertexAttribPointer(
                attrib,   // index
                3,        // size
                GL_FLOAT, // type
                GL_FALSE, // normalized?
                stride,   // stride
                (void*)0  // array buffer offset
            );
            break;
        case 1:
            // normal
            glVertexAttribPointer(
                attrib,                      // index
                3,                           // size
                GL_FLOAT,                    // type
                GL_FALSE,                    // normalized?
                stride,                      // stride
                (void*)(3 * sizeof(GLfloat)) // array buffer offset
            );
            break;
        case 2:
            // texCoord
            glVertexAttribPointer(
                attrib,                            // index
                2,                                 // size
                GL_FLOAT,                          // type
                GL_FALSE,                          // normalized?
                stride,                            // stride
                (void*)((3 + 3) * sizeof(GLfloat)) // array buffer offset
            );
            break;
        }
    }
}

void Shader::gl_endAttributes() {
    EMC_VERIFY(_gl.numAttribs != 0);
    for (int i = 0; i < _gl.numAttribs; i++) {
        glDisableVertexAttribArray(i);
    }
    _gl.numAttribs = 0;
}


//****************************************************************************************************
// Render To Texture
//****************************************************************************************************

struct RenderToTexture {
    friend class ModelData;
//private: // Make private ????????????????????
    AppBase *_appBase     = nullptr;
    bool     _isShadowMap = false;
    int      _width       = 0;
    int      _height      = 0;
    int      _maxWidth    = 0;
    int      _maxHeight   = 0;
    Shader  *_shader      = nullptr;
    struct {
        GLuint frameBuffer       = (GLuint)-1;
        GLuint renderedTexture   = (GLuint)-1;
        GLuint depthRenderBuffer = (GLuint)-1;
    } _gl;
	
	GLuint _draft_vertexBuffer = -1;

public:
    void init(AppBase *appBase, bool isShadowMap, int width, int height);
	void makeData();
    void draw(bool drawOnDefaultFrameBuffer);
    void destroy();
};

void RenderToTexture::init(AppBase *appBase, bool isShadowMap, int width, int height) {
    _appBase = appBase;
    _isShadowMap = isShadowMap;
    _width = width;
    _height = height;
    _maxWidth = width;
    _maxHeight = height; 

    if (g_renderingApi == RENDERINGAPI_OPENGL) {
        #ifdef USE_OPENGL
            // Referenced from:
            // - [Basic OpenGL > "tutorial14_render_to_texture"](https://www.opengl-tutorial.org/beginners-tutorials/)
            // - [Basic OpenGL > "tutorial16_shadowmaps"       ](https://www.opengl-tutorial.org/beginners-tutorials/)

            //appBase->getPrimaryContext_s()->makeCurrent(); // TODO: Fix this ????????????????????

            // The framebuffer, which regroups 0, 1, or more textures, and 0 or 1 depth buffer.
            EMC_VERIFY(_gl.frameBuffer == -1);
            glGenFramebuffers(1, &_gl.frameBuffer);
            glBindFramebuffer(GL_FRAMEBUFFER, _gl.frameBuffer);

            // The texture we're going to render to 
            EMC_VERIFY(_gl.renderedTexture == -1);
            glGenTextures(1, &_gl.renderedTexture);
            glBindTexture(GL_TEXTURE_2D, _gl.renderedTexture);
            if (_isShadowMap == false) {
                // Give an empty image to OpenGL (the last "0")
                glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, _maxWidth, _maxHeight, 0,GL_RGB, GL_UNSIGNED_BYTE, 0);

                // Poor filtering
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

                // The depth buffer
                EMC_VERIFY(_gl.depthRenderBuffer == -1);
                glGenRenderbuffers(1, &_gl.depthRenderBuffer);
                glBindRenderbuffer(GL_RENDERBUFFER, _gl.depthRenderBuffer);
                glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, _maxWidth, _maxHeight);
                glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _gl.depthRenderBuffer);

                // Set "renderedTexture" as our colour attachement #0
                glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _gl.renderedTexture, 0);

                // Set the list of draw buffers.
                GLenum drawBuffers[1] = {GL_COLOR_ATTACHMENT0};
                glDrawBuffers(1, drawBuffers); // "1" is the size of DrawBuffers
            } else {
                // Depth texture. Slower than a depth buffer, but you can sample it later in your shader
                glTexImage2D(GL_TEXTURE_2D, 0,GL_DEPTH_COMPONENT16, _maxWidth, _maxHeight, 0,GL_DEPTH_COMPONENT, GL_FLOAT, 0);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
                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_COMPARE_FUNC, GL_LEQUAL);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);

                glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, _gl.renderedTexture, 0);

                // No color output in the bound framebuffer, only depth.
                glDrawBuffer(GL_NONE);
            }

            // Always check that our framebuffer is ok
            if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
                EMC_ERROR("Failed.");
                return;
            }

            // Shader
            if (_isShadowMap == false) {
                _shader = EMC_NEW Shader(
                    appBase,
                    engineDataDir(_PROJECT_NAME) + "/shaders/render-to-texture.vert",
                    engineDataDir(_PROJECT_NAME) + "/shaders/render-to-texture.frag"
                );
            }
            else {
                _shader = EMC_NEW Shader(
                    appBase,
                    engineDataDir(_PROJECT_NAME) + "/shaders/shadow-map-depth.vert",
                    engineDataDir(_PROJECT_NAME) + "/shaders/shadow-map-depth.frag"
                );
            }
        #endif
    }
    else if (g_renderingApi == RENDERINGAPI_VULKAN) {
        #ifdef USE_VULKAN
            // TODO
        #endif
    }
    else if (g_renderingApi == RENDERINGAPI_DIRECTX12) {
        #ifdef USE_DIRECTX
            // TODO
        #endif
    }
}

void RenderToTexture::makeData() {
    if (g_renderingApi == RENDERINGAPI_OPENGL) {
        #ifdef USE_OPENGL
            GLfloat TMP_vertexBufferData[] = { 
                -1.0f, -1.0f, 0.0f,
                1.0f, -1.0f, 0.0f,
                0.0f,  1.0f, 0.0f,
            };

			glGenBuffers(1, &_draft_vertexBuffer);
			glBindBuffer(GL_ARRAY_BUFFER, _draft_vertexBuffer);
			glBufferData(GL_ARRAY_BUFFER, sizeof(TMP_vertexBufferData), TMP_vertexBufferData, GL_STATIC_DRAW);
		#endif
	}
}

void RenderToTexture::destroy() {
    // TODO: Destroy shader.
    if (g_renderingApi == RENDERINGAPI_OPENGL) {
        #ifdef USE_OPENGL
            glDeleteFramebuffers(1, &_gl.frameBuffer); _gl.frameBuffer = -1;
            if (_isShadowMap == false) {
                // TODO: Delete a few things.
            }
            else {
                // TODO: Delete a few things.
            }
        #endif
    }
    else if (g_renderingApi == RENDERINGAPI_VULKAN) {
        #ifdef USE_VULKAN
            // TODO
        #endif
    }
    else if (g_renderingApi == RENDERINGAPI_DIRECTX12) {
        #ifdef USE_DIRECTX
            // TODO
        #endif
    }
}


//****************************************************************************************************
// main()
//****************************************************************************************************

int main(void) {
	// Initialise GLFW
	if( !glfwInit() )
	{
		fprintf( stderr, "Failed to initialize GLFW\n" );
		getchar();
		return -1;
	}

	glfwWindowHint(GLFW_SAMPLES, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	// Open a window and create its OpenGL context
	int windowWidth = 1024;
	int windowHeight = 768;
	window = glfwCreateWindow( windowWidth, windowHeight, "Render To Texture", NULL, NULL);
	if( window == NULL ){
		fprintf( stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n" );
		getchar();
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);

	// Initialize GLEW
	glewExperimental = true; // Needed for core profile
	if (glewInit() != GLEW_OK) {
		fprintf(stderr, "Failed to initialize GLEW\n");
		getchar();
		glfwTerminate();
		return -1;
	}

	// Ensure we can capture the escape key being pressed below
	glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);

	//--------------------------------------------------

	AppBase *appBase = new AppBase();
	RenderToTexture rtt;
	rtt.init(appBase, false, 1024, 1024);
	rtt.makeData();
	GLuint rtt_texID = glGetUniformLocation(rtt._shader->_gl.programID, "renderedTexture");

	// Dark blue background
	glClearColor(0.0f, 0.0f, 0.4f, 0.0f);

	GLuint VertexArrayID;
	glGenVertexArrays(1, &VertexArrayID);
	glBindVertexArray(VertexArrayID);

	// Create and compile our GLSL program from the shaders
	Shader *shader = new Shader(
		appBase,
		engineDataDir(_PROJECT_NAME) + "/shaders/SimpleVertexShader.vertexshader",
		engineDataDir(_PROJECT_NAME) + "/shaders/SimpleFragmentShader.fragmentshader"
	);

	static const GLfloat g_vertex_buffer_data[] = { 
		-0.9f,-0.9f,0.0f, 0.0f,0.0f,1.0f,  0.0f,0.0f,
		 0.9f,-0.9f,0.0f, 0.0f,0.0f,1.0f,  1.0f,0.0f,
		-0.9f, 0.9f,0.0f, 0.0f,0.0f,1.0f,  0.0f,1.0f,

		 0.9f,-0.9f,0.0f, 0.0f,0.0f,1.0f,  1.0f,0.0f,
		 0.9f, 0.9f,0.0f, 0.0f,0.0f,1.0f,  1.0f,1.0f,
		-0.9f, 0.9f,0.0f, 0.0f,0.0f,1.0f,  0.0f,1.0f,
	};

	GLuint vertexbuffer;
	glGenBuffers(1, &vertexbuffer);
	glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
	glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

	do {
		// Render to texture
		if (DEBUG_RENDER_TO_TEXTURE) {
			glBindFramebuffer(GL_FRAMEBUFFER, (DEBUG_RTT_ON_DEFAULT_FRAME_BUFFER == 0) ? rtt._gl.frameBuffer : 0);
			glViewport(0, 0, rtt._width, rtt._height);

			if (rtt._isShadowMap == false) {
				// .
			}
			else {
				glEnable(GL_CULL_FACE);
				glCullFace(GL_BACK);
			}

			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

			PVMStruct PVM;
			PVM.projection = glm::mat4(1);
			PVM.view       = glm::mat4(1);
			PVM.model      = glm::mat4(1);
			rtt._shader->gl_useProgram(PVM);

			rtt._shader->gl_beginAttributes_pos();
				glBindBuffer(GL_ARRAY_BUFFER, rtt._draft_vertexBuffer);
				GLsizei numVertices = 3 * (1);
				glDrawArrays(GL_TRIANGLES, 0, numVertices);
			rtt._shader->gl_endAttributes();
		}

		// Render the scene
		if (DEBUG_RENDER_SCENE) {
			glBindFramebuffer(GL_FRAMEBUFFER, 0);
			glViewport(0, 0, windowWidth, windowHeight);

			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

			// Use our shader
			PVMStruct PVM;
			PVM.projection = glm::mat4(1.0f);
			PVM.view       = glm::mat4(1.0f);
			PVM.model      = glm::mat4(1.0f);
			shader->gl_useProgram(PVM);

			// The rendered texture
			glActiveTexture(GL_TEXTURE0);
			glBindTexture(GL_TEXTURE_2D, rtt._gl.renderedTexture);
			glUniform1i(rtt_texID, 0);

			// Draw
			shader->gl_beginAttributes_pos_nor_uv();
				glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
				int numVertices = 3 * (2);
				glDrawArrays(GL_TRIANGLES, 0, numVertices);
			shader->gl_endAttributes();
		}

		// Swap buffers
		glfwSwapBuffers(window);
		glfwPollEvents();
	} // Check if the ESC key was pressed or the window was closed
	while( glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS &&
		   glfwWindowShouldClose(window) == 0 );

	// Cleanup VBO
	glDeleteBuffers(1, &vertexbuffer);
	glDeleteVertexArrays(1, &VertexArrayID);
	delete shader; shader = nullptr;

	// Close OpenGL window and terminate GLFW
	glfwTerminate();

	return 0;
}

I finally I found a better method, use the official tutorial and modify it line per line until reach the structure I want. I agree that my previous post is not perfect. So I will always use official tutorials, in that case, I will get better performance and better ideas.

// Render to texture
{
    glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
    glViewport(0,0,windowWidth,windowHeight); // Render on the whole framebuffer, complete from the lower left corner to the upper right

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    shader->gl_useProgram();

    // Compute the MVP matrix from keyboard and mouse input
    computeMatricesFromInputs();
    glm::mat4 ProjectionMatrix = getProjectionMatrix();
    glm::mat4 ViewMatrix = getViewMatrix();
    glm::mat4 ModelMatrix = glm::mat4(1.0);
    glm::mat4 MVP = ProjectionMatrix * ViewMatrix * ModelMatrix;
    shader->setMVP(MVP);
    shader->setModelMatrix(ModelMatrix);
    shader->setViewMatrix(ViewMatrix);

    glm::vec3 lightPos = glm::vec3(4,4,4);
    glUniform3f(LightID, lightPos.x, lightPos.y, lightPos.z);

    // Bind our texture in Texture Unit 0
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, Texture);
    // Set our "myTextureSampler" sampler to use Texture Unit 0
    glUniform1i(TextureID, 0);

    shader->gl_attributes_pos_nor_uv_index(vertexBuffer, normalBuffer, uvBuffer, indexBuffer, indices);
}

// Render to the screen
{
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glViewport(0, 0, windowWidth, windowHeight);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    quadShader->gl_useProgram();

    // Bind our texture in Texture Unit 0
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, renderedTexture);
    quadShader->setRenderedTextureUnit(0); // Set our "renderedTexture" sampler to use Texture Unit 0
    quadShader->setTime(10.0f * glfwGetTime());

    {
        // Step 1: glBindBuffer() must be called before glVertexAttribPointer().
        glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer);

        // Step 2:
        quadShader->gl_beginAttributes_pos();
            glDrawArrays(GL_TRIANGLES, 0, 3 * (2)); // 3*2 indices starting at 0 --> 2 triangles
        quadShader->gl_endAttributes();
    }

    // Swap buffers
    glfwSwapBuffers(window);
    glfwPollEvents();
}