Alpha Blending Oddities

I’m working on a project with a friend and we are using SDL with OpenGL, and we also have alpha blending.

Now, alpha blending is giving us a few oddities, and here is a link to how it normally looks.

http://img249.imageshack.us/img249/5103/normal.png

Sometimes, however, it transforms into this…

http://img249.imageshack.us/img249/9671/strange.png

When the program really hates me, it does this –

http://img256.imageshack.us/img256/4835/strange2.png

So, any ideas as to why its doing this?


I understand that rendering order is important: the screen is rendered as such:

(grid then selections then UI then units – see code below if this was confusing)


void GameScreen::render()
{
        grid.Draw();

	if((draw_selection))
	{
		// If we are not in the panel's plane, then don't draw the selection box
		if(!UI.CursorIsInPanels(MouseOffsetsScreen.x, MouseOffsetsScreen.y))
			DrawPrimitives->DrawSquare(selection_startX - ClipPlane->GetX(), selection_startY - ClipPlane->GetY(), selection_endX - ClipPlane->GetX(), selection_endY - ClipPlane->GetY(), blue);

		for(int i = 0; i < characters.size(); i++)
			if(characters[i]->Intersects(GetRect(selection_startX, selection_startY, selection_endX, selection_endY)))
				characters[i]->DrawRect();
	}

        glDepthMask(GL_FALSE);
	RenderSelections();

	UI.DrawTrans();
	overlay.DrawTrans();
	soundGUI.DrawTrans();
	aNavGraph.DisplayEdges();
	DrawPrimitives->DrawSquare(10, 555, 120, 590, black, 1.0f);
	
	soundGUI.DrawOpaq();
	overlay.DrawOpaq();
	UI.DrawOpac();

	glDepthMask(GL_TRUE);

    //Calculate the frames per second and create the string
	TextDisplay->DisplayMessage("FPS: ", red, 20, 560, float(FONT_SIZE_MEDIUM));
	TextDisplay->DisplayMessage(fpsCaption, red, 80, 560, float(FONT_SIZE_MEDIUM));

	if(fpsCounter.get_ticks() > 150)
	{
		
		_itoa_s(int(fpsFrame / (fpsCounter.get_ticks() / 1000.f)), fpsCaption, 10);
		fpsCounter.start();
		fpsFrame = 0;
		
	}

	fpsFrame++;

	RenderEntities();
}

Here is how the grid is rendered


void Grid::Draw()
{
	
	for(int x = 0; x < MAP_WIDTH; x++)
		for(int y = 0; y < MAP_HEIGHT; y++)
		{
			Entity::ResetZLevel();
			for(int i = 0; i < NUMBER_OF_LAYERS; ++i)
			{

			        if(!gridButtons[i][x][y].IsIgnoredTile())
				       gridButtons[i][x][y].applySurface();

				

			        if(gridButtons[i][x][y].GetDrawBox())
				       gridButtons[i][x][y].DrawBox();

				
		}
				
	}

	
}

The DrawTrans // RenderSelection functions just call this function:


void GLPrimitives::DrawSquare(int x1, int y1, int x2, int y2, OGL_Color color)
{
	glColor4f(color.r, color.g, color.b, color.a);
	SDL_Rect aRect = GetRect(x1, y1, x2, y2);

	glBegin(GL_QUADS);
		glVertex3f( GLfloat(aRect.x), GLfloat(aRect.y), 1.0f );
		glVertex3f( GLfloat(aRect.x + aRect.w), GLfloat(aRect.y), 1.0f );
		glVertex3f( GLfloat(aRect.x + aRect.w), GLfloat(aRect.y + aRect.h), 1.0f );
		glVertex3f( GLfloat(aRect.x), GLfloat(aRect.y + aRect.h), 1.0f );
	glEnd();
}

The RenderEntities function just calls the applySurface function


void Entity::applySurface()
{

        if(imageID == ignoredImage) // optimization; don't draw something that isn't going to show up!
                return;                                


	SDL_Rect aRect(Rect);

        // Zooming in / out
	aRect.x *= m_scaler;
	aRect.y *= m_scaler;
	aRect.w *= m_scaler;
	aRect.h *= m_scaler;

	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

	if(ClipPlane->IsInPlane(aRect))
	{
	        glScalef(m_scaler, m_scaler, 1.0f);

	        glTranslatef(Rect.x - ClipPlane->GetX(), Rect.y - ClipPlane->GetY(), m_z);
		glCallList(m_displayLists[imageID]);
		glLoadIdentity();
	}
}

Here is the texture loading function (includes glCallList(…), marked with a big ----------------- comment)


void Entity::Load_Image(const char* filename)
{

	// ... big or small endian code commented out
	
	// Load in the image from the filename
	SDL_Surface *orig;
	orig = IMG_Load(filename);
	if(orig == NULL)
	{
		assert("Texture not correctly loaded" && false);
	}

	SDL_Surface* paddedOrig = SDL_CreateRGBSurface(SDL_SWSURFACE, unsignedNextPowerOfTwo(orig->w), unsignedNextPowerOfTwo(orig->h), WINDOW_BPP,
		RGBAFormat.Rmask, RGBAFormat.Gmask, RGBAFormat.Bmask, RGBAFormat.Amask);

	SDL_Surface *surface;
	if(orig->w != unsignedNextPowerOfTwo(orig->w) || orig->h != unsignedNextPowerOfTwo(orig->h))
	{
		SDL_BlitSurface(orig, NULL, paddedOrig, NULL);
		surface = SDL_ConvertSurface(paddedOrig, &RGBAFormat, SDL_SWSURFACE);
	}
	else 
		surface = SDL_ConvertSurface(orig, &RGBAFormat, SDL_SWSURFACE);

	// Convert the surface to a format readable by openGL
	m_images_sizes.resize(m_images_sizes.size() + 1);
	m_images_sizes[m_images_sizes.size() - 1].h = paddedOrig->h;
	m_images_sizes[m_images_sizes.size() - 1].w = paddedOrig->w;

	SDL_FreeSurface(orig);
	SDL_FreeSurface(paddedOrig);

	// get the number of channels in the SDL surface
    nOfColors = surface->format->BytesPerPixel;
    if (nOfColors == 4)     // contains an alpha channel
    {
            if (surface->format->Rmask == 0x000000ff)
                    texture_format = GL_RGBA;
            else
                    texture_format = GL_BGRA;
    } else if (nOfColors == 3)     // no alpha channel
    {
            if (surface->format->Rmask == 0x000000ff)
                    texture_format = GL_RGB;
            else
                    texture_format = GL_BGR;
    } else {
            assert("Loaded image is not truecolor" && false);
    }

	// Have OpenGL generate a texture object handle for us
	glGenTextures( 1, &texture );
 
	// Bind the texture object
	glBindTexture( GL_TEXTURE_2D, texture );
 
	// Set the texture's stretching properties
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
 
	// Edit the texture object's image data using the information SDL_Surface gives us
	glTexImage2D( GL_TEXTURE_2D, 0, nOfColors, surface->w, surface->h, 0,
                      texture_format, GL_UNSIGNED_BYTE, surface->pixels );



	// Free memory and return the texture
	SDL_FreeSurface(surface);

    m_images.push_back(texture);


	// Increase display list size
	m_displayLists.resize(m_displayLists.size() + 1);
	m_displayLists[m_displayLists.size() - 1] = glGenLists(1);


        /* ---------------------------------------------------------------------- */
	/* Build the list ------------------------------------------------------- */
        /* ---------------------------------------------------------------------- */
	glNewList(m_displayLists[m_displayLists.size() - 1], GL_COMPILE);
	
		// Set color to (nothing)
		glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

		// Bind the texture
		glBindTexture(GL_TEXTURE_2D, texture);


		SDL_Rect aRect;
		aRect.x = 0;
		aRect.y = 0;

		aRect.x = (aRect.x - ClipPlane->GetX());
		aRect.y = (aRect.y - ClipPlane->GetY());
		aRect.w = m_images_sizes[m_images_sizes.size() - 1].w;
		aRect.h = m_images_sizes[m_images_sizes.size() - 1].h;


		glBegin(GL_QUADS);
			//Top-left vertex (corner)
			glTexCoord2i( 0, 0 );
			glVertex3f( aRect.x, aRect.y, 0.0f );
				
			//Bottom-left vertex (corner)
			glTexCoord2i( 1, 0 );
			glVertex3f( GLfloat(aRect.x + aRect.w), GLfloat(aRect.y), 0.0f );
				
			//Bottom-right vertex (corner)
			glTexCoord2i( 1, 1 );
			glVertex3f( GLfloat(aRect.x + aRect.w), GLfloat(aRect.y + aRect.h), 0.0f );
				
			//Top-right vertex (corner)
			glTexCoord2i( 0, 1 );
		        glVertex3f( GLfloat(aRect.x), GLfloat(aRect.y + aRect.h), 0.0f );
	        glEnd();
	glEndList();
}

Here is how I have set up OpenGL


void initGL()
{

    glClearColor( 0, 0, 0, 1 );

    glMatrixMode( GL_PROJECTION );
        glPushMatrix();
    glLoadIdentity();

    glOrtho( 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0.0f, -1.0f, 2.0f );

    glMatrixMode( GL_MODELVIEW );
	glPushMatrix();
    glLoadIdentity();

    glEnable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    glFrontFace(GL_CW);
    glEnable(GL_ALPHA_TEST);
    glEnable(GL_TEXTURE_ENV);

}

And finally, here is how I have set up OpenGL


void initGL()
{

    glClearColor( 0, 0, 0, 1 );

    glMatrixMode( GL_PROJECTION );
        glPushMatrix();
    glLoadIdentity();

    glOrtho( 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0.0f, -1.0f, 2.0f );

    glMatrixMode( GL_MODELVIEW );
	glPushMatrix();
    glLoadIdentity();

    glEnable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    glFrontFace(GL_CW);
    glEnable(GL_ALPHA_TEST);
    glEnable(GL_TEXTURE_ENV);

}

Sorry for the long post; hopefully you all can help me solve this rather obnoxious issue.

Thanks.

My theory is that your setting the blendfunc somewhere else as well.

OpenGL is a state machine, that means it saves the rendering state until it’s changed.
However it also means people get careless and think these states will be there forever, you can never be sure of that.
So the proper way to do it is to enable and disable states for each group of polygons you render, this includes blending.

I just went through the project and wherever objects are rendered I added glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA). (Right above the code.) Sadly, this did not help the situation. Thanks, and do you happen to have any other ideas? If you want to see the entire project, its at http://code.google.com/p/basicrpg34253/source/browse/

Just took a very quick look at the source.
Perhaps your global color variables (green) get corupted somewhere, since it does also affect other UI elements.

The only issue that I have with them being corrupted is that the colors snap back. Also, it would probably help if I told you all that I discovered something: the alpha levels only change when the bottom right hand pixel of the screen is over water.

Again, thanks. I’ll look into the global color variables as well and check to see if they are being corrupted.

Edit:
Could loading images in at different BPP levels be a cause of this?

Thanks.

no.
Btw. did you disable and manage textures correctly, the reason is that sometimes you might get the last drawn texture in the form of a solid color from a corner or something, it could be getting the corrupt alpha from there

zeoverlord – I thought about what you said and decided to try a glDisable(GL_TEXTURE_2D) right before I draw any primitives and it worked. :smiley:


Thanks all. I have no idea why something like this would effect it, but then again, OpenGL is one big behemoth.