从本篇起,我将进入DirectX图形和游戏的世界。更多Managed DirectX的信息,可以参考IceSharkBlog文章,我这里简单提一下我的感受。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绘图程序相比,程序还是有点特别的,呼呼。