Cześć. Chciałbym renderować tekst używając freetype i freetype-gl. Obejrzałem filmiki z kanału TheChernoProject i stwierdziłem, że mógłbym w ten sam sposób zrobić to w mojej aplikacji.
Problem jest taki, że przy renderowaniu, zamiast tekstu pojawiają się kwadraty, co oznacza brak danych w teksturze. Takie coś dzieje się tylko przy rysowaniu czcionek. Tekstury z formatem .png rysują się bardzo dobrze.
#define RENDERER_MAX_SPRITES 60000
#define RENDERER_SPRITE_SIZE RENDERER_VERTEX_SIZE * 4
#define RENDERER_BUFFER_SIZE RENDERER_SPRITE_SIZE * RENDERER_MAX_SPRITES
#define RENDERER_INDICES_SIZE RENDERER_MAX_SPRITES * 6
#define RENDERER_MAX_TEXTURES 32
class BatchRenderer
{
public:
BatchRenderer() = default;
BatchRenderer() = default;
void Initialize()
{
//Generowanie, bindowanie vao, vbo, ibo i dodawanie `CalculateIndices` jako dane do ibo
}
void Start()
{
glCall(glEnable(GL_BLEND));
glCall(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
m_vertex2D = (Vertex2D*)glMapNamedBufferRange(m_vbo, NULL, RENDERER_BUFFER_SIZE, GL_MAP_WRITE_BIT);
}
void DrawString(const std::string& text, const Vector2& position, GLuint color, const Font& font)
{
float textureSlot = 0;
bool found = false;
for (GLuint i = 0; i < m_textures.size(); ++i)
{
if (m_textures[i] == font.getTexture())
{
textureSlot = (float)(i + 1);
found = true;
break;
}
}
if (!found)
{
if (m_textures.size() >= RENDERER_MAX_TEXTURES)
{
End();
Render();
Start();
}
m_textures.push_back(font.getTexture());
textureSlot = (float)(m_textures.size());
}
float x = position.x;
texture_font_t* ftFont = font.FTFont;
for (GLuint i = 0; i < text.length(); ++i)
{
texture_glyph_t* glyph = texture_font_get_glyph(ftFont, text.c_str() + i);
if (glyph)
{
if (i > 0)
{
float kerning = texture_glyph_get_kerning(glyph, text.c_str() + i - 1);
x += kerning;
}
float x0 = x + glyph->offset_x;
float y0 = position.y + glyph->offset_y;
float x1 = x0 + glyph->width;
float y1 = y0 - glyph->height;
float u0 = glyph->s0;
float v0 = glyph->t0;
float u1 = glyph->s1;
float v1 = glyph->t1;
m_vertex2D->position = Vector2(x0, y0);
m_vertex2D->texUV = Vector2(u0, v0);
m_vertex2D->texID = textureSlot;
m_vertex2D->color = color;
m_vertex2D++;
m_vertex2D->position = Vector2(x0, y1);
m_vertex2D->texUV = Vector2(u0, v1);
m_vertex2D->texID = textureSlot;
m_vertex2D->color = color;
m_vertex2D++;
m_vertex2D->position = Vector2(x1, y1);
m_vertex2D->texUV = Vector2(u1, v1);
m_vertex2D->texID = textureSlot;
m_vertex2D->color = color;
m_vertex2D++;
m_vertex2D->position = Vector2(x1, y0);
m_vertex2D->texUV = Vector2(u1, v0);
m_vertex2D->texID = textureSlot;
m_vertex2D->color = color;
m_vertex2D++;
m_indexCount += 6;
x += glyph->advance_x;
}
}
}
void End()
{
glCall(glUnmapNamedBuffer(m_vbo));
}
void Render()
{
for (GLuint i = 0; i < m_textures.size(); ++i)
{
glCall(glActiveTexture(GL_TEXTURE0 + i));
glCall(glBindTexture(GL_TEXTURE_2D, m_textures[i]));
}
glCall(glBindVertexArray(m_vao));
glCall(glDrawElements(GL_TRIANGLES, m_indexCount , GL_UNSIGNED_INT, nullptr));
glCall(glBindVertexArray(0));
m_indexCount = 0;
m_textures.clear();
}
GLuint* CalculateIndices()
{
GLuint* indices = new GLuint[RENDERER_INDICES_SIZE],
offset = 0;
for (int i = 0; i < RENDERER_INDICES_SIZE; i += 6)
{
indices[i + 0] = offset + 0;
indices[i + 1] = offset + 1;
indices[i + 2] = offset + 2;
indices[i + 3] = offset + 2;
indices[i + 4] = offset + 3;
indices[i + 5] = offset + 0;
offset += 4;
}
return indices;
}
Vertex2D* m_vertex2D;
GLuint m_vao;
GLuint m_vbo;
GLuint m_ibo;
GLuint m_indexCount;
std::vector<GLuint> m_textures;
};
int main(int argc, char* args[])
{
SDL_Init(SDL_INIT_EVERYTHING);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_Window* window = SDL_CreateWindow("Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
SDL_GLContext context = SDL_GL_CreateContext(window);
SDL_Event event;
glewExperimental = true;
glewInit();
glViewport(0, 0, 1280, 720);
Renderer renderer;
renderer.Initialize();
Font* font = new Font("font.ttf", 50.0f);
bool quit = false;
while (!quit) {
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
quit = true;
}
}
glClearColor(0.5, 0.5, 0.5, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
renderer.Start();
renderer.DrawString("JAKIS TEKST", Vector2(100.0f, 100.0f), 0xffff00ff, *font);
renderer.End();
renderer.Render();
SDL_GL_SwapWindow(window);
}
return 0;
}
class Font
{
public:
Font(const std::string& filename, float size)
{
atlas = ftgl::texture_atlas_new(512, 512, 3);
font = ftgl::texture_font_new_from_file(atlas, size, filename.c_str());
texture = CreateFromSource(512, 512, GL_RGB, atlas->data);
}
GLuint getTexture() const
{
UpdateAtlas();
return texture;
}
void UpdateAtlas() const
{
UpdateData(texture, 512, 512, GL_RGB, FTAtlas->data);
}
ftgl::texture_atlas_t* atlas;
ftgl::texture_font_t* font;
GLuint texture = 0;
};
Zwykłe funkcje
GLuint CreateFromSource(int w, int h, int format, const void* pixels)
{
GLuint texture = 0;
glCall(glGenTextures(1, &texture));
glCall(glBindTexture(GL_TEXTURE_2D, texture));
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));
glCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
glCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glCall(glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, format, GL_UNSIGNED_BYTE, nullptr));
glCall(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, format, GL_UNSIGNED_BYTE, pixels));
glCall(glGenerateMipmap(GL_TEXTURE_2D));
glCall(glBindTexture(GL_TEXTURE_2D, 0));
return texture;
}
void UpdateData(GLuint tex, int w, int h, int format, const void* pixels)
{
glCall(glBindTexture(GL_TEXTURE_2D, tex));
glCall(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, format, GL_UNSIGNED_BYTE, pixels));
}
Vertex shader
#version 430 core
layout (location = 0) in vec3 v3position;
layout (location = 1) in vec4 v4color;
layout (location = 2) in vec2 v2UV;
layout (location = 3) in float ftexID;
out vec3 position;
out vec4 color;
out vec2 UV;
out float texID;
uniform mat4 mvp;
void main()
{
gl_Position = mvp * vec4(v3position, 1.0);
position = v3position;
color = v4color;
texUV = vec2(v2UV.x, 1 - v2UV.y);
texID = ftexID;
}
pixel
#version 430 core
out vec4 outColor;
in vec2 position;
in vec4 color;
in vec2 UV;
in float texID;
uniform sampler2D textureSampler[32];
void main()
{
vec4 texColor = color;
if (texID > 0.0) {
int itexID = int(texID - 0.5);
texColor = color * texture(textureSampler[itexID], UV);
}
outColor = texColor;
}
Jak już wspominałem na początku przy teksturach typu .png, .bmp wszystko ładnie, pięknie, ale gdy chcę narysować tekst wtedy pojawiają się same kwadraty bez tekstury. Zmieniłem w shadere pixeli żeby zamiast int itexID = int(texID - 0.5f);
było po prostu int itexID = int(texID);
i pojawił się tekst, ale zawsze jest jakieś ale:
a) zniknęły tekstury typu .png
b) tekst był nie w tym miejsu
c) była w okół niego czarna tekstura (czyli jakby nie działało blendowanie, ale prędzej nie było z tym problemu).
Dlaczego tak się dzieje w moim przykładzie?
W jaki sposób to naprawić?