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?
#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();
}
}
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);
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. Ifpitch != width * bytesPerPixel
you need to create deep copy of surface data tightly packed. Then it can be passed toglTexImage2D
. Be careful withGL_UNPACK_ALIGNMENT
, which was mentioned comment above.