OpenGL Rendering Image weird

I am experiencing issues with OpenGL with loading images, I have tried several images however the colour is not displaying correctly and the texture is displaying offset.

I was following code from Learnopengl.com to load images with the help of SDL2 and have even gone as far to copy and paste code after a couple days of frustration I was wondering if this has anything to do with the OpenGL State machine or It is purely my lack of experience.

I am trying to segregate my image loading code into a Texture Manager class. I was wondering if that is where I am at fault?

Display Output

#include <iostream>
#include <Window/Window.hpp>
#include <Shaders/Shader.hpp>
#include <Managers/TextureManager.hpp>


int main(int argc, char* argv[]) {
    Kaim::Window window("Game", 1080, 720);

    Kaim::Shader shader("Assets/shaders/basic/vertex.glsl", "Assets/shaders/basic/fragment.glsl");
    Kaim::Manager::TextureManager textureManager;

        float vertices[] = {
        // positions          // colors           // texture coords
        0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f, // top right
        0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f, // bottom right
        -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f, // bottom left
        -0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f  // top left 
    };
    unsigned int indices[] = {
        0, 1, 3, // first triangle
        1, 2, 3  // second triangle
    };
    unsigned int 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 attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // color attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
    // texture coord attribute
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    glEnableVertexAttribArray(2);


    textureManager.LoadTexture("box", "Assets/images/Box.png");


    while (window.IsOpen()) {
        GLenum error = glGetError();
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                window.Close();
            }
        }
        window.Update(0.2f, 0.3f, 0.3f, 1.0f);

        shader.use();
        textureManager.Bind("box");
        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);


        window.Render();
    }
    std::cout << "Hello, World!" << std::endl;
    return 0;
}

TextureManager.cpp

#include "TextureManager.hpp"

namespace Kaim { namespace Manager {
TextureManager::TextureManager() {
    INFO("Texture Manager Created");
}
TextureManager::~TextureManager() {
    INFO("Texture Manager Destroyed");
}

void TextureManager::LoadTexture(std::string name, std::string path) {
    for(auto const& [key, val] : textures) {
        if (val.path == path) {
            WARNING("Texture with path %s already exists", path.c_str());
            return;
        }
        if(key == name) {
            WARNING("Texture with name %s already exists", name.c_str());
            return;
        }
    }

    SDL_Surface* surface = IMG_Load(path.c_str());
    if (surface == NULL) {
        ERROR("Failed to load image: %s", IMG_GetError());
        return;
    }
    unsigned int texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    
    // get number of channels in the SDL surface
    GLint nOfColors = surface->format->BytesPerPixel;
    unsigned int format;

    if (nOfColors == 4)     // contains an alpha channel
    {
            if (surface->format->Rmask == 0x000000ff)
                    format = GL_RGBA;
            else
                    format = GL_BGRA;
    } else if (nOfColors == 3)     // no alpha channel
    {
            if (surface->format->Rmask == 0x000000ff)
                    format = GL_RGB;
            else
                    format = GL_BGR;
    } else {
            ERROR("Image is not truecolor");
            return;
    }
    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);   // set texture wrapping to GL_REPEAT (default wrapping method)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // set texture filtering parameters
    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, format, surface->w, surface->h, 0, format, GL_UNSIGNED_BYTE, surface->pixels);
    
    glGenerateMipmap(GL_TEXTURE_2D);
    SDL_FreeSurface(surface);

    Texture textureStruct;
    textureStruct.id = texture;
    textureStruct.path = path;
    textures[name] = textureStruct;
}
void TextureManager::Bind(std::string path) {
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textures[path].id);
}
void TextureManager::Unbind() {
    glBindTexture(GL_TEXTURE_2D, 0);
}
}}

Vertex Shader

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;

out vec3 ourColor;
out vec2 TexCoord;

void main()
{
    gl_Position = vec4(aPos, 1.0);
    ourColor = aColor;
    TexCoord = vec2(aTexCoord.x, aTexCoord.y);
}

Fragment Shader

#version 330 core
out vec4 FragColor;

in vec3 ourColor;
in vec2 TexCoord;

// texture samplers
uniform sampler2D texture1;

void main()
{
    FragColor = texture(texture1, TexCoord);
}

Trying to display images from openGL from a texture manager class

EDIT:
Thought i should add the window class since that is where i Initalize everything

#include "Window.hpp"

namespace Kaim {
Window::Window(std::string title, int width, int height) {
    SDL_Init(SDL_INIT_EVERYTHING);
    IMG_Init(IMG_INIT_PNG | IMG_INIT_JPG);
    this->width = width;
    this->height = height;
    window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL);
    if (window == NULL) {
        ERROR("Failed to create window: %s", SDL_GetError());
        open = false;
        return;
    } 
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

    context = SDL_GL_CreateContext(window);
    if (glewInit() != GLEW_OK) {
        ERROR("OpenGL Could not Initalize");
        open = false;
    }

    glewExperimental = GL_TRUE;

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glViewport(0, 0, width, height);

    open = true;
}
void Window::Update(float r, float g, float b, float a) {
    glClearColor(r, g, b, a);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void Window::Render() {
    SDL_GL_SwapWindow(window);
}

SDL_Window* Window::GetWindow() {
    return this->window;
}
bool Window::IsOpen() {
    return this->open;
}
void Window::Close() {
    this->open = false;
}

Window::~Window() {
    SDL_GL_DeleteContext(context);
    SDL_DestroyWindow(this->window);
    IMG_Quit();
    SDL_Quit();
}
}

  • 1

    Rows of pixels in a surface aren’t guaranteed to be consecutive in memory, I believe?

    – 

  • However, it is assumed that the rows be aligned to 4 bytes by default. You can change this with glPixelStorei(GL_UNPACK_ALIGNMENT, 1)

    – 




  • You have to handle pitch of SDL_Surface. If pitch != width * bytesPerPixel you need to create deep copy of surface data tightly packed. Then it can be passed to glTexImage2D. Be careful with GL_UNPACK_ALIGNMENT, which was mentioned comment above.

    – 




It is assumed that the lines of an image are aligned to 4 bytes by default. You have to change this if you load an RGB image, because 3 color channels need only 3 bytes and 3 * width of the image may not be divisible by 4. Set the GL_UNPACK_ALIGNMENT parameter with glPixelStore before specifying the image (before glTexImage2D):

glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

Leave a Comment