I made opengl rendering engine, and i can render shapes, 3d shapesand textures. Then i wanted to render fonts. I used freetype library to do this. But i have one serious problem with it. When i render font it looks terrible. First i will explain how i initialized freetype, and loaded font, then how i render textures, so you will be able to see itf there are any mistakes.
I initialize freetype and load font like this:
FT_Library library;
FT_Face face;
FT_GlyphSlot slot;
FT_Init_FreeType(&library);
FT_New_Face(library, "arial.ttf", 0, &face);
FT_Set_Char_Size(face, 0, 20 * 64, 300, 300);
slot = face->glyph;
FT_Load_Char(face, 'a', FT_LOAD_RENDER);
Then i load it into my texture:
this->tex.LoadFromBuffer(slot->bitmap.buffer, slot->bitmap.width, slot->bitmap.rows);
The tex object is just texture container which looks like this:
class Texture
{
public:
void LoadFromBuffer(unsigned char* buf, int w, int h);
float sizex, sizey;
GLuint texName;
};
And load from buffer function looks like this:
void Texture::LoadFromBuffer(unsigned char* buf, int w, int h)
{
this->sizex = w;
this->sizey = h;
glGenTextures(1, &this->texName);
glBindTexture(GL_TEXTURE_2D, this->texName);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, buf);
glBindTexture(GL_TEXTURE_2D, 0);
stbi_image_free(buf);
}
Then texture is created and i render it just normaly with opengl on squad:
void Renderer::Render(Quad& quadv)
{
glEnable(GL_TEXTURE_2D);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
glBindTexture(GL_TEXTURE_2D, quadv.texture->texName);
glBegin(GL_QUADS);
glColor3f(quadv.color.r, quadv.color.g, quadv.color.b);
glTexCoord2f(0.0, 0.0); glVertex2f(quadv.vertices[0].x, quadv.vertices[0].y);
glTexCoord2f(1.0, 0.0); glVertex2f(quadv.vertices[1].x, quadv.vertices[1].y);
glTexCoord2f(1.0, 1.0); glVertex2f(quadv.vertices[2].x, quadv.vertices[2].y);
glTexCoord2f(0.0, 1.0); glVertex2f(quadv.vertices[3].x, quadv.vertices[3].y);
glEnd();
glDisable(GL_TEXTURE_2D);
}
Quadv is object which contains vertices of quad, but its not important here.
Texture in quadv is the texture with letter a, that i loaded before.
Code looks just normal to me, but when i run it, the texture looks like this:
A letter is triple no matter what size texture is or size of quad.
And whole image is terrible.
I don't recall what is the default PixelMode when using FT.
But it looks to me like you are assuming that the bitmap provided by FT comes as a 32-bit RGBA color, which is probably not the case.
I'm gonna assume it comes as an 8-bit color. Try changing:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, buf);
to
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, buf);
We replaced GL_RGB which was the input format to GL_RED such that the tex image function expects 1 component per pixel. FT2 Documentation for further information
The bitmap contains 1 channel per pixel, encoded in a single byte. Since you're using legacy OpenGL, I recommend using the internal texture format GL_ALPHA. Therefore each element of the texture is treated as a single alpha component.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, buf);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, buf);
Enable Blending to show only the opaque part of the glyphs:
void Renderer::Render(Quad& quadv)
{
glEnable(GL_TEXTURE_2D);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
glBindTexture(GL_TEXTURE_2D, quadv.texture->texName);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBegin(GL_QUADS);
glColor3f(quadv.color.r, quadv.color.g, quadv.color.b);
glTexCoord2f(0.0, 0.0); glVertex2f(quadv.vertices[0].x, quadv.vertices[0].y);
glTexCoord2f(1.0, 0.0); glVertex2f(quadv.vertices[1].x, quadv.vertices[1].y);
glTexCoord2f(1.0, 1.0); glVertex2f(quadv.vertices[2].x, quadv.vertices[2].y);
glTexCoord2f(0.0, 1.0); glVertex2f(quadv.vertices[3].x, quadv.vertices[3].y);
glEnd();
glDisable(GL_TEXTURE_2D);
}
Related
I try to do an animation in OpenGL, my vertices and animations are working, but I would like to put a background image to it, with a file such as a bmp, or whatever.
So after a few reads I try the quads technique, which is simply to show a quad and bind a texture to it.
I use the STB_Image library, and it seems that I correctly point to my file (if I make mistakes on the filename I definitely got a much faster response from my program).
And I implemented a print to see if it catches the right file and it does!
My code look like this, and my result is a white square that appears in the correct coordinates, but no texture appears, the file is correctly loaded (with correct sizes printed), it just doesn't bind or appear on the square...
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 1.0, 1.0);
int width, height, nrChannels;
unsigned char* data = stbi_load("test.jpg", &width, &height, &nrChannels, 0);
if (data == NULL) {
printf("Error in loading the image\n");
exit(1);
}
printf("Loaded image with a width of %dpx, a height of %dpx and %d channels\n", width, height, nrChannels);
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glBegin(GL_QUADS);
glVertex2f(2.0, 1.0);
glTexCoord2f(2.0, 1.0);
glVertex2f(8.0, 1.0);
glTexCoord2f(8.0, 1.0);
glVertex2f(8.0, 7.0);
glTexCoord2f(8.0, 7.0);
glVertex2f(2.0, 7.0);
glTexCoord2f(2.0, 7.0);
glEnd();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
glDisable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
Any ideas?
glTexImage2D specify the two-dimensional texture image for the texture object. You have to do that the quad is drawn.
Furthermore 2 dimensional texturing has to be enabled by glEnable(GL_TEXTURE_2D).
The texture coordinates have to be in range [0.0, 1.0] (See How do opengl texture coordinates work?).
glTexCoord2f has to be set before glVertex2f, because the current color, normal and texture coordinates are associated with the vertex when glVertex is called.
Since you don't generate mipmaps (glGenerateMipmap), the texture minifying function (GL_TEXTURE_MIN_FILTER) has to be set to GL_LINEAR or GL_NEAREST. Else the texture would be mipmap incomplete.
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
glBindTexture(GL_TEXTURE_2D, 0);
glClear(GL_COLOR_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D, texture);
glEnable(GL_TEXTURE_2D);
glColor4f(1.0, 1.0, 1.0, 1.0);
glBegin(GL_QUADS);
glTexCoord2f(0.0, 1.0);
glVertex2f(2.0, 1.0);
glTexCoord2f(1.0, 1.0);
glVertex2f(8.0, 1.0);
glTexCoord2f(1.0, 0.0);
glVertex2f(8.0, 7.0);
glTexCoord2f(0.0, 0.0);
glVertex2f(2.0, 7.0);
glEnd();
glDisable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
glColor3f(1.0, 1.0, 1.0);
I'm using opengl to draw the graphics for simple game like space invaders. So far I have it rendering moving meteorites and a gif file quite nicely. I get the basics. But I just cant get the framebuffer working properly which I indent to render bitmap font to.
The first function will be called inside the render function only when the score changes, This will produce a texture containing the score characters. The second function will draw the texture containing the bitmap font characters to the screen every time the render function is called. I thought this would be a more efficient way to draw the score. Right now I'm just trying to get it drawing a square using the frameBuffer, but it seems that the coordinates range from -1 to 0. I thought the coordinates for a texture went from 0 to 1? I commented which vertex effects which corner of the square and it seems to be wrong.
void Score::UpdateScoreTexture(int* success)
{
int length = 8;
char* chars = LongToNumberDigits(count, &length, 0);
glDeleteTextures(1, &textureScore);//last texture containing previous score deleted to make room for new score
glGenTextures(1, &textureScore);
GLuint frameBufferScore;
glGenTextures(1, &textureScore);
glBindTexture(GL_TEXTURE_2D, textureScore);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
glGenFramebuffers(1, &frameBufferScore);
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferScore);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureScore, 0);
GLenum status;
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
std::cout << "status is: ";
std::cout << "\n";
switch (status)
{
case GL_FRAMEBUFFER_COMPLETE:
std::cout << "good";
break;
default:
PrintGLStatus(status);
while (1 == 1);
}
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferScore);
glBegin(GL_POLYGON);
glVertex3f(-1, -1, 0.0);//appears to be the bottom left,
glVertex3f(0, -1, 0.0);//appears to be the bottom right
glVertex3f(0, 0, 0.0);//appears to be the top right
glVertex3f(-1, 0, 0.0);//appears to be the top left
glEnd();
glDisable(GL_TEXTURE_2D);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D,0);
glDeleteFramebuffers(1, &frameBufferScore);
//glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, chars2);
}
void Score::DrawScore(void)
{
glEnable(GL_TEXTURE_2D);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glBindTexture(GL_TEXTURE_2D, textureScore);
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0); glVertex3f(0.7, 0.925, 0.0);
glTexCoord2f(0.0, 1.0); glVertex3f(0.7, 0.975, 0.0);
glTexCoord2f(1.0, 1.0); glVertex3f(0.975, 0.975, 0.0);
glTexCoord2f(1.0, 0.0); glVertex3f(0.975, 0.925, 0.0);
glEnd();
glFlush();
glDisable(GL_TEXTURE_2D);
}
Any ideas where I'm going wrong?
You have not set the glViewport, this may give you problems.
Another possibility is that you have the matrix set to something other than identity.
Ensure that you have reset the model-view and projection matrices to identity (or what you want them to be) before glBegin(GL_POLYGON) in UpdateScoreTexture() (You may wish to push the matrices to the stack before you make changes):
glViewport(0,0, framebufferWidth, framebufferHeight)
glMatrixMode(GL_PROJECTION)
glPushMatrix()
glLoadIdentity()
glMatrixMode(GL_MODELVIEW)
glPushMatrix()
glLoadIdentity()
Then put them back at the end of the function:
glViewport(0,0, width, height)
glMatrixMode(GL_PROJECTION)
glPopMatrix()
glMatrixMode(GL_MODELVIEW)
glPopMatrix()
I would like to draw text over content drawn in GL. I want the symbols themselves to be opaque, whilst the rest transparent, allowing the drawn content to be seen. The following code yields a text, which is the correct one, yet with a perfectly white background. My drawn content is completely absent.
How am I to solve this? I am using SDL 2.0, VSC
glPushMatrix();
/*Content drawn in GL*/
GLuint TextureID = 0;
SDL_Color Color = {30, 30, 30, 0};
TTF_Font * Font = TTF_OpenFont("Times.ttf", 30);
SDL_Surface * Message = TTF_RenderText_Blended(Font, "ASDASDASD", Color);
glGenTextures(1, &TextureID);
glBindTexture(GL_TEXTURE_2D, TextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, Message->w, Message->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, Message->pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, TextureID);
glBegin(GL_QUADS);
{
glColor4f(.5, .5, .5, 1);
glTexCoord2f(0,0); glVertex2f(MouseX/10, MouseY/10);
glTexCoord2f(1,0); glVertex2f((MouseX/10) + (Message->w / 10), MouseY/10);
glTexCoord2f(1,1); glVertex2f((MouseX/10) + (Message->w / 10), (MouseY/10) + (Message->h / 10));
glTexCoord2f(0,1); glVertex2f(MouseX/10, (MouseY/10) + (Message->h / 10));
}
glEnd();
glPopMatrix();
SDL_FreeSurface(Message);
SDL_GL_SwapWindow(GameWindow);
Think of it this way: When you create the texture above, you're drawing dark grey fully transparent (30,30,30,0) text against a fully transparent background (0,0,0,0 due to TTF_RenderText_Blended). You're then using immediate mode color blending to raster a quad of the same size (HINT: with no blend func and no mention of z buffer disable!).
P.S. you're leaking like crazy in the example:
glGenTexture every frame without a glDeleteTextures (you only need a
new one if it changes)
TTF_OpenFont without a TTF_CloseFont in every
frame (keep it only until you do not require rendering of that font)
TTF_RenderText_Blended surface isn't leaked but you could free it right after
glTexImage2D
I assume you're meaning to use the font as a mask to subtract from a solid color background? I generally create font textures white (full alpha) then apply color or masking in the shader. To accomplish a similar effect in immediate mode you could:
// startup
glEnable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
SDL_Color Color = {255, 255, 255, 255};
TTF_Font * Font = TTF_OpenFont("Times.ttf", 30);
SDL_Surface * Message = TTF_RenderText_Blended(Font, "ASDASDASD", Color);
TTF_CloseFont(Font);
glGenTextures(1, &TextureID);
glBindTexture(GL_TEXTURE_2D, TextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, Message->w, Message->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, Message->pixels);
SDL_FreeSurface(Message);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// frame render
glClear(GL_COLOR_BUFFER_BIT);
// you'll see this in many (immediate mode) 2D rendering examples
// without the blend eq below, this would render the white text on top of the quad's background color
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// use text as a mask to cut away from what is below
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
glBindTexture(GL_TEXTURE_2D, TextureID);
glBegin(GL_QUADS);
{
glColor4f(.5, .5, .5, 1);
glTexCoord2f(0,0); glVertex2f(MouseX/10, MouseY/10);
glTexCoord2f(1,0); glVertex2f((MouseX/10) + (Message->w / 10), MouseY/10);
glTexCoord2f(1,1); glVertex2f((MouseX/10) + (Message->w / 10), (MouseY/10) + (Message->h / 10));
glTexCoord2f(0,1); glVertex2f(MouseX/10, (MouseY/10) + (Message->h / 10));
}
glEnd();
// if done every frame, make sure to: glDeleteTextures(1, &TextureID);
SDL_GL_SwapWindow(GameWindow);
Very handy site here: http://www.andersriggelsen.dk/glblendfunc.php
I'm using the following to draw text in OpenGL (using SDL)
void renderText(const TTF_Font *font, const SDL_Color color,
const double& x, const double& y, const double& z, const std::string& text) {
bool textured = glIsEnabled(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_2D);
//test color: black
glColor3f(0,0,0);
SDL_Surface *Message = TTF_RenderText_Blended(const_cast<TTF_Font*>(font), text.c_str(), color);
GLuint Texture = 0;
//Generate an OpenGL 2D texture from the SDL_Surface
glGenTextures(1, &Texture);
glBindTexture(GL_TEXTURE_2D, Texture);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Message->w, Message->h, 0, GL_BGRA,
GL_UNSIGNED_BYTE, Message->pixels);
//Draw this texture on a quad with the given xyz coordinates.
glBegin(GL_QUADS);
glTexCoord2d(0, 0); glVertex3d(x, y, z);
glTexCoord2d(1, 0); glVertex3d(x+Message->w, y, z);
glTexCoord2d(1, 1); glVertex3d(x+Message->w, y+Message->h, z);
glTexCoord2d(0, 1); glVertex3d(x, y+Message->h, z);
glEnd();
//Clean up
glDeleteTextures(1, &Texture);
SDL_FreeSurface(Message);
if (!textured)
glDisable(GL_TEXTURE_2D);
}
However, the only thing shown is a rectangle at (x,y) (in this case (10,10)) with the background color set with glColor3f (black in this case). The texture created from the SDL_Surface isn't shown on the quad. The arguments passed to the function are all valid:
font: previously loaded TTF_Font != NULL
color: SDL_Color {255,255,255} (white)
x = 10
y = 10
z = 0
text = "test"
What's wrong here?
glOrtho(0.0, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0, -1.0, 10.0);
OpenGL v2.2
SDL v1.2.15
Kubuntu Raring x64
Update: When using ..._Solid instead of ..._Blended, calling glColor3f(1,1,1) and passing SDL_Color {255,255,255} results in some strange stuff:
The quad appears where it should appear, and it shows strange content. What's wrong?
THe problem was that I didn't call glBlendFunc before calling above method. So OpenGL had a wrong blending mode set. After using glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);it looks fine.
I've trying to load different textures to the GL_TEXTUREX variables and then assign them to different spheres. but so far I've having problems. I tried some of the suggestions in post like this and this
but couldnt solve it.
This is part of my code:
GLuint textures[2];
void LoadTextures(std::string const& dirname)
{
glGenTextures(2, textures);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures[0]);
Image_t sun = loadPNG(std::string(dirname + "/sun.png"));
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1024, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, &(sun.data[0]));
glBindTexture(GL_TEXTURE_2D, textures[0]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textures[1]);
Image_t mercury = loadPNG(std::string(dirname + "/mercury.png"));
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1024, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, &(mercury.data[0]));
}
and
void draw(void)
{
glEnable (GL_TEXTURE_2D);
glBindTexture (GL_TEXTURE_2D, textures[0]);
glPushMatrix();
glTranslatef(0.4,0,0);
gluSphere(sun, 0.5, 36, 36); //I want this sphere to use the texture GL_TEXTURE0
glPopMatrix();
glDisable(GL_TEXTURE_2D);
}
You need to call glBindTexture just before performing a draw call:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures[0]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textures[1]);
draw_things();
In the above example, whatever you draw with draw_things will have access to both textures 0 and 1 (GL_TEXTURE0 and GL_TEXTURE1).
Now, if what you want is to draw many things, but each with a different texture, then you need to:
glActiveTexture(GL_TEXTURE0); // activate any texture unit
glEnable(GL_TEXTURE_2D); // make sure texturing is enabled
glBindTexture(GL_TEXTURE_2D, thing1_texture);
draw_thing1();
glBindTexture(GL_TEXTURE_2D, thing2_texture);
draw_thing2();
// etecetera