在3D图形编程中,纹理映射是一种常见的技术,用于在3D模型上添加图像细节。本文将介绍如何在C#中使用Direct3D技术将带有Alpha通道的纹理映射到顶点缓冲区,并使用矩阵堆栈创建简单的层次场景。将使用.png、.bmp和.jpg文件作为纹理来源。
纹理映射的过程可以分为两个关键步骤。第一步是将加载的纹理坐标映射到存储在顶点缓冲区中的一组顶点坐标上。第二步是将顶点缓冲区的局部坐标映射到最终的窗口坐标。在示例中,顶点仅仅是两个三角形带,它们形成一个正方形平面,将正方形位图纹理映射到上面。
顶点到窗口坐标的转换是通过Direct3D设备在渲染时内部执行的矩阵乘法来完成的。另一种思考转换的方式是作为“依赖关系”,其中局部位置“依赖”于世界位置,而世界位置又“依赖”于视图位置等。
使用MatrixStack类可以“堆叠”(相乘)局部顶点矩阵,使得一个对象的位置依赖于另一个对象的位置。直观上,这在2D中每次拖动父窗口时都会发生,子窗口会随之移动。类似地,矩阵堆栈可以用来创建3D窗口系统,通过处理从堆栈中推入和弹出的树的依赖链。
此代码使用托管DirectX 9程序集,因此需要将它们安装在搜索路径中。托管DirectX的一个优点是可以将程序集DLL简单地复制到EXE目录中。
要运行演示,请解压缩Zip文件并运行3DAlphaTransparency.exe。当从解决方案构建源代码时,可执行程序集将放置在"BinariesAndBitmaps/"子目录中。C#MainForm中硬编码了三个示例位图路径,位图位于3DAlphaTransparency.exe的同一目录中。
有两个主要类:MainForm和TDPanel。TDPanel创建一个可见的纹理和顶点缓冲区,MainForm实例化三个TDPanel对象,将它们存储在ArrayList中,并使用MatrixStack和Direct3D设备将它们渲染到MainForm窗口上。
在MainForm的设备重置事件中启用Alpha混合:
public void OnResetDevice(object sender, EventArgs e) {
...
// 在设备中启用Alpha混合
device.RenderState.SourceBlend = Direct3D.Blend.SourceAlpha;
device.RenderState.DestinationBlend = Direct3D.Blend.InvSourceAlpha;
device.RenderState.AlphaBlendEnable = true;
...
}
TDPanel使用TextureLoader加载纹理,任何品红色像素都将被渲染为透明:
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());
}
位图纹理的角(u,v)然后映射到形成正方形的两个三角形带的顶点上,使用PositionTextured格式:
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();
}
MainForm创建三个TDPanel对象并存储在ArrayList中。MainForm还通过创建MatrixStack,遍历TDPanels,并将世界矩阵和顶点信息发送到设备进行渲染:
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);
}
}