-1

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

Emre Can
  • 24
  • 6
  • 4
    The tutorial might have abstracted everything but people here don't want to guess your abstractions, or the tutorial's abstractions. Please show the actual OpenGL calls. You can remove ones that are irrelevant (e.g. blending, or if you just draw half the rectangle then you don't need an index buffer). By the way there is no draw call... – user253751 Aug 04 '22 at 19:22
  • Everything was working before adding texture. I thought adding these might make the post too long. But I added all code. – Emre Can Aug 05 '22 at 05:20
  • @Emre Can in your Fragment shader try to change this line to color = vec4(v_TexCoord , 0 , 1); and check if the texture Coordinates variates. – Summit Aug 05 '22 at 10:15
  • @Summit Only the rectangle color became black. I guess there is something wrong with coordinates. – Emre Can Aug 05 '22 at 11:04
  • 1
    Also you have a issue here as Squid233 pointed out "layout(location = 0) in vec4 position;" , you are only sending two values. – Summit Aug 05 '22 at 11:25

2 Answers2

2

The position of the buffer has only 2 components but the shader requires 4 components.

        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
        };
        layout.Push<float>(2); // 2 floats for each position
layout(location = 0) in vec4 position;

Try to change vec4 to vec2.

layout(location = 0) in vec2 position;
gl_Position = vec4(position, 0.0, 1.0);
squid233
  • 151
  • 1
  • 1
  • 8
2

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)))
Summit
  • 2,112
  • 2
  • 12
  • 36
  • Thank you so much! I changed the offest to "(const void*)(i * element.count * LayoutElement::GetSizeOfType(element.type));" and it worked perfectly well. I had changed this part while following the tutorial. And I completely forgot about the datatype part. – Emre Can Aug 05 '22 at 13:44