.NET Framework 中新托管 Direct3D 图形 API 的简介

来源:互联网 发布:查询价格区间 sql 编辑:程序博客网 时间:2024/06/14 05:31

.NET Framework 中新托管 Direct3D 图形 API 的简介

 

发布日期: 12/6/2004 | 更新日期: 12/6/2004

Yahya H. Mirza 和 Henry da Costa

本文假设您熟悉 C++ 和 DirectX

摘要

DirectX 9.0 是最新发展的面向 Windows 的 Microsoft 3D 图形技术。Direct3D 是 DirectX Graphics 子系统的一个主要组件,它在过去几年中的发展是如此迅速,以至于基础编程范例与最初相比已发生了很大的变化。本文介绍了非托管 Direct3D 体系结构的基础概念,并说明了托管 Direct3D 层如何提取非托管层。本文作者还介绍了 Geometry、Texture、Device 和其他类,并使用了 Samples SDK 中的代码。

*
本页内容
Direct3D 体系结构Direct3D 体系结构Direct3D 管线Direct3D 管线非托管的 Direct3D非托管的 Direct3DDirect3D 扩展Direct3D 扩展托管的 Direct3D托管的 Direct3D设备类设备类图形状态类图形状态类与几何形状相关的类与几何形状相关的类纹理类纹理类托管的 D3DX 库托管的 D3DX 库托管 DirectX 9.0 示例框架托管 DirectX 9.0 示例框架GraphicsSample 类GraphicsSample 类初始化和清除初始化和清除帧更新和呈现帧更新和呈现小结小结

硬件加速的图形 API 所面临的基本挑战就是,让应用程序开发人员利用 3D 硬件领域中发生的快速技术革新的同时,允许图形硬件解决方案之间存在一定程度的兼容性和一致性。达到此目的的一种方法就是,由委员会定义一种标准,然后让每个供应商支持该标准。图形硬件供应商可以通过商定的扩展机制来革新和创建专用扩展。随着时间的推移,硬件供应商可以游说标准制定机构接受它们的专用扩展并将其作为标准的一部分。OpenGL 1.1 版就是该方法实现硬件互操作性的一个示例。它的一个局限是,需要很长的时间才能将供应商特定的革新合并到多供应商标准中,因此会面临标准作废的风险。

在 DirectX?9.0 中,DirectDraw® 和 Direct3D® 的功能组合到名为 DirectX Graphics 的单个 API 中。该组件的 Direct3D 部分将是本文的主要重点。在 Microsoft?Direct3D 中,程序员有两种选择:依赖固定功能管线或可编程管线。固定功能管线本身依赖于由 Direct3D 标准化的现有算法。这些固定功能是通过一组类似于 OpenGL 的固定的枚举值来公开的,这意味着 Direct3D 和 OpenGL 的固定功能管线均使用内部 switch 语句。在 switch 语句中与枚举值相对应的某些 case 可能是硬件加速的,这基于运行库所依赖的图形卡的功能。在 Direct3D 中,在使用固定功能管线时,程序员首先检查运行库,查看图形卡是否支持特定功能。

因为一些图形卡并不支持通过 Direct3D 公开的所有功能,所以在 Direct3D 中提供了一个用来探测图形硬件的机制。如果硬件不支持特定的图形功能,该检查将失败,并允许程序员查找其他的硬件加速算法。请记住以下要点:Direct3D 固定功能管线公开硬件加速的功能。尽管 Direct3D 有一个名为参考设备的仅限软件的仿真模式,但是,它设计用于调试和功能测试目的 — 而不是用来提供应用程序。

对于硬件和软件共同发展所面临的问题,另一个更有趣的方法是可编程管线。在可编程管线中,程序员可以定义自己的算法,而不是选取预定义的枚举值并让 Direct3D 执行算法。运行库将动态地编译基础图形硬件的算法。在这种情况下,Direct3D 运行库有一个实时 (JIT) 编译器,该编译器是硬件设备驱动程序的显式部分。硬件供应商负责为其特定的图形硬件提供 JIT 编译器。因此,Direct3D 充当图形虚拟机,它使用自定义的图形指令集高效地虚拟化图形处理器 (GPU)。

Direct3D 体系结构

尽管托管和非托管 Direct3D 编程层都是通过一系列对象公开的,但是您不应当将它们视为应用程序级别编程框架。Direct3D 体系结构的主要角色就是为更高级别的解决方案(如游戏引擎或布景图形 API)提供基本功能。为了帮助实现这些解决方案,Direct3D 扩展库显式提供其他功能。为了充分理解 Direct3D 体系结构,您不但必须了解所提取的功能,而且还要了解该功能的组织和公开方式。在以下几节中,我们将概述 Direct3D 体系结构的基本元素,并讨论如何通过非托管的 COM API 和托管 .NET 抽象层来公开它们。

返回页首返回页首

Direct3D 管线

正如计算机硬件体系结构所常见的那样,3D 图形体系结构中使用的两个性能优化技术是管线化和并行化。通过 Direct3D 公开的算法在逻辑上组织为管线。图 1 说明了 Direct3D 管线。

Direct3D 管线应当被视为一组作用于 3D 几何数量的算法,对于 Direct3D 来说,这些几何数量是预定义的顶点和基元。管线的主要目的是将几何数据转换为在视频显示器上呈现的图像。Direct3D 网格化阶段用于网格化(转换为三角形)由 Direct3D 预定义的一组更高阶的固定基元,这些基元包括三角形块、矩形块和 N 边形块(尽管三角形块仍是最常见的几何形状)。目前,网格化阶段不是可编程的,因此,Direct3D 不公开任何允许针对图形硬件生成程序几何的机制。就最小化通过总线发送的数据而言,程序几何提供了许多好处。在不久的将来,您有可能看到为可编程的网格化阶段提供的硬件支持。

在管线的变换和光照阶段,会将顶点位置和法线从模型坐标系统变换为世界坐标系和照相机坐标系,这是通过世界变换和视图变换来完成的。首先执行每顶点光照计算,以便确定反射光和漫射颜色组分。然后由投影变换功能转换顶点位置,以便创建透视投影、正投影或其他类型的投影。尽管固定功能管线仍像以前那样在同一个 API约定中公开这些变换和光照算法,但是,在大多数图形卡中,它们是在图形处理器的微代码中实现的,而不是作为一组硬件加速的算法来实现。例如,在 Radeon 9700 处理器中,过去的整个变换和光照模块可以而且应当在可编程的管线中作为顶点 Shader 来实现。

为了在光栅化阶段提高性能,属于照相机无法看到的对象的顶点将被剪裁掉。另外,还可能会执行背面选择,以免光栅化背对照相机的三角形。此外,还执行属性评估,以便配置和选择将在光栅化阶段使用的实际算法。最后,执行光栅化功能,以便实际呈现像素。

在像素处理阶段,您可以选择使用固定功能的多纹理化或可编程的像素 Shader 来确定像素的颜色值。固定功能多纹理化通过一系列纹理阶段来公开,在每一个阶段,都允许针对像素的颜色和 alpha 值执行一组固定的运算。像素 Shader 通过自定义的汇编语言公开针对颜色和 alpha 值执行的运算,从而提供更大的灵活性。在像素处理阶段实现的算法包括凹凸贴图、阴影化、环境贴图等。

帧缓冲区处理涉及到一组名为呈现表面、深度缓冲区和模板缓冲区的内存区域。在该阶段,会执行一系列计算来确定诸如深度、alpha 和模板的值。深度缓冲区是另一种用来删除隐藏线和表面的呈现优化。深度测试用来确定哪些像素是隐藏的而且不需要呈现,它可以通过使用 z-缓冲区或 w-缓冲区来确定,每种算法都有其各自的优缺点。帧缓冲区处理功能可实现许多效果,如透明度、雾化和阴影。

对于 Direct3D 管线来说,需要强调的最后一点是可通过图形状态来修改它的行为。图形状态用来配置多变换、光照、光栅化、像素处理和帧缓冲区处理算法,这些算法由 Direct3D 提供,用于呈现帧。这些阶段包括呈现、变换、取样器和纹理阶段状态。

返回页首返回页首

非托管的 Direct3D

用来管理 DirectX 图形的主要对象是 Direct3D 对象。Direct3D 对象是通过 Direct3DCreate9 函数创建的,Direct3DCreate9 函数是由核心 Direct3D API 公开的唯一一个全局函数。Direct3D 对象实现 IDirect3D9 接口并负责确定特定设备的细节。除了设备枚举,IDirect3D9 接口负责通过它的 CreateDevice 工厂方法来创建 Direct3DDevice 对象。

Direct3DDevice 对象实现 IDirect3DDevice9 接口并充当 Direct3D 图形的主要支持者。Direct3DDevice9 I接口提取在图 1 中说明的整个管线。它提供的方法可以为变换、光照、光栅化、像素和帧缓冲区处理公开许多种算法,并且由符合 Direct3D 的特定硬件设备提供。IDirect3DDevice9 接口中的方法可以按如下类别组织:一组属性(用来配置 Direct3D 图形管线或者提供有关其当前状态的信息)、工厂方法(用来在 Direct3D 体系结构中创建其他对象)或者一组方法(用来执行实际图形算法)。

Direct3D 管线的状态可以通过 IDirect3DDevice9 接口的一组访问器方法来配置。GetRenderState 和 SetRenderState 方法检索和/或设置设备的呈现状态值。非托管的 D3DRENDERSTATETYPE 枚举类型为您提供一组可用于配置当前呈现状态的选项。GetTextureStageState 和 SetTextureStageState 方法检索和/或设置当前分配的纹理的状态值。非托管的 D3DTEXTURESTAGESTATE 枚举类型还向开发人员提供一组可用于配置单个纹理阶段的选项。图形状态的其他属性可通过从非托管的 D3DTRANSFORMSTATETYPE 和 D3DSAMPLERSTATE 枚举类型选择选项来配置。

非托管的 Direct3D 中的几何形状是通过 VertexBuffer 和 IndexBuffer COM 对象使用的。这些对象是通过工厂方法在 IDirect3DDevice9 接口上创建的。VertexBuffer 对象实现 IDirect3DVertexBuffer9 接口并存储用户定义的一系列顶点。这些顶点由特定应用程序所需的数据组成,并且可以通过使用灵活的 Direct3D 顶点格式来声明。灵活的 Direct3D 顶点格式通过一组宏来使用。灵活的顶点格式存在一个基本局限,那就是它们只能使用单个数据流,因此可能会浪费内存。灵活的顶点格式通常与固定功能管线结合使用。IndexBuffer 对象实现 IDirect3DIndexBuffer9 接口,并将索引数组存储到 VertexBuffer 中。IndexBuffer 通过重用共享的顶点来提供可节省内存的优化。

返回页首返回页首

Direct3D 扩展

非托管的 Direct3D 扩展 (D3DX) 实用工具库为广泛的 2D、3D 和 4D 矢量数学运算提供大量的接口、函数、宏和支持类。另外,还提供了可支持程序纹理、凹凸贴图和环境贴图的函数。Direct3D 一个重要的子集是它的 .X 文件格式。它提供了适用于网格、动画和纹理的函数。D3DX 的一个最新功能是它对 Shader 和效果文件的支持。运行库汇编程序是通过 D3DXAssembleShader、D3DXAssembleShaderFromFile 和 D3DXAssembleShaderFromResource API 函数为 Shader 提供的。

返回页首返回页首

托管的 Direct3D

托管的 Direct3D 已经变成核心 Direct3D 运行库的重要部分,这与 D3DX 之类的扩展库相反。因此,尝试让托管抽象尽可能接近最初的非托管抽象。为了更好地理解托管 Direct3D,一定要先了解托管层如何提取非托管的 Direct3D。对于大部分来说,托管的 Direct3D 提供从非托管层中的接口、结构和枚举到托管层中的类、结构和枚举的一对一映射。为了显式使用托管代码的一些新功能,如类实现继承、C# 属性、索引器和事件,托管层中的几个抽象需要按稍微不同于其非托管副本的方式公开。在以下几节中,我们将介绍由托管 Direct3D 公开的基础类。

返回页首返回页首

设备类

托管的 Direct3D 提供了基于类的设备抽象,该抽象是通过使用非托管的 IDirect3DDevice9 接口实现的。编写 Direct3D 托管代码的程序员使用 Device 类的方法来访问其他 Direct3D 对象,以便设置各种图形状态并呈现几何形状。为了执行这些函数,Device 类提供了许多属性、方法和事件。图 2 列出了 Device 类最重要的方法,这些方法由 3D 应用程序调用。

谈到对象激活,一种观点认为,非托管的 Direct3D 之所以使用工厂方法,只是因为 COM 不支持新运算符。COM 的标准激活模式是基于工厂的构造。CoCreateInstance 函数模仿 COM 的新运算符,但它只是一个在内部使用 COM 工厂的包装。使用工厂(相对于新运算符)的另一个原理就是,在使用新运算符时,需要将对象的特定实现绑定到客户端的代码中;因此,如果大量使用新运算符,可能会导致版本控制问题。工厂方法通常用于将代码的客户端或用户与代码的创建者去耦。非托管的 COM IDirect3DDevice9 接口提供 14 种显式工厂方法,以便创建在使用 Direct3D 时需要的对象(如像素和顶点 Shader 以及顶点和索引缓冲区)。

目前,托管的 Direct3D 在很大程度上依赖于使用新运算符来构造类。尽管 Device 托管类只提供三种工厂方法,但是,一个值得注意的有趣现象是,非托管的 IDirect3DDevice9 工厂方法和由 Device 类公开的公共属性之间存在一对一关联。以下代码行说明了由 Device 托管类公开的三种工厂方法,每种方法都使用多个参数:

public Surface CreateRenderTarget(...);public Surface CreateDepthStencilSurface(...);public Surface CreateOffscreenPlainSurface(...);

在托管的 Direct3D 中大量使用方法重载。下面的代码示例说明了非托管的 Device.ColorFill 方法及其托管的重载包装:

typedef DWORD D3DCOLOR;// Methods from the unmanaged IDirect3DDevice9 interface.HRESULT ColorFill(IDirect3DSurface9 *pSurface, CONST RECT *pRect,     D3DCOLOR color);// Methods from the managed Device class.public void ColorFill(Surface surface, Rectangle rect, Color color);public void ColorFill(Surface surface, Rectangle rect, int color);

与传统的图形系统一样,非托管的 Direct3D 需要将颜色值作为 32 位整数值在内部传递。另一方面,Microsoft .NET Framework 显式提供用来处理 ARGB 颜色值的 System.Drawing.Color 类,其中包含几种用来在不同表示形式之间进行转换的方法。为方便那些已习惯于使用整数值来表示颜色的程序员,托管的 Direct3D 在需要颜色参数时使用这两种表示形式来提供方法重载。

非托管的 IDirect3DDevice9 接口既公开只读属性,又公开读写属性,这两种属性都可以带参数,也可以不带参数。在最新版本的 .NET Framework 中,属性不带参数。因此,在将非托管的 Direct3D 映射到托管的 Direct3D 时,不使用参数的属性可以很好地映射到 C# 属性,而使用参数的 Direct3D 非托管属性只作为 Get 和 Set 方法公开。图 3 列出了 Device 类的一些最重要的属性。

最后,Direct3D Device 托管类提供了五个用来与设备进行交互的显式事件。基于 Direct3D 的应用程序可以通过以下方法来将事件作为 Device 的来源:启动应用程序、调整窗口的大小、从窗口模式更改到全屏模式或者退出应用程序。一个有趣的发现是由 Direct3D 使用的基于 .NET 委托的事件模式。对于 Device 类的每个事件声明,提供了添加、删除和引发方法,以及用来存储事件处理程序的私有实例变量。在 C# 中,客户端事件注册和注销的语法形式分别是通过 += 和 -= 运算符来提供的(请参阅图 4)。

返回页首返回页首

图形状态类

如前所述,非托管代码的图形状态是通过 IDirect3DDevice9 接口的方法来配置的。例如,在非托管代码中,变换状态可通过 GetTransform 和 SetTransform 方法直接访问,这两种方法都使用 D3DTRANSFORMTYPE 类型的枚举值。

// From unmanaged IDirect3DDevice9 interface:HRESULT GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX *pMatrix);HRESULT SetTransform(D3DTRANSFORMSTATETYPE State,     CONST D3DMATRIX *pMatrix);

托管的 Direct3D 为非托管层提供一些抽象,这会使客户端代码变得更简洁。下面显示的 Device 类说明了一组只读属性:

public class Device : MarshalByRefObject, IDisposable{   // Additional properties, methods and events.   public Transforms    Transform    { get; }    public RenderStates  RenderState  { get; }   public SamplerStates SamplerState { get; }   public TextureStates TextureState { get; }}

这些属性返回 Transforms、RenderStates、SamplerStates 和 TextureStates 类的实例。这些实例随后用于配置托管 Direct3D 的图形状态。例如,Device 类不直接公开使用枚举值的 Transform 属性。Device 托管类提供可返回 Transforms 对象的属性。Transforms 对象随后公开一组可用来访问当前世界、视图和投影矩阵的属性,这些属性本身是 TransformType 托管枚举的元素。

RenderStates 类提供了大量的属性来配置为光栅化阶段选择的特定算法。下面的客户端代码显示了如何设置 Direct3D 托管管线的状态。

// cull, spec, dither states.device.RenderState.CullMode        = Cull.None;device.RenderState.SpecularEnable  = false;device.RenderState.DitherEnable    = false;// Filter states.device.SamplerState[0].MagFilter   = TextureFilter.Linear;device.SamplerState[0].MinFilter   = TextureFilter.Linear;

请注意,使用索引器来访问取样器状态的多纹理层叠所在的阶段。

取样器状态指定用于特定阶段的筛选、平铺和纹理寻址模式,这些模式可通过 Sampler 类的属性来公开。筛选功能用于指定如何将图像映射到特定的 Direct3D 基元。根据图像(纹理)的大小以及它需要映射到的屏上基元的大小,可能需要放大或缩小图像。Direct3D 提供了三种筛选方法:MipFilter、MinFilter 和 MagFilter,每种方法都具有可由 TextureFilter 枚举配置的模式。平铺功能用于将一组单个的纹理汇集成单个纹理并减少切换纹理的需要。平铺功能是通过 Sampler 类的一组属性获得支持的。最后,纹理寻址模式通过 TextureAddress 枚举的元素为 [0,1] 边界外部的纹理坐标定义操作:

public class SamplerStates{   private  Sampler[] m_lpTex;   private  bool pureDevice;   public   Sampler get_SamplerState(int index);   internal SamplerStates(Device dev, bool pure);   public   Sampler this[int index] { get; }}

TextureStates 和 SamplerStates 类提供只读索引器,该索引器可返回特定阶段的 TextureState 实例。TextureState 类定义的属性可获得或指定特定纹理阶段状态的元素。诸如 ColorArgumentn 或 AlphaArgumentn(其中 n 可以是 0、1 或 2)属性可以是 TextureArgument 枚举的元素,而 ColorOperation 或 AlphaOperation 可以属于 TextureOperation 枚举,如下所示:

// color = tex mod diffuse.device.TextureState[0].ColorArgument1 = TextureArgument.TextureColor;device.TextureState[0].ColorOperation = TextureOperation.Modulate;device.TextureState[0].ColorArgument2 = TextureArgument.Diffuse;// alpha = select texture alpha.device.TextureState[0].AlphaArgument1 = TextureArgument.TextureColor;device.TextureState[0].AlphaOperation = TextureOperation.SelectArg1;device.TextureState[0].AlphaArgument1 = TextureArgument.Diffuse;
返回页首返回页首

与几何形状相关的类

Direct3D 中的几何形状被定义为顶点定义的数组。不同的图形算法需要存储在每个顶点的不同信息,了解这一点很重要。这些顶点组分需要由应用程序进行声明,具体情况取决于程序员希望实现的效果。例如,如果程序员希望将纹理映射到 Direct3D 将呈现的几何形状,则程序员必须进行如下声明:在每个顶点,都将有 tu 和 tv 值(纹理坐标)。托管的 Direct3D 提供了两种顶点定义机制:VertexFormats 和 VertexDeclarations。使用这些机制,可以在每个顶点定义其他数据,如颜色、混合权重、纹理坐标或应用程序定义的任何用法。作为一种方便的方法,为经常使用的顶点格式定义提供了 CustomVertex helper 类。CustomVertex 类提供 11 种不同的顶点定义,这些定义经常用于 Direct3D 应用程序中。

在托管的 Direct3D 中,应用程序用来定义其顶点布局的顶点格式由 Device 类的读和写 VertexFormat 属性来访问。VertexFormat 属性访问 VertexFormats 枚举的元素,并且只需通过以下方法来实现:针对 IDirect3DDevice9 接口使用 GetFVF 和 SetFVF 方法,调用内部的非托管 Direct3D Device COM 对象。托管的 VertexFormats 枚举映射到在 d3dtypes.h 中定义的 VertexFormat 常量位,如下所示:

public struct Vertex{   public Vector3 position;   public float tu, tv;   public static readonly VertexFormats Format =        VertexFormats.Position | VertexFormats.Texture1;};// From a typical managed Direct3D applications Render() method.device.VertexFormat = Vertex.Format;

在使用可编程的管线时,需要自定义的顶点声明。与使用灵活的顶点格式定义的顶点缓冲区不同,自定义顶点声明可以通过使用多个流来定义,从而可节省带宽:

VertexElement[] declaration = new VertexElement[] {    new VertexElement(       0,                         // short             stream       0,                         // short             offset       DeclarationType.Float2,    // DeclarationType   declType       DeclarationMethod.Default, // DeclarationMethod declMethod       DeclarationUsage.Position, // DeclarationUsage  declUsage       0                          // byte              usageIndex                    ),   VertexElement.VertexDeclarationEnd };VertexDeclaration declaration = new VertexDeclaration(device,                                 declaration);

托管 Direct3D 中的几何形状通常是通过处理 VertexBuffer.Created 事件来创建的。图 5 显示了从 SDK 中的 Tutorial2 示例提取的简单三角形形状,并说明了预定义的自定义顶点 (CustomVertex.TransformedColored) 的用法。

返回页首返回页首

纹理类

在托管的 Direct3D 中,与纹理相关的类从抽象类 Resource 继承,并且在极大程度上在与纹理相关的非托管接口上建立层。托管的 Direct3D 有三个纹理类:Texture、CubeTexture 和 VolumeTexture。Texture 类表示可映射到基元上的图像,用于呈现或用于凹凸贴图、法线贴图或其他效果。纹理的基本函数用于将一对纹理坐标映射到某个值(通常是颜色值),但是可以通过其他方式进行解释。纹理坐标通常是顶点数据的一部分。纹理坐标到颜色或其他值的映射是在呈现管线的像素处理阶段完成的。Direct3D 允许一次最多使用八个纹理。CubeTexture 类是用于立方体环境贴图的特殊种类的纹理,立方体环境贴图这种技术允许环境(如室内或外景)由反光物体反射出来,这是通过以下方法来实现的:使用纹理来为立方体周围的六个面各定义一个图像,并由所呈现的物体来反射。VolumeTexture 类表示存在于三维矩形空间中的颜色或其他值。同样,它将纹理坐标的三元组映射到颜色或其他值。这些类最重要的方面是,它们都支持一组用于锁定和取消锁定纹理的重载方法。

经常用于加载和保存纹理的类是 TextureLoader。TextureLoader 类包含用来从文件或流加载纹理、立方体纹理或体积纹理的方法。TextureLoader 类的其他用法包括填充和筛选纹理以及计算法线贴图。用来创建纹理的有用的 helper 方法由 GraphicsUtility 类提供:文件的 GraphicsUtility.CreateTexture 方法只是查找指定纹理的路径,然后调用 TextureLoader.FromFile 方法以加载它。以下代码说明了 GraphicsUtility 类的用法:

Texture texture = GraphicsUtility.CreateTexture(device,"banana.bmp");
返回页首返回页首

托管的 D3DX 库

托管的 D3DX 实用工具库具有用来处理网格、纹理、Shader、效果、文本和动画的类。但是,与非托管的 D3DX 库不同的是,它没有数学类,如矩阵、矢量和四元组类。在托管 DirectX 中,数学类是 DirectX 库的一部分。

用来处理网格的类包括 Mesh、SimplificationMesh、ProgressiveMesh、SkinMesh 和 PatchMesh。Mesh 和 ProgressiveMesh 从 BaseMesh 类继承。BaseMesh 管理内存(包括顶点和索引缓冲区)中的网格表示形式,并提供用来呈现网格的 DrawSubset 方法。其他方法包括用来复制网格的 Clone 方法,以及用来计算顶点法线并获取相邻项信息的方法。

Mesh 类使用可将网格保存到 .X 文件(标准的 DirectX 文件类型)或二进制流中并从中加载网格的方法来扩展 BaseMesh。还提供了用来生成表示以下图形的网格的方法:n 边形、圆柱、球形、圆环、给定字体的 3D 文本或者甚至是茶壶。用户可以网格化网格以获得更光滑的对象(通过将每个三角形解释为 N-patch),简化网格(通过减少三角形的数量)、优化网格(通过对三角形重新排序,以便利用硬件中的顶点缓存并提高性能)或者执行与网格的光交叉。

SimplificationMesh 对象是从 Mesh 构造的,它具有可减少表面(三角形)或顶点数量的方法,从而可产生比初始网格简单但是质量更低的网格形式。ProgressiveMesh 对象是从 Mesh 构造的。它允许实时简化网格,以便在网格对象和照相机之间的距离增大时,最小化所呈现三角形的数量。SkinMesh 类(由主干制作动画的网格)提供的方法可用来从 .X 文件数据对象加载外观网格。包含了用来公开基础网格和外观的信息(如每根骨头对结构的影响)的属性。

PatchMesh 类表示更高阶的表面,如 N-Patch、Triangle Patch 或 Rectangle Patch 表面。PatchMesh 包括用来执行如下操作的方法:从 Mesh 对象创建 N-Patch 网格、从 .X 文件加载 PatchMesh、网格化 PatchMesh 等。用来处理纹理的类主要包括:前面介绍的 TextureLoader 类、RenderToEnvironmentMap 类和 RenderToSurface 类。RenderToEnvironmentMap 和 RenderToSurface 类用来在运行时创建立方体环境贴图和纹理。如果在运行时呈现到纹理中,则允许您获得特殊效果,如让类似于镜面的物体反射布景中的其他物体,或者制造双窗格呈现效果。用来处理文本的类主要包括 Font 类,Font 类可以从 System.Drawing.Font 对象实例化,并且具有可以在支持 Direct3D 的设备上呈现文本的方法。用来处理动画的类由 KeyFrameInterpolator 类组成,KeyFrameInterpolator 类具有用来以内插值替换对象的缩放、旋转和/或平移的方法。内插值替换是通过使用一组 3D 矢量(用来缩放和平移)和四元组项(用于旋转)来执行的。

返回页首返回页首

托管 DirectX 9.0 示例框架

DirectX 9.0 示例框架通过实现经常执行的任务来提供许多可以加速图形应用程序开发的类。示例框架可通过源代码形式使用。要使用它,只需向图形应用程序项目中添加所需的源文件。

当通过托管 DirectX 应用程序向导(在 Microsoft Visual C#? .NET 中)创建应用程序时,会基于示例框架生成代码。使用该向导生成图形应用程序是开始使用示例框架的最快捷的方法。示例框架由 DirectX 9.0 SDK 附带的示例使用。DirectX 9.0 示例显示了如何利用 Direct3D 以外的示例框架。

即使在不直接使用示例框架时,也可以引用示例框架中的代码,以便查看如何通过灵活可靠的方式来驱动 DirectX 9.0 代码。

返回页首返回页首

GraphicsSample 类

使用 DirectX 示例框架的 C# 应用程序继承 GraphicsSample 类,该类具有执行如下操作的代码:

初始化和终止 DirectX。

查找要使用的最佳窗口显示模式或全屏显示模式。

显示一个允许用户更改设备设置(如显示模式)的窗体。

处理窗口操作(如移动、调整大小、最小化和最大化)。

在窗口获得和失去焦点时处理窗口激活和停用。

处理用户接口事件(如按键)和主菜单导航事件。

执行帧更新和呈现操作。

以暂停和单步执行动画的形式执行基元动画。

收集和显示统计信息。

处理异常。

GraphicsSample 类本身从 System.Windows.Forms.Form 继承,因此图形应用程序从根本上说是 Windows 窗体。

GraphicsSample 类定义了几种方法,子类可以重写这些方法以改变默认的行为或添加功能。它还具有许多可用于执行如下操作的字段和属性:控制图形应用程序行为;访问支持 DirectX Graphics 的设备。

图 6 显示了使用 GraphicsSample 类所必需的最小代码,该代码是从 DirectX 9.0 示例提取的。图 6 中的 Render 方法只是清除显示表面,因此该代码将只是显示黑色背景。实际上,该向导添加用来在蓝色背景上显示茶壶的代码(如果开发人员这样选择的话),但是为简单起见,已将该代码省略。在一般情况下,会将茶壶呈现代码替换为应用程序特定的呈现代码。

实际上,DirectX 9.0 C# 向导会创建一个它在其中放置 Main 方法的单独的类,因此所生成的代码不同于图 6 中显示的代码。如果要生成图 6 中的代码,则有必要向该项目中添加 D3DApp.cs 文件,以便 GraphicsSample 类可用。

返回页首返回页首

初始化和清除

图 7 列出了特定于初始化或清除的可重写的方法。这些方法可用于许多情况:启动应用程序、终止应用程序、更改设备、丢失设备和重置。图 8 详述了导致其中每种情况的调用顺序。

除了图 7 中的方法以外,GraphicsSample 类还有一个 RenderTarget 属性,在调用 CreateGraphicsSample 以更改呈现目标之前,派生类可以设置 RenderTarget 属性。在默认情况下,呈现目标是 Windows 窗体本身。派生类可以定义一个单独的控件或窗口来充当呈现目标。

返回页首返回页首

帧更新和呈现

GraphicsSample 类调用 FrameUpdate 方法,派生类可以重写该方法以更新每个帧的布景。FrameUpdate 方法可以从鼠标、游戏杆或其他输入设备获得输入,并相应地更新照相机或其他布景对象。通常,您将在 FrameUpdate 方法中放置与动画相关的更新代码(如模仿、冲突检测或与游戏相关的人工智能)。

GraphicsSample 类调用 Render 方法,任何派生类必须重写该方法才能呈现布景。Render 方法负责清除显示表面区域,并在调用设备的 BeginScene 和 EndScene 方法之间呈现布景。

图 9 显示了由托管 DirectX 应用程序向导生成的用来呈现茶壶的 Render 方法实现,为清楚起见,对该实现的格式稍作改变。在这个简单的示例中,Render 方法设置光照以及世界矩阵、视图矩阵和投影矩阵。由于这些呈现状态在帧之间不发生更改,因此它们也可以直接在 RestoreDeviceObjects 方法中设置,这将提高效率。如果在 RestoreDeviceObjects 中设置这些不发生变化的呈现状态,将只在设备被初始化时对这些状态设置一次,而不是在每个帧期间都设置。图 9 中显示的代码需要另一个文件 — DXUtil.cs,该文件可以在示例框架中找到。

返回页首返回页首

小结

托管的 Direct3D 是 DirectX 9.0 中 DirectX Graphics 组件的一部分,它使开发人员更有机会使用 .NET Framework 来向其应用程序中添加 3D 功能。托管的 Direct3D 不仅与其非托管副本几乎同样高效,而且在与高级别的阴影语言结合使用时,它为高性能的 RAD 游戏开发提供了更光明的未来。 

原创粉丝点击