c++openglglfw

OpenGL code can't display image correctly when image buffer size increases


I finished writing my first OpenGL code, and while it seems to work correctly when using a smaller input buffer that contains a red triangle, it doesn't work at all when using a bigger input buffer. The following buffers bellow display just the RED channel, so each 00 correspond to 00 00 00, and each FF correspond to FF 00 00.

Bellow is also my entire OpenGL code. I don't have any idea what could be wrong, and then better to shared everything.

The main calls Screen::initialize passing 500 and 500 for the window width and height, and then calls Screen::display_image passing 12 for both image width and height when using the small buffer, and 23 when using the bigger buffer.

Any idea what I'm doing wrong?

Small Buffer

00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 FF FF FF FF FF 00 
00 00 00 00 00 00 FF FF FF FF 00 00 
00 00 00 00 00 00 FF FF FF 00 00 00 
00 00 00 00 00 00 FF FF 00 00 00 00 
00 00 00 00 00 00 FF 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 

enter image description here

Big Buffer

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 FF FF 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 FF 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

enter image description here

#ifndef _SCREEN_H_
#define _SCREEN_H_

#include <GLFW/glfw3.h>
#include <stdint.h>
#include <string>

namespace Screen
{
    class Screen
    {
    public:
        Screen(){};
       ~Screen(){};

        bool initialize(uint32_t ScreenWidth, uint32_t ScreenHeight, std::string& Title);

        void display_image(uint8_t* ImageData, uint32_t ImageWidth, uint32_t ImageHeight);

        void deinitialize();

    private:
        GLFWwindow* _window                 = nullptr;
        uint32_t    _shader_program         = 0u;
        uint32_t    _object_vertex_array    = 0u;
        uint32_t    _object_vertex_buffer   = 0u;
        uint32_t    _object_element_buffer  = 0u;
        uint8_t*    _image_data             = nullptr;
    };
}

#endif // _SCREEN_H_

#include <GL/glew.h>
#include <stdint.h>
#include <string>

#include "Screen.h"


namespace Screen
{
    namespace
    {
        bool build_shader_vertex(uint32_t& ShaderVertex)
        {
            const char* source_shader_vertex =
                "#version 330 core\n"
                "layout (location = 0) in vec4 input_position;\n"
                "layout (location = 1) in vec2 input_texture;\n"
                "out vec2 output_texture;\n"
                "void main()\n"
                "{\n"
                    "gl_Position    = input_position;\n"
                    "output_texture = input_texture;\n"
                "}\n"
                "\0";

            ShaderVertex = glCreateShader(GL_VERTEX_SHADER);

            glShaderSource(ShaderVertex, 1, &source_shader_vertex, NULL);

            glCompileShader(ShaderVertex);

            GLint success = 0;

            glGetShaderiv(ShaderVertex, GL_COMPILE_STATUS, &success);

            if (!success)
            {
                fprintf(stderr, "%s:%d:%s: Failed to compile vertex shader\n", __FILE__, __LINE__, __FUNCTION__);
                return false;
            }

            return true;
        }


        bool build_shader_fragment(uint32_t& ShaderFragment)
        {
            const char* source_shader_fragment =
                "#version 330 core\n"
                "out vec4 fragment_color;\n"
                "in  vec2 output_texture;\n"
                "uniform sampler2D sampled_texture;\n"
                "void main()\n"
                "{\n"
                    "fragment_color = texture(sampled_texture, output_texture);\n"
                "}\n"
                "\0";

            ShaderFragment = glCreateShader(GL_FRAGMENT_SHADER);

            glShaderSource(ShaderFragment, 1, &source_shader_fragment, NULL);

            glCompileShader(ShaderFragment);

            GLint success = 0;

            glGetShaderiv(ShaderFragment, GL_COMPILE_STATUS, &success);

            if (!success)
            {
                fprintf(stderr, "%s:%d:%s: Failed to compile fragment shader\n", __FILE__, __LINE__, __FUNCTION__);
                return false;
            }

            return true;
        }


        bool build_program(uint32_t& ShaderProgram)
        {
            bool        ret_val         = true;
            uint32_t    shader_vertex   = 0u;
            uint32_t    shader_fragment = 0u;
            GLint       success         = 0;

            if (!build_shader_vertex(shader_vertex))
            {
                fprintf(stderr, "%s:%d:%s: Failed to build shader\n", __FILE__, __LINE__, __FUNCTION__);
                ret_val = false;
                goto terminate;
            }

            if (!build_shader_fragment(shader_fragment))
            {
                fprintf(stderr, "%s:%d:%s: Failed to build shader\n", __FILE__, __LINE__, __FUNCTION__);
                ret_val = false;
                goto terminate;
            }

            ShaderProgram = glCreateProgram();

            glAttachShader(ShaderProgram, shader_vertex);
            glAttachShader(ShaderProgram, shader_fragment);

            glLinkProgram(ShaderProgram);

            glGetProgramiv(ShaderProgram, GL_LINK_STATUS, &success);

            if (!success)
            {
                fprintf(stderr, "%s:%d:%s: Failed to link shader program\n", __FILE__, __LINE__, __FUNCTION__);
                ret_val = false;
                goto terminate;
            }

        terminate:
            glDeleteShader(shader_vertex);
            glDeleteShader(shader_fragment);

            return ret_val;
        }
    }


    bool Screen::initialize(uint32_t ScreenWidth, uint32_t ScreenHeight, std::string& Title)
    {
        constexpr int32_t major_number = 3;
        constexpr int32_t minor_number = 3;

        uint32_t shader_program = 0u;

        if (glfwInit() != GLFW_TRUE)
        {
            fprintf(stderr, "%s:%d:%s: Failed to initialize GLFW\n", __FILE__, __LINE__, __FUNCTION__);
            return false;
        }

        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,  major_number);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,  minor_number);
        glfwWindowHint(GLFW_OPENGL_PROFILE,         GLFW_OPENGL_CORE_PROFILE);

        _window = glfwCreateWindow(ScreenWidth, ScreenHeight, Title.c_str(), NULL, NULL);

        if (!_window)
        {
            fprintf(stderr, "%s:%d:%s: Failed to create window\n", __FILE__, __LINE__, __FUNCTION__);
            return false;
        }

        glfwMakeContextCurrent(_window);

        if (glewInit() != GLEW_OK)
        {
            fprintf(stderr, "%s:%d:%s: Failed to initialize GLEW\n", __FILE__, __LINE__, __FUNCTION__);
            return false;
        }

        if (!build_program(shader_program))
        {
            fprintf(stderr, "%s:%d:%s: Failed to initialize GLEW\n", __FILE__, __LINE__, __FUNCTION__);
            return false;
        }

        float vertices[] =
        {
            0.5f,  0.5f, 0.0f, 1.0f,   1.0f, 1.0f,
            0.5f, -0.5f, 0.0f, 1.0f,   1.0f, 0.0f,
           -0.5f, -0.5f, 0.0f, 1.0f,   0.0f, 0.0f,
           -0.5f,  0.5f, 0.0f, 1.0f,   0.0f, 1.0f,
        };

        unsigned int indices[] =
        {
            0, 1, 3,
            1, 2, 3,
        };

        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);

        glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);

        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(4 * sizeof(float)));
        glEnableVertexAttribArray(1);

        unsigned int texture;

        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);

        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);

        glUseProgram(shader_program);

        return true;
    }


    void Screen::display_image(uint8_t* ImageData, uint32_t ImageWidth, uint32_t ImageHeight)
    {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, ImageWidth, ImageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, ImageData);

        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        glfwSwapBuffers(_window);

        glfwPollEvents();
    }


    void Screen::deinitialize()
    {
        if (_image_data)
        {
            delete[] _image_data;
        }

        glDeleteVertexArrays(1, &_object_vertex_array);
        glDeleteBuffers     (1, &_object_vertex_buffer);
        glDeleteBuffers     (1, &_object_element_buffer);

        _image_data = nullptr;

        glfwTerminate();
    }
}


Solution

  • glPixelStorei

    GL_UNPACK_ALIGNMENT

    Specifies the alignment requirements for the start of each pixel row in memory. The allowable values are 1 (byte-alignment), 2 (rows aligned to even-numbered bytes), 4 (word-alignment), and 8 (rows start on double-word boundaries).

    In OpenGL 4.6 the value defaults to 4 (see table on the specific page).

    The row length of the small image is a multiple of 4 but the row length of the bigger image is not, therefore the distortion.

    In your case, add the line

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //byte-alignment
    

    before uploading the image data (glTexImage2D), which should fix the issue.

    For a better understanding, read chapter 8 Textures and Samplers of the OpenGL specification (at least 8.4 and 8.5).