Alpha blending is, to say the least, handy. In 3d modelling, you can find plenty of uses for it; say, creating hair or leaf textures, translucent or transparent materials, and so on. Needless to say, XNA can do it.
For this example, we’ll use a terrain model that’s made up of two meshes; a grid, which will represent the ground, and a cube, which will represent the water. The Blender file for this model can be downloaded here. Yes, I know it’s ugly.
The material applied to the water mesh has an alpha of 0.572, which means that it’s only partially opaque. Materials with low alpha values are more translucent, with 0 being totally transparent. Materials with higher alpha values are more opaque, with 1 being totally solid. Depending on the tools or frameworks in question, these ranges may vary (WPF brushes describe alpha as a value between 0 and 255 for instance, while some tools use a percentage value), but the two extremes hold true in whatever case.
To load the model into our XNA project, we can export it from Blender using the .FBX exporter (File->Export->Autodesk FXB). Once the file is exported, we can add it to the content in our project, and draw it as we would any other model. Except that, unfortunately, what we get is this:
Let’s be clear
What happened to the transparency? Well, the framework normally just ignores transparencies, since they can be quite an overhead to work out. To enable transparency, you need to enable it explicitly when you are drawing your model:
1: GraphicsDevice.RenderState.AlphaBlendEnable = true;
2: GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
3: GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
4:
5: // Do stuff with the mesh effects here.
6: // Render the mesh.
7:
8: GraphicsDevice.RenderState.AlphaBlendEnable = false;
Note: Line 8 is your friend. Never, ever, ever leave that out. Tattoo it on the insides of your eyelids if you have to. For the purposes of this demo it’s not going to do much, but if you’re going to go for more complex blending effects, leaving it out could leave you seriously wierded out.
Once we add the above, we get the effect we were after:
Function over form
Of course this is simply a straight forward effect; there are other blending functions you can play with, defined in the aptly named BlendFunction property of the RenderState. You can also combine this with different SourceBlend and DestinationBlend values for various effects. The image below shows the same scene as above, except that the water now looks substantially more horrid:
1: GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
2: GraphicsDevice.RenderState.DestinationBlend = Blend.DestinationColor;
3: GraphicsDevice.RenderState.BlendFunction = BlendFunction.Add;
Order, order
While I was preparing this model and playing about with transparency, I learned something very important: the order in which the meshes are rendered is vital when you have transparency effects. Any opaque meshes need to be rendered first, and then the transparent ones can be painted on top. Don’t just take my word for it; in the sample project, load up terrain-1 instead of terrain-2. The files are identical, except for the loading order of the meshes – the grid is loaded before the cube, so it gets rendered first. No amount of AlphaBlendEnable is going to get you transparency this way.
I’m not very familiar with Blender, so I don’t really know if there is a cleaner way to do this, but, to sort this out (pun intended… sorry) you can open the FBX file in a text editor, and search for “Connections:”. In the case of terrain-1.fbx, you should find (around line 30,107):
Connections: {
Connect: “OO”, “Model::blend_root”, “Model::Scene”
Connect: “OO”, “Model::Cube”, “Model::blend_root”
Connect: “OO”, “Model::Grid”, “Model::blend_root”
Connect: “OO”, “Material::Material.002″, “Model::Cube”
Connect: “OO”, “Material::Material.003″, “Model::Grid”
}
Moving the line saying ‘Connect: “OO”, “Model::Grid”, “Model::blend_root”‘ to the second position will make the grid mesh load first, solving the transparency problem.
I’d be interested in knowing if there’s any way to set this order explicitly from Blender. Any advice on where to look would be greatly appreciated.
February 19, 2009 at 4:40 pm
dude, you are soo cheesy – “let’s be clear” – i love it
February 19, 2009 at 4:51 pm
Whoa! Someone is actually reading this!
Thanks for the comment
October 2, 2009 at 3:27 pm
Hi karlagius,
you solved my problem with transparency not working on my model due to the connections order.
I’m not a blender guru, but I think the solution is hacking the fbx exporter, ordering the connections based on some property (e.g alpha value or a alpha mapped texture).
If I’ll found any solution I’ll let you know.
Thanks
Nicola
October 2, 2009 at 3:47 pm
Hi Nicola, glad this post helped! Thanks for the comment. If you do find anything about the ordering, please do let me know. Thanks again!
October 3, 2009 at 7:03 am
Hi Karlagius,
as promised I hacked a bit the fbx python exporter to have the transparent meshes rendered as last ones. Here is the modified code (browse the connections section of the exporter):
# NDA_ini
# for ob_generic in ob_all_typegroups: # all blender ‘Object’s we support
# for my_ob in ob_generic:
# if my_ob.fbxParent:
# file.write(‘\n\tConnect: “OO”, “Model::%s”, “Model::%s”‘ % (my_ob.fbxName, my_ob.fbxParent.fbxName))
# else:
# file.write(‘\n\tConnect: “OO”, “Model::%s”, “Model::blend_root”‘ % my_ob.fbxName)
for ob_generic in ob_all_typegroups: # all blender ‘Object’s we support
transparent_mesh_list= list()
for my_ob in ob_generic:
skip= False
if ob_generic == ob_meshes:
my_mesh= my_ob
for mat_map in my_mesh.blenMaterials:
if not mat_map: continue
# check if material has alpha color value != 255
if mat_map.alpha< 0.999:
skip= True
transparent_mesh_list.append(my_mesh)
else:
mat_mtex_list = mat_map.getTextures()
# check if alphaMap defined
if not mat_mtex_list: continue
for mat_mtex in mat_mtex_list:
if not mat_mtex: continue
if mat_mtex.tex.type == Texture.Types.IMAGE:
if mat_mtex.mapto & Texture.MapTo.ALPHA:
skip= True
transparent_mesh_list.append(my_mesh)
if skip: continue
if my_ob.fbxParent:
file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (my_ob.fbxName, my_ob.fbxParent.fbxName))
else:
file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % my_ob.fbxName)
# write transparent meshes
for my_ob in transparent_mesh_list:
if my_ob.fbxParent:
file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (my_ob.fbxName, my_ob.fbxParent.fbxName))
else:
file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % my_ob.fbxName)
#NDA_end
if you prefer I can mail you the code , just provide your email address.
NOTE: AlphaMap texture is just checked for image type texture, you can adjust that as you prefer.
I think this is just a temporary workaround but it's just enough for me, so far. Further investigating about some Blender embedded command or procedure to get meshes sorted.
I think should be there a xna 'coded way' to do that … I never heard anyone facing mesh ordering problems.
Bye
Nicola
October 4, 2009 at 5:54 pm
May be this is the correct way to go.
http://blogs.msdn.com/shawnhar/archive/2009/02/18/depth-sorting-alpha-blended-objects.aspx
P.S. I’d just read the article headers, but seems great.
Bye
Nicola
October 4, 2009 at 6:59 pm
Hello again Nicola. Thanks for the script, I’ll be sure to check it out asap
August 25, 2010 at 7:03 am
Incredibly helpful my friend!
August 25, 2010 at 7:36 am
Thanks Darknesious
October 2, 2010 at 7:16 am
Amazingly helpful! Thanks heaps.
October 2, 2010 at 8:42 am
Thanks Valkyr!
Glad it helped though
Wish I had the time to follow up on on it