Acorn
Acorn

Reputation: 1165

OpenGL Texture not rendering

I'm currently trying to learn OpenGL. I've successfully rendered rainbow triangles to the screen, and I've successfully moved them about the screen. However, I can not seem to properly render a texture to two triangles.

Whenever I run this program, I am greeted with a black screen with nothing rendering. I've checked, and the image file is definately in the correct directory (it wouldn't run otherwise!)

define STB_IMAGE_IMPLEMENTATION

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <stb/stb_image.h>

int main()
{
    //Initialize glfw
    if (!glfwInit())
    {
        fprintf(stderr, "ERROR: Could not start GLFW3.\n");
        return 1;
    }

    //Create the rendering context
    GLFWwindow* window = glfwCreateWindow(480, 600, "Hello Rendering",
                                          nullptr, nullptr);

    if(!window)
    {
        fprintf(stderr, "ERROR: Could not create a rendering context with "
            "GLFW3.\n");
        return 1;
    }

    glfwMakeContextCurrent(window);

    //Start GLEW
    glewExperimental=GL_TRUE;
    GLenum err=glewInit();
    if(err!=GLEW_OK)
    {
        //Problem: glewInit failed, something is seriously wrong.
        std::cout<<"glewInit failed, aborting."<<std::endl;
    }



    ////////////////////////
    //Loading PNG
    ////////////////////////
    int x = 0;
    int y = 0;
    int n = 0;
    int force_channels = 4;
    unsigned char* image_data = stbi_load("spooky.png", &x, &y, &n,
                                          force_channels);
    if(!image_data)
    {
        fprintf(stderr, "ERROR: Could not load spooky.png\n.");
        return 1;
    }

    //NPOT Check
    if((x & (x - 1)) != 0 || (y & (y - 1)) != 0)
    {
        fprintf(stderr, "ERROR: Image is not a power of 2.\n");
        fprintf(stderr, "h: %d w: %d", y, x);
        return 1;
    }

    //Flip the image
    int width_in_bytes = x * 4;
    unsigned char* top = nullptr;
    unsigned char* bottom = nullptr;
    unsigned char temp = 0;
    int half_height = y / 2;

    for(int row = 0; row < half_height; row++)
    {
        top = image_data + row * width_in_bytes;
        bottom = image_data + (y - row - 1) * width_in_bytes;
        for(int col = 0; col < width_in_bytes; col++)
        {
            temp = *top;
            *top = *bottom;
            *bottom = temp;
            top++;
            bottom++;
        }
    }


    GLuint tex = 0;
    glGenTextures(1, &tex);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA,
                 GL_UNSIGNED_BYTE, image_data);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);


    //////////////////////////////////
    //Vertex shader
    //////////////////////////////////
    const char* vertShader =
        "#version 330 core"
        "layout(location = 0) in vec3 vertexPosition_modelspace;"
        "layout(location = 1) in vec2 vt;"
        "out vec2 texture_coordinates;"
        "void main() {"
        "   texture_coordinates = vt;"
        "   gl_Position.xyz = vertexPosition_modelspace;"
        "   gl_Position.w = 1.0;"
        "}";

    ///////////////////////////////////
    //Frag shader
    ///////////////////////////////////
    const char* fragShader =
        "#version 330 core"
        "uniform sampler2D basic_texture;"
        "out vec4 frag_color;"
        "void main() {"
        "   vec4 texel = texture(basic_texture, texture_coordinates);"
        "   frag_color = texel;"
        "}";


    ////////////////////////////////////
    //Create shader program
    ///////////////////////////////////
    GLuint vsp = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vsp, 1, &vertShader,  nullptr);
    glCompileShader(vsp);

    GLuint fsp = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fsp, 1, &fragShader, nullptr);
    glCompileShader(fsp);

    GLuint shader_program = glCreateProgram();
    glAttachShader(shader_program, fsp);
    glAttachShader(shader_program, vsp);
    glLinkProgram(shader_program);

    glUseProgram(shader_program);



    ////////////////////////////////////
    //Texture coordinates
    ////////////////////////////////////
    GLfloat texcoords[] = {
        0.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.0f,
        1.0f, 0.0f,
        1.0f, 1.0f,
        0.0f, 1.0f
    };

    GLuint vt_vbo;
    glGenBuffers(1, &vt_vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vt_vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof( texcoords), texcoords,
                 GL_STATIC_DRAW);

    GLuint vao;;
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    glBindBuffer(GL_ARRAY_BUFFER, vt_vbo);
    int dimensions = 2;
    glVertexAttribPointer(1, dimensions, GL_FLOAT, GL_FALSE, 0, nullptr);
    glEnableVertexAttribArray(1);

    while(!glfwWindowShouldClose(window))
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        //Use the shader
        glUseProgram(shader_program);
        //Draw the two triangles (6 points)
        glDrawArrays(GL_TRIANGLES, 0, 6);
        glfwPollEvents();
        glfwSwapBuffers(window);
        if(GLFW_PRESS == glfwGetKey(window, GLFW_KEY_ESCAPE))
        {
            glfwSetWindowShouldClose(window, 1);
        }
    }

    std::cout << "Hello, World!" << std::endl;
    return 0;
}

Upvotes: 1

Views: 2693

Answers (1)

Rabbid76
Rabbid76

Reputation: 210877

First of all, you should retrieve the error messages when compiling and linking your shader:

GLint status = GL_TRUE;
char error_msg[1024];
GLsizei read;

GLuint vsp = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vsp, 1, &vertShader,  nullptr);
glCompileShader(vsp);
glGetShaderiv( vsp, GL_COMPILE_STATUS, &status );
if ( status != GL_TRUE )
{
    glGetShaderInfoLog( vsp, 1024, &read, error_msg );
    std::cout << "compile error:" << std::endl << error_msg << std::endl;
}

GLuint fsp = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fsp, 1, &fragShader, nullptr);
glCompileShader(fsp);
glGetShaderiv( fsp, GL_COMPILE_STATUS, &status );
if ( status != GL_TRUE )
{
    glGetShaderInfoLog( fsp, 1024, &read, error_msg );
    std::cout << "compile error:" << std::endl << error_msg << std::endl;
}

GLuint shader_program = glCreateProgram();
glAttachShader(shader_program, fsp);
glAttachShader(shader_program, vsp);
glLinkProgram(shader_program);
glGetProgramiv( shader_program, GL_LINK_STATUS, &status );
if ( status != GL_TRUE )
{
    glGetProgramInfoLog( shader_program, 1024, &read, error_msg );
    std::cout << "compile error:" << std::endl << error_msg << std::endl;
}

The first error message you will get is somehow like this:

0(1) : error C0205: invalid profile "corelayout"
0(1) : error C0206: invalid token "" in version line

This is because you did not write the line ending "\n" after the first line of the shader code ("#version 330 core").

When you fixe this then you get the next error message:

0(2) : error C1008: undefined variable "texture_coordinates"

You have to add the declaration of the in variable texture_coordinates to the fragment shader.

in vec2 texture_coordinates;

The final code looks like this:

const char* vertShader =
    "#version 330 core\n"
    "layout(location = 0) in vec3 vertexPosition_modelspace;"
    "layout(location = 1) in vec2 vt;"
    "out vec2 texture_coordinates;"
    "void main() {"
    "   texture_coordinates = vt;"
    "   gl_Position.xyz = vertexPosition_modelspace;"
    "   gl_Position.w = 1.0;"
    "}";

const char* fragShader =
    "#version 330 core\n"
    "uniform sampler2D basic_texture;"
    "out vec4 frag_color;"
    "in vec2 texture_coordinates;"
    "void main() {"
    "   vec4 texel = texture(basic_texture, texture_coordinates);"
    "   frag_color = texel;"
    "}";


Of course you have to provide vertex data for the vertex attribute vertexPosition_modelspace:

GLfloat texcoords[] = {
    0.0f, 1.0f,
    0.0f, 0.0f,
    1.0f, 0.0f,
    1.0f, 0.0f,
    1.0f, 1.0f,
    0.0f, 1.0f
};

GLfloat vertex[] = {
    -1.0f,  1.0f, 0.0f,
    -1.0f, -1.0f, 0.0f,
     1.0f, -1.0f, 0.0f,
     1.0f, -1.0f, 0.0f,
     1.0f,  1.0f, 0.0f,
    -1.0f,  1.0f, 0.0f
};

GLuint vert_vbo;
glGenBuffers(1, &vert_vbo);
glBindBuffer(GL_ARRAY_BUFFER, vert_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof( vertex ), vertex, GL_STATIC_DRAW);

GLuint vt_vbo;
glGenBuffers(1, &vt_vbo);
glBindBuffer(GL_ARRAY_BUFFER, vt_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof( texcoords ), texcoords, GL_STATIC_DRAW);

GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vert_vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vt_vbo);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(1);


Finally you should set the index of the texture unit, wher the texture is bound to, to the texture sampler uniform variable basic_texture:

int texture_unit_index = 0;
GLuint tex = 0;
glGenTextures(1, &tex);
glActiveTexture(GL_TEXTURE0 + texture_unit_index );
glBindTexture(GL_TEXTURE_2D, tex);

.....

// to do after    glLinkProgram(shader_program);
int tex_sampler_loc = glGetUniformLocation( shader_program, "basic_texture" );

.....

// to do after    glUseProgram(shader_program);
glUniform1i( tex_sampler_loc, texture_unit_index );  

Upvotes: 1

Related Questions