2
votes

On my 2nd project I work with opengl and SDL2. I want to render the text to a surface with SDL2 and then convert it.

I am already loading textures from files with SDL so I have a function that can convert Surfaces. Still when I supply a surface generated by the TTF_Render function the result is this: enter image description here

Now I dont know why this is happening so some support would be appreciated.

I am using the SDL_ttf libary to load the file. I am using visual studio 2015 as the ide.

Here is my convert function:

GLuint JUMA_Texture::loadFromSurface(SDL_Surface *img, GLenum target, GLenum filtering) {
    GLuint TextureID = 0;
    glGenTextures(1, &TextureID);
    glBindTexture(GL_TEXTURE_2D, TextureID);
    std::cout << " got " << img;
    if (target == GL_TEXTURE_2D)
    {
        glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);   // Set texture wrapping to GL_REPEAT
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    }
    int Mode = GL_RGB;
    if (img->format->BytesPerPixel == 4) {
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        transparent = true;
        Mode = GL_RGBA;
    }

    glTexImage2D(GL_TEXTURE_2D, 0, Mode, img->w, img->h, 0, Mode, GL_UNSIGNED_BYTE, img->pixels);
    glGenerateMipmap(target);
    return TextureID;
}

I printed the address of the surface pointers before and after it has been send to the function and they seem identical.

Also I am writing the surface to a file using SDL_SaveBmp and it seems fine aswell.

Edit:

Due to request I will now post the code for the use functions as well as the constructors of my shader and texture classes.

Shader constructor/loader

printf("Loading shader");
    // 1. Retrieve the vertex/fragment source code from filePath
    std::string vertexCode;
    std::string fragmentCode;
    std::ifstream vShaderFile;
    std::ifstream fShaderFile;
    strcpy_s(Fragpath, fragmentPath);
    strcpy_s(Vertexpath, vertexPath);

    printf(".");
    // ensures ifstream objects can throw exceptions:
    vShaderFile.exceptions(std::ifstream::badbit);
    fShaderFile.exceptions(std::ifstream::badbit);
    try
    {
        // Open files
        vShaderFile.open(vertexPath);
        fShaderFile.open(fragmentPath);
        std::stringstream vShaderStream, fShaderStream;
        // Read file's buffer contents into streams
        vShaderStream << vShaderFile.rdbuf();
        fShaderStream << fShaderFile.rdbuf();
        // close file handlers
        vShaderFile.close();
        fShaderFile.close();
        // Convert stream into string
        vertexCode = vShaderStream.str();
        fragmentCode = fShaderStream.str();
        printf(".");
    }
    catch (std::ifstream::failure e)
    {
        std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
    }
    const GLchar* vShaderCode = vertexCode.c_str();
    const GLchar * fShaderCode = fragmentCode.c_str();
    // 2. Compile shaders
    GLuint vertex, fragment;
    GLint success;
    GLchar infoLog[512];
    // Vertex Shader
    vertex = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertex, 1, &vShaderCode, NULL);
    glCompileShader(vertex);
    // Print compile errors if any
    glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
    printf(".");
    if (!success)
    {
        glGetShaderInfoLog(vertex, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    // Fragment Shader
    fragment = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragment, 1, &fShaderCode, NULL);
    glCompileShader(fragment);
    // Print compile errors if any
    glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(fragment, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    printf(".");
    // Shader Program
    this->Program = glCreateProgram();
    glAttachShader(this->Program, vertex);
    glAttachShader(this->Program, fragment);
    glLinkProgram(this->Program);
    // Print linking errors if any
    glGetProgramiv(this->Program, GL_LINK_STATUS, &success);
    if (!success)
    {
        glGetProgramInfoLog(this->Program, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
    }
    printf(".");
    // Delete the shaders as they're linked into our program now and no longer necessery
    glDeleteShader(vertex);
    glDeleteShader(fragment);
    printf("done\n");

Shader use function

void JUMA_Shader::Use()
{
    glUseProgram(this->Program);
}

Texture constructor

printf("loading Texture");
    int pos;
    pos = filePath.find(".");
    if (filePath.substr(pos) == ".png") {

        IMG_Init(IMG_INIT_PNG);
    }
    else if (filePath.substr(pos) == ".jpg")
        IMG_Init(IMG_INIT_JPG);

    SDL_Surface *img = IMG_Load(filePath.c_str());
    id = loadFromSurface(img,target,filtering);

Texture::loadFromSurface (NOTE: I HAVE NOT YET IMPLEMENTED CHECKING FOR GL_BGR AND GL_BGRA HOWEVER I HAVE MANUALLY ATTEMPTED BOTH OF THESE MODES WITH NO DIFFERENT EFFECT AT THIS MOMENT.)

GLuint JUMA_Texture::loadFromSurface(SDL_Surface *img, GLenum target, GLenum filtering) {
    GLuint TextureID = 0;
    glGenTextures(1, &TextureID);
    glBindTexture(GL_TEXTURE_2D, TextureID);
    std::cout << " got " << img;
    if (target == GL_TEXTURE_2D)
    {
        glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);   // Set texture wrapping to GL_REPEAT
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    }
    int Mode = GL_RGB;
    if (img->format->BytesPerPixel == 4) {
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        transparent = true;
        Mode = GL_RGBA;
    }

    glTexImage2D(GL_TEXTURE_2D, 0, Mode, img->w, img->h, 0, Mode, GL_UNSIGNED_BYTE, img->pixels);
    glGenerateMipmap(target);
    return TextureID;
}

Texture::USE

int JUMA_Texture::use(GLenum Channel) {
    glActiveTexture(Channel);   // Activate the texture unit first before binding texture
    glBindTexture(GL_TEXTURE_2D, id);

    if (transparent)
    {
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    }
    return 1;
}

Also here is what I am seeing right now (Note that I have checked again and this is not the color I set for my font as I previously stated. I dont know where this blue color is coming from. I am currently trying to figure this out) enter image description here

Lastly here is my vertex shader:

#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 texcoords;
out vec2 texCoords;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec4 pos;
void main()
{
    pos=gl_Position = projection*view*model*vec4(position,1.0f);
    texCoords=vec2(texcoords.xy);
};

And my fragment shader:

#version 330 core
out vec4 color;
in vec2 texCoords;
in vec4 pos;
uniform sampler2D ourTexture;
uniform vec4 mixCol;

void main()
{
    vec4 texColor = texture(ourTexture, texCoords);
    if(texColor.a < 0.1f)
         discard;

    color = texColor;
};

My font constructor

    JUMA_Font::JUMA_Font(char *path, int size, SDL_Color color, char* text, char *uniform) {
    TTF_Init();
    font = TTF_OpenFont(path, 40);
    sourceSurface = TTF_RenderText_Blended(font, text, color);
    printf("%d %d %d %d", color.r, color.g, color.b, color.a);
};

convert font to texture

JUMA_Texture convertToTexture() {
        JUMA_Texture _this;
        SDL_SaveBMP(sourceSurface, "out.bmp"); //outputs font correctly
        std::cout << "Expected " << sourceSurface;
        _this.id = _this.loadFromSurface(sourceSurface,GL_TEXTURE_2D,GL_NEAREST);

        return _this;
    }
1
I'm a bit confused. The code you added is loading the texture from a file (Texture constructor), which you stated works fine. Is that still the case? I.e. when you load an image from file and send the surface to loadFromSurface(), does everything work correctly? In that case, the interesting part of the code is the creation of the surface from a font (TTF_Render*), which is still missing.Meyer
Alright I am adding the complete Font class... Please holdMoustacheSpy
@Meyer I have added everything you wanted. Also the images are still loaded correctlyMoustacheSpy
@Meyer also I might have to add that transparency does not work even when loading images.MoustacheSpy
@Meyer After messing around a bit I found out that changing the 2nd mode in ` glTexImage2D(GL_TEXTURE_2D, 0, Mode, img->w, img->h, 0, Mode, GL_UNSIGNED_BYTE, img->pixels);` to GL_BGRA makes the color of the rect change from blue to the color I selected for the font.MoustacheSpy

1 Answers

1
votes

This looks like you generated the surface with TTF_Render*_Solid() or TTF_Render*_Shaded(). However, these functions return an 8-bit palettized surface, which OpenGL does not understand. This is not detected in your function, where you set the source format (Mode) either to GL_RGB or GL_RGBA, which both are incorrect in that case.

As a solution, make sure to render the text in the blended mode, using one of the TTF_Render*_Blended() functions. These will create a full BGRA surface, which can then be used with glTexImage2D().

Also note that it is recommended to use a specific internal format with glTexImage2D(), so the function call should look like this:

glTexImage2D(GL_TEXTURE_2D, 0,
  GL_RGBA8,
  img->w, img->h, 0,
  GL_BGRA,
  GL_UNSIGNED_BYTE, img->pixels);