Introduction
This example illustrates how bitmap texture files with an alpha channel or a key color transparency can be mapped onto managed Direct3D vertex buffers in C#. In this example, .png, .bmp, and .jpg files provide the textures.
The DirectX MatrixStack
class is also used to create a simple hierarchical "scene" of the three textured vertex planes, each having its position dependent on the previous one. This "stacking" of transformations is useful for creating 3D user interfaces, robot-arm type movements, or can be used anywhere where one object location depends on another.
How the Texture Mapping Works
To render a texture bitmap, there are two key mapping or "transform" steps. The first step maps the loaded texture coordinates to a set of vertex coordinates stored in a vertex buffer. The second step maps the vertex buffer local coordinates to the final window coordinates. In the sample the vertices are simply two triangle strips that form a square plane onto which the square bitmap textures are mapped.
The transformation of vertex to window coordinates is performed by matrix multiplications that the Direct3D device performs internally when rendering. Another way to think of transforms are as "dependencies", where the local position "depends" on the world position which in turn "depends" on the view position and so on.
Local vertex matrices can be "stacked" (multiplied) using the MatrixStack
class to have the position of one object depend on the position of the other. Intuitively, this happens in 2D every time a parent window is dragged and the child moves with it. Similarly, matrix stacks can be used to create 3D windowing systems by processing this chain of dependencies from a tree that has been pushed and popped from the stack during traversal.
Running the Sample
This code uses the managed DirectX 9 assemblies, so they need to be installed in the search path. A nice feature of managed DirectX is that assembly DLLs can be simply copied into the EXE directory.
To run the demo, unpack the Zip and run 3DAlphaTransparency.exe. When building the source from the solution, the executable assembly will be placed in the "BinariesAndBitmaps/" subdirectory. Three sample bitmap paths are hard coded inside the C# MainForm
, and the bitmaps are located in the same directory as the 3DAlphaTransparency.exe.
Explanation of the C# Source Code
There are two main classes: the MainForm
, and the TDPanel
. The TDPanel
creates a single visible texture and vertex buffer, and the MainForm
instantiates three TDPanel
objects, stores them in an array list, and renders them using the MatrixStack
and Direct3D Device onto the MainForm
window.
Configuring the Device for Alpha Blending
Alpha blending is enabled in MainForm
on device reset:
public void OnResetDevice(object sender, EventArgs e)
{
...
device.RenderState.SourceBlend = Direct3D.Blend.SourceAlpha;
device.RenderState.DestinationBlend = Direct3D.Blend.InvSourceAlpha;
device.RenderState.AlphaBlendEnable = true;
...
}
Loading the Textures with a Transparency Key Color
The TDPanel
loads in the texture using the TextureLoader
, and a bitmap with any magenta colored pixels will be rendered as transparent:
public void CreateVertexBuffer()
{
PanelTexture = Direct3D.TextureLoader.FromFile(
TDDevice,
TextureFile,
0,
0,
1,
Direct3D.Usage.None,
Direct3D.Format.Unknown,
Direct3D.Pool.Managed,
Direct3D.Filter.None,
Direct3D.Filter.None,
System.Drawing.Color.Magenta.ToArgb());
}
Mapping the Texture to Two Triangle Strips
The corners of bitmap texture (u,v) are then mapped onto the vertices of the two triangle strips that form a square in local coordinates, using the PositionTextured
format:
public void OnCreateVertexBuffer(object sender, EventArgs e)
{
Direct3D.CustomVertex.PositionTextured[] verts =
(Direct3D.CustomVertex.PositionTextured[])vb.Lock(0,0);
verts[0].X=-1.0f; verts[0].Y=-1.0f;
verts[0].Z=0.0f; verts[0].Tu = 0.0f; verts[0].Tv=0.0f;
verts[1].X=-1.0f; verts[1].Y=1.0f ; verts[1].Z=0.0f;
verts[1].Tu = 1.0f; verts[1].Tv=0.0f;
verts[2].X=1.0f; verts[2].Y=-1.0f; verts[2].Z=0.0f;
verts[2].Tu = 0.0f; verts[2].Tv=1.0f;
verts[3].X=1.0f; verts[3].Y=1.0f; verts[3].Z=0.0f;
verts[3].Tu = 1.0f; verts[3].Tv=1.0f;
vb.Unlock();
}
Rendering using the MatrixStack Class
The MainForm
creates three TDPanel
objects and stores them in an ArrayList
. The MainForm
also performs the rendering by creating the MatrixStack
, looping through the TDPanel
s, and sending the world matrix and vertex information to the device to render:
private void Render()
{
DirectX.MatrixStack matrixStack = new DirectX.MatrixStack();
foreach(TDPanel tdPanel in panelList)
{
this.device.SetTexture(0,tdPanel.PanelTexture);
this.device.VertexFormat = Direct3D.CustomVertex.PositionTextured.Format;
this.device.SetStreamSource(0, tdPanel.PanelVertexBuffer, 0);
matrixStack.Push();
tdPanel.RotateMatrixTimer();
matrixStack.MultiplyMatrixLocal(tdPanel.LocalMatrix);
this.device.Transform.World = matrixStack.Top;
this.device.DrawPrimitives(Direct3D.PrimitiveType.TriangleStrip, 0, 2);
}
}
Further Creative Ideas
The use of alpha channels, transparency, and matrix stacks can be extended to create custom user interfaces and controls. Managed Direct3D provides a host of very powerful and high level rendering capabilities with just a few lines of code, and the visualization possibilities are only limited by imagination!
History
This is my very first submission to The Code Project. I enjoy reading many of the articles, and I look forward to reading your feedback.