I have an OpenGL context on which I draw successfully using OpenGL.
I need to draw a specific rectangle of an IOSurface to this context.
What is the best way to do this on 10.8?
NOTE:
I know how to do this on 10.9 using CoreImage (by createing a CIImage from the IOSurface, and render it with [CIContext drawImage:inRect:fromRect]).
However, this does not work well for me on 10.8 (each raw of the image is displayed with a different offset, and the image is distorted diagonally).
Edit: Here is the code that works on 10.9 but not on 10.8:
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
CIImage* ciImage = [[CIImage alloc] initWithIOSurface:surface plane:0 format:kCVPixelFormatType_32BGRA options:#{kCIImageColorSpace : (__bridge id)colorSpace}];
NSRect flippedFromRect = fromRect;
// Flip rect before passing to CoreImage:
{
flippedFromRect.origin.y = IOSurfaceGetHeight(surface) - fromRect.origin.y - fromRect.size.height;
}
[ciContext drawImage:ciImage inRect:inRect fromRect:flippedFromRect];
CGColorSpaceRelease(colorSpace);
Here is the solution by wrapping the IOSurface with an OpenGL texture and draw the texture to the screen. This assumes a similar API to [CIContext render:toIOSurface:bounds:colorSpace:] but a vertically flipped OpenGL coordinate system.
// Draw surface on OpenGL context
{
// Enable the rectangle texture extenstion
glEnable(GL_TEXTURE_RECTANGLE_EXT);
// 1. Create a texture from the IOSurface
GLuint name;
{
CGLContextObj cgl_ctx = ...
glGenTextures(1, &name);
GLsizei surface_w = (GLsizei)IOSurfaceGetWidth(surface);
GLsizei surface_h = (GLsizei)IOSurfaceGetHeight(surface);
glBindTexture(GL_TEXTURE_RECTANGLE_EXT, name);
CGLError cglError =
CGLTexImageIOSurface2D(cgl_ctx, GL_TEXTURE_RECTANGLE_EXT, GL_RGBA, surface_w, surface_h, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, surface, 0);
glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 0);
}
// 2. Draw the texture to the current OpenGL context
{
glBindTexture(GL_TEXTURE_RECTANGLE_EXT, name);
glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glBegin(GL_QUADS);
glColor4f(0.f, 0.f, 1.0f, 1.0f);
glTexCoord2f( (float)NSMinX(fromRect), (float)(NSMinY(fromRect)));
glVertex2f( (float)NSMinX(inRect), (float)(NSMinY(inRect)));
glTexCoord2f( (float)NSMaxX(fromRect), (float)NSMinY(fromRect));
glVertex2f( (float)NSMaxX(inRect), (float)NSMinY(inRect));
glTexCoord2f( (float)NSMaxX(fromRect), (float)NSMaxY(fromRect));
glVertex2f( (float)NSMaxX(inRect), (float)NSMaxY(inRect));
glTexCoord2f( (float)NSMinX(fromRect), (float)NSMaxY(fromRect));
glVertex2f( (float)NSMinX(inRect), (float)NSMaxY(inRect));
glEnd();
glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 0);
}
glDeleteTextures(1, &name);
}
If you need to draw in the display's color profile, you can explicitly call ColorSync and pass it your source profile and destination profile. It will return to you a “recipe” to perform the color correction. That recipe actually has a linearization, a color conversion (a 3x3 conversion matrix) and a gamma.
FragmentInfo = ColorSyncTransformCopyProperty (transform, kColorSyncTransformFullConversionData, NULL);
If you like, you can combine all those operations into a 3D lookup table. That's actually what happens in the color management of many of the OS X frameworks and applications.
References:
Apple TextureUpload sample code
Draw IOSurfaces to another IOSurface
OpenGL Options for Advanced Color Management
Related
I have got several meshes (~100) of the same complex object in various poses with slightly different rotation and translation parameters. The object consists of multiple rigid components like arms and legs.
The goal is to generate a unique grayscale picture showing the accumulation of these poses for a particular body part. The heat-map obtained gives an idea of probable pixel locations for the body part, where white represents maximum probability, and black minimum (the lighter the higher probability). Say I'm interested in the accumulation of the legs. If many leg pose samples lie on the same (x,y) pixel location, than I expect to see light pixels there. Ultimately the leg poses might not exactly overlap, so I also expect to see a smooth transition to the black low probability around the leg silhouette boundaries.
To solve this task I have decided to use rendering in OpenGL frame buffers as these are known to be computationally cheap, and because I need to run this accumulation procedure very often.
What I did is the following. I accumulate the corresponding renderings of the body part I'm interested in (let's still keep the leg example) on the same frame buffer 'fboLegsId' using GL_BLEND. In order to discriminate between the legs
and the rest of the body, I texture the mesh with two colors:
rgba(gray,gray,gray,255) for the legs, where gray = 255 / Number of samples = 255/100
rgba(0,0,0,0) for the rest of the body
Then I accumulate the 100 renderings (which for the leg should sum up to white = 255) by doing the following:
glBindFramebuffer(GL_FRAMEBUFFER, fboLegsId);
glClearColor(0,0,0,255);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBlendFunc(GL_ONE, GL_ONE);
glEnable(GL_BLEND);
for each sample s = 0...100
mesh.render(pose s);
end
glReadPixels(...)
This performs almost as I expected. I do obtain the smooth grayscale heat-map I wanted. However there are self-occlusion problems
which arise even when I use only 1 sample. Say for a single pose sample, one of the arms moved before the leg, partially occluding them. I expect the influence of the occluded leg parts to be cancelled during rendering. However it renders as if the arm is invisible/translucent, allowing for pixels behind to be fully shown. This leads to wrong renderings and therefore wrong accumulations.
If I simple disable blending, I see the correct self-occlusion aware result. So, apparently the problem lies somewhere at blending time.
I also tried different blending functions, and so far the following one produced the closer results to a self-occlusion aware accumulation approach:
glBlendFunc(GL_ONE, GL_SRC_ALPHA);
Anyway there is still a problem here: one single sample looks now correct; two or more accumulated samples instead show overlapping artefacts with other samples. It looks like each accumulation replaces the current buffer pixel if the pixel is not part of the legs. And if the leg was found many times in front of the (let's say) the arm, than it becomes darker and darker, instead of lighter and lighter.
I tried to fix this by clearing depth buffer at each rendering iteration enabling depth computations, but this did not solve the problem.
I feel like there is either something conceptually wrong in my approach, or a small mistake somewhere.
I've tried a different approach based on the suggestions which performs as expected. Now I'm working with 2 frame buffers. The first one (SingleFBO) is used to render single samples with correct self-occlusion handling. The second (AccFBO) is used to accumulate the 2D textures from the first buffer using blending. Please, check my code below:
// clear the accumulation buffer
glBindFramebuffer(GL_FRAMEBUFFER, AccFBO);
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
for each sample s = 0...100
{
// set rendering destination to SingleFBO
glBindFramebuffer(GL_FRAMEBUFFER, SingleFBO);
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
mesh->render(pose s);
glDisable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
// set rendering destination to the accumulation buffer
glBindFramebuffer(GL_FRAMEBUFFER, AccFBO);
glClear(GL_DEPTH_BUFFER_BIT);
glBlendFunc(GL_ONE, GL_ONE);
glEnable(GL_BLEND);
// draw texture from previous buffer to a quad
glBindTexture(GL_TEXTURE_2D, textureLeg);
glEnable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glDepthMask(GL_FALSE);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glBegin( GL_QUADS );
{
glTexCoord2f(0,0); glVertex2f(-1.0f, -1.0f);
glTexCoord2f(1,0); glVertex2f(1.0f, -1.0f);
glTexCoord2f(1,1); glVertex2f(1.0f, 1.0f);
glTexCoord2f(0,1); glVertex2f(-1.0f, 1.0f);
}
glEnd();
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
// restore
glDisable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glDepthMask(GL_TRUE);
glDisable(GL_BLEND);
}
glBindFramebuffer(GL_FRAMEBUFFER, AccFBO);
glReadPixels(...)
Please, check also my (standard) code for initializing the SingleFBO (similarly for AccFBO):
// create a texture object
glGenTextures(1, &textureLeg);
glBindTexture(GL_TEXTURE_2D, textureLeg);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
GL_RGB, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, 0);
// create a renderbuffer object to store depth info
glGenRenderbuffers(1, &rboLeg);
glBindRenderbuffer(GL_RENDERBUFFER, rboLeg);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT,
width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// create a framebuffer object
glGenFramebuffers(1, &SingleFBO);
glBindFramebuffer(GL_FRAMEBUFFER, SingleFBO);
// attach the texture to FBO color attachment point
glFramebufferTexture2D(GL_FRAMEBUFFER, // 1. fbo target: GL_FRAMEBUFFER
GL_COLOR_ATTACHMENT0, // 2. attachment point
GL_TEXTURE_2D, // 3. tex target: GL_TEXTURE_2D
textureLeg, // 4. tex ID
0); // 5. mipmap level: 0(base)
// attach the renderbuffer to depth attachment point
glFramebufferRenderbuffer(GL_FRAMEBUFFER, // 1. fbo target: GL_FRAMEBUFFER
GL_DEPTH_ATTACHMENT, // 2. attachment point
GL_RENDERBUFFER, // 3. rbo target: GL_RENDERBUFFER
rboLeg); // 4. rbo ID
// check FBO status
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if(status != GL_FRAMEBUFFER_COMPLETE)
error(...);
// switch back to window-system-provided framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
Here's a different approach:
Create two frame buffers: normal and acc. normal frame buffer should have a texture storage (with glFramebufferTexture2D).
Here's the basic algorithm:
Clear acc to black
Bind normal, clear to black, and render scene with white legs, and other parts black
Bind acc, render a full screen rectangle, with normal texture on it, with blend mode GL_ONE, GL_ONE
Forward the animation, and if it haven't finished, goto 2.
You have the result in acc
So, basically, acc will contain the individual frames summed.
Note: I am using Legacy OpenGL
So I have encountered a problem where only a few .png images get rendered correctly.
Here is an example screenshot of rendered game (Left: enemyRed.png, Right: (ship.png)):
Here is the code I use to load images:
SpriteImage* SDLOpenGL::loadImage(std::string path) {
SDL_Surface * surface = IMG_Load(path.c_str());
SpriteImage * image = new SpriteImage(&*surface);
return image;
}
Here is the SpriteImage class:
SpriteImage::SpriteImage(SDL_Surface *surface) {
this->surface = surface;
this->TextureID = 0;
}
void SpriteImage::bind() {
glTexImage2D(GL_TEXTURE_2D,
0,
this->mode,
this->surface->w,
this->surface->h,
0,
this->mode,
GL_UNSIGNED_BYTE,
this->surface->pixels
);
glBindTexture(GL_TEXTURE_2D, this->TextureID);
}
int SpriteImage::getWidth() {
return this->surface->w;
}
int SpriteImage::getHeight() {
return this->surface->h;
}
Here is where I render the images:
(note, this->getCurrentImage() returns a "SpriteImage")
void Sprite::draw(float delta) {
this->getCurrentImage()->bind();
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(0.0f, 0.0f);
glTexCoord2f(0, 1);
glVertex2f(0.0f, this->getHeight());
glTexCoord2f(1, 1);
glVertex2f(this->getWidth(), this->getHeight());
glTexCoord2f(1, 0);
glVertex2f(this->getWidth(), 0.0f);
glEnd();
this->next();
}
The image that doesn't render has a width indivisible by four. You should use glPixelStorei with GL_UNPACK_ALIGNMENT before glTexImage2D to specify alignment of your rows in memory -- which are probably unaligned (OpenGL assumes by default that they are four-byte aligned).
You should first glBindTexture and then upload the data with glTexImage2D.
Do you ever call to glGenTextures? You should initialize the TextureID with glGenTextures.
You should not upload the texture each and every time you bind the texture. Instead upload it in the constructor, then to switch textures you only need to glBindTexture your TextureID.
Important: I have to work with the fixed pipeline (I have no voice in this matter).
I have to modify some existing OpenGL code (a panoramic picture viewer, where the panorama is split into the six faces of a cube) so we're able to draw lines/points on top of the loaded textures, where the points are the mouse coordinates unprojected to object coordinates.
I wrote a test program with a coloured cube just to try the line painting on top of it:
I got this with the code pushing the GL_DEPTH_BUFFER_BIT attribute to the stack, disabling it before painting the points and poping the stack attribute after I have done with the painting.
I tried to use that same approach in the existing application, but I got these results (here, I'm trying only to paint a point):
I specified red as the color for the point but, as you can see, it doesn't have the desired one. I thought it might be due to blending and that it might be mixing its color with the underlying texture, so I pushed the GL_BLEND attribute to the stack as well and disabled it before painting, but the point isn't getting the desired color anyway.
What is happening here? Is there a way to "force" the pipeline to paint the point red?
initCube() : this is call before updating the GL scene.
void panoViewer::initCube() {
makeCurrent();
if(texture){
glDisable( texture );
textName = 0;
texture = 0;
}
glDisable( GL_TEXTURE_GEN_S );
glDisable( GL_TEXTURE_GEN_T );
glDisable( GL_TEXTURE_GEN_R );
glFrontFace( GL_CCW );
glEnableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
texture = GL_TEXTURE_CUBE_MAP;
textName = texnms[1];
glEnableClientState(GL_NORMAL_ARRAY);
glTexGenf( GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP );
glTexGenf( GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP );
glTexGenf( GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP );
glEnable( GL_TEXTURE_GEN_S );
glEnable( GL_TEXTURE_GEN_T );
glEnable( GL_TEXTURE_GEN_R );
// Add the textures to the cube faces.
// ...
}
initializeGL() :
void panoViewer::initializeGL() {
qglClearColor(Qt::black);
glShadeModel(GL_SMOOTH);
glEnable(GL_CULL_FACE);
glEnable( GL_DEPTH_TEST );
// create texture objects
glGenTextures( 1, textName );
glBindTexture( GL_TEXTURE_CUBE_MAP, textName );
// find the largest feasible textures
maxTex2Dsqr = maxTexSize( GL_PROXY_TEXTURE_2D, max2d, max2d );
maxTex2Drec = maxTexSize( GL_PROXY_TEXTURE_2D, max2d, max2d / 2 );
maxTexCube = maxTexSize( GL_PROXY_TEXTURE_CUBE_MAP, maxcube, maxcube );
// constant texture mapping parameters...
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
// for cube maps...
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// enable alpha blending for overlay
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_POINT_SMOOTH);
glEnable(GL_LINE_SMOOTH);
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glDisable(GL_LIGHTING);
// Create display list: dispList
// ...
}
paintGL() :
void panoViewer::paintGL() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if(texture) {
glBindTexture(texture, textName);
glEnable( texture );
}
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glRotated( 180, 0, 1, 0 ); // camera looks at the front of the van
glRotated( 180, 0, 0, 1 ); // van's roof points to the sky
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// double hFOV, vFOV; // angular size at sphere center (deg)
// double minFOV, maxFOV; // limits on vFOV
// double wFOV; // vert angle at eye (deg) sets magnification
double hhnear = Znear * tan( 0.5 * RAD(wFOV) ),
hwnear = hhnear * aspectRatio,
dxnear = 2 * hwnear * fcompx,
dynear = 2 * hhnear * fcompy;
glFrustum( -(hwnear + dxnear), hwnear - dxnear,
-(hhnear + dynear), hhnear - dynear,
Znear, Zfar
);
glRotated( 180, 0, 1, 0 );
glTranslated( eyex, eyey, eyez );
glRotated( tiltAngle, 1, 0, 0 );
glRotated( panAngle, 0, 1, 0 );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glCallList(dispList);
glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
// Paint the point in red
// ...
glPopAttrib();
}
UPDATE: I forgot to mention that the code is based in Qt. It uses the QtOpenGL module extensively.
UPDATE #2: I've added some code.
In the fixed function pipeline, there are many states which could lead to the vertex color beeing completely ignored.
As Reto Koradi pointed out in the comments, when lighting is enabled, the colors have no effect (unless GL_COLOR_MATERIAL is enabled, in which case the color value is used to update the material parameters used for the lighting equation.)
As I pointed out in the comments, another case is texturing. Depending on the GL_TEX_ENV_MODE selected, the fragment's color (as determined by lighting, or directly interpolated from the vertex colors) is modulated by the texture color, or completely replaced. In that case, disabling texturing for every texture unit in use can solve the issue.
i am seeing this problem where the textures disappear after the application has been used for a minutes or two. why would the textures be disappearing? the 3d cube remains on the screen at all times. the place which the textures were appear as white boxes when the textures disappear.
my DrawGLScene method looks like this:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer
glLoadIdentity(); // Reset The Current Modelview Matrix
glTranslatef(0.0f, 0.0f, -7.0f); // Translate Into The Screen 7.0 Units
//rotquad is a value that is updated as the user interacts with the ui by +/-9 to rotate the cube
glRotatef(rotquad, 0.0f, 1.0f, 0.0f);
//cube code here
RECT desktop;
const HWND hDesktop = GetDesktopWindow();
GetWindowRect(hDesktop, &desktop);
long horizontal = desktop.right;
long vertical = desktop.bottom;
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(-5.0, 3, 3, -5.0, -1.0, 10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable(GL_CULL_FACE);
glEnable(GL_TEXTURE_2D);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glClear(GL_DEPTH_BUFFER_BIT);
glColor4f(255.0f, 255.0f, 255.0f, 0.0f);
if (hoverRight) {
imageLoaderOut(outImage);
imageLoaderIn(inImage);
imageLoaderUp(upImage);
imageLoaderLeft(leftHover);
imageLoaderDown(upImage);
imageLoaderRight(rightImage);
}
// code for hover left, up and down are the same as hover right code above
glDisable(GL_TEXTURE_2D);
return TRUE; // Keep Going
}
this method is one of the imageLoad methods (others being called are almost identical, except for location/position..
void imageLoaderOut(const char* value)
{
FIBITMAP* bitmap60 = FreeImage_Load(
FreeImage_GetFileType(value, 0),
value, PNG_DEFAULT);
FIBITMAP *pImage60 = FreeImage_ConvertTo32Bits(bitmap60);
int nWidth60 = FreeImage_GetWidth(pImage60);
int nHeight60 = FreeImage_GetHeight(pImage60);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nWidth60, nHeight60, 0, GL_RGBA, GL_UNSIGNED_BYTE, (void*)FreeImage_GetBits(pImage60));
FreeImage_Unload(pImage60);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex2f(2.8f, -1.1f); // moves BOTTOM EDGE UP or DOWN - stretches length of image
glTexCoord2f(0.0f, 1.0f); glVertex2f(2.8f, -1.9f);
glTexCoord2f(1.0f, 1.0f); glVertex2f(2.1f, -1.9f);
glTexCoord2f(1.0f, 0.0f); glVertex2f(2.1f, -1.1f); // moves BOTTOM EDGE UP or DOWN - stretches length of image
glEnd();
}
It's just a guess, but you have a severe design issue in your code, combined with memory leak, that can lead to such undefined results as you've described.
First, in imageLoaderOut() you are reading all the textures each frame from HDD, converting it to 32 bpp and sending data to OpenGL. You call it from DrawGLScene, which means you do it each frame. It's really invalid way to do things. You don't need to load resources each frame. Do it once and for all in some kind if Initialize() function, and just use GL resource on drawing.
Then, I think here you have memory leak, because you never unloading bitmap60. As you make loading each frame, possibly thousands times per second, this unreleased memory accumulating. So, after some time, something goes really bad and FreeImage refuses to load textures.
So, possible solution is to:
move resource loading to initialization phase of your application
free leaked resources: FreeImage_Unload(bitmap60) in each loading function
Hope it helps.
The problem seems to be in glTexImage2D. The manual can be found here: http://www.opengl.org/sdk/docs/man/xhtml/glTexImage2D.xml
In particular, they said that:
glTexImage2D specifies the two-dimensional texture for the current texture unit, specified with glActiveTexture.
Once you are calling glTexImage2D multiple times, it seems that your are overwriting the same location multiples times.
When I render my text using TTF_RenderUTF8_Blended I obtain a solid rectangle on the screen. The color depends on the one I choose, in my case the rectangle is red.
My question
What am I missing? It seems like I'm not getting the proper Alpha values from the surface generated with SDL_DisplayFormatAlpha(TTF_RenderUTF8_Blended( ... )), or am I? Does anyone recognize or know the problem?
Additionnal informations
If I use TTF_RenderUTF8_Solid or TTF_RenderUTF8_Shaded the text is drawn properly, but not blended of course.
I am also drawing other textures on the screen, so I draw the text last to ensure the blending will take into account the current surface.
Edit:SDL_Color g_textColor = {255, 0, 0, 0}; <-- I tried with and without the alpha value, but I get the same result.
I have tried to summarize the code without removing too much details. Variables prefixed with "g_" are global.
Init() function
// This function creates the required texture.
bool Init()
{
// ...
g_pFont = TTF_OpenFont("../arial.ttf", 12);
if(g_pFont == NULL)
return false;
// Write text to surface
g_pText = SDL_DisplayFormatAlpha(TTF_RenderUTF8_Blended(g_pFont, "My first Text!", g_textColor)); //< Doesn't work
// Note that Solid and Shaded Does work properly if I uncomment them.
//g_pText = SDL_DisplayFormatAlpha(TTF_RenderUTF8_Solid(g_pFont, "My first Text!", g_textColor));
//g_pText = SDL_DisplayFormatAlpha(TTF_RenderUTF8_Shaded(g_pFont, "My first Text!", g_textColor, g_bgColor));
if(g_pText == NULL)
return false;
// Prepare the texture for the font
GLenum textFormat;
if(g_pText->format->BytesPerPixel == 4)
{
// alpha
if(g_pText->format->Rmask == 0x000000ff)
textFormat = GL_RGBA;
else
textFormat = GL_BGRA_EXT;
}
// Create the font's texture
glGenTextures(1, &g_FontTextureId);
glBindTexture(GL_TEXTURE_2D, g_FontTextureId);
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, g_pText->format->BytesPerPixel, g_pText->w, g_pText->h, 0, textFormat, GL_UNSIGNED_BYTE, g_pText->pixels);
// ...
}
DrawText() function
// this function is called each frame
void DrawText()
{
SDL_Rect sourceRect;
sourceRect.x = 0;
sourceRect.y = 0;
sourceRect.h = 10;
sourceRect.w = 173;
// DestRect is null so the rect is drawn at 0,0
SDL_BlitSurface(g_pText, &sourceRect, g_pSurfaceDisplay, NULL);
glBindTexture(GL_TEXTURE_2D, g_FontTextureId);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBegin( GL_QUADS );
glTexCoord2f(0.0f, 0.0f);
glVertex2f(0.0f, 0.0f);
glTexCoord2f(0.0f, 1.0f);
glVertex2f(0.0f, 10.0f);
glTexCoord2f(1.0f, 1.0f);
glVertex2f(173.0f, 10.0f);
glTexCoord2f(1.0f, 0.0f);
glVertex2f(173.0f, 0.0f);
glEnd();
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
}
You've made a fairly common mistake. It's on the OpenGL end of things.
When you render the textured quad in DrawText(), you enable OpenGL's blending capability, but you never specify the blending function (i.e. how it should be blended)!
You need this code to enable regular alpha-blending in OpenGL:
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
This info used to be on the OpenGL website, but I can't find it now.
That should stop it from coming out solid red. The reasons the others worked is because they're not alpha-blended, they're actually just red-on-black images with no alpha, so the blending function doesn't matter. But the blended one only contains red color, with an alpha channel to make it less-red.
I notice a few other small problems in your program though.
In the DrawText() function, you are blitting the surface using SDL and rendering with OpenGL. You should not use regular SDL blitting when using OpenGL; it doesn't work. So this line should not be there:
SDL_BlitSurface(g_pText, &sourceRect, g_pSurfaceDisplay, NULL);
Also, this line leaks memory:
g_pText = SDL_DisplayFormatAlpha( TTF_RenderUTF8_Blended(...) );
TTF_RenderUTF8_Blended() returns a pointer to SDL_Surface, which must be freed with SDL_FreeSurface(). Since you're passing it into SDL_DisplayFormatAlpha(), you lose track of it, and it never gets freed (hence the memory leak).
The good news is that you don't need SDL_DisplayFormatAlpha here because TTF_RenderUTF8_Blended returns a 32-bit surface with an alpha-channel anyway! So you can rewrite this line as:
g_pText = TTF_RenderUTF8_Blended(g_pFont, "My first Text!", g_textColor);