导出 (0) 打印
全部展开
信息
您所需的主题如下所示。但此主题未包含在此库中。

Windows Phone 8 的 XAML 和 Direct3D 应用

2014/6/18

仅适用于:Windows Phone 8 和 Windows Phone Silverlight 8.1

本主题描述了 XAML 和 Direct3D 应用的结构,并且演示了 Windows Phone SDK 8.0 中包含的项目模板。此种类型的应用使用 DrawingSurface 控件,该控件允许使用 Direct3D 来呈现显示在 XAML 控件和内容后方或与其并列显示的图形。处理 DrawingSurface 的大小和布局与使用其他 XAML 控件进行处理的方式相同。

另一种不同的应用类型是 Direct3D 和 XAML 应用,它使用 DrawingSurfaceBackgroundGrid 控件。使用该控件,您的图形将显示在整个屏幕上以及页面上任意其他 XAML 元素(包括帧中的任意元素)下。有关选择适合应用的控件的信息,请参见为 Windows Phone 8 的游戏选择正确的项目模板

XAML 和 Direct3D 应用由两个组件组成:一个基于 XAML 的 Windows Phone 应用和一个基于 Windows Phone 运行时 的组件。Windows Phone 运行时 组件会创建一个 Direct3D 图形设备,以用于创建与 XAML 应用共享的纹理。Windows Phone 运行时 组件使用 Direct3D 在纹理上进行绘制。XAML 引擎包含一个交换链,用于将纹理复制到 DrawingSurface 控件边界内的屏幕。共享的纹理被称为“同步纹理”,因为 XAML 引擎和 Windows Phone 运行时 组件不同时在纹理上操作,这一点很重要。协调所有这些碎片的操作非常复杂,但幸运的是,Windows Phone SDK 8.0 提供了一个可为您实现低级别基础结构的项目模板。您可以使用此项目模板创建新的应用,然后按 F5,您即可看到三维立方体被呈现到您设备的屏幕或 Windows Phone 模拟器。本主题的运算符将向您演示 XAML 和 Direct3D 应用的项目模板。

如果您的应用使用 XAML 和 Direct3D 项目模板,并且包含一页以上,则应该对项目模板中包含的文件进行以下更改。当用户在页面之间导航时,这样可以确保内存得到正确管理。这些更改将包含在将来发行的项目模板中。

更新项目模板的步骤

  1. 在 MainPage.xaml.cs 中,将

    
    private Direct3DInterop m_d3dInterop = new Direct3DInterop();
    
    
    

    行替换为

    
    private Direct3DInterop m_d3dInterop = null;
    
    
    
  2. 在 MainPage.xaml.cs 中,将 DrawingSurface 控件的 Loaded 事件处理程序替换为:

    
    private void DrawingSurface_Loaded(object sender, RoutedEventArgs e)
    {
        if (m_d3dInterop == null)
        {
            m_d3dInterop = new Direct3DInterop();
    
            // Set window bounds in dips
            m_d3dInterop.WindowBounds = new Windows.Foundation.Size(
                (float)DrawingSurface.ActualWidth,
                (float)DrawingSurface.ActualHeight
                );
    
            // Set native resolution in pixels
            m_d3dInterop.NativeResolution = new Windows.Foundation.Size(
                (float)Math.Floor(DrawingSurface.ActualWidth * Application.Current.Host.Content.ScaleFactor / 100.0f + 0.5f),
                (float)Math.Floor(DrawingSurface.ActualHeight * Application.Current.Host.Content.ScaleFactor / 100.0f + 0.5f)
                );
    
            // Set render resolution to the full native resolution
            m_d3dInterop.RenderResolution = m_d3dInterop.NativeResolution;
    
            // Hook-up native component to DrawingSurface
            DrawingSurface.SetContentProvider(m_d3dInterop.CreateContentProvider());
            DrawingSurface.SetManipulationHandler(m_d3dInterop);
        }
    }
    
    
    
  3. 在 Direct3DInterop.cpp 中,将 CreateContentProvider 方法的定义替换为:

    
    IDrawingSurfaceContentProvider^ Direct3DInterop::CreateContentProvider()
    {
    	ComPtr<Direct3DContentProvider> provider = Make<Direct3DContentProvider>(this);
    	return reinterpret_cast<IDrawingSurfaceContentProvider^>(provider.Get());
    }
    
    
    

本节将向您演示包含在 XAML 和 Direct3D 应用项目模板中的文件。

如果您要创建 XAML 和 Direct3D 应用,您应该从 Windows Phone SDK 8.0 随附的 XAML 和 Direct3D 应用项目模板开始。要创建新的项目,在 Visual Studio 中的“文件”菜单上,单击“新建项目”,然后在 Visual C#Visual Basic 下,单击 Windows Phone XAML 和 Direct3D 应用

此模板将创建包含两个项目的新方案: 一个托管的基于 XAML 的应用和一个 Windows Phone 运行时 组件。XAML 应用与常规 Windows Phone 应用大致相同。在此应用中,您可以使用 XAML 和托管的代码实现 UI,实现方式与在不使用任何 Direct3D 图形的应用中的实现方式相同。唯一的不同在于,一些额外的代码行将添加到项目,该项目添加显示 Direct3D 图形的 DrawingSurface 控件,并设置对 Windows Phone 运行时 组件的连接。

Windows Phone 运行时 组件项目包含一些与 Windows Phone 纯本机 Direct3D 应用中的文件和 Windows 8 的 Direct3D 项目模板相似的文件。这些文件可以为您处理图形设备的设置,并且拥有一些可在屏幕上绘制立方体的示例代码。还包括用于在 Windows Phone 运行时 组件和主应用程序之间进行通信的其他文件,以及用于将 Direct3D 图形复合到 XAML UI 的基础 XAML 引擎。如果您迫切地想要开始生成您的应用,并且对允许这些应用运行的低级别技术详细信息不感兴趣,您可以阅读承载您基于 XAML 的 UI 和托管代码的 MainPage 类、在其中执行对共享纹理的实际绘图的 CubeRenderer 类和探讨触控输入的 Direct3DInterop 一节。本主题的剩余部分将对模板中提供的代码进行更为详细的解释。

托管的 XAML 应用

如前所述,作为 XAML 和 Direct3D 模板的一部分创建的 XAML 应用项目与不使用任何 Direct3D 的常见 Windows Phone 应用大致相同。但是,在两个文件中存在一些多余的代码。

MainPage.xaml

基于 XAML 的 Windows Phone 应用在此文件中定义它们的 UI。使用此项目模板,将为您添加一个 DrawingSurface 控件。您可以像手机上支持的其他 XAML 控件一样使用此控件。可以调整控件的大小。您可以在它之前或之后添加其他控件,或使用诸如 StackPanelGrid 之类的容器来控制它在 UI 中的显示位置。


<Grid x:Name="LayoutRoot" Background="Transparent">
    <DrawingSurface x:Name="DrawingSurface" Loaded="DrawingSurface_Loaded" />
</Grid>


MainPage.xaml.cs

在主页的代码隐藏文件中,DrawingSurface 控件与执行实际 Direct3D 绘图的 Windows Phone 运行时 组件相挂钩。在页面的顶部,using 指令引用包含 Windows Phone 运行时 组件的命名空间。命名空间名称是指您在创建项目时提供的名称且名称附带“Comp”。在本例中,项目的名称为“XAMLD3DExample”。


using XAMLD3DExampleComp;


接下来,将声明 Direct3DInterop 类型的成员变量。Direct3DInterop 是由 Windows Phone 运行时 组件公开的类。在此演练中将进一步探讨此类。


private Direct3DInterop m_d3dInterop = null;


模板在 MainPage.xaml.cs 中创建的唯一方法是用于 DrawingSurface 控件的 Loaded 事件处理程序。 该方法的第一部分设置 Direct3DInterop 类的 WindowBoundsNativeResolutionRenderResolution 属性。这些属性允许 Windows Phone 运行时 组件创建与 DrawingSurface 控件相同尺寸的同步纹理。WindowBounds 属性使用以与设备无关的像素 (dips) 表示的尺寸,DrawingSurface 和所有控件也使用该单位表示其 ActualWidthActualHeight 属性。 NativeResolutionRenderResolution 属性要求尺寸以物理像素表示,因此大小通过乘以 ScaleFactor 属性再除以 100 进行转换。这可以确保所有支持的屏幕分辨率的大小(以像素表示)都是正确的。公式和 Floor 函数调用的额外 0.5 个像素将尺寸四舍五入到最接近的整数。

设置好大小属性之后,将使用由 Direct3DInterop 方法返回的对象 CreateContentProvider 调用 DrawingSurface 控件的 SetContentProvider(Object) 方法。此调用将 Windows Phone 运行时 组件设置为提供程序,以使用 Direct3D 绘制 DrawingSurface 控件的内容。正如在本演练的随后部分中所看到的那样,项目模板已进行了结构化,因此它能够使用 CreateContentProvider 帮助器方法来帮助封装用于设置内容提供程序的低级别调用。

接着,将调用 SetManipulationHandler(Object) 并传入 Direct3DInterop 对象。当用户在 DrawingSurface 控件中进行触摸时,这将注册 Direct3DInterop 以接收 PointerPressed()PointerMoved()PointerReleased() 触控事件。比起在 MainPage 代码隐藏页中获取触控事件,然后手动将该数据传递到组件,直接在您的 Windows Phone 运行时 组件中使用这些事件可使您获得更好的性能。


private void DrawingSurface_Loaded(object sender, RoutedEventArgs e)
{
    if (m_d3dInterop == null)
    {
        m_d3dInterop = new Direct3DInterop();

        // Set window bounds in dips
        m_d3dInterop.WindowBounds = new Windows.Foundation.Size(
            (float)DrawingSurface.ActualWidth,
            (float)DrawingSurface.ActualHeight
            );

        // Set native resolution in pixels
        m_d3dInterop.NativeResolution = new Windows.Foundation.Size(
            (float)Math.Floor(DrawingSurface.ActualWidth * Application.Current.Host.Content.ScaleFactor / 100.0f + 0.5f),
            (float)Math.Floor(DrawingSurface.ActualHeight * Application.Current.Host.Content.ScaleFactor / 100.0f + 0.5f)
            );

        // Set render resolution to the full native resolution
        m_d3dInterop.RenderResolution = m_d3dInterop.NativeResolution;

        // Hook-up native component to DrawingSurface
        DrawingSurface.SetContentProvider(m_d3dInterop.CreateContentProvider());
        DrawingSurface.SetManipulationHandler(m_d3dInterop);
    }
}


Windows 运行时组件

本节讨论了创建为 XAML 和 Direct3D 应用项目模板一部分的 Windows 运行时 组件。此项目中的许多文件可以处理将 DrawingSurface 控件与组件挂钩的低级别详细信息。大多数情况下,您无需修改它们。

Direct3DInterop.h

此标头文件声明了 Direct3DInterop 类。此类用作 XAML 引擎和 Direct3D 代码之间的代理。请注意,此类可实现包含 PointerPressed()PointerMoved()PointerReleased() 事件的 IDrawingSurfaceManipulationHandler 界面。


public ref class Direct3DInterop sealed : public Windows::Phone::Input::Interop::IDrawingSurfaceManipulationHandler


Direct3DInterop.cpp

在 Direct3DInterop 实现文件中,您可以查看从 MainPage.xaml.cs 中的 DrawingSurface_Loaded 事件处理程序调用的 CreateContentProvider 方法的定义。此方法将初始化 Direct3DContentProvider 类的新实例并将其强制转换为 IDrawingSurfaceContentProvider。如果您查看 IDrawingSurfaceContentProvider 的定义,您将看到它未定义任何成员。这是因为它并非一个在使用代码时实现的接口。Direct3DContentProvider 类是使用 Windows 运行时 C++ 模板库 (WRL) 实现的。此方法将类强制转换为 Windows Phone 运行时 接口以便它能够由 XAML 引擎访问。开发应用时,您无需担心任何这些实现的详细信息。对它们的介绍仅供参考。


IDrawingSurfaceContentProvider^ Direct3DInterop::CreateContentProvider()
{
	ComPtr<Direct3DContentProvider> provider = Make<Direct3DContentProvider>(this);
	return reinterpret_cast<IDrawingSurfaceContentProvider^>(provider.Get());
}


接下来,Direct3DInterop.cpp 将提供从 MainPage.xaml.cs 中的 DrawingSurface_Loaded 事件处理程序调用的 SetManipulationHost(DrawingSurfaceManipulationHost) 方法的实现,以便 Direct3DInterop 类能够接收来自 DrawingSurface 控件的触控输入事件。此模板还将为您能够添加自己代码的触控输入事件,提供无存根处理程序。


void Direct3DInterop::SetManipulationHost(DrawingSurfaceManipulationHost^ manipulationHost)
{
	manipulationHost->PointerPressed +=
		ref new TypedEventHandler<DrawingSurfaceManipulationHost^, PointerEventArgs^>(this, &Direct3DInterop::OnPointerPressed);

	manipulationHost->PointerMoved +=
		ref new TypedEventHandler<DrawingSurfaceManipulationHost^, PointerEventArgs^>(this, &Direct3DInterop::OnPointerMoved);

	manipulationHost->PointerReleased +=
		ref new TypedEventHandler<DrawingSurfaceManipulationHost^, PointerEventArgs^>(this, &Direct3DInterop::OnPointerReleased);
}



void Direct3DInterop::OnPointerPressed(DrawingSurfaceManipulationHost^ sender, PointerEventArgs^ args)
{
	// Insert your code here.
}

void Direct3DInterop::OnPointerMoved(DrawingSurfaceManipulationHost^ sender, PointerEventArgs^ args)
{
	// Insert your code here.
}

void Direct3DInterop::OnPointerReleased(DrawingSurfaceManipulationHost^ sender, PointerEventArgs^ args)
{
	// Insert your code here.
}


然后定义 RenderResolution 属性的 Setter。您可以看到,如果存在呈现器(后面将会说明),那么将用新的分辨率更新它,然后调用 RecreateSychronizedTexture 以用新的大小重新创建同步纹理。


void Direct3DInterop::RenderResolution::set(Windows::Foundation::Size renderResolution)
{
	if (renderResolution.Width  != m_renderResolution.Width ||
		renderResolution.Height != m_renderResolution.Height)
	{
		m_renderResolution = renderResolution;

		if (m_renderer)
		{
			m_renderer->UpdateForRenderResolutionChange(m_renderResolution.Width, m_renderResolution.Height);
			RecreateSynchronizedTexture();
		}
	}
}


接下来,Direct3DInterop.cpp 将定义 ConnectDisconnect 方法。在 SetContentProvider(Object) 调用完成后,将调用 Connect。在此方法中,将创建新的 CubeRenderer 并将调用其 Initialize 方法。在此时初次创建 Direct3D 图形设备和同步纹理。在此调用期间的同时,CubeRenderer 也将创建与设备相关的资源,如顶点缓冲区和着色器。请注意,此处提供在 MainPage.xaml.cs 中计算的尺寸,以便创建大小正确的同步纹理。关联的 DrawingSurface 控件离开 XAML 可视树时,调用 Disconnect


HRESULT Direct3DInterop::Connect(_In_ IDrawingSurfaceRuntimeHostNative* host)
{
	m_renderer = ref new CubeRenderer();
	m_renderer->Initialize();
	m_renderer->UpdateForWindowSizeChange(WindowBounds.Width, WindowBounds.Height);
	m_renderer->UpdateForRenderResolutionChange(m_renderResolution.Width, m_renderResolution.Height);

	// Restart timer after renderer has finished initializing.
	m_timer->Reset();

	return S_OK;
}

void Direct3DInterop::Disconnect()
{
	m_renderer = nullptr;
}


呈现纹理之前,XAML 引擎为每一帧调用 PrepareResources 方法。 如果您的应用正使用 DrawingSurface 来绘制不会随每一帧更改的内容(例如,当前没有进行动画处理的 UI 元素 或 3D 场景),您可能想要允许同步纹理在不重新呈现的情况下为多个帧显示。为此,您可以在 PrepareResources 中执行检查,确定是否应重新呈现该纹理,如果不应,则将 contentDirty 变量设置为 false。当 contentDirty 为 false 时,将不为当前帧调用 GetTexture(接下来在文件中定义)。


HRESULT Direct3DInterop::PrepareResources(_In_ const LARGE_INTEGER* presentTargetTime, _Out_ BOOL* contentDirty)
{
	*contentDirty = true;

	return S_OK;
}


Direct3DInterop 类中,有两个 GetTexture 方法的重载。第一个调用 CubeRenderer 对象上的“更新”“呈现”。在这些方法中,您的代码将更新游戏的状态并执行 Direct3D 调用以对共享的纹理进行绘制。

每次更新 UI 时,XAML 引擎都将调用 PrepareResources,如上所示。调用 RequestAdditionalFrame 的操作告诉 XAML 引擎在当前帧结束后尽快重绘 UI。在调用 Connect 之后以及调用 Disconnect 之前,可以从 Windows Phone 运行时 组件中的任意位置执行 RequestAdditionalFrame 的调用。若要尽量降低耗电量,您可以将对 RequestAdditionalFrame 的调用从 GetTexture 中移除,改为仅在需要重绘时从代码内的任意位置调用。在绘制静态内容时,您可以在两次调用之间等待几秒钟,也可以每秒钟为动画内容调用多次。如果从 GetTexture 中调用 RequestAdditionalFrame,则会尽快连续请求新帧,从而使绘制表面的帧速率尽可能达到最大。


HRESULT Direct3DInterop::GetTexture(_In_ const DrawingSurfaceSizeF* size, _Out_ IDrawingSurfaceSynchronizedTextureNative** synchronizedTexture, _Out_ DrawingSurfaceRectF* textureSubRectangle)
{
	m_timer->Update();
	m_renderer->Update(m_timer->Total, m_timer->Delta);
	m_renderer->Render();

	RequestAdditionalFrame();

	return S_OK;
}


XAML 引擎调用 GetTexture 的第二个重载(无需参数),以获取对在 DrawingSurface 控件和 Windows Phone 运行时 组件之间共享的同步纹理的引用。大多数情况下,您不需要修改此方法的代码。


ID3D11Texture2D* Direct3DInterop::GetTexture()
{
	return m_renderer->GetTexture();
}


Direct3DBase.cpp

Direct3DBase 是一个类,您可在许多 Direct3D 项目模板(包括面向 Windows Phone 的 Direct3D 应用 的模板以及面向 Windows 8 的模板)中找到它。此类的实现与 XAML 和 Direct3D 应用模板的实现稍有不同,但是其结构和功能非常相似。此类将公开一个立即调用 CreateDeviceResourcesInitialize 方法。此方法将调用 D3D11CreateDevice 以创建 Direct3D 图形设备。请注意,交换链不是为设备创建的。这是因为 Windows Phone 运行时 对同步纹理进行绘制,而不是直接对屏幕进行绘制。此设备的交换链由可将同步纹理绘制到屏幕的 XAML 引擎创建和维护。

如本演练中先前所述,Direct3DBase 公开在设置了 DrawingSurface 组件大小后,调用的 UpdateForWindowSizeChangeUpdateForRenderResolutionChange 方法。UpdateForRenderResolutionChange 将与同步纹理相关的资源设置为 null,然后再调用 CreateWindowSizeDependentResources 重新创建它们。这些资源包括表示 XAML 引擎和 Windows Phone 运行时 组件之间共享的同步纹理的 ID3DTexture2D,以及需要绘制到纹理的呈现器目标视图、深度模具纹理和视区。

CubeRenderer.cpp

CubeRenderer 类继承自 Direct3Dbase。它提供一些将立方体绘制到共享纹理的示例 Direct3D 代码。此外,此文件通用于不同平台上的 Direct3D 模板。如果您最近才开始使用 Direct3D,您可能希望花一些时间只修改此文件中的代码,以更改绘制到屏幕的内容。最后,您很可能会使用专为游戏创建的类替换此类,但是您应该保持基本结构相同。例如,您的类应该继承自 Direct3DBase

CubeRenderer.cpp 包含 CreateDeviceResources 的方法定义。它首先会调用具有相同名称的 Direct3DBase 方法(图像设备将在该方法中创建)。执行调用后,该方法能够创建依赖于该设备的资源。该模板中的示例代码不仅可以创建用于定义立方体绘制到共享纹理的方式的顶点着色器和像素着色器,还可以创建用于定义立方体的几何图形的顶点缓冲区和索引缓冲区。

接下来,将定义 CreateWindowSizeDependentResources 方法。这将调用名称相同的 Direct3DBase 类方法,然后创建基于 DrawingSurface 控件大小的投影矩阵。

最后,CubeRenderer.cpp 将实现为每个帧调用一次的 UpdateRender 方法。Update 通常用于将游戏中每个帧的对象更新到它们的新位置。在示例代码中,将对视图和模型矩阵进行更新,以使立方体能够绕屏幕旋转一周。Render 用于执行 Direct3D 调用以对共享的纹理进行绘制。您放置在此方法中的代码将根据您的游戏产生很大的变化,但是需要特别注意的是,在呈现之前,呈现器目标已设置为在 Direct3DBase 中创建的呈现器目标视图。该调用意味着后续绘图调用将对在 XAML 引擎和 Windows Phone 运行时 组件之间共享的纹理进行绘制,而不是设备的后台缓冲区或一些其他的呈现目标。


	m_d3dContext->OMSetRenderTargets(
		1,
		m_renderTargetView.GetAddressOf(),
		m_depthStencilView.Get()
		);


Direct3DContentProvider.cpp

Direct3DContentProvider.cpp 可处理 XAML 引擎与 Windows Phone 运行时 组件挂钩的低级别详细信息。此行为被封装在此类中,因为在大多数情况下您不需要修改它即可实现您的游戏。

值得注意的是,在该类的 GetTexture 方法中,您将看到对共享纹理的 BeginDrawEndDraw 方法的调用。这些调用可定义您对纹理进行绘制的时段。由于纹理在线程之间是同步的,在此代码块之前或之后尝试修改纹理都将失败。您仍然可以使用应用的图形设备对已创建的其他纹理进行绘制,但不是对同步纹理进行绘制。


		hr = m_synchronizedTexture->BeginDraw();
		
		if (SUCCEEDED(hr))
		{
			hr = m_controller->GetTexture(size, synchronizedTexture, textureSubRectangle);
		}

		m_synchronizedTexture->EndDraw();


显示:
© 2014 Microsoft