transparency and irrlicht images

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
Ulf
Posts: 281
Joined: Mon Jun 15, 2009 8:53 am
Location: Australia

transparency and irrlicht images

Post by Ulf »

I am trying to draw some irrlicht images (in the "media" folder) using the 2d drawing methods and using transparency.

I am using openGL driver.
Only the outer black pixels are transparent.

I suspect that something is wrong because you wouldn't want a big black ring around your particle.

The problem I have is depicted in this link..
http://sites.google.com/site/projectulf ... ncyproblem

I make the color key as follows:

Code: Select all

pVideoDriver->makeColorKeyTexture( texture, core::position2di(0,0) );
The color array for the draw function: (the color for most objects is white)

Code: Select all

video::SColor const & color = this->getColor();
video::SColor const arColors[4] = { color, color, color, color };
I use the draw2DImage function as follows:

Code: Select all

pVideoDriver->draw2DImage( texture, destRect, textureFrame, clipRect, arColors, true );
I can hear birds chirping
:twisted:

I live in the Eye of Insanity.
Lonesome Ducky
Competition winner
Posts: 1123
Joined: Sun Jun 10, 2007 11:14 pm

Post by Lonesome Ducky »

This would be because you use a jpeg it looks like, or the blending isn't perfect. so those pixels aren't black. They may just be (1,1,1) but they still aren't black. As bad as it may seem, you might have to write your own code that draws a textured quad that uses EMT_TRANSPARENT_ADD_COLOR. I have some code you could use if you like.

EDIT: Here's the code to draw a 2D image with additive blending(it uses 3d to fake 2d):

Code: Select all

void draw2DImage(irr::video::IVideoDriver* driver, irr::video::ITexture* texture, irr::core::rect<irr::s32> sourceRect, irr::core::position2d<irr::s32> position, irr::core::position2d<irr::s32> rotationPoint = irr::core::position2d<irr::s32>(0,0), irr::f32 rotation = 0.0f, irr::core::vector2df scale = irr::core::vector2df(1,1), bool useAlphaChannel = true, irr::video::SColor color = irr::video::SColor(255,255,255,255), irr::video::E_MATERIAL_FLAG filter = irr::video::EMF_BILINEAR_FILTER) {
	
	irr::video::SMaterial material;
	// Store and clear the projection matrix
	irr::core::matrix4 oldProjMat = driver->getTransform(irr::video::ETS_PROJECTION);
	driver->setTransform(irr::video::ETS_PROJECTION,irr::core::matrix4());
	
	// Store and clear the view matrix
	irr::core::matrix4 oldViewMat = driver->getTransform(irr::video::ETS_VIEW);
	driver->setTransform(irr::video::ETS_VIEW,irr::core::matrix4());


	// Find the positions of corners
	irr::core::vector2df corner[4];

	corner[0] = irr::core::vector2df(position.X,position.Y);
	corner[1] = irr::core::vector2df(position.X+sourceRect.getWidth()*scale.X,position.Y);
	corner[2] = irr::core::vector2df(position.X,position.Y+sourceRect.getHeight()*scale.Y);
	corner[3] = irr::core::vector2df(position.X+sourceRect.getWidth()*scale.X,position.Y+sourceRect.getHeight()*scale.Y);

	// Rotate corners
	if (rotation != 0.0f) 
		for (int x = 0; x < 4; x++)
			corner[x].rotateBy(rotation,irr::core::vector2df(rotationPoint.X, rotationPoint.Y));


	// Find the uv coordinates of the sourceRect
	float subTexelX = 0.5f/texture->getSize().Width;
	float subTexelY = 0.5f/texture->getSize().Height;
	irr::core::vector2df uvCorner[4];
	uvCorner[0] = irr::core::vector2df(sourceRect.UpperLeftCorner.X,sourceRect.UpperLeftCorner.Y);
	uvCorner[1] = irr::core::vector2df(sourceRect.LowerRightCorner.X,sourceRect.UpperLeftCorner.Y);
	uvCorner[2] = irr::core::vector2df(sourceRect.UpperLeftCorner.X,sourceRect.LowerRightCorner.Y);
	uvCorner[3] = irr::core::vector2df(sourceRect.LowerRightCorner.X,sourceRect.LowerRightCorner.Y);
	for (int x = 0; x < 4; x++) {
		float uvX = uvCorner[x].X/(float)texture->getSize().Width;
		float uvY = uvCorner[x].Y/(float)texture->getSize().Height;
		uvCorner[x] = irr::core::vector2df(uvX,uvY);
	}
	uvCorner[0].X += subTexelX;
	uvCorner[0].Y += subTexelY;
	uvCorner[1].X -= subTexelX;
	uvCorner[1].Y += subTexelY;
	uvCorner[2].X += subTexelX;
	uvCorner[2].Y -= subTexelY;
	uvCorner[3].X -= subTexelX;
	uvCorner[3].Y -= subTexelY;
	// Vertices for the image
	irr::video::S3DVertex vertices[4];
	irr::u16 indices[6] = { 0, 1, 2, 3 ,2 ,1 };

	// Convert pixels to world coordinates
	float screenWidth = driver->getScreenSize().Width;
	float screenHeight = driver->getScreenSize().Height;
	for (int x = 0; x < 4; x++) {
		float screenPosX = ((corner[x].X/screenWidth)-0.5f)*2.0f;
		float screenPosY = ((corner[x].Y/screenHeight)-0.5f)*-2.0f;
		vertices[x].Pos = irr::core::vector3df(screenPosX,screenPosY,1);
		vertices[x].TCoords = uvCorner[x];
		vertices[x].Color = color;
	}
	material.Lighting = false;
	material.ZWriteEnable = false;
	material.TextureLayer[0].Texture = texture;
	material.setFlag(irr::video::EMF_ANISOTROPIC_FILTER,false);
	material.setFlag(irr::video::EMF_BILINEAR_FILTER,false);
	material.setFlag(irr::video::EMF_TRILINEAR_FILTER,false);
	material.setFlag(filter,true);


	material.MaterialType = irr::video::EMT_TRANSPARENT_ADD_COLOR;



	driver->setMaterial(material);
	driver->drawIndexedTriangleList(&vertices[0],4,&indices[0],2);

	// Restore projection and view matrices
	driver->setTransform(irr::video::ETS_PROJECTION,oldProjMat);
	driver->setTransform(irr::video::ETS_VIEW,oldViewMat);

}
Ulf
Posts: 281
Joined: Mon Jun 15, 2009 8:53 am
Location: Australia

Post by Ulf »

This would be because you use a jpeg it looks like,
No they are bmp and tga.
or the blending isn't perfect. so those pixels aren't black. They may just be (1,1,1) but they still aren't black.
That's what I think. The pixels aren't fully black or alpha value not 255.
As bad as it may seem, you might have to write your own code that draws a textured quad that uses EMT_TRANSPARENT_ADD_COLOR.
So how does irrlicht make those partial-black pixels transparent?
I don't understand. If you can only select one transparent color how does that work?

Can't it be done with 2d draw functions alone?
I can hear birds chirping
:twisted:

I live in the Eye of Insanity.
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

If you use tga (and also with png and 32bit bmp) you can add the alpha channel directly to the images. Just use that one. Or use additive blending as suggested, as it also blends other darker colors to almost invisible. Or make your transparent areas better colored.
Alan10
Posts: 5
Joined: Sun May 02, 2010 5:55 am

Post by Alan10 »

I'd been beating my head against the wall trying to display scaled 2d images with an alpha channel using the IVideoDriver::draw2DImage interface. It worked, but I couldn't get bilinear filtering working no matter what I tried. Your code worked with only minimal modifications. Thank you, Lonesome Ducky!
Lonesome Ducky
Competition winner
Posts: 1123
Joined: Sun Jun 10, 2007 11:14 pm

Post by Lonesome Ducky »

Alan10 wrote:I'd been beating my head against the wall trying to display scaled 2d images with an alpha channel using the IVideoDriver::draw2DImage interface. It worked, but I couldn't get bilinear filtering working no matter what I tried. Your code worked with only minimal modifications. Thank you, Lonesome Ducky!
No problem, glad to hear it was of use to you
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

For bilinear filtering you have to use the 2dmaterial interface, which was introduced in Irrlicht 1.7. Check example 6 for the proper usage.
Alan10
Posts: 5
Joined: Sun May 02, 2010 5:55 am

Post by Alan10 »

In case it's of use to anyone else, here is the draw2DImage I ended up using. It's heavily based on Lonesome Ducky's. Changes:

* Instead of a position and scaling, it takes a destination rect.
* Removed the rotation arguments and supporting code
* Removed the alpha argument. It was never used.
* Removed the color argument and supporting code.
* Saves and restores the world and texture matrices. Some other code (wild guess: the Quake 3 support) was leaving these in a weird state and causing problems.
* Disable checking the Z-Buffer.
* Silenced some warnings under g++ by explicitly casting to int to float.
* Uses an alpha channel on the texture instead of additive mixing.

Code: Select all

void draw2DImage(irr::video::IVideoDriver* driver, irr::video::ITexture* texture, irr::core::rect<irr::s32> sourceRect, irr::core::rect<irr::s32> destRect, irr::video::E_MATERIAL_FLAG filter = irr::video::EMF_BILINEAR_FILTER) {
	const irr::video::SColor color = irr::video::SColor(255,255,255,255);
   
   irr::video::SMaterial material;
   // Store and clear the projection matrix
   irr::core::matrix4 oldProjMat = driver->getTransform(irr::video::ETS_PROJECTION);
   driver->setTransform(irr::video::ETS_PROJECTION,irr::core::matrix4());
   
   // Store and clear the view matrix
   irr::core::matrix4 oldViewMat = driver->getTransform(irr::video::ETS_VIEW);
   driver->setTransform(irr::video::ETS_VIEW,irr::core::matrix4());

   // Store and clear the world matrix
   irr::core::matrix4 oldWorldMat = driver->getTransform(irr::video::ETS_WORLD);
   driver->setTransform(irr::video::ETS_WORLD,irr::core::matrix4());

   // Store and clear the texture matrices
   irr::core::matrix4 oldTexture0Mat = driver->getTransform(irr::video::ETS_TEXTURE_0);
   driver->setTransform(irr::video::ETS_TEXTURE_0,irr::core::matrix4());
   irr::core::matrix4 oldTexture1Mat = driver->getTransform(irr::video::ETS_TEXTURE_1);
   driver->setTransform(irr::video::ETS_TEXTURE_1,irr::core::matrix4());
   irr::core::matrix4 oldTexture2Mat = driver->getTransform(irr::video::ETS_TEXTURE_2);
   driver->setTransform(irr::video::ETS_TEXTURE_2,irr::core::matrix4());
   irr::core::matrix4 oldTexture3Mat = driver->getTransform(irr::video::ETS_TEXTURE_3);
   driver->setTransform(irr::video::ETS_TEXTURE_3,irr::core::matrix4());


   // Find the positions of corners
   irr::core::vector2df corner[4];

   corner[0] = irr::core::vector2df(float(destRect.UpperLeftCorner.X), float(destRect.UpperLeftCorner.Y));
   corner[1] = irr::core::vector2df(float(destRect.LowerRightCorner.X), float(destRect.UpperLeftCorner.Y));
   corner[2] = irr::core::vector2df(float(destRect.UpperLeftCorner.X), float(destRect.LowerRightCorner.Y));
   corner[3] = irr::core::vector2df(float(destRect.LowerRightCorner.X), float(destRect.LowerRightCorner.Y));

   // Find the uv coordinates of the sourceRect
   float subTexelX = 0.5f/float(texture->getSize().Width);
   float subTexelY = 0.5f/float(texture->getSize().Height);
   irr::core::vector2df uvCorner[4];
   uvCorner[0] = irr::core::vector2df(float(sourceRect.UpperLeftCorner.X),float(sourceRect.UpperLeftCorner.Y));
   uvCorner[1] = irr::core::vector2df(float(sourceRect.LowerRightCorner.X),float(sourceRect.UpperLeftCorner.Y));
   uvCorner[2] = irr::core::vector2df(float(sourceRect.UpperLeftCorner.X),float(sourceRect.LowerRightCorner.Y));
   uvCorner[3] = irr::core::vector2df(float(sourceRect.LowerRightCorner.X),float(sourceRect.LowerRightCorner.Y));
   for (int x = 0; x < 4; x++) {
      float uvX = uvCorner[x].X/(float)texture->getSize().Width;
      float uvY = uvCorner[x].Y/(float)texture->getSize().Height;
      uvCorner[x] = irr::core::vector2df(uvX,uvY);
   }
   uvCorner[0].X += subTexelX;
   uvCorner[0].Y += subTexelY;
   uvCorner[1].X -= subTexelX;
   uvCorner[1].Y += subTexelY;
   uvCorner[2].X += subTexelX;
   uvCorner[2].Y -= subTexelY;
   uvCorner[3].X -= subTexelX;
   uvCorner[3].Y -= subTexelY;
   // Vertices for the image
   irr::video::S3DVertex vertices[4];
   irr::u16 indices[6] = { 0, 1, 2, 3 ,2 ,1 };

   // Convert pixels to world coordinates
   float screenWidth = float(driver->getScreenSize().Width);
   float screenHeight = float(driver->getScreenSize().Height);
   for (int x = 0; x < 4; x++) {
      float screenPosX = ((corner[x].X/screenWidth)-0.5f)*2.0f;
      float screenPosY = ((corner[x].Y/screenHeight)-0.5f)*-2.0f;
      vertices[x].Pos = irr::core::vector3df(screenPosX,screenPosY,1);
      vertices[x].TCoords = uvCorner[x];
      vertices[x].Color = color;
   }
   material.Lighting = false;
   material.ZWriteEnable = false;
   material.ZBuffer = irr::video::ECFN_ALWAYS;
   material.TextureLayer[0].Texture = texture;
   material.setFlag(irr::video::EMF_ANISOTROPIC_FILTER,false);
   material.setFlag(irr::video::EMF_BILINEAR_FILTER,false);
   material.setFlag(irr::video::EMF_TRILINEAR_FILTER,false);
   material.setFlag(filter,true);

   //material.MaterialType = irr::video::EMT_TRANSPARENT_ADD_COLOR;
   material.MaterialType = irr::video::EMT_TRANSPARENT_ALPHA_CHANNEL;

   driver->setMaterial(material);
   driver->drawIndexedTriangleList(&vertices[0],4,&indices[0],2);

   // Restore projection and view matrices
   driver->setTransform(irr::video::ETS_PROJECTION,oldProjMat);
   driver->setTransform(irr::video::ETS_VIEW,oldViewMat);

   driver->setTransform(irr::video::ETS_WORLD,oldWorldMat);
   driver->setTransform(irr::video::ETS_TEXTURE_0,oldTexture0Mat);
   driver->setTransform(irr::video::ETS_TEXTURE_1,oldTexture1Mat);
   driver->setTransform(irr::video::ETS_TEXTURE_2,oldTexture2Mat);
   driver->setTransform(irr::video::ETS_TEXTURE_3,oldTexture3Mat);

}
Alan10
Posts: 5
Joined: Sun May 02, 2010 5:55 am

Post by Alan10 »

Why did I use the above, and not, say, manipulating the SMaterial returned by IVideoDriver::getMaterial2D as hybrid recommends (with the attendant enableMaterial2D(true/false)? I couldn't get it working. Example 06 worked. I could build simple test programs that worked. But when I integrated it into my full system, it failed. Unfortunately the specific way it failed escaped me now. I was on a short deadline and was floundering about. Failures I was running into included ignoring the alpha channel entirely, dropping the alpha channel to 1 bit, and just not rendering my graphic at all. I don't remember which problem getMaterial2D caused.

Interestingly, I had problems with the draw2DImage Ducky provided. Depending on where I was in my scene, by 2d image would appear and disappear. Reseting all of the driver's matrices fixed the problem. I ran into problems when I got close to a Quake 3 mesh in my scene. My hypothesis is that it was rendering last and leaving the matrices in a state that caused a problem. If I was further away my heightmap would be closer and not cause problems. Whatever the core problem, it may be the same poblem that I was hitting with *Material2D(). It's entirely possible that resetting the matrices before using the stock draw2DImage would have fixed the problem, but I didn't realize it at the time.

Thank you all for your help!
Post Reply