新建了Direct2D中的资源后,接下来初始化用于绘制图形的应用窗口。在解决方案资源管理器窗口中右键点击项目图标,在弹出的菜单栏中选中"添加", 并在"添加"的子菜单栏中选择"新建项",在出现的"添加新项"窗口中选中"C++ 文件(.cpp)",添加名为"D2DBasicAnimation.cpp"的源文件。然后使用同样的方法在"添加新项"窗口中选中"头文件(.h)",添加名为"D2DBasicAnimation.h"的头文件。在本示例中用于激活应用窗口的实现代码,绘制几何图形的实现代码以及项目的主入口函数的实现代码将添加到D2DBasicAnimation.cpp源文件中,而D2DBasicAnimation.h头文件则用于声明在D2DBasicAnimation.cpp源文件中使用到的函数和变量。
添加了D2DBasicAnimation.h头文件和D2DBasicAnimation.cpp源文件以后,接下来打开D2DBasicAnimation.h头文件,并添加如下的代码定义一个D2DBasicAnimation类。
#include "DirectXBase.h"
#include "DirectXHelper.h"
//定义类D2DBasicAnimation,D2DBasicAnimation继承DirectXBase类和IFrameworkView接口
ref class D2DBasicAnimation : public DirectXBase, public Windows::ApplicationModel::Core::IFrameworkView
{
internal:
//构造函数
D2DBasicAnimation();
internal:
// 重写DirectXBase中的CreateDeviceResources函数
virtual void CreateDeviceResources() override;
public:
// 实现 IFrameworkView 中的函数
//激活应用窗口
virtual void Initialize(_In_ Windows::ApplicationModel::Core::CoreApplicationView^ applicationView);
//初始化应用窗口
virtual void SetWindow(_In_ Windows::UI::Core::CoreWindow^ window);
//在Run函数调用前加载视图所使用的资源
virtual void Load(_In_ Platform::String^ entryPoint);
//用于绘制三角形
virtual void Run();
virtual void Uninitialize();
};
在上面的代码中,首先使用include关键字引用DirectXBase.h头文件和DirectXHelper.h头文件,接着定义D2DBasicAnimation类,此类继承自DirectXBase类并实现IFrameworkView接口。在D2DBasicAnimation类中,声明构造函数D2DBasicAnimation,并重写DirectXBase类的CreateDeviceResources函数,此函数的实现代码将在第三部分进行介绍。在IFrameworkView接口中声明了Initialize函数、SetWindow函数、Load函数、Run函数和Uninitialize函数,为了实现这些函数,需要先在D2DBasicAnimation类中进行声明。
本部分将介绍Initialize函数、SetWindow函数、Load函数和Uninitialize函数的实现代码,其中SetWindow函数和Initialize函数分别用于初始化应用窗口和激活应用窗口,而在本示例中Run函数用于绘制几何图形,此函数的实现代码将在第三部分进行介绍。另外要注意的是为了避免程序出现异常,需要在CreateDeviceResources函数、Initialize函数、SetWindow函数、Load函数、Run函数和Uninitialize函数之前添加virtual关键字。
定义了D2DBasicAnimation类以后,接下来介绍如何初始化应用窗口。首先打开D2DBasicAnimation.cpp源文件,并引用如下的头文件和命名空间,在初始化和激活应用窗口时将会使用到定义在这些头文件和命名空间中的类。
#include "pch.h"
#include "D2DBasicAnimation.h"
using namespace Microsoft::WRL;
using namespace Windows::ApplicationModel;
using namespace Windows::ApplicationModel::Core;
using namespace Windows::ApplicationModel::Activation;
using namespace Windows::UI::Core;
using namespace Windows::System;
using namespace Windows::Foundation;
using namespace Windows::Graphics::Display;
using namespace D2D1;
引用上述的头文件和命名空间以后,接着在D2DBasicAnimation.cpp源文中添加Load函数和Uninitialize函数的实现代码,虽然在本示例中Load函数和Uninitialize函数并没有实现任何功能,但仍需要添加这两个函数的实现代码,保证项目运行时不会出现异常。代码如下所示:
//Load函数实现代码
void D2DBasicAnimation::Load(_In_ Platform::String^ entryPoint)
{
}
//Uninitialize函数实现代码
void D2DBasicAnimation::Uninitialize()
{
}
添加了Load函数和Uninitialize函数的实现代码以后,接下来在D2DBasicAnimation.cpp源文中添加SetWindow函数的实现代码,在此函数中将调用DirectXBase类的Initialize函数来初始化应用窗口。具体代码如下所示:
//初始化窗口
void D2DBasicAnimation::SetWindow(_In_ CoreWindow^ window)
{
DirectXBase::Initialize(window, DisplayProperties::LogicalDpi);
}
本示例将DirectXBase类的Initialize函数的实现代码添加在DirectXBase.cpp源文件中,为了实现此函数,首先打开DirectXBase.h头文件,并在DirectXBase类中添加如下代码用来声明window变量和Initialize函数。
protected private:
//声明成员变量window
Platform::Agile<Windows::UI::Core::CoreWindow> window;
internal:
//初始化应用窗口
void Initialize(Windows::UI::Core::CoreWindow^ window, float dpi);
声明了Initialize函数以后,接着在DirectXBase.cpp源文件中添加Initialize函数的实现代码,具体代码如下所示:
//初始化应用窗口
void DirectXBase::Initialize(CoreWindow^ coreWindow, float dpi)
{
//给window变量赋值
window = coreWindow;
//新建独立于设备的资源
CreateDeviceIndependentResources();
//新建依赖于设备的资源
CreateDeviceResources();
//设置Direct2D设备上下文的DPI值
SetDpi(dpi);
}
在上面的代码中,首先将Initialize函数的参数coreWindow赋值给window变量,然后调用CreateDeviceIndependentResources函数和CreateDeviceResources函数分别新建独立于设备的资源和依赖于设备的资源,最后调用SetDpi函数来设置Direct2D设备上下文的DPI值,DPI值即每英寸的像素,Direct2D设备上下文包含了用于绘制图形的相关信息,包括每英寸的像素等。
添加了Initialize函数的实现代码以后,接下来实现上面的SetDpi函数。在DirectXBase.h头文件的DirectXBase类中添加如下代码,声明函数SetDpi和成员变量dpi、numBuffers。
protected private:
//声明成员变量dpi
float dpi;
//声明成员变量numBuffers
unsigned int numBuffers;
internal:
//设置Direct2D设备上下文的DPI值
void SetDpi(float dpi);
在初始化应用窗口的过程中将会用到dpi变量和numBuffers变量,首先需要为这两个变量赋初始值。在DirectXBase.cpp源文件中添加DirectXBase构造函数的实现代码,将dpi变量和numBuffers变量分别赋值为-1.0f和2,具体代码如下所示:
//DirectXBase构造函数
DirectXBase::DirectXBase() :
dpi(-1.0f),
numBuffers(2)
{
}
接着在DirectXBase.cpp源文件中添加SetDpi函数的实现代码,具体代码如下所示:
//设置Direct2D设备上下文的DPI值
void DirectXBase::SetDpi(float dpiParameter)
{
//当前DPI值改变时更新DPI值
if (dpiParameter!= dpi)
{
dpi = dpiParameter;
//更新Direct2D设备上下文对象的DPI值
d2dContext->SetDpi(dpi, dpi);
//更新应用窗口的大小
UpdateForWindowSizeChange();
}
}
在上面的代码中,如果dpi变量保存的DPI值不等于参数dpiParameter保存的DPI值时,将参数dpiParameter赋值给dpi变量,并以dpi变量作为参数调用d2dContext指针所指向的对象的SetDpi函数来设置Direct2D设备上下文的DPI值。最后调用UpdateForWindowSizeChange函数更新应用窗口的大小。
添加了SetDpi函数的实现代码以后,接下来实现上面的UpdateForWindowSizeChange函数。首先在DirectXBase.h头文件的DirectXBase类中添加如下的代码:
protected private:
//声明成员变量d2dTargetBitmap
Microsoft::WRL::ComPtr<ID2D1Bitmap1> d2dTargetBitmap;
//声明成员变量renderTargetView
Microsoft::WRL::ComPtr<ID3D11RenderTargetView> renderTargetView;
//声明成员变量depthStencilView
Microsoft::WRL::ComPtr<ID3D11DepthStencilView> depthStencilView;
//声明成员变量windowBounds
Windows::Foundation::Rect windowBounds;
internal:
//更新应用窗口的大小
void UpdateForWindowSizeChange();
在上面的代码中,使用protected private关键字声明四个受保护的私有成员变量,分别为d2dTargetBitmap、renderTargetView、depthStencilView和windowBounds,其中d2dTargetBitmap为ID2D1Bitmap1类型的指针,renderTargetView为ID3D11RenderTargetView类型的指针,depthStencilView为ID3D11DepthStencilView类型的指针,windowBounds为Rect类型的变量。然后使用internal关键字声明内部访问的UpdateForWindowSizeChange函数。
声明了上述的成员变量和UpdateForWindowSizeChange函数以后,接着在DirectXBase.cpp源文件中添加UpdateForWindowSizeChange函数的实现代码,具体代码如下所示:
//更新应用窗口的大小
void DirectXBase::UpdateForWindowSizeChange()
{
//当应用窗口大小改变时,更新应用窗口的大小,重新新建应用窗口资源
if (window->Bounds.Width != windowBounds.Width || window->Bounds.Height != windowBounds.Height)
{
//将d2dTargetBitmap指针设为空指针
d2dTargetBitmap = nullptr;
//将renderTargetView指针设为空指针
renderTargetView = nullptr;
//将depthStencilView指针设为空指针
depthStencilView = nullptr;
//更新应用窗口的大小
windowBounds = window->Bounds;
//新建与应用窗口大小相关的资源
CreateWindowSizeDependentResources();
}
}
在上面的代码中,首先判断window->Bounds属性的Width成员和Height成员是否等于windowBounds变量的Width属性和Height属性,即应用窗口的大小是否发生改变。如果应用窗口的大小发生了改变,将d2dTargetBitmap指针、renderTargetView指针和depthStencilView指针都赋值为空指针,然后将windowBounds变量赋值为window->Bounds属性来更新应用窗口的大小,并调用CreateWindowSizeDependentResources函数新建与应用窗口大小相关的资源。
接下来实现上面的CreateWindowSizeDependentResources函数。首先在DirectXBase.h头文件的DirectXBase类中添加如下的代码,用来声明CreateWindowSizeDependentResources函数。
internal:
//新建与应用窗口大小相关的资源
void CreateWindowSizeDependentResources();
添加了CreateWindowSizeDependentResources函数的声明代码以后,在DirectXBase.cpp源文件中添加CreateWindowSizeDependentResources函数的实现代码,具体代码如下所示:
//新建与应用窗口大小相关的资源
void DirectXBase::CreateWindowSizeDependentResources()
{
// 避免在大小相同的情况下重新生成所有内容
if (swapChain != nullptr)
{
// 如果交换链已存在,调整其后台缓存大小
DX::ThrowIfFailed(
swapChain->ResizeBuffers(numBuffers, 0, 0, DXGI_FORMAT_B8G8R8A8_UNORM, 0)
);
}
else
{
// 否则,使用与现有 Direct3D 设备相同的适配器新建一个
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
// 设置成员变量Width
swapChainDesc.Width = 0;
// 设置成员变量Height
swapChainDesc.Height = 0;
// 设置成员变量Format
swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
// 设置成员变量Stereo
swapChainDesc.Stereo = false;
// 设置成员变量SampleDesc
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
// 设置成员变量BufferUsage
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
// 设置成员变量BufferCount
swapChainDesc.BufferCount = numBuffers;
// 设置成员变量Scaling
swapChainDesc.Scaling = DXGI_SCALING_NONE;
// 设置成员变量SwapEffect
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
// 设置成员变量Flags
swapChainDesc.Flags = 0;
// 创建Device设备
ComPtr<IDXGIDevice1> dxgiDevice;
DX::ThrowIfFailed(
d3dDevice.As(&dxgiDevice)
);
// 创建适配器
ComPtr<IDXGIAdapter> dxgiAdapter;
DX::ThrowIfFailed(
dxgiDevice->GetAdapter(&dxgiAdapter)
);
// 创建工厂
ComPtr<IDXGIFactory2> dxgiFactory;
DX::ThrowIfFailed(
dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory))
);
// 创建交换链
CoreWindow^ coreWindow = window.Get();
DX::ThrowIfFailed(
dxgiFactory->CreateSwapChainForCoreWindow(d3dDevice.Get(),reinterpret_cast<IUnknown*>(coreWindow),&swapChainDesc,nullptr,&swapChain)
);
DX::ThrowIfFailed(
dxgiDevice->SetMaximumFrameLatency(numBuffers-1)
);
}
// 创建交换链后台缓冲区的呈现目标视图
ComPtr<ID3D11Texture2D> backBuffer;
DX::ThrowIfFailed(
swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))
);
// 创建渲染目标
DX::ThrowIfFailed(
d3dDevice->CreateRenderTargetView(backBuffer.Get(),nullptr,&renderTargetView)
);
D3D11_TEXTURE2D_DESC backBufferDesc = {0};
backBuffer->GetDesc(&backBufferDesc);
CD3D11_TEXTURE2D_DESC depthStencilDesc(DXGI_FORMAT_D24_UNORM_S8_UINT,backBufferDesc.Width,backBufferDesc.Height,1,1,D3D11_BIND_DEPTH_STENCIL);
//创建一组2D纹理
ComPtr<ID3D11Texture2D> depthStencil;
DX::ThrowIfFailed(
d3dDevice->CreateTexture2D(&depthStencilDesc,nullptr,&depthStencil)
);
//创建深度视图
CD3D11_DEPTH_STENCIL_VIEW_DESC viewDesc = CD3D11_DEPTH_STENCIL_VIEW_DESC(D3D11_DSV_DIMENSION_TEXTURE2D);
DX::ThrowIfFailed(
d3dDevice->CreateDepthStencilView(depthStencil.Get(),&viewDesc,&depthStencilView)
);
//设置用于确定整个窗口的呈现视区
CD3D11_VIEWPORT viewport(0.0f,0.0f,static_cast<float>(backBufferDesc.Width),static_cast<float>(backBufferDesc.Height));
d3dContext->RSSetViewports(1, &viewport);
D2D1_BITMAP_PROPERTIES1 bitmapProperties = BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),dpi,dpi);
ComPtr<IDXGISurface> dxgiBackBuffer;
DX::ThrowIfFailed(
swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer))
);
DX::ThrowIfFailed(
d2dContext->CreateBitmapFromDxgiSurface(dxgiBackBuffer.Get(),&bitmapProperties,&d2dTargetBitmap)
);
d2dContext->SetTarget(d2dTargetBitmap.Get());
//将文本抗锯齿模式设为使用灰度抗锯齿
d2dContext->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
}
在上述的代码中,首先判断swapChain指针是否为空指针,如果swapChain指针不为空指针,调用swapChain指针所指向的对象的ResizeBuffers函数来调整交换链后台缓存的大小。这里的交换链主要用于更新前台缓存和后台缓存,使用Direct2D所绘制的图形保存在后台缓存中,前台缓存则用于将所绘制的图形显示到应用窗口中。
如果swapChain指针为空指针,定义一个DXGI_SWAP_CHAIN_DESC1结构体的变量swapChainDesc,并设置swapChainDesc结构体变量中的成员变量用来描述交换链中的信息。然后以swapChainDesc结构体变量作为参数调用CreateSwapChainForCoreWindow函数,得到一个IDXGISwapChain1类型的对象,使用swapChain指针指向这个对象。接着声明一个ID3D11Texture2D类型的指针backBuffer,并调用swapChain指针所指向的对象的GetBuffer函数来得到交换链的后台缓存对象,使用backBuffer指针指向这个后台缓存对象。然后以backBuffer指针作为参数调用CreateRenderTargetView函数得到一个ID3D11RenderTargetView类型的对象,使用renderTargetView指针指向这个对象。
接下来定义一个D3D11_TEXTURE2D_DESC结构体的变量backBufferDesc,并将此结构体变量的Width属性和Height属性赋值给renderTargetView指针所指向的对象的Width属性和Height属性。接着创建一个CD3D11_TEXTURE2D_DESC类的对象depthStencilDesc,并声明一个ID3D11Texture2D类型的指针depthStencil。然后以depthStencilDesc对象作为参数调用d3dDevice指针所指向的对象的CreateTexture2D函数得到一个ID3D11Texture2D类型的对象,使用depthStencil指针指向这个对象。
继续定义一个CD3D11_DEPTH_STENCIL_VIEW_DESC结构体的变量viewDesc,并以此结构体变量作为参数调用d3dDevice指针所指向的对象的CreateDepthStencilView函数,得到一个ID3D11DepthStencilView类型的对象,使用depthStencilView指针指向这个对象。然后创建一个CD3D11_VIEWPORT类的对象viewport,并将这个对象作为参数传递给d3dContext指针所指向的对象的RSSetViewports函数。
接着调用d2dContext指针所指向的对象的CreateBitmapFromDxgiSurface函数得到一个ID2D1Bitmap1类型的对象,使用d2dTargetBitmap指针指向这个对象,并将d2dTargetBitmap指针作为参数传递给d2dContext指针所指向的对象的SetTarget函数。最后调用d2dContext指针所指向的对象的SetTextAntialiasMode函数设置文本的抗锯齿模式为D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE,表示使用灰度抗锯齿。
初始化应用窗口以后,接下来激活这个应用窗口,这部分的实现代码将添加在D2DBasicAnimation.cpp源文件中。打开D2DBasicAnimation.cpp源文件,并添加Initialize函数的实现代码,为applicationView参数的Activated事件添加事件处理函数OnActivated用于激活应用窗口,具体代码如下所示:
void D2DBasicAnimation::Initialize(_In_ CoreApplicationView^ applicationView)
{
//激活窗口,调用OnActivated方法
applicationView->Activated +=
ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(this, &D2DBasicAnimation::OnActivated);
}
接下来在D2DBasicAnimation.h头文件的D2DBasicAnimation类中添加如下的代码,用来声明OnActivated函数。
private:
//激活应用窗口
void OnActivated(_In_ Windows::ApplicationModel::Core::CoreApplicationView^ applicationView,_In_ Windows::ApplicationModel::Activation::IActivatedEventArgs^ args);
添加了OnActivated函数的声明代码以后,接下来在D2DBasicAnimation.cpp源文件中添加OnActivated函数的实现代码,调用window指针所指向的对象的Activate函数来激活应用窗口,具体代码如下所示:
void D2DBasicAnimation::OnActivated(_In_ CoreApplicationView^ applicationView,_In_ IActivatedEventArgs^ args)
{
//激活窗口
window->Activate();
}
到此已经实现了初始化应用窗口和激活应用窗口,下面的部分将介绍如何在这个应用窗口中绘制三角形。