Hi!
I recently tried to render text using FreeType, I followed a tutorial findable at the website of learnopengl and even though the code seems to be OK it’s not working, I don’t have any errors, just nothing displayed.
I posted a message there but I’m a little in a hurry, so I came here. I hope you’ll find the error in this code!
Main Loop:
void Scene::mainLoop() {
// Matrices
mat4 projection;
projection = perspective(70.0, (double) m_winWidth / m_winHeight, 0.1, 100.0);
mat4 modelview = mat4(1.0);
// Camera
Camera camera(vec3(3, 3, 5), vec3(0, 0, 0), vec3(0, 1, 0), 0.5, 0.5);
// Init FreeType
m_output.initShader();
m_output.initFreeType();
// Enable Blend
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// MAIN LOOP
while (!m_input.getEndProgram()) {
// Setting loop started time
m_loopStart = SDL_GetTicks();
// Events handling
m_input.updateEvents();
// Move mouse function
camera.move(m_input);
// Escape - Exit program
if (m_input.getKeyCode(SDL_SCANCODE_ESCAPE))
break;
// Screen clearing
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Camera handling
camera.lookAt(modelview);
m_output.RenderText("This is sample text", 50.0f, 50.0f, 1.0f, glm::vec3(0.5, 0.8f, 0.2f));
m_output.RenderText("This is sample text2", 50.0f, -50.0f, 1.0f, glm::vec3(0.5, 0.8f, 0.2f));
m_output.RenderText("This is sample text3", 500.0f, 475.0f, 0.5f, glm::vec3(0.5, 0.8f, 0.2f));
m_output.RenderText("This is sample text4", 0.0f, 0.0f, 1.0f, glm::vec3(0.5, 0.8f, 0.2f));
// Window updating
SDL_GL_SwapWindow(m_window);
// Check FPS
calculateFPS(m_fps, m_frameCount, m_currentTime, m_previousTime);
// Check execution time
checkElapsedTime();
}
}
Output class (header):
#ifndef DEF_OUTPUT
#define DEF_OUTPUT
// Windows includes
#ifdef WIN32
#include <GL/glew.h>
#include <SDL2/SDL_image.h>
#endif
// GLM Includes
#include <glm/glm.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/type_ptr.hpp>
// Other includes
#include <iostream>
#include <map>
#include <string>
#include <vector>
// File include
#include "shader.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#ifndef BUFFER_OFFSET
#define BUFFER_OFFSET(offset) ((char*)NULL + (offset))
#endif
class Output {
public:
// Constructors and destructors
Output();
~Output();
// Method
void initFreeType();
void initShader();
void RenderText(std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color);
private:
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
};
// Windows parameters
int m_winWidth;
int m_winHeight;
Shader m_shader;
glm::mat4 m_orthoMat;
GLuint m_vbo;
GLuint m_vao;
std::map<GLchar, Character> Characters;
};
#endif
initFreeType function:
void Output::initFreeType() {
// Init freetype
FT_Library ft;
if (FT_Init_FreeType(&ft)) {
std::cout << "Could not init freetype library!" << std::endl;
}
// Init font
FT_Face face;
if (FT_New_Face(ft, "arial.ttf", 0, &face)) {
std::cout << "Could not open font!" << std::endl;
}
// Set size
FT_Set_Pixel_Sizes(face, 0, 48);
// Disable byte-alignment restriction
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
for (GLubyte c = 0; c < 128; c++) {
// Load character glyph
if (FT_Load_Char(face, c, FT_LOAD_RENDER)) {
std::cout << "Failed to load Glyph" << std::endl;
continue;
}
// Generate texture
GLuint texture = 0;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RED,
face->glyph->bitmap.width,
face->glyph->bitmap.rows,
0,
GL_RED,
GL_UNSIGNED_BYTE,
face->glyph->bitmap.buffer);
// Set texture options
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Now store character for later use
Character character = {
texture,
glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
face->glyph->advance.x
};
Characters.insert(std::pair<GLchar, Character>(c, character));
}
// Release
FT_Done_Face(face);
FT_Done_FreeType(ft);
// Initialize VAO and VBO
// Deleting a potential former VBO
if(glIsBuffer(m_vbo) == GL_TRUE)
glDeleteBuffers(1, &m_vbo);
// Generate VBO ID
glGenBuffers(1, &m_vbo);
// Binding VBO
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
// Allocate video memory
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 24, 0, GL_DYNAMIC_DRAW); // Changed NULL to 0
// Unbinding VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Deleting a potential former VAO
if(glIsVertexArray(m_vao) == GL_TRUE)
glDeleteVertexArrays(1, &m_vao);
// Generate VAO ID
glGenVertexArrays(1, &m_vao);
// Binding VAO
glBindVertexArray(m_vao);
// Binding VBO
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
// Access vertices in the VRAM
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
glEnableVertexAttribArray(3);
// Unbinding VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Unbinding VAO
glBindVertexArray(0);
}
RenderText function:
void Output::RenderText(std::string text, GLfloat x, GLfloat y, GLfloat scale, glm::vec3 color) {
if (!m_isShaderLoaded)
return;
// Activate corresponding render state
glUseProgram(m_shader.getProgramID());
// Send colour
glUniform3f(glGetUniformLocation(m_shader.getProgramID(), "textColor"), color.x, color.y, color.z);
glActiveTexture(GL_TEXTURE0);
// Send ortho matrix
m_shader.sendMat4("projection", m_orthoMat);
glBindVertexArray(m_vao);
// 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);
// Update content of VBO memory
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Render quad
glDrawArrays(GL_TRIANGLES, 0, 6);
// Now advance cursors for next glyph (note that advance is number of 1/64 pixels)
x += (ch.Advance >> 6) * scale; // Bitshift by 6 to get value in pixels (2^6 = 64)
}
// Unbindings
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
// Exit shader program
glUseProgram(0);
}
Vertex Shader:
// GLSL Version
#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;
}
Fragment Shader:
// GLSL Version
#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;
}
Projection matrix initialization:
m_orthoMat = glm::ortho(0.0f, m_winWidth, 0.0f, m_winHeight);
Here is the complete project files in case you’d want it:
https://drive.google.com/open?id=0B1HyjpNmBqd7LU9hWGIxMlV2MlU