1
votes

I'm trying to learn displaying Texture using Shader program using OpenGL 3.30. I'm able to load the image but I can't get it displayed correctly on each cube's face.

The code below defines properties of a Vertex, which is its vertices and texture coordinates associated with each cube's vertex. Then 2 VBOs are created, one holds cube vertices and the other one holds textures information.

I think I'm very closed to the final result: I got the cube, loaded image successfully, able to get image information. So what did I do wrongly here?

I'm using stblib to load the image.

This is my code:

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stddef.h> /* must include for the offsetof macro */
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <stdlib.h>
#include "utils.h"

#include "stb_image.h"

struct Vertex {
    GLdouble position[3];
    GLdouble texture[2];
    };
/* These pointers will receive the contents of our shader source code files */
GLchar *vertexsource, *fragmentsource;
/* These are handles used to reference the shaders */
GLuint vertexshader, fragmentshader;
/* This is a handle to the shader program */
GLuint shaderprogram;
GLuint vbo[1]; /* Create handles for our Vertex Array Object and One Vertex Buffer Object */

//GLuint text_2d_1;

const struct Vertex plane[24] = {
                // Front Face
        {{1.0f,  1.0f, 1.0f}, {0.0, 0.0}},
        {{-1.0f,  1.0f, 1.0f}, {1.0, 0.0}},
        {{-1.0f, -1.0f, 1.0f}, {1.0, 1.0}},
        {{ 1.0f, -1.0f, 1.0f},  {0.0, 1.0}},
        // Back Face
        {{-1.0f, -1.0f, -1.0f}, {1.0, 0.0}},
        {{-1.0f,  1.0f, -1.0f}, {1.0, 1.0}},
        {{ 1.0f,  1.0f, -1.0f}, {0.0, 1.0}},
        {{ 1.0f, -1.0f, -1.0f}, {0.0, 0.0}},
        // Top Face
        {{-1.0f,  1.0f, -1.0f}, {0.0, 1.0}},
        {{-1.0f,  1.0f,  1.0f}, {0.0, 0.0}},
        {{ 1.0f,  1.0f,  1.0f}, {1.0, 0.0}},
        {{ 1.0f,  1.0f, -1.0f}, {1.0, 1.0}},
        // Bottom Face
        {{-1.0f, -1.0f, -1.0f}, {1.0, 1.0}},
        {{ 1.0f, -1.0f, -1.0f}, {0.0, 1.0}},
        {{ 1.0f, -1.0f,  1.0f}, {0.0, 0.0}},
        {{-1.0f, -1.0f,  1.0f}, {1.0, 0.0}},
        // Right face
        {{1.0f, -1.0f, -1.0f}, {1.0, 0.0}},
        {{1.0f,  1.0f, -1.0f}, {1.0, 1.0}},
        {{1.0f,  1.0f,  1.0f}, {0.0, 1.0}},
        {{1.0f, -1.0f,  1.0f}, {0.0, 0.0}},
        // Left Face
        {{-1.0f, -1.0f, -1.0f}, {0.0, 0.0}},
        {{-1.0f, -1.0f,  1.0f}, {1.0, 0.0}},
        {{-1.0f,  1.0f,  1.0f}, {1.0, 1.0}},
        {{-1.0f,  1.0f, -1.0f}, {0.0, 1.0}}

};

void SetupGeometry() {
    /* Allocate and assign One Vertex Buffer Object to our handle */
    glGenBuffers(1, vbo);
    /* Bind our VBO as being the active buffer and storing vertex attributes (coordinates + colors) */
    glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
    /* Copy the vertex data from plane to our buffer */
    /* 12 * sizeof(GLfloat) is the size of the tetrahedrom array, since it contains 12 Vertex values */
    glBufferData ( GL_ARRAY_BUFFER, 24 * sizeof ( struct Vertex ), plane, GL_STATIC_DRAW );
    /* Specify that our coordinate data is going into attribute index 0, and contains three doubles per vertex */
    /* Note stride = sizeof ( struct Vertex ) and pointer = ( const GLvoid* ) 0 */
    glVertexAttribPointer ( ( GLuint ) 0, 3, GL_DOUBLE, GL_FALSE,  sizeof ( struct Vertex ), ( const GLvoid* ) offsetof (struct Vertex,position) );
    /* Enable attribute index 0 as being used */
    glEnableVertexAttribArray(0);
    /* Specify that our color data is going into attribute index 1, and contains three floats per vertex */
    /* Note stride = sizeof ( struct Vertex ) and pointer = ( const GLvoid* ) ( 3 * sizeof ( GLdouble ) ) i.e. the size (in bytes)
     occupied by the first attribute (position) */
    glVertexAttribPointer ( ( GLuint ) 1, 3, GL_FLOAT, GL_FALSE, sizeof ( struct Vertex ), ( const GLvoid* ) offsetof(struct Vertex,texture) ); // bug );
    /* Enable attribute index 1 as being used */
    glEnableVertexAttribArray ( 1 );/* Bind our fourth VBO as being the active buffer and storing vertex attributes (texture) */
    printf("vertex %d\n", offsetof(struct Vertex,position));
    printf("texture %d\n", offsetof(struct Vertex,texture));
}

void SetupShaders(void) {
    /* Read our shaders into the appropriate buffers */
    vertexsource = filetobuf("plane/plane.vert");
    fragmentsource = filetobuf("plane/plane.frag");
    /* Assign our handles a "name" to new shader objects */
    vertexshader = glCreateShader(GL_VERTEX_SHADER);
    fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
    /* Associate the source code buffers with each handle */
    glShaderSource(vertexshader, 1, (const GLchar**)&vertexsource, 0);
    glShaderSource(fragmentshader, 1, (const GLchar**)&fragmentsource, 0);
    /* Compile our shader objects */
    glCompileShader(vertexshader);
    glCompileShader(fragmentshader);
    /* Assign our program handle a "name" */
    shaderprogram = glCreateProgram();
    glAttachShader(shaderprogram, vertexshader);/* Attach our shaders to our program */
    glAttachShader(shaderprogram, fragmentshader);
    glBindAttribLocation(shaderprogram, 0, "in_Position"); /* Bind attribute 0 (coordinates) to in_Position and attribute 1 (colors) to in_Texture */
    glBindAttribLocation(shaderprogram, 1, "in_Texture");
    glLinkProgram(shaderprogram);/* Link our program, and set it as being actively used */
    glUseProgram(shaderprogram);
    }

void Render(int i) {
    GLfloat angle;
    glm::mat4 Projection = glm::perspective(45.0f, 1.0f, 0.1f, 100.0f);
    angle = (GLfloat) (i % 360);
    glm::mat4 View = glm::mat4(1.);
    View = glm::translate(View, glm::vec3(0.f, 0.f, -5.0f));
    View = glm::rotate(View, angle * -1.0f, glm::vec3(1.f, 0.f, 0.f));
    View = glm::rotate(View, angle * 0.5f, glm::vec3(0.f, 1.f, 0.f));
    View = glm::rotate(View, angle * 0.5f, glm::vec3(0.f, 0.f, 1.f));
    glm::mat4 Model = glm::mat4(1.0);
    glm::mat4 MVP = Projection * View * Model;
    glUniformMatrix4fv(glGetUniformLocation(shaderprogram, "mvpmatrix"), 1, GL_FALSE, glm::value_ptr(MVP));
    /* Bind our modelmatrix variable to be a uniform called mvpmatrix in our shaderprogram */
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glDrawArrays(GL_QUADS, 0, 24);
    /* Invoke glDrawArrays telling that our data consists of individual triangles */
}

int reverse = 1;

static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
    if ((key == GLFW_KEY_ESCAPE || key == GLFW_KEY_Q) && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
    if ((key == GLFW_KEY_R) && action == GLFW_PRESS)
        reverse = 1 - reverse; // togrls reverse from 0 to 1 to o to ...
    }


int main( void ) {
    int k = 0;
    GLFWwindow* window;
    if( !glfwInit() ) {
            printf("Failed to start GLFW\n");
            exit( EXIT_FAILURE );
            }
    window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
    if (!window) {
            glfwTerminate();
            printf("GLFW Failed to start\n");
            return -1;
            }
    /* Make the window's context current */
    glfwMakeContextCurrent(window); // IMPORTANT: Must be done so glew recognises OpenGL
    glewExperimental = GL_TRUE;
    int err = glewInit();
    if (GLEW_OK != err) {
            /* Problem: glewInit failed, something is seriously wrong. */
            fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
            }
    fprintf(stderr, "Glew done\n");
    glfwSetKeyCallback(window, key_callback);
    fprintf(stderr, "GL INFO %s\n", glGetString(GL_VERSION));
    SetupGeometry();
    SetupShaders();
    int w, h, n, O;
    char *filename = "plane/rock.bmp";
    unsigned char * data = stbi_load(filename, &w, &h, &n, 0); //image data
    if(data == NULL) {
        print("Image not loaded");
        const char *error = stbi_failure_reason();
        printf("Failure reason %s\n", error);
        exit(0);
    }else{
        print("Image loaded successfully");
    }
    printf("Image Stats %d %d %d\n", w, h, n);
//    for(int d = 0; d < w * h * 3; d++)
//        printf("img content: %i\n",data[d]);
    printf (" first 4 bytes are: %i %i %i %i\n", data[ 0], data[ 1], data[ 2], data[ 3] );

    GLuint tex;
    glGenTextures(1, &tex);
    Print("Texture");
    Print(GL_TEXTURE0);
    print((int) tex);
    //glActiveTexture(GL_TEXTURE0);
    /*
     * To get the texture to activate, take the base texture GL_TEXTURE0 and add the value of the generated texture.
     * This needs checking with more than one texture.
     * beware of NIDIA specific.
     */
     //glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_BGR, GL_UNSIGNED_BYTE, data);
    glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    stbi_image_free(data); // free up image data
    //glDisable(gl_te)

    glEnable(GL_DEPTH_TEST);
    glClearColor(0.0, 0.0, 0.0, 1.0);/* Make our background black */
    while( !glfwWindowShouldClose(window) ) {// Main loop
        Render(k);// OpenGL rendering goes here...
            if(reverse)
                k--;
            else
                k++;

        Sleep(20);
        glfwSwapBuffers(window);// Swap front and back rendering buffers
        glfwPollEvents();
        }
    glfwTerminate();// Close window and terminate GLFW
    exit( EXIT_SUCCESS );// Exit program
    }

The vertex shader:

#version 330
precision highp float;
in vec3 in_Position;
in vec2 in_Texture;

// mvpmatrix is the result of multiplying the model, view, and projection matrices */
uniform mat4 mvpmatrix;

out vec2 UV;
void main(void) {
// Multiply the mvp matrix by the vertex to obtain our final vertex position
    gl_Position = mvpmatrix * vec4(in_Position, 1.0);
    UV = in_Texture;
}

Fragment shader:

#version 330
precision highp float;

in  vec2 UV;

out vec3 color;

uniform sampler2D myTexture;

void main(void) {
    color = texture(myTexture, UV).rgb;
}

This is input image: rock.bmp

This is output of the program: output

2

2 Answers

2
votes

The problem lies here:

/* Specify that our color data is going into attribute index 1,
   and contains three floats per vertex */
/* Note stride = sizeof ( struct Vertex ) and
   pointer = ( const GLvoid* ) ( 3 * sizeof ( GLdouble ) )
   i.e. the size (in bytes) occupied by the first attribute
   (position) */
glVertexAttribPointer ( ( GLuint ) 1, 3, GL_FLOAT, GL_FALSE, sizeof ( struct Vertex ), ( const GLvoid* ) offsetof(struct Vertex,texture) );

The vertex texture coordinate attribute has two elements, but you're telling OpenGL that there were 3 elements in succession. This obviously neither matches your vertex data nor the shader vertex attribute type. Adjust the element size to 2 and it should work. Oh, and you don't have to explicitly cast a number literal to GLuint.

2
votes

There is some inconsistency in the data types you are using. The declaration of your struct uses doubles for both position and texture coordinates:

struct Vertex {
    GLdouble position[3];
    GLdouble texture[2];
};

During attribute setup, you're using different types:

glVertexAttribPointer ( ( GLuint ) 0, 3, GL_DOUBLE, GL_FALSE,  sizeof ( struct Vertex ), ( const GLvoid* ) offsetof (struct Vertex,position) );
glVertexAttribPointer ( ( GLuint ) 1, 3, GL_FLOAT, GL_FALSE, sizeof ( struct Vertex ), ( const GLvoid* ) offsetof(struct Vertex,texture) ); // bug );

The texture coordinates are used as GL_FLOAT here. Also, you specify 3 components here, where the struct has only 2.

There's very rarely a good reason to use doubles for coordinates. OpenGL will use 32-bit precision internally anyway. So if you specify attributes as doubles, you will only add conversions, and use more memory.

To use float for everything, the struct should be declared like this:

struct Vertex {
    GLfloat position[3];
    GLfloat texture[2];
};

The attribute specification then looks like this:

glVertexAttribPointer ( ( GLuint ) 0, 3, GL_FLOAT, GL_FALSE,  sizeof ( struct Vertex ), ( const GLvoid* ) offsetof (struct Vertex,position) );
glVertexAttribPointer ( ( GLuint ) 1, 2, GL_FLOAT, GL_FALSE, sizeof ( struct Vertex ), ( const GLvoid* ) offsetof(struct Vertex,texture) ); // bug );