rougier / freetype-gl

OpenGL text using one vertex buffer, one texture and FreeType
Other
1.65k stars 266 forks source link

Why there are weird artefacts? #196

Closed Jaszczolt closed 4 years ago

Jaszczolt commented 6 years ago

Hi! While rendering text there are weird artefacts. In the photos text is large, but we can see it even when it is much smaller.

testbezzaznaczenia testzaznaczenie

When I run demo, there is no problems What causes it? How to get rid of it?

struct VertexData
{
    glm::vec2 p_uv;
    glm::vec3 p_Position;
    GLuint p_Color;
};

class Renderer
{
public:
    Renderer() = default;

    void Initialize();

    void Start();
    void DrawString(const std::string& text, const glm::vec2& position, GLuint color);
    void End();

    Shader p_Shader;

    std::vector<VertexData> pv_VertexBuffer;
    std::vector<GLuint> pv_ElementBuffer;

    GLuint p_Vbo = 0,
           p_Vao = 0,
           p_Ebo = 0,
           p_Texture;

    void setVertexData(int index, const glm::vec3& position,
        const glm::vec2& uv,
        GLuint color);

    texture_font_t* pp_Font = nullptr;
    texture_atlas_t* pp_Atlas = nullptr;

};
const char* vtx = R"(

#version 430 core

layout(location = 0) in vec3 a_position;
layout(location = 1) in vec4 a_color;
layout(location = 2) in vec2 a_uv;

out vec3 v_position;
out vec4 v_color;
out vec2 v_uv;

uniform mat4 u_projection;

void main()
{
    gl_Position = u_projection * vec4(a_position, 1.0);

    v_position = a_position;
    v_color = a_color;
    v_uv = vec2(a_uv.x, a_uv.y);
}

)";

const char* frag = R"(

#version 430 core

layout(location = 0) out vec4 outColor;

in vec2 v_position;
in vec4 v_color;
in vec2 v_uv;

uniform sampler2D u_textureSampler;

void main()
{
    float textColor = texture(u_textureSampler, v_uv).r;
    outColor = vec4(v_color.rgb, textColor);
}

)";

void Renderer::Initialize()
{
    glGenBuffers(1, &p_Vbo);
    glBindBuffer(GL_ARRAY_BUFFER, p_Vbo);

    glGenBuffers(1, &p_Ebo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_Ebo);

    pp_Atlas = ftgl::texture_atlas_new(512, 512, 1);
    pp_Font = ftgl::texture_font_new_from_file(pp_Atlas, 100.0f, "Media/Vera.ttf");

    glGenTextures(1, &p_Texture);
    glBindTexture(GL_TEXTURE_2D, p_Texture);

    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_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 512, 512, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 512, 512, GL_RED, GL_UNSIGNED_BYTE, pp_Atlas->data);
    glGenerateMipmap(GL_TEXTURE_2D);

    glBindTexture(GL_TEXTURE_2D, 0);

    p_Shader.VertFromSrc(vtx);
        p_Shader.FragFromSrc(frag);

    p_Shader.UseProgram();
    p_Shader.Uniform1i("u_textureSampler", 0);
    p_Shader.UniformMatrix4fv("u_projection", glm::ortho(0.0f, 1280.0f, 0.0f, 720.0f));
}

void Renderer::Start()
{
    pv_VertexBuffer.clear();
    pv_ElementBuffer.clear();
}

void Renderer::DrawString(const std::string& text, const glm::vec2& position, GLuint color)
{
    using namespace ftgl;
    float x = position.x;

    glBindTexture(GL_TEXTURE_2D, p_Texture);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 512, 512, GL_RED, GL_UNSIGNED_BYTE, pp_Atlas->data);

    for (GLuint i = 0; i < text.length(); ++i) {
        texture_glyph_t* glyph = texture_font_get_glyph(pp_Font, 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 x1 = x0 + glyph->width;

            float y0 = position.y + glyph->offset_y;
            float y1 = y0 - glyph->height;

            float u0 = glyph->s0;
            float u1 = glyph->s1;

            float v0 = glyph->t0;
            float v1 = glyph->t1;

            int index = pv_VertexBuffer.size();

            pv_VertexBuffer.resize(pv_VertexBuffer.size() + 4);
            setVertexData(index, glm::vec3(x0, y0, 0.0f), glm::vec2(u0, v0), color);
            setVertexData(index + 1, glm::vec3(x0, y1, 0.0f), glm::vec2(u0, v1), color);
            setVertexData(index + 2, glm::vec3(x1, y1, 0.0f), glm::vec2(u1, v1), color);
            setVertexData(index + 3, glm::vec3(x1, y0, 0.0f), glm::vec2(u1, v0), color);

            pv_ElementBuffer.push_back(index);
            pv_ElementBuffer.push_back(index + 1);
            pv_ElementBuffer.push_back(index + 2);
            pv_ElementBuffer.push_back(index + 2);
            pv_ElementBuffer.push_back(index + 3);
            pv_ElementBuffer.push_back(index);

            x += glyph->advance_x;
        }
    }
}

void Renderer::End()
{
    glGenVertexArrays(1, &p_Vao);
    glBindVertexArray(p_Vao);

    glBindBuffer(GL_ARRAY_BUFFER, p_Vbo);
    glBufferData(GL_ARRAY_BUFFER, pv_VertexBuffer.size() * sizeof(VertexData), pv_VertexBuffer.data(), GL_DYNAMIC_DRAW);

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);

    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), (const void*)offsetof(VertexData, p_Position));
    glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(VertexData), (const void*)offsetof(VertexData, p_Color));
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), (const void*)offsetof(VertexData, p_UV));

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_Ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, pv_ElementBuffer.size() * sizeof(GLuint), pv_ElementBuffer.data(), GL_STATIC_DRAW);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, p_Texture);

    glBindVertexArray(p_Vao);
    glDrawElements(GL_TRIANGLES, pv_ElementBuffer.size() , GL_UNSIGNED_INT, nullptr);
    glBindVertexArray(0);

    glDeleteVertexArrays(1, &p_Vao);
}

void Renderer::setVertexData(int index, const glm::vec3& position, const glm::vec2& uv, GLuint color)
{
    pv_VertexBuffer[index].p_Position = position;
    pv_VertexBuffer[index].p_UV = uv;
    pv_VertexBuffer[index].p_Color = color;
}
Findus79 commented 5 years ago

Is there a solution to this? I stumbled across the same problem (when using SDF font rendering) Could it be that the font atlas is too tightly packed? Is there a way to create an "unpacked" atlas?

rougier commented 4 years ago

Yes, there is a border option to enlarge the gap between glyphs. That might be the source of the problem.

Findus79 commented 4 years ago

Hi, thanks for the heads up, but where can I find/set this border option? I only found a border around the whole atlas texture, but set in code, not as an option

rougier commented 4 years ago

Look at https://github.com/rougier/freetype-gl/blob/master/texture-font.c#L562. But I'm not sure this code is correct. You can try a larger padding to check if it is the source of the problem and if so, we can think of either increasing the value of padding for SDF or having a parameter.

PixelizedSoul commented 4 years ago

I have also stumbled across this issue which was even more prominent when rotating text. I can confirm that I have solved the issue in my case by adding padding to top and left (which were previously set to 0).

rougier commented 4 years ago

@Findus79 Did that solve the problem ?

Findus79 commented 4 years ago

Hi, yes updating to a newer version and setting the padding fixes the issue. Thx for the tip! :)