raisa_
raisa_

Reputation: 604

How to Load Multiple Texture using OpenGL and C++?

Following my previous question here to swap image using OpenGL and Obj-C, I decided to also go with the original tutorial using C++ with shader and texture. What I'm trying to do is switching different texture. Here's my [UPDATED] code

#include <iostream>
#define  GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "SOIL2/SOIL2.h"
#include "Shader.h"

using namespace std;

//window
const GLuint WIDTH = 750, HEIGHT = 750;
int main( )
{
    glfwInit( );

    //GLFW
    glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 3 );
    glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 3 );
    glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE );
    glfwWindowHint( GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE ); //only for mac
    glfwWindowHint( GLFW_RESIZABLE, GL_FALSE );

    GLFWwindow *window = glfwCreateWindow( WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr );

    int screenWidth, screenHeight;
    glfwGetFramebufferSize( window, &screenWidth, &screenHeight );

    if ( nullptr == window )
    {
        cout << "Failed to create GLFW window" << endl;
        glfwTerminate( );
        return EXIT_FAILURE;
    }

    glfwMakeContextCurrent( window );

    //ModernOpenGL
    glewExperimental = GL_TRUE;

    if ( GLEW_OK != glewInit( ) )
    {
        cout << "Failed to initialize GLEW" << endl;
        return EXIT_FAILURE;
    }

    //viewport
    glViewport( 0, 0, screenWidth, screenHeight );

    //enable alpha
    glEnable( GL_BLEND );
    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

    //shaders
    Shader ourShader( "resources/shaders/core.vs", "resources/shaders/core.frag" );

    //vertices
    GLfloat vertices[] =
    {
        //position            //color              //texture Coords
        1.0f,  1.0f, 0.0f,    1.0f, 0.0f, 0.0f,    1.0f, 1.0f, // Top Right
        1.0f, -1.0f, 0.0f,    0.0f, 1.0f, 0.0f,    1.0f, 0.0f, // Bottom Right
        -1.0f, -1.0f, 0.0f,   0.0f, 0.0f, 1.0f,    0.0f, 0.0f, // Bottom Left
        -1.0f,  1.0f, 0.0f,   1.0f, 1.0f, 0.0f,    0.0f, 1.0f  // Top Left
    };

    GLuint indices[] =
    {
        0, 1, 3, // 1st triangle
        1, 2, 3  // 2nd triangle
    };

    GLuint VBO, VAO, EBO;
    glGenVertexArrays( 1, &VAO );
    glGenBuffers( 1, &VBO );
    glGenBuffers( 1, &EBO );

    glBindVertexArray( VAO );

    glBindBuffer( GL_ARRAY_BUFFER, VBO );
    glBufferData( GL_ARRAY_BUFFER, sizeof( vertices ), vertices, GL_STATIC_DRAW );

    glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, EBO );
    glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof( indices ), indices, GL_STATIC_DRAW );

    //position
    glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof( GLfloat ), ( GLvoid * ) 0 );
    glEnableVertexAttribArray(0);

    //color
    glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof( GLfloat ), ( GLvoid * )( 3 * sizeof( GLfloat )));
    glEnableVertexAttribArray(1);

    //texture Coord
    glVertexAttribPointer( 2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof( GLfloat ), ( GLvoid * )( 6 * sizeof( GLfloat )));
    glEnableVertexAttribArray( 2 );

    glBindVertexArray( 0 ); // unbind VAO



    //CREATE TEXTURE
    GLuint textures[2];
    glGenTextures(2, textures);

    int width, height;
    unsigned char * image;

    //texture1
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textures[0]);

    image = SOIL_load_image("resources/res/images/green.png", &width, &height, 0, SOIL_LOAD_RGBA);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
    SOIL_free_image_data(image);
    // glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);
    glBindTexture(GL_TEXTURE_2D, textures[0]);


    //texture2
    glActiveTexture(GL_TEXTURE1);
     glBindTexture(GL_TEXTURE_2D, textures[1]);

     image = SOIL_load_image("resources/res/images/img1.png", &width, &height, 0, SOIL_LOAD_RGBA);
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
     SOIL_free_image_data(image);
     glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);
     glBindTexture(GL_TEXTURE_2D, textures[1]);



    //parameters
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
    //filtering
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
    glGenerateMipmap( GL_TEXTURE_2D );


    //loop
    while ( !glfwWindowShouldClose( window ) )
    {
        glfwPollEvents( );

        //render
        glClearColor( 0.2f, 0.3f, 0.3f, 1.0f );
        glClear( GL_COLOR_BUFFER_BIT );

        //draw triangle
        //if
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, textures[0]);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);


        //draw container
        glBindVertexArray( VAO );
        glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0 );
        glBindVertexArray( 0 );

        glfwSwapBuffers( window );
    }

    //de-alocated stuff
    glDeleteVertexArrays( 1, &VAO );
    glDeleteBuffers( 1, &VBO );
    glDeleteBuffers( 1, &EBO );

    glfwTerminate( );
    return EXIT_SUCCESS;
}

And here's the texture frag

    #version 330 core
    in vec3 ourColor;
    in vec2 TexCoord;

    out vec4 color;

    uniform sampler2D ourTexture1;
    uniform sampler2D ourTexture2;
    uniform int usetexture = 0;

    void main()
    {
        if (usetexture == 0)
            color = texture(ourTexture1, TexCoord);
        if (usetexture == 1)
            color = texture(ourTexture1, TexCoord);
    }

How do I call them properly and make them switch ? Even one texture doesn't show. I want to change between img1 and img2 from SOIL load image. But has been stuck. Any advice ?

Upvotes: 1

Views: 3875

Answers (1)

Rabbid76
Rabbid76

Reputation: 210977

You have to bind the 1st texture to texture unit 0 (GL_TEXTURE0) and the 2nd texture to texture unit 1 (GL_TEXTURE1):

GLuint textures[2];
int width, height;
unsigned char * image;

glGenTextures(2, textures);

// testure 1
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures[0]);

glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

image = SOIL_load_image("resources/res/images/green.png", &width, &height, 0, SOIL_LOAD_RGBA);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
SOIL_free_image_data(image);

// texture 2
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textures[1]);

glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

image = SOIL_load_image("resources/res/images/red.png", &width, &height, 0, SOIL_LOAD_RGBA);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
SOIL_free_image_data(image);

The values of the texture sampler uniforms ourTexture1 and ourTexture2, hav to be the numbers of the texture units 0 and 1. The value of the uniforms cam be set, after the shader program object is installed as a part of the current rendering state (glUseProgram):

// complie shaders and link shader program (glLinkProgram)
Shader ourShader( "resources/shaders/core.vs", "resources/shaders/core.frag" );
GLint tex1_loc = glGetUniformLocation( ourShader.Program, "ourTexture1" );
GLint tex2_loc = glGetUniformLocation( ourShader.Program, "ourTexture2" );

glUseProgram( ourShader.Program );
glUniform1i( tex1_loc, 0 );
glUniform1i( tex2_loc, 1 );

To choose/switch between the textures, I recommend to use the glsl function mix. The function interpolates between 2 values.
The 3rd parameter is a floating point value in range [0.0, 1.0], which defines the linear interpolation between the 1st and 2nd parameter. If the 3rd parameter of mix is 0.0, the the result of the function is the 1st parameter. If it is 1.0, then the result is the 2nd parameter.
This allows you to crate a mixture of both texture or to draw on of them completely.

The fragment shader may look like this:

#version 330 core
in vec3 ourColor;
in vec2 TexCoord;

out vec4 color;

uniform sampler2D ourTexture1;
uniform sampler2D ourTexture2;
uniform float mixtexture;

void main()
{
    vec4 color1 = texture(ourTexture1, TexCoord);
    vec4 color2 = texture(ourTexture2, TexCoord);
    color       = mix(color1, color2, mixtexture);
}

To test the code you can use the following loop, which creates a transition form ourTexture1 to ourTexture2:

GLint mix_loc = glGetUniformLocation( ourShader.Program, "mixtexture" ); 
GLfloat mix_value = 0.0; // in [0.0, 1.0]
while ( !glfwWindowShouldClose( window ) )
{
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glUniform1f(mix_loc, mix_value);
    mix_value = mix_value >= 1.0f ? 0.0f : mix_value + 0.01f;

    glBindVertexArray( VAO );
    glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0 );
    glBindVertexArray( 0 );

    glfwSwapBuffers( window );
    glfwPollEvents( );
}

Upvotes: 5

Related Questions