I'm currently building my own game in c++ using OpenGL. I wanted to try something new because I mainly code in C# and Unity. My goal is to first find out how everything works before I begin making my game. So I tried Rendering strings or text to the Screen but failed. I tried many things from working with stb_truetype and shader to firetype. My current try is with freetype because I got suggest to do so.
But after multiple tries I still wasn't able to print a "Hello World" to the screen. I added debugging stuff and tried arround and found out that the problem lays inside the "RenderText" function.
Output of the "Debugger"/the logging:
Shader compiled successfully
Shader compiled successfully
Program linked successfully
after glBufferSubData glError (0x502)
after glDrawArrays glError (0x502)
I tried playing arround with variables and librarys and diffrent shader code but didn't got it working. (All paths are correct)
main.cpp:
#include <iostream>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <vector>
#include <fstream>
#include <sstream>
#include <map>
void checkGlError(const char* op) {
for (GLint error = glGetError(); error; error = glGetError()) {
std::cout << "after " << op << " glError (0x" << std::hex << error << ")" << std::endl;
}
}
struct Character {
GLuint TextureID; // ID handle of the glyph texture
glm::ivec2 Size; // Size of glyph
glm::ivec2 Bearing; // Offset from baseline to left/top of glyph
GLuint Advance; // Offset to advance to next glyph
};
std::map<GLchar, Character> Characters;
GLuint VAO, VBO;
GLuint compileShader(const GLchar *shaderCode, GLenum shaderType) {
GLuint shader = glCreateShader(shaderType);
glShaderSource(shader, 1, &shaderCode, NULL);
glCompileShader(shader);
GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
GLchar infoLog[512];
glGetShaderInfoLog(shader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::COMPILATION_FAILED\n" << infoLog << std::endl;
}
if(success) {
std::cout << "Shader compiled successfully" << std::endl;
}
return shader;
}
GLuint linkProgram(GLuint vertexShader, GLuint fragmentShader) {
GLuint program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
GLint success;
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success) {
GLchar infoLog[512];
glGetProgramInfoLog(program, 512, NULL, infoLog);
std::cout << "ERROR::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
if (success) {
std::cout << "Program linked successfully" << std::endl;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return program;
}
void RenderText(GLuint shader, std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color) {
// Activate corresponding render state
glUseProgram(shader);
checkGlError("glUseProgram");
glUniform3f(glGetUniformLocation(shader, "textColor"), color.x, color.y, color.z);
checkGlError("glUniform3f");
glActiveTexture(GL_TEXTURE0);
checkGlError("glActiveTexture");
glBindVertexArray(VAO);
checkGlError("glBindVertexArray");
// Iterate through all characters
std::string::const_iterator c;
for (c = text.begin(); c != text.end(); c++) {
Character ch = Characters[*c];
GLfloat xpos = x + ch.Bearing.x * scale;
GLfloat ypos = y - (ch.Size.y - ch.Bearing.y) * scale;
GLfloat w = ch.Size.x * scale;
GLfloat h = ch.Size.y * scale;
// Update VBO for each character
GLfloat vertices[6][4] = {
{ xpos, ypos + h, 0.0, 0.0 },
{ xpos, ypos, 0.0, 1.0 },
{ xpos + w, ypos, 1.0, 1.0 },
{ xpos, ypos + h, 0.0, 0.0 },
{ xpos + w, ypos, 1.0, 1.0 },
{ xpos + w, ypos + h, 1.0, 0.0 }
};
// Render glyph texture over quad
glBindTexture(GL_TEXTURE_2D, ch.TextureID);
checkGlError("glBindTexture");
// Update content of VBO memory
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
checkGlError("glBufferSubData");
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Render quad
glDrawArrays(GL_TRIANGLES, 0, 6);
checkGlError("glDrawArrays");
x += (ch.Advance >> 6) * scale;
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
std::string readShaderFromFile(const std::string& filepath) {
std::ifstream shaderFile(filepath);
std::stringstream shaderData;
shaderData << shaderFile.rdbuf();
shaderFile.close();
return shaderData.str();
}
int main() {
if (!glfwInit()) {
// Initialization failed
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
#endif
GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
if (!window) {
// Window or OpenGL context creation failed
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// GLEW initialization
glewExperimental = GL_TRUE; // Needed in core profile
if (glewInit() != GLEW_OK) {
std::cout << "Failed to initialize GLEW" << std::endl;
return -1;
}
FT_Library ft;
if (FT_Init_FreeType(&ft)) {
std::cout << "Could not init FreeType Library" << std::endl;
return -1;
}
FT_Face face;
if (FT_New_Face(ft, "../Arial.ttf", 0, &face)) {
std::cout << "Failed to load font" << std::endl;
return -1;
}
FT_Set_Pixel_Sizes(face, 0, 48);
if (FT_Load_Char(face, 'X', FT_LOAD_RENDER)) {
std::cout << "Failed to load Glyph" << std::endl;
return -1;
}
// Compile shaders and link them into a program
std::string vertexShaderSrc = readShaderFromFile("../shader.vs");
std::string fragmentShaderSrc = readShaderFromFile("../shader.fs");
GLuint vertexShader = compileShader(vertexShaderSrc.c_str(), GL_VERTEX_SHADER);
GLuint fragmentShader = compileShader(fragmentShaderSrc.c_str(), GL_FRAGMENT_SHADER);
GLuint shaderProgram = linkProgram(vertexShader, fragmentShader);
while (!glfwWindowShouldClose(window)) {
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
std::cout << glGetError() << std::endl;
RenderText(shaderProgram, "Hello World!", 25.0f, 25.0f, 1.0f, glm::vec3(0.5, 0.8f, 0.2f));
glfwPollEvents();
glfwSwapBuffers(window);
}
return 0;
}
fragment shader:
#version 330 core
in vec2 TexCoords;
out vec4 color;
uniform sampler2D text;
uniform vec3 textColor;
void main()
{
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);
color = vec4(textColor, 1.0) * sampled;
}
vertex shader:
#version 330 core
layout (location = 0) in vec4 vertex; // <vec2 pos, vec2 tex>
out vec2 TexCoords;
uniform mat4 projection;
void main()
{
gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);
TexCoords = vertex.zw;
}
Thanks for your time