How to render 3D Text in OpenGL? - c++

I want to Draw the 3D text in OpenGL viewport.
I have applied following method but it shows text at 2D positions.
void renderBitmapString(float x, float y, float z,void *font,const char *string){
const char * c;
//glRasterPos2f(x, y);
// glutBitmapCharacter(font, string);
glRasterPos3f(x, y ,z);
//glRasterPos3i(x, y ,z);
for (c=string; *c != '\0'; c++) {
glutBitmapCharacter(font, *c);
}
}

OpenGL does not render text. It is not part of the standard. What it does render is textures or bitmap images. So the way to render text is to use some sort of 2D rendering library like Cairo. This should help you create a bitmap with text in it. Once you have a bitmap, you can then render the bitmap as a texture. Just be careful though, Cairo uses BGRA format for its bitmaps so you might need to swizzle the red and blue components to get things working.

Switch to glutStrokeCharacter().
Or render your glutBitmapCharacter()s to a texture via FBO.

Related

SDL2 (C++) How to render an image smaller

I have finally started getting used to SDL 2's basic functions for rendering and I have stumbled across a problem that I believe the public might be able to answer. In my code I generate some text and using some code from a tutorial, am able to load the text as a texture (namely Lazy foo's tutorial). This texture now has a width and a height based on font size and how much text was entered. Another function I use loads in a square made of fancy boardering that I wish to use as a menu. This square is 200x200. As an example, if the text texture is 100x160, I want the square to now render as perhaps a 120x180 image (essentially compressing it to be a similar size as the text texture.
tl;dr:
I have 200x200 square.
I have 100x160 text texture
I want to render 200x200 square as a 120x160 square and render 100x160 text inside square.
***loadFromRenderedText takes a ttf font, a string, and a color (RGBA) to create an image texture based on the string -> generates own width/height
menuTextTexture.loadFromRenderedText(menuFont, "Info Item Skill Back",menuTextColor);
menuSize.x = 0;
menuSize.y = 0;
menuSize.w = menuTextTexture.getWidth() + boarderW;
menuSize.h = menuTextTexture.getHeight() + boarderW;
***menuSize is an SDL_Rect
menuBoxTexture.TextRender(XmenuRenderLocX, XmenuRenderLocY, &menuSize, 0, NULL, SDL_FLIP_NONE);
menuTextTexture.render(XmenuRenderLocX+boarderW, XmenuRenderLocY+boarderW);
TextRender and render do the same thing except render uses a scaling factor to multiply the clip size to be bigger (which I leave blank -> clip is then NULL and the basic height/width are used). For TextRender, I specify the render dimensions by passing the menuSize SDL rect. this takes the 200x200 square and renders only the 120x160 of the square at the (XmenuRenderLocX, XmenuRenderLocY)... thus essentially cropping the square, which is not what I want... I want to resize the square.
Any help will be greatly appreciated
Originally, I was using the provided LTexture::render function that was created for Lazy Foo's tutorial. See below code
void LTexture::render( int x, int y, SDL_Rect* clip, double angle, SDL_Point* center, SDL_RendererFlip flip )
{
//Set rendering space and render to screen
SDL_Rect renderQuad = { x, y, mWidth, mHeight };
//Set clip rendering dimensions
if( clip != NULL )
{
renderQuad.w = SCALE_SIZE*(clip->w);
renderQuad.h = SCALE_SIZE*(clip->h);
}
else if(mTexture ==NULL)
{
printf("Warning: Texture to Render is NULL!\n");
}
//Render to screen
SDL_RenderCopyEx( gRenderer, mTexture, clip, &renderQuad, angle, center, flip );
}
But because I didn't fully understand until now how exactly the function renders, I wasn't actually telling it to render at a new dimension (except in the case where I magnify everything with a SCALE_SIZE)
I made a new function for more control
void LTexture::DefinedRender(SDL_Rect* Textureclip, SDL_Rect* renderLocSize, double angle, SDL_Point* center, SDL_RendererFlip flip)
{
//Set clip rendering dimensions
if(mTexture ==NULL)
{
printf("Warning: Texture to Render is NULL!\n");
}
//Render to screen
SDL_RenderCopyEx( gRenderer, mTexture, Textureclip, renderLocSize, angle, center, flip );
}
And now everything works like I want it to.

Set the pixel format AND create texture from surface in SDL

I am currently trying some things using pixel manipulation and I would like to set the pixel format of a SDL_Surface. The problem here is that I am loading up an image with the SDL_image.h. So I have to create a texture from a surface like this:
surface = IMG_Load(filePath);
texture = SDL_CreateTextureFromSurface(renderer, surface);
So I can't use the following function, which I would like to, or can I?:
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, 640, 480);
The thing is, that I want to set the SDL_PixelFormat to be able to mess around with the pixels. How can I both do this and create the texture based on a surface?
The SDL2 API provides the function SDL_ConvertSurface:
SDL_Surface* SDL_ConvertSurface(SDL_Surface* src,
const SDL_PixelFormat* fmt,
Uint32 flags)
You should be able to do
surface = IMG_Load(filePath);
// Call SDL_ConvertSurface to set the pixel format of the surface you have created.
texture = SDL_CreateTextureFromSurface(renderer, surface);
Reference: https://wiki.libsdl.org/SDL_ConvertSurface

Set an image as background in Open GL

I want to achieve the following result in OpenGL (and C++): I have a plot I want to use as background of an animation and I want it to be fix when some points move on its surface.
For example (see the image): I want to calculate the level plot of 3 variables function (black and white in the photo), I want to set it as background and show an animation of points moving on the surface (red points in the photo).
What is the best method to achieve it and have good performances?
One possibility would be to draw your background image as a texture on a quad behind the other points (e.g., points at Z=0.1, quad at Z=0.2). Since you (presumably) don't want the quad scaled compared to the points, you probably want to use an orthographic projection, not perspective.
It really depends on which version of OpenGL you are using.
If you are using SDL and OpenGL 2.x, it is very easy, but if you are using something like GLFW and OpenGL 3.x, it will be quite hard.
But this is how you would do it with SDL and OpenGL 2.1(not all code is included, you will have to write some of it yourself):
void plot(SDL_Surface *surf, int x, int y, int r, int g, int b)
{
int color = SDL_MapRGB(surf->format, r, g, b);
Uint32 *framebuffer = (Uint32*) surf->pixels;
framebuffer[y * surf->w + x] = color;
}
int main()
{
/* set up SDL and OpenGL */
SDL_Surface *img = SDL_LoadBMP("base_background.bmp");
/* plot functions like this: */
plot(img, x, f(x), 255, 0, 0); // example colours
/* create an OpenGL surface(code can be found on google easily) */
GLuint tex = create_GL_texture(img);
/* draw it like you would normally */
glBindTexture(GL_TEXTURE_2D, tex);
glBegin(GL_QUADS);
...
glEnd();
return 0;
}
For OpenGL 3.x, it is a bit harder. You can find tutorials here.

SDL + SDL_ttf: Transparent blended text?

I want to render an anti-aliased string on an SDL_Surface with a given alpha channel.
I figured out it is possible to render:
an anti-aliased string with the Blended variant of the string render method (ie: TTR_RenderText_Blended). But then I can't make it transparent.
An anti-aliased string with the Shaded method. But then there is a solid background. The background and the drawn string can be made transparent, but then the solid background is still there. Passing it a transparent background color is also not possible.
an non-anti-aliased string, which I can make transparent like I want with the Solid variant. But it is not anti-aliased.
Thanks
I know I'm a bit late on this one :/
According to SDL documentation on SDL_SetAlpha:
Note that per-pixel and per-surface alpha cannot be combined; the per-pixel alpha is always used if available.
So regular SDL_BlitSurface/SDL_SetAlpha won't work here. But it can be done:
The only ways I can think of alpha-blending TTF_RenderText_Blended output are to use OpenGL, or adjust the alpha values of each pixel in the surface.
By adjusting per-pixel alpha values
You can do this by scaling the per-pixel alpha values from [0, 255] to a new range [0, alpha]:
// Changes a surface's alpha value, by altering per-pixel alpha if necessary.
void SetSurfaceAlpha (SDL_Surface *surface, Uint8 alpha)
{
SDL_PixelFormat* fmt = surface->format;
// If surface has no alpha channel, just set the surface alpha.
if( fmt->Amask == 0 ) {
SDL_SetAlpha( surface, SDL_SRCALPHA, alpha );
}
// Else change the alpha of each pixel.
else {
unsigned bpp = fmt->BytesPerPixel;
// Scaling factor to clamp alpha to [0, alpha].
float scale = alpha / 255.0f;
SDL_LockSurface(surface);
for (int y = 0; y < surface->h; ++y)
for (int x = 0; x < surface->w; ++x) {
// Get a pointer to the current pixel.
Uint32* pixel_ptr = (Uint32 *)(
(Uint8 *)surface->pixels
+ y * surface->pitch
+ x * bpp
);
// Get the old pixel components.
Uint8 r, g, b, a;
SDL_GetRGBA( *pixel_ptr, fmt, &r, &g, &b, &a );
// Set the pixel with the new alpha.
*pixel_ptr = SDL_MapRGBA( fmt, r, g, b, scale * a );
}
SDL_UnlockSurface(surface);
}
}
I know it looks scary, but it's pretty straight-forward. The key line is here:
*pixel_ptr = SDL_MapRGBA( fmt, r, g, b, scale * a );
You can use it like this:
text_surface = TTF_RenderText_Blended( font, "Hello World!", color );
SetSurfaceAlpha( text_surface, 128 );
With OpenGL
If using OpenGL, things are lot easier. Assuming you convert the SDL_Surface from TTF_RenderText_Blended to a GL texture, you can just use:
glColor4f( 1.0, 1.0, 1.0, Alpha );
before you render it on a textured quad.
But don't forget to enable alpha blending first!
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
All you have to do is call SDL_SetAlpha on your SDL_Surface after creating the sprite on which the text is rendered, but before calling SDL_DisplayFormatAlpha on that sprite.
// Make the sprite with the text on it
SDL_Surface *swSprite = TTF_RenderText_Solid( font, text, textColor ) ;
// CALL SET ALPHA NOW
SDL_SetAlpha( swSprite, SDL_SRCALPHA, 128 ) ; // 50% opacity
// OK, NOW you can convert it to display format. I'm presuming
// you called `SDL_SetVideoMode` with the `SDL_HWSURFACE` flag set previously
SDL_Surface* hwSprite = SDL_DisplayFormatAlpha( swSprite ) ;
// If you invert the above 2 steps, it won't work.
// We don't need the software sprite anymore
SDL_FreeSurface( swSprite ) ;
// Now draw the hwSprite as normal
SDL_BlitSurface( hwSprite, NULL, screen, &spriteLocation );
The way to do this with a TTF is to use SDL_SetTextureAlphaMod() Something like this
SDL_Surface *surface;
SDL_Texture *texture;
SDL_Color color = {255, 255, 255, 128}
surface = TTF_RenderText_Blended_Wrapped(myFont, "HI!", color, 100);
// myFont and the _renderer is pre-defined somewhere in the game
texture = SDL_CreateTextureFromSurface(_renderer, surface);
SDL_SetTextureAlphaMod(texture, color.a);
SDL_RenderCopy(_renderer, texture, NULL, &dest);
SDL_DestroyTexture(texture);
SDL_FreeSurface(surface);
/*...*/
SDL_RenderPresent(_renderer);
https://wiki.libsdl.org/SDL_SetTextureAlphaMod
Why not use bitmapped fonts? You could build a png image with alpha channel. I think that SDL_ttf works with the same system, it builds an image an internally uses bitmapped fonts.

Is there a way to split one texture into an array of them using SOIL in C++?

I'm using SOIL in my project, and I need to take in a single texture, and than convert it into an array of textures using different parts of the first texture. (To use a sprite sheet).
I'm using SDL and OpenGL by the way.
The typical way to use sprite sheeting with a modern 3D api like OpenGL is to use texture coordinates to address different parts of your individual texture. While you can split it up it is much more resource friendly to use texture coordinates.
For example, if you had a simple sprite sheet with 3 frames horizontally, each 32 pixels by 32 pixels (for a total size of 96x32), you would use the following code to draw the 3rd frame:
// I assume you have bound your source texture
// This is the U coordinate's origin in texture space
float xStart = 64.0f / 96.0f;
// This is one frame width in texture space
float xIncrement = 32.0f / 96.0f;
glBegin(GL_QUADS);
glTexCoord2f(xStart, 0);
glVertex2f(-16.0f, 16.0f);
glTexCoord2f(xStart, 1.0f);
glVertex2f(-16.0f, -16.0f);
glTexCoord2f(xStart + xIncrement, 0);
glVertex2f(16.0f, 16.0f);
glTexCoord2f(xStart + xIncrement, 1.0f);
glVertex2f(16.0f, -16.0f);
glEnd();