信息
您所需的主题如下所示。但此主题未包含在此库中。

Windows Phone 8 的 Direct3D 和 XAML 应用

2014/6/18

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

本主题介绍带 XAML 应用的 Direct3D 的结构,并且演示了 Windows Phone SDK 8.0 中包含的项目模板。此类应用使用 DrawingSurfaceBackgroundGrid 控件,该控件允许您使用 Direct3D 在 UI 中其他 XAML 控件的后面,呈现在应用整个背景上显示的图形。DrawingSurfaceBackgroundGrid 必须是 XAML 树的根元素并且始终是屏幕的完整大小。

不同的应用类型是指使用 DrawingSurface 控件的 XAML 和 Direct3D 应用。控件可为任意大小并且可以放置在上方、下方和与其他 XAML 控件内联。有关选择适合应用的控件的信息,请参见为 Windows Phone 8 的游戏选择正确的项目模板

为演示 Windows 应用商店 应用的游戏开发而创建的 Marble Maze 示例已经被移植,以使用带 XAML 项目模板的 Direct3D。要下载该示例,请参见 Direct3D with XAML Marble Maze

带 XAML 应用的 Direct3D 由两个组件组成:一个基于 XAML 的 Windows Phone 应用和一个基于 Windows Phone 运行时 的组件。XAML 引擎创建和维护 Direct3D 图形设备。XAML 引擎以每帧一次的频率将图形设备和相关的呈现目标传递到 Windows Phone 运行时 组件。更新图形设备后,将调用 Windows Phone 运行时 组件的呈现方法。此时,应用可以执行 Direct3D 调用以在提供的呈现目标上进行绘制。由于图形设备已使用 XAML 引擎进行了共享,Render 方法是应用使用设备进行绘制的唯一机会。Windows Phone SDK 8.0 提供可实现此类应用要求的低级别基础结构的项目模板。您可以使用此项目模板创建新的应用,然后按 F5,您即可看到三维立方体被呈现到您设备的屏幕或 Windows Phone 模拟器。本主题的运算符将向您演示带 XAML 应用项目模板的 Direct3D。

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

更新项目模板的步骤

  1. 在 MainPage.xaml.cs 中,将

    
    private Direct3DBackground m_d3dBackground = new Direct3DBackground();
    
    
    

    行替换为

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

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

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

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

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

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

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

托管的 XAML 应用

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

MainPage.xaml

基于 XAML 的 Windows Phone 应用在此文件中定义它们的 UI。使用此项目模板,将为您添加一个 DrawingSurfaceBackgroundGrid 控件。该控件是 XAML 页面的根元素。可以作为 DrawingSurfaceBackgroundGrid 的子元素添加其他 XAML 控件,但是不能将任意元素放置在 DrawingSurfaceBackgroundGrid 的之前、之后或包含该元素。


<DrawingSurfaceBackgroundGrid x:Name="DrawingSurfaceBackground" Loaded="DrawingSurfaceBackground_Loaded">
</DrawingSurfaceBackgroundGrid>


MainPage.xaml.cs

在主页的代码隐藏文件中,DrawingSurfaceBackgroundGrid 控件与执行实际 Direct3D 绘图的 Windows Phone 运行时 组件相挂钩。在页面的顶部,using 指令应用包含 Windows Phone 运行时 组件(“PhoneDirect3DXamlAppComponent”)的命名空间。


using PhoneDirect3DXamlAppComponent;


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


private Direct3DBackground m_d3dBackground = null;


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

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

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


private void DrawingSurfaceBackground_Loaded(object sender, RoutedEventArgs e)
{
    if (m_d3dBackground == null)
    {
        m_d3dBackground = new Direct3DBackground();

        // Set window bounds in dips
        m_d3dBackground.WindowBounds = new Windows.Foundation.Size(
            (float)Application.Current.Host.Content.ActualWidth,
            (float)Application.Current.Host.Content.ActualHeight
            );

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

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

        // Hook-up native component to DrawingSurfaceBackgroundGrid
        DrawingSurfaceBackground.SetBackgroundContentProvider(m_d3dBackground.CreateContentProvider());
        DrawingSurfaceBackground.SetBackgroundManipulationHandler(m_d3dBackground);
    }
}


Windows 运行时组件

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

PhoneDirect3DXamlAppComponent.h

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

PhoneDirect3DXamlAppComponent.cpp

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


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


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


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

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

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



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

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

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


接下来,PhoneDirect3DXamlAppComponent.cpp 定义 ConnectDisconnect 方法。在 SetContentProvider(Object) 调用完成后,将调用 Connect。在此方法中,将创建新的 CubeRenderer 并将调用其 Initialize 方法。在此调用期间的同时,CubeRenderer 将创建与设备相关的资源,如顶点缓冲区和着色器。如果相关的 DrawingSurfaceBackgroundGrid 控件离开可视化树,将调用 Disconnect


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

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

	return S_OK;
}

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


XAML 引擎对每个帧调用 PrepareResources 方法。 此时,Windows Phone 运行时 组件可以设置呈现目标的请求大小。模板将呈现目标的宽和高设置为屏幕的大小(屏幕的大小已经由 DrawingSurfaceBackground_Loaded 中的 XAML 应用进行了设置)。您可以将大小设置为更小(例如,实际屏幕分辨率的四分之一),以便加快呈现。XAML 引擎将自动使用硬件缩放,以有效地将较小的呈现目标缩放至最大整个屏幕的大小。

警告说明警告:

XAML 引擎使用硬件缩放,以缩放带 XAML 应用的 Direct3D 的呈现目标。由于 MediaElement 也使用硬件缩放,XAML 可视化树中的 MediaElement 是否存在对于 DrawingSurfaceBackgroundGrid 性能非常重要。 使用不具有 MediaElementDrawingSurfaceBackgroundGrid 允许运行时进一步优化,从而实现性能的改进。


HRESULT Direct3DBackground::PrepareResources(_In_ const LARGE_INTEGER* presentTargetTime, _Inout_ DrawingSurfaceSizeF* desiredRenderTargetSize)
{
	m_timer->Update();
	m_renderer->Update(m_timer->Total, m_timer->Delta);

	desiredRenderTargetSize->width = RenderResolution.Width;
	desiredRenderTargetSize->height = RenderResolution.Height;

	return S_OK;
}


在 PhoneDirect3DXamlAppComponent.cpp 中定义的最后的方法是 Draw。在此方法中首先发生的事情是调用在 Direct3Dbase.cpp 中定义的 UpdateDevice 以更新图形设备、上下文和呈现目标。如果用户离开您的应用,然后再返回,XAML 引擎可能会丢失,然后重新创建其图形设备。如果发生这种状况,此方法允许您的应用重新创建设备相关的资源,例如纹理和着色器。

每次 XAML 引擎更新 XAML UI 时,它都会调用 Draw 方法。当前帧完成后,对 RequestAdditionalFrame 的调用将立即请求 XAML 引擎重新绘制屏幕。在调用 Connect 之后以及调用 Disconnect 之前,可以从 Windows Phone 运行时 组件中的任意位置执行 RequestAdditionalFrame 的调用。要降低应用的耗电量,您可以修改模板代码,以便每次调用 Draw 方法时不会调用 RequestAdditionalFrame。每次更新 UI 时,XAML 引擎仍然调用 Draw,但它不会立即尝试重新绘制。当应用显示静态内容或不需要在尽可能最高的帧速率下进行重新绘制的内容时,这特别有用。


HRESULT Direct3DBackground::Draw(_In_ ID3D11Device1* device, _In_ ID3D11DeviceContext1* context, _In_ ID3D11RenderTargetView* renderTargetView)
{
	m_renderer->UpdateDevice(device, context, renderTargetView);
	m_renderer->Render();

	RequestAdditionalFrame();

	return S_OK;
}


Direct3DBase.cpp

Direct3DBase 是一个类,您可在许多 Direct3D 项目模板(包括面向 Windows Phone 的 Direct3D 应用 的模板以及面向 Windows 8 的模板)中找到它。此类的实现与 XAML 和 Direct3D 应用模板的实现稍有不同,但是其结构和功能非常相似。在大多数情况下,您不需要修改此类。此类将公开一个立即调用 CreateDeviceResourcesInitialize 方法。此方法的基实现不执行任何操作。CubeRenderer.cpp 中的重载方法使用此方法创建设备相关的资源,例如纹理和着色器。请记住,如果图形设备在应用运行时发生更改,可以多次调用此方法。

接下来,Direct3DBase 将实现 UpdateDevice 方法。在以前,我们看到每帧从 Direct3DBackground 类调用此方法一次。如果图形设备自前一帧发生了更改,将再次调用 CreateDeviceResources。如果呈现目标的尺寸已经发生了更改,将调用 CreateWindowSizeDependentResources。最后,将从渲染目标尺寸创建视口对象。

CubeRenderer.cpp

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

CubeRenderer.cpp 包含 CreateDeviceResources 的方法定义。此时您可以创建与设备相关的资源。该模板中的示例代码不仅可以创建用于定义立方体绘制呈现目标的方式的顶点着色器和像素着色器,还可以创建用于定义立方体的几何图形的顶点缓冲区和索引缓冲区。

接下来,将定义 CreateWindowSizeDependentResources 方法。此方法使用呈现目标的尺寸创建透视矩阵。

最后,CubeRenderer.cpp 将实现为每个帧调用一次的 UpdateRender 方法。Update 通常用于将游戏中每个帧的对象更新到它们的新位置。在示例代码中,将对视图和模型矩阵进行更新,以使立方体能够绕屏幕旋转一周。Render 用于执行 Direct3D 调用以对呈现目标进行绘制。放置在此方法中的代码将根据游戏而出现较大的差异。请务必注意,由于图形设备归 XAML 引擎所有,您仅可以在 Render 方法期间使用图形设备对呈现目标进行绘制。

Direct3DContentProvider.cpp

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

显示: