c++opengltexturestexture2d

Cpp OpenGL doesn't draw any texture. Instead it draws a color from the texture


Code doesn't give any error but the texture doesn't show up on screen.

I tried different PNG images but the only thing that changes is the color of the rectangle. I can also change the color of the rectangle by multiplying the color value with the texture uniform in the shader.

Main

#include <GL/glew.h> //GLEW has to be included before GLFW
#include <GLFW/glfw3.h>

#include "Renderer.h"

#include "VertexBuffer.h"
#include "IndexBuffer.h"
#include "VertexArray.h"
#include "Shader.h"
#include "Texture.h"

#define GL_VERSION_MAJOR 3
#define GL_VERSION_MINOR 3
#define GL_PROFILE GLFW_OPENGL_CORE_PROFILE

#define WINDOW_TITLE "OpenGL 3.3"
#define RESOLUTION_X 1600
#define RESOLUTION_Y 900

int main(void)
{
    GLFWwindow* window;
    
    if (!glfwInit())    return -1;

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, GL_VERSION_MAJOR);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, GL_VERSION_MINOR);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GL_PROFILE);

    window = glfwCreateWindow(RESOLUTION_X, RESOLUTION_Y,
        WINDOW_TITLE, nullptr, nullptr);

    if (!window)
    {
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);
    glfwSwapInterval(1);
    
    if(glewInit() != GLEW_OK)   return -1;

    {
        //  posx,posy,texCoordinateX,texCoordinateY
        const float positions[] = {
            -0.5f, -0.5f, 0.0f, 0.0f, // 0
            +0.5f, -0.5f, 1.0f, 0.0f, // 1
            +0.5f, +0.5f, 1.0f, 1.0f, // 2
            -0.5f, +0.5f, 0.0f, 1.0f, // 3
        };

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

        glCall(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA))

        VertexArray vertexArray;
        VertexBuffer vertexBuffer(positions, 4 * 4 * sizeof(float));
        VertexBufferLayout layout;
        layout.Push<float>(2); // 2 floats for each position
        layout.Push<float>(2); // 2 floats for each texture coordinate
        vertexArray.AddBuffer(vertexBuffer, layout);

        IndexBuffer indexBuffer(indices, 6);

        Shader shader("res/shaders/Basic.shader");
        shader.Bind();
        // shader.SetUniform4f("u_Color", 0.0f, 0.0f, 0.3f, 1.0f);

        Texture texture("res/textures/glass-bin.png");
        texture.Bind();
        shader.SetUniform1i("u_Texture", 0);

        vertexArray.Unbind();
        vertexBuffer.Unbind();
        indexBuffer.Unbind();
        shader.Unbind();

        Renderer renderer;

        //float r = 0.0f;
        //float increment = 0.01f;
        while (!glfwWindowShouldClose(window))
        {
            renderer.Clear();

            //if(r > 1.0f) increment = -0.01f;
            //else if(r < 0.0f) increment = 0.01f;
            
            //r += increment;
            shader.Bind();
            // shader.SetUniform4f("u_Color", r, 0.0f, 0.3f, 1.0f);

            renderer.Draw(vertexArray,indexBuffer, shader);

            /* Swap front and back buffers */
            glCall(glfwSwapBuffers(window));

            /* Poll for and process events */
            glCall(glfwPollEvents());
        }
    }
    glfwTerminate();
    return 0;
}

Shader

// HEADER

#pragma once
#include <string>
#include <unordered_map>


struct ShaderProgramSource
{
    std::string VertexSource;
    std::string FragmentSource;
};

class Shader
{
private:
    std::unordered_map<std::string, int> _uniformLocationCache;

    std::string _filePath;

    unsigned int _rendererId;

    ShaderProgramSource ParseShader(const std::string& shader) const;
    unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader);
    unsigned int CompileShader(unsigned int type, const std::string& source);;

    int GetUniformLocation(const std::string& name);


public:
    Shader(const std::string& filePath);
    ~Shader();

    void Bind() const;
    void Unbind() const;

    void SetUniform4f(const std::string&name, float x, float y, float z, float w);
    void SetUniform3f(const std::string&name, float x, float y, float z);
    void SetUniform2f(const std::string&name, float x, float y);
    void SetUniform1f(const std::string&name, float x);
    void SetUniform1i(const std::string&name, int x);
    
};

// CPP FILE

#include <GL/glew.h>
#include <iostream>
#include <sstream>
#include <fstream>

#include "Renderer.h"
#include "Shader.h"

Shader::Shader(const std::string& filePath) : _filePath(filePath), _rendererId(0)
{
    ShaderProgramSource source = ParseShader(filePath);
    _rendererId = CreateShader(source.VertexSource, source.FragmentSource);
}

Shader::~Shader()
{
    glCall(glDeleteProgram(_rendererId));
}

ShaderProgramSource Shader::ParseShader(const std::string& shader) const
{
    std::ifstream stream(shader);

    enum class ShaderType
    {
        none = -1,
        vertex = 0,
        fragment = 1
    };

    ShaderType type = ShaderType::none;
    std::string line;
    std::stringstream ss[2];

    while(getline(stream, line))
    {
        if(line.find("#shader") != std::string::npos)
        {
            type = line.find("vertex") != std::string::npos ?
                        ShaderType::vertex:
                   line.find("fragment") != std::string::npos ? 
                        ShaderType::fragment:
                        ShaderType::none;
        }
        else
        {
            ss[static_cast<int>(type)] << line << '\n';
        }
    }
    return { ss[0].str(), ss[1].str() };
}

unsigned int Shader::CreateShader(const std::string& vertexShader, const std::string& fragmentShader)
{
    const unsigned int program = glCreateProgram();

    const unsigned int vShader = CompileShader(GL_VERTEX_SHADER, vertexShader);
    const unsigned int fShader = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);


    glCall(glAttachShader(program, vShader))
    glCall(glAttachShader(program, fShader))

    glCall(glLinkProgram(program))
    glCall(glValidateProgram(program))

    return program;
}

unsigned int Shader::CompileShader(unsigned int type, const std::string& source)
{
    glCall(const unsigned int id = glCreateShader(type))
    const char* src = source.c_str();

    glCall(glShaderSource(id, 1, &src, nullptr))
    glCall(glCompileShader(id))


    int result;
    glCall(glGetShaderiv(id, GL_COMPILE_STATUS, &result))
    if (result == GL_FALSE)
    {
        int length;
        glCall(glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length))
        
        char* message = static_cast<char*>(_malloca(length * sizeof(char)));
        glCall(glGetShaderInfoLog(id, length, &length, message))

        const std::string shaderType = type == GL_VERTEX_SHADER ? "vertex" : "fragment";
        std::cout << "Failed to compile " << shaderType << " shader!" << std::endl;
        std::cout << message << std::endl;

        glCall(glDeleteShader(id));
        return 0;
    }
    return id;
}

void Shader::Bind() const
{
    glCall(glUseProgram(_rendererId))
}

void Shader::Unbind() const
{
    glCall(glUseProgram(0))
}

void Shader::SetUniform4f(const std::string& name, float x, float y, float z, float w)
{
    glCall(glUniform4f(GetUniformLocation(name), x, y, z, w))
}

void Shader::SetUniform3f(const std::string& name, float x, float y, float z)
{
    glCall(glUniform3f(GetUniformLocation(name), x, y, z))
}

void Shader::SetUniform2f(const std::string& name, float x, float y)
{
    glCall(glUniform2f(GetUniformLocation(name), x, y))
}

void Shader::SetUniform1f(const std::string& name, float x)
{
    glCall(glUniform1f(GetUniformLocation(name), x))
}

void Shader::SetUniform1i(const std::string& name, int x)
{
    glCall(glUniform1i(GetUniformLocation(name), x))
}

int Shader::GetUniformLocation(const std::string& name)
{
    if(_uniformLocationCache.contains(name)) return _uniformLocationCache[name];

    glCall(const int location = glGetUniformLocation(_rendererId, name.c_str()))

    if(location == -1)
    {
        std::cout << "Uniform " << name << " not found!" << std::endl;
    }

    return location;
}

VertexArray

// HEADER

#pragma once
#include "VertexBuffer.h"
#include "VertexBufferLayout.h"


class VertexArray
{
private:
    unsigned int _rendererId;
public:
    VertexArray();
    ~VertexArray();

    void AddBuffer(const VertexBuffer& buffer, const VertexBufferLayout& layout) const;

    void Bind() const;
    void Unbind() const;
};

// CPP FILE

#include "VertexArray.h"
#include "Renderer.h"

#include <GL/glew.h>

VertexArray::VertexArray()
{

    glCall(glGenVertexArrays(1, &_rendererId));
}

VertexArray::~VertexArray()
{
    glCall(glDeleteVertexArrays(1, &_rendererId));
}


void VertexArray::AddBuffer(const VertexBuffer& buffer, const VertexBufferLayout& layout) const
{
    Bind();
    buffer.Bind();

    const auto& elements = layout.GetElements();
    const auto& size = layout.GetStride();
    const void* offset;

    for(unsigned int i = 0; i < elements.size(); i++)
    {
        const auto& element = elements[i];
        offset = (const void*)(i * element.count);

        glEnableVertexAttribArray(i);
        glVertexAttribPointer(i, element.count, element.type,
                              element.normalized, size, offset);
    }
}

void VertexArray::Bind() const
{
    glCall(glBindVertexArray(_rendererId));
}

void VertexArray::Unbind() const
{
    glCall(glBindVertexArray(0));
}

VertexBuffer

// HEADER

#pragma once

class VertexBuffer
{
private:
    unsigned int _rendererId;
public:
    VertexBuffer(const void* data, const unsigned int& size);
    ~VertexBuffer();

    void Bind() const;
    void Unbind() const;
};

// CPP FILE

#include <GL/glew.h>

#include "VertexBuffer.h"
#include "Renderer.h"

VertexBuffer::VertexBuffer(const void* data, const unsigned int& size)
{
    glCall(glGenBuffers(1, &_rendererId));
    glCall(glBindBuffer(GL_ARRAY_BUFFER, _rendererId));
    glCall(glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW));
}

VertexBuffer::~VertexBuffer()
{
    glCall(glDeleteBuffers(1, &_rendererId));
}

void VertexBuffer::Bind() const
{
    glCall(glBindBuffer(GL_ARRAY_BUFFER, _rendererId));
}

void VertexBuffer::Unbind() const
{
    glCall(glBindBuffer(GL_ARRAY_BUFFER, 0));
}

VertexBufferLayout

// HEADER FILE

#pragma once
#include <vector>

#define GL_FALSE 0
#define GL_TRUE 1
#define GL_UNSIGNED_BYTE 0x1401
#define GL_UNSIGNED_INT 0x1405
#define GL_FLOAT 0x1406

struct LayoutElement
{
    unsigned int count;
    unsigned int type;
    unsigned int normalized;

    static unsigned int GetSizeOfType(unsigned int type)
    {
        switch (type)
        {
            case GL_FLOAT:          return sizeof(float);
            case GL_UNSIGNED_INT:   return sizeof(unsigned int);
            case GL_UNSIGNED_BYTE:  return sizeof(unsigned char);
        }
        //ASSERT(false);
        return 0;
    }
};

class VertexBufferLayout
{
    std::vector<LayoutElement> _Elements;
    unsigned int _Stride;
public:
    VertexBufferLayout() : _Stride(0) {}

    template<typename T>
    void Push(const unsigned int& count);

    template<>
    void Push<float>(const unsigned int& count);

    template<>
    void Push<unsigned int>(const unsigned int& count);

    template<>
    void Push<unsigned char>(const unsigned int& count);

    inline std::vector<LayoutElement> GetElements() const { return _Elements; }
    inline unsigned int GetStride() const { return _Stride; }
};


// CPP FILE

#include "VertexBufferLayout.h"

template <typename T>
void VertexBufferLayout::Push(const unsigned int& count)
{
//Static_assert doesn't work?
}

template <>
void VertexBufferLayout::Push<float>(const unsigned int& count)
{
    _Elements.push_back(LayoutElement{count, GL_FLOAT, GL_FALSE});
    _Stride += count * LayoutElement::GetSizeOfType(GL_FLOAT);
}

template <>
void VertexBufferLayout::Push<unsigned int>(const unsigned int& count)
{
    _Elements.push_back(LayoutElement{count, GL_UNSIGNED_INT, GL_TRUE});
    _Stride += count * LayoutElement::GetSizeOfType(GL_UNSIGNED_INT);
}
 
template <>
void VertexBufferLayout::Push<unsigned char>(const unsigned int& count)
{
    _Elements.push_back(LayoutElement{count, GL_UNSIGNED_BYTE, GL_TRUE});
    _Stride += count * LayoutElement::GetSizeOfType(GL_UNSIGNED_BYTE);
}

IndexBuffer

// HEADER

#pragma once


class IndexBuffer
{
private:
    unsigned int _rendererId;
    unsigned int _count;
public:
    IndexBuffer(const unsigned int* data, unsigned int count);
    ~IndexBuffer();

    void Bind() const;
    void Unbind() const;

    inline unsigned int GetCount() const { return _count; }
};

// CPP FILE

#include <GL/glew.h>

#include "IndexBuffer.h"
#include "Renderer.h"

IndexBuffer::IndexBuffer(const unsigned int* data, unsigned int count) : _count(count)
{
    ASSERT(sizeof(unsigned int) == sizeof(GLuint))
    
    glCall(glGenBuffers(1, &_rendererId))
    glCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _rendererId))
    glCall(glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(unsigned int), data, GL_STATIC_DRAW))
}

IndexBuffer::~IndexBuffer()
{
    glCall(glDeleteBuffers(1, &_rendererId))
}

void IndexBuffer::Bind() const
{
    glCall(glBindBuffer(GL_ARRAY_BUFFER, _rendererId))
}

void IndexBuffer::Unbind() const
{
    glCall(glBindBuffer(GL_ARRAY_BUFFER, 0))
}

Renderer

// HEADER

#pragma once

#include "IndexBuffer.h"
#include "VertexArray.h"
#include "Shader.h"


#define ASSERT(x) if(!(x)) __debugbreak();
#define glCall(x) glClearError();\
    x;\
    ASSERT(glLogCall(#x, __FILE__, __LINE__))

void glClearError();
bool glLogCall(const char* function, const char* file, int line);

class Renderer
{
public:
    void Draw(const VertexArray& vertexArray, const IndexBuffer& indexBuffer, const Shader& shader) const;
    void Clear() const;
};

// CPP FILE

#include "Renderer.h"
#include <iostream>
#include <GL/glew.h>

void glClearError()
{
    while (glGetError() != GL_NO_ERROR);
}
bool glLogCall(const char* function, const char* file, int line)
{
    while (const GLenum error = glGetError())
    {
        std::cout << "OpenGL Error: " << error << std::endl;
        std::cout << function
                  << " in " << file
                  << " at Line : " << line << std::endl;
        return false;
    }
    return true;
}

void Renderer::Draw(const VertexArray& vertexArray,const IndexBuffer& indexBuffer,
                    const Shader& shader) const
{
    shader.Bind();
    vertexArray.Bind();
    indexBuffer.Bind();

    glCall(glDrawElements(GL_TRIANGLES, indexBuffer.GetCount(), GL_UNSIGNED_INT, nullptr));

}

void Renderer::Clear() const
{
    glCall(glClear(GL_COLOR_BUFFER_BIT))
    glCall(glClearError())
}

Basic.shader

#shader vertex
#version 330 core

layout(location = 0) in vec4 position;
layout(location = 1) in vec2 texCoord;

out vec2 v_TexCoord;

void main()
{
    gl_Position = position;
    v_TexCoord = texCoord;
};

#shader fragment
#version 330 core

layout(location = 0) out vec4 color;

in vec2 v_TexCoord;

uniform vec4 u_Color;
uniform sampler2D u_Texture;

void main()
{
    vec4 texColor = texture(u_Texture, v_TexCoord);
    color = texColor;
};

Texture

// HEADER

#pragma once
#include <string>

class Texture
{
private:
    unsigned int _rendererID;
    std::string _filePath;
    unsigned char* _localBuffer; 
    int _width, _height, _bytePerPixel;
public:
    Texture(std::string path);
    ~Texture();

    void Bind(unsigned int slot = 0) const;
    void Unbind() const;

    inline int GetHeight() const { return _height; }
    inline int GetWidth() const { return _width; }
};

// CPP FILE

#include <GL/glew.h>
#include <iostream>

#include "vendor/stb_image/stb_image.h"
#include "Texture.h"
#include "Renderer.h"

Texture::Texture(std::string path)
    : _rendererID(0), _filePath(std::move(path)), _localBuffer(nullptr),
    _width(0), _height(0), _bytePerPixel(0)
{
    stbi_set_flip_vertically_on_load(1);

    _localBuffer = stbi_load(_filePath.c_str(),
        &_width, &_height, &_bytePerPixel, 4);

    glCall(glGenTextures(1, &_rendererID))
    glCall(glBindTexture(GL_TEXTURE_2D, _rendererID))

    glCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR))
    glCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR))
    glCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE))
    glCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE))

    if (_localBuffer)
    {
        glCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, _width, _height,
                0, GL_RGBA, GL_UNSIGNED_BYTE, _localBuffer))
        glCall(glGenerateMipmap(GL_TEXTURE_2D))
        glCall(glBindTexture(GL_TEXTURE_2D, 0))
    }
    else
    {
        std::cout << "\nError: Failed to load texture" << std::endl;
        std::cout << stbi_failure_reason() << std::endl;
    }

    stbi_image_free(_localBuffer);
}

Texture::~Texture()
{
    glCall(glDeleteTextures(1, &_rendererID))
}

void Texture::Bind(unsigned int slot) const
{
    glCall(glActiveTexture(GL_TEXTURE0 + slot))
    glCall(glBindTexture(GL_TEXTURE_2D, _rendererID))
}

void Texture::Unbind() const
{
    glCall(glBindTexture(GL_TEXTURE_2D, 0))
}

Image

Rendered


Solution

  • The offset value for the first glEnableVertexAttribArray should be

    glEnableVertexAttribArray(0);
                glVertexAttribPointer(0, element.count, element.type,
                                      element.normalized, size, (void*)0)
    

    The offset value for the Second glEnableVertexAttribArray should be

     glEnableVertexAttribArray(1);
                glVertexAttribPointer(1, element.count, element.type,
                                      element.normalized, size, (void*)(2 * sizeof(float)))