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