从本篇起,我将进入DirectX图形和游戏的世界。更多Managed DirectX的信息,可以参考IceShark的Blog文章,我这里简单提一下我的感受。Managed DirectX是对DirectX大部分功能的托管封装,可以用任何支持.NET的语言开发。MDX只对DirectX做了非常低层次的封装,因此保持了用COM接口DirectX开发时的大部分原貌。MDX的性能是不用担心的,因为它还是象COM DirectX一样提供对硬件层次的访问,大部分功能都是在你的显卡/网卡/声卡上起作用的,托管部分只是它的接口。事实证明MDX在DirectX Graphics中的性能与COM接口的DirectX不相上下。既然MDX的开发方式、API和性能都与COM接口的DirectX差不多,那为什么要用MDX呢?我自己对这个问题的回答是:
1、托管代码的对象模型更好。MDX基于类库的组织结构,比用COM接口的处理方式更方便。
2、用MDX,一般不用操心资源释放的问题。很大一部分资源释放的操作,都被封装好了。
3、与更多现代技术结合得更好。我们可以让DirectX程序使用XML、WebService和智能客户端等技术。
Direct3D程序最基本的流程是:创建Windows窗口、创建设备、处理消息循环、物体图形显示、退出和清理。在MDX的世界里,窗口创建和处理消息循环我们交给Windows Forms模型来做,剩下最主要的任务就是创建设备和物体图形显示。Direct3D设备是Direct3D开发最基本的入口,它定义了Direct3D所有的绘图组件,大部分的操作都需要从Direct3D设备开始。创建设备需要设定几个参数,包括显卡序号、设备类型、所属窗口、3D运算方式等。除了这些信息外,还需要一个PresentParameters类型的参数,其中定义了Direct3D设备所需的相关信息。下面代码中的InitializeDirect3D函数完整的演示了创建设备的步骤
设备类型:Direct3D支持3种设备,其中HAL和REF最为重要。HAL通过硬件进行光栅化、坐标变换和光照处理等,速度最快。REF则是用软件实现相关的操作,仅用于硬件不支持某种操作的情况。DeviceType枚举定义了设备类型可能的选项。
3D运算方式包括一些选项,如HardwareVertexProcessing,PureHardwareVertexProcessing等,用于指定顶点运算由硬件执行还是软件执行等。
PresentParameters还包括一些设置,如后台缓冲区的高度、宽度和像素格式,以及从后台缓冲区复制到前台缓存屏幕显示的方式等等。如果Direct3D采用窗口方式运行,像素格式必须查询当前的显示模式获得。
Direct3D设备创建成功以后,就可以进入图形显示阶段。在下面的代码中以Render()函数的形式出现。在绘制图形前,需要调用Device::Clear()函数重制ViewPort的颜色缓冲区。ViewPort就是3D形状投射到平面显示器上供我们看到的那个区域。Clear函数的Flag参数指定了对颜色缓冲区、深度缓冲区还是模板缓冲区进行初始化。因为我用ATI显卡,所以我选择了将颜色缓冲区初始化为红色:)。接下来是调用BeginScene()函数和EndScene()函数。实际的绘图中,所有渲染的代码都必须放在BeginScene函数和EndScene函数之间,否则就会出错。最后调用Present()函数,将后台缓冲区中的数据复制到前台缓冲区,我们就能看见图形了。
下面就是完成这个步骤所需的类。我将其定义为sealed,并将构造函数私有。这样做是为了让这个类仅仅成为我所用静态函数的容器。(在C#中,可以用静态类;在VB中,可以用模块)。
public ref class DirectXProgram sealed
{
private:
DirectXProgram(void)
{
}
public:
//Direct3D 设备全局对象
static Device^ d3dDevice;
static Boolean InitializeDirect3D(Form^ window)
{
//获取显示适配器信息,以便查询显示模式信息
AdapterListCollection^ adapters = Manager::Adapters;
DisplayMode d3ddm = adapters->Default->CurrentDisplayMode;
//创建设备所需的参数
PresentParameters^ params = gcnew PresentParameters();
params->Windowed = true;
params->SwapEffect = SwapEffect::Discard;
params->BackBufferFormat = d3ddm.Format;
try
{
d3dDevice = gcnew Device(0,
DeviceType::Hardware,
window,
CreateFlags::HardwareVertexProcessing,
params);
return true;
}
catch(DirectXException^)
{
return false;
}
}
static void Render()
{
if(!d3dDevice)
{
return;
}
//后台缓冲区设置为红色
d3dDevice->Clear(ClearFlags::Target, Color::Red, 1.0f, 0);
d3dDevice->BeginScene();
//在这里加入图形绘制程序
d3dDevice->EndScene();
d3dDevice->Present();
}
static void CleanUp()
{
if(d3dDevice)
{
d3dDevice->Dispose();
}
}
};
这是DirectXProgram.h的完整代码
我们还需要一个窗体类。添加一个空的Windows Form,除了大小之外无需其他的设置。我们所要做的就是重写它的OnPaint方法,在窗体重绘时调用Render方法绘制3D图形:
void OnPaint(PaintEventArgs^ e)override
{
DirectXProgram::Render();
}
此外我们还应该在它的Dispose方法中增加对Direct3D设备资源释放的代码:
void Dispose(Boolean disposing)
{
if (disposing && components)
{
components->Dispose();
DirectXProgram::CleanUp();
}
__super::Dispose(disposing);
}
这是Form1.h的完整代码
最后,我们在main函数中完成整个流程的控制:
[STAThreadAttribute]
int Main()
{
TestMDXForm^ f = gcnew TestMDXForm();
if (DirectXProgram::InitializeDirect3D(f))
{
//显示窗口
f->Show();
Application::DoEvents();
Application::Run(f);
}
return 0;
}
其中程序,我们可以看到我们设置的颜色缓冲区颜色。

这个就是Direct3D程序最基本的结构。与普通的Windows绘图程序相比,程序还是有点特别的,呼呼。