Jump to content
  • Advertisement
Sign in to follow this  
fathoni

OpenGL Accessing Multiple FBO Textures using GLSL

This topic is 2600 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

[font="Arial"][color="#333333"]Hi, I'm an OpenGL newbie and I have posted a question here about this question,but i am stuck and i could not get an answer. I think FBO Multiple Texture access using GLSL is pretty tough topics for newbies, therefore, I really want to find the answer. I'm sorry if it is not appropriate to post this question here . However, I have modified some codes here and provide the VS2008 project file in this post.

I have created 4 textures, each consist of teapot, torus, tetrahedron, and octahedron. I attached all of these textures to FBO, and named them fbo_texture0 - fbo_texture3. Here are the screenshot of the successfully created texture:[/font]

[font="Trebuchet MS"][color="#333333"][size="4"]7SXy7.png
[/font]

[font="Arial"][color="#333333"]I intend to generate 5th texture (fbo_texture4), the idea is to make this texture from the existing textures (fbo_texture0-fbo_texture3) using GLSL. I intend to access the pixels from fbo_texture0 and input them in the new texture ([color="#333333"]fbo_texture4[color="#333333"]) by attaching them to the GL_COLOR_ATTACHMENT. However, this is all i got:[/font]

[font="Trebuchet MS"][color="#333333"][size="4"]2mxom.png
[/font]

[color="#333333"][font="Arial"]Here is my vertex shader and fragment shader codes, respectively: [/font]

[font="Trebuchet MS"][color="#333333"][size="4"][/font]
[font="Trebuchet MS"][color="#333333"][size="4"]void main(void)
{
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[1] = gl_MultiTexCoord1;
gl_TexCoord[2] = gl_MultiTexCoord2;
gl_TexCoord[3] = gl_MultiTexCoord3;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
[/font]

[font="Trebuchet MS"][color="#333333"][size="4"]
uniform sampler2D tex0;
uniform sampler2D tex1;
uniform sampler2D tex2;
uniform sampler2D tex3;

void main(void)
{
vec4 texval0 = texture2D(tex0, vec2(gl_TexCoord[0]));
vec4 texval1 = texture2D(tex1, vec2(gl_TexCoord[1]));
vec4 texval2 = texture2D(tex2, vec2(gl_TexCoord[2]));
vec4 texval3 = texture2D(tex3, vec2(gl_TexCoord[3]));

gl_FragData[0] = texval0;
gl_FragData[1] = texval1;
gl_FragData[2] = texval2;
gl_FragData[3] = texval3;
gl_FragData[4] = texval0;
[/font]

[color="#333333"]
[font="Arial"]My questions are:

[/font]
  • [font="Arial"]How can i access these fbo textures in GLSL?[/font]
  • [font="Arial"]How can i input the 1st texture to the 5th texture and display them correctly?[/font][font="Arial"]The following is the program's source:[/font]

    [font="Trebuchet MS"][color="#333333"][size="4"]
    #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;
    GLint pixelDataSize;

    static const GLenum fboBuffs[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4 };

    int window_width = 500; // The width of our window
    int window_height = 500; // The height of our window

    void ShutdownRC(void)
    {
    // Make sure default FBO is bound
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
    glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);

    // Cleanup textures
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, 0);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, 0);

    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, 0);

    glActiveTexture(GL_TEXTURE3);
    glBindTexture(GL_TEXTURE_2D, 0);

    glDeleteTextures(1, &fbo_texture0);
    glDeleteTextures(1, &fbo_texture1);
    glDeleteTextures(1, &fbo_texture2);
    glDeleteTextures(1, &fbo_texture3);

    // Cleanup RBOs
    glDeleteRenderbuffers(1, &fbo_depth);

    // Cleanup FBOs
    glDeleteFramebuffers(1, &fbo);
    }


    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) {

    glEnable(GL_DEPTH_TEST);
    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_DRAW_FRAMEBUFFER, fbo); // Bind our frame buffer
    glDrawBuffers(4, fboBuffs);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glFramebufferTexture2D(GL_DRAW_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_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, fbo_texture1, 0);
    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, fbo_texture2, 0);
    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, fbo_texture3, 0);
    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT4, GL_TEXTURE_2D, fbo_texture4, 0);
    glFramebufferRenderbuffer(GL_DRAW_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_DRAW_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

    pixelDataSize = window_width*window_height*3*sizeof(unsigned int); // XXX This should be unsigned byte
    void* data = (void*)malloc(pixelDataSize);
    memset(data, 0x00, pixelDataSize);

    glActiveTexture(GL_TEXTURE0);
    //glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, fbo_texture0);
    glUniform1i(glGetUniformLocation(shaderProgram, "tex0"), 0);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
    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_RGB, window_width, window_height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);

    //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);
    glUniform1i(glGetUniformLocation(shaderProgram, "tex0"), 0);
    glUniform1i(glGetUniformLocation(shaderProgram, "tex1"), 1);
    glUniform1i(glGetUniformLocation(shaderProgram, "tex2"), 2);
    glUniform1i(glGetUniformLocation(shaderProgram, "tex3"), 3);
    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);
    }

    glutReshapeFunc(reshape); // Tell GLUT to use the method "reshape" for reshaping
    glutDisplayFunc(display); // Tell GLUT to use the method "display" for rendering
    glutIdleFunc(display); // Tell GLUT to use the method "display" for rendering
    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

    initShader();
    init();

    glutMainLoop(); // Enter GLUT's main loop

    ShutdownRC();
    return 0;

    }[/font]
    [font="'Trebuchet MS"][color="#333333"]

    [/font]


    [font="Arial"][color="#333333"]The visual studio project is attached. And for note, in the attachment, other than glActiveTexture([color="#333333"](GL_TEXTURE0);[color="#333333"] other [color="#333333"]glActiveTexture() [color="#333333"]commands is disabled. This is because there are some inconsistencies when other [color="#333333"]glActiveTexture() is activated.

    [color="#333333"]Thanks in advance, again, I'm sorry if this post is improper.[/font]




Share this post


Link to post
Share on other sites
Advertisement
I'm not an expert in GLSL but I've noted an inconsistence in your code:
-here line 139




glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); // Bind our frame buffer
glDrawBuffers(4, fboBuffs);




shouldn't be glDrawBuffers([color="#ff0000"]5, fboBuffs); instead?

Also other parts of your code handle only 4 textures when accessing 5.

Share this post


Link to post
Share on other sites
It looks like your multiple output shader is active the whole time, even when you're only rendering to one attachment of the fbo?

Share this post


Link to post
Share on other sites
Can you explain a little bit more which textures you're trying to read from, and which one you're trying to write to?

If you just want to read from the 4 original textures, then you don't bind them to your FBO, you just bind them to texture samplers. Then you only write a single texture from your FBO.

It's not even legal to read and write the same texture in a fragment shader, which is what you're doing.

Share this post


Link to post
Share on other sites

I'm not an expert in GLSL but I've noted an inconsistence in your code:
-here line 139




glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); // Bind our frame buffer
glDrawBuffers(4, fboBuffs);



shouldn't be glDrawBuffers([color="#ff0000"]5, fboBuffs); instead?


Thanks! I have revised the code as you suggested.




Also other parts of your code handle only 4 textures when accessing 5.
[/quote]

I'm sorry i don't get your point, by handle 4 textures, do you mean only create 4 glActiveTexture() commands?

Thank you.

Share this post


Link to post
Share on other sites

It looks like your multiple output shader is active the whole time, even when you're only rendering to one attachment of the fbo?



Yes, I am trying to render to one color attachment, based on previous textures rendered.

Do you mean the textures active all the time? What i want to do is to make all the textures active so that i could access them in the shader. If activating all this textures is improper, do you have any suggestion to fix this problem?

Thank you.

Share this post


Link to post
Share on other sites

Can you explain a little bit more which textures you're trying to read from, and which one you're trying to write to?

What i am trying to do is to render 4 textures, (fbo_texture0-fbo_texture3) and attach them to GL_COLOR_ATTACHMENT0 - GL_COLOR_ATTACHMENT3 in the FBO.
I want to access these rendered textures in GLSL, and create another texture (attached to GL_COLOR_ATTACHMENT4) in the FBO, based on previously rendered texture.
In the fragment shader, i tried to copy the first texture (fbo_texture0) to the last texture (fbo_texture4).



If you just want to read from the 4 original textures, then you don't bind them to your FBO, you just bind them to texture samplers. Then you only write a single texture from your FBO.
[/quote]
These textures are rendered from a glut Function (e.g. glutSolidTeapot() ). From what i have understood, to display rendered texture and associate a texture with a color attachment, we need to bind them to FBO so we could display them correctly. So, what you're suggesting is not to attach fbo_texture0 - fbo_texture4 into FBO? But then i don't have any idea on how to associate GL_COLOR_ATTACHMENT0-GL_COLOR_ATTACHMENT4 with the fbo_texture0 - fbo_texture3.


It's not even legal to read and write the same texture in a fragment shader, which is what you're doing.
[/quote]
I think what I am doing (or what i want to do) is reading different textures, and writing to another texture using this previously created textures, but these textures are attached to the same FBO. Please give me a correction and further suggestion.

Thank you :)



Share this post


Link to post
Share on other sites
You'll have to either attach the 5th texture to a different FBO, or unattach the other 4 when you want to read from them. You cannot read from a texture that is also currently bound as a write-target.

What I think you want to do is this:

1) Create 2 FBO, one with 4 attachments, the other with 1 attachment.
2) Render to 4 textures using the first FBO.
3) Unbind FBO #1, bind FBO #2.
4) Attach the four originally rendered textures to texture samplers (via glBindTexture)
5) Render the 5th texture.

Share this post


Link to post
Share on other sites

You'll have to either attach the 5th texture to a different FBO, or unattach the other 4 when you want to read from them. You cannot read from a texture that is also currently bound as a write-target.

What I think you want to do is this:

1) Create 2 FBO, one with 4 attachments, the other with 1 attachment.


I have created 2 FBOs, one using GL_DRAW_FRAMEBUFFER target (for the one with 4 attachment, on line 138), one using GL_READ_FRAMEBUFFER target (for the one with 1 attachment, on line 157), given by the followin snippets:



glGenFramebuffers(1, &fbo_write); // Generate one frame buffer and store the ID in fbo

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_write); // Bind our frame buffer

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_texture0, 0);// Attach the texture fbo_texturen to the color buffer in our frame buffer
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, fbo_texture1, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, fbo_texture2, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, fbo_texture3, 0);
glFramebufferRenderbuffer(GL_DRAW_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_DRAW_FRAMEBUFFER, 0); // Unbind our frame buffer


glGenFramebuffers(1, &fbo_read); // Generate one frame buffer and store the ID in fbo

glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_read); // Bind our frame buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT4, GL_TEXTURE_2D, fbo_texture4, 0);
//glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo_depth); // Attach the depth buffer fbo_depth to our frame buffer
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_READ_FRAMEBUFFER, 0); // Unbind our frame buffer





2) Render to 4 textures using the first FBO.
[/quote]

Below is the code snippets on how i rendered these textures using FBO:



glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_write); // Bind our frame buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_texture0, 0);// Attach the texture fbo_texturen to the color buffer in our frame buffer
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, fbo_texture1, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, fbo_texture2, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, fbo_texture3, 0);
glFramebufferRenderbuffer(GL_DRAW_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_DRAW_FRAMEBUFFER, 0); // Unbind our frame buffer





glBindFramebuffer(GL_FRAMEBUFFER, fbo_write); // 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

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

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



3) Unbind FBO #1, bind FBO #2.
[/quote]

The FBO has been unbinded from the previous code snippet, and here is how i bind the second FBO:

glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_read); // Bind our frame buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT4, GL_TEXTURE_2D, fbo_texture4, 0);
//glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo_depth); // Attach the depth buffer fbo_depth to our frame buffer
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_READ_FRAMEBUFFER, 0); // Unbind our frame buffer




4) Attach the four originally rendered textures to texture samplers (via glBindTexture)
[/quote]

I think this part where the problem starts:



glActiveTexture(GL_TEXTURE0);
//glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, fbo_texture0);
glUniform1i(glGetUniformLocation(shaderProgram, "tex0"), 0);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
//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_RGB, window_width, window_height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);

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


When i activate more than one glActiveTexture commands, the problem arise, such as all the textures becomes black or all the textures drawn as the same object.


5) Render the 5th texture.
[/quote]

Here is how i tried to draw the 5th texture, and i expect this texture is obtained from gl_FragData[4] in the fragment shader:



glLoadIdentity();
glTranslatef(4.1f, 1.0f, -4.0f);

glBindTexture(GL_TEXTURE_2D, fbo_texture4); // Bind our frame buffer texture
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 textures

glutSwapBuffers();


Yet, here is what i got by only activating glActiveTexture(GL_TEXTURE1).

P3qri.png


I had no idea on what went wrong with the code. The revised version of the code is attached as a VS2008 Project:

Share this post


Link to post
Share on other sites
In the previous program, here is step by step that I did:

1. Initializing GLUT, DisplayMode , WindowSize, WindowPosition, CreatwWIndow
2. Initializing GLEW
3. Call GLUT reshape function callback
4. Call GLUT Display Function Callback
a. Use the shader program
b. Bind fbo_write
i. Set the viewport
ii. Set the lighting
iii. Render Teapot to to GL_COLOR_ATTACHMENT0
iv. Render Torus to to GL_COLOR_ATTACHMENT1
v. Render Tetrahedron to to GL_COLOR_ATTACHMENT2
vi. Render Octahedron to to GL_COLOR_ATTACHMENT3
c. Unbind fbo_write
d. Activate Texture0, Bind fbo_texture0, set the uniform sampler2d in the GLSL with texture ID 0
e. Activate Texture1, Bind fbo_texture1, set the uniform sampler2d in the GLSL with texture ID 1
f. Activate Texture2, Bind fbo_texture2, set the uniform sampler2d in the GLSL with texture ID 2
g. Activate Texture0, Bind fbo_texture3, set the uniform sampler2d in the GLSL with texture ID 3
h. Swap buffers
5. Call GLUT Idle Function Callback ? same as display function
6. Call GLUT Keyboard Function Callback - no specific key assignment here
7. Initializing Shader
a. Create, read, and compile vertex shader
b. Create, read, and compile fragment shader
c. Create shader program
d. Attach vertex & fragment shader to shader program
e. Link shader program
8. Call User-defined Init Fuction
a. Enable depth test
b. Generate, bind, and create render buffer storage for depth component named fbo_depth. Unbind render buffer
c. Generate textures0 ? textures4
d. Generate & Bind an FBO named fbo_write as GL_DRAW_FRAMEBUFFER
i. Attach Texture0 ? Texture3 into GL_COLOR_ATTACHMENT0-GL_COLOR_ATTACHMENT3.
ii. Attach fbo_depth into GL_DEPTH_ATTACHMENT
iii. Check FBO Completeness
e. Unbind FBO1
f. Generate & Bind FBO named fbo_read as GL_READFRAMEBUFFER
i. Attach Texture4 into GL_COLOR_ATTACHMENT4
ii. Check FBO completeness
g. Unbind fbo_read
9. Call glutMainLoop Function
10. Call Shutdown Function
a. Unbind GL_DRAW_FRAMEBUFFER and GL_READ_FRAMEBUFFER
b. Unbind the active textures
c. Delete Textures
d. Delete Render Buffers
e. Delete FBOs (fbo_write and fbo_read)
11. Return 0

Based on the above procedure, Is there any idea on where do i do it wrong and how can i fix the program so i could access the textures correctly using GLSL?
I really appreciate your time for staying with this question and thank you.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!