DirectX11 With Windows SDK--08 Direct2D与Direct3D互操作性以及利用DWrite显示文字
2018-05-29 18:26
916 查看
前言
在DX11,要显示文字可以说是一件比较麻烦的事情。DX9诸如Id3dXFont用于显示文字的接口类都已经被抛弃掉了。目前行之有效的两种显示文字的方法如下:
使用包含文字的位图/矢量图,然后通过一定的方式来获取对应文字的矩形区域,最后渲染出来。
通过实现Direct2D与Direct3D互操作性,然后配合DWrite在程序写入文字。
对于个人来说,第一种方式做起来比较麻烦。对于第二种方法,我通过查阅MSDN文档,并进行了一定尝试,很快就实现了文字显示。因此接下来将围绕第二种方法进行讨论(这里不关注贴位图和绘制几何体等在Direct2D的其余操作,这些都可以在Direct3D做到)
DirectX11 With Windows SDK完整目录
Github项目源码
通过DXGI进行互操作
从 Direct3D 10.1开始, Direct3D Runtime使用DXGI进行资源管理。DXGI Runtime提供了跨进程共享视频内存图面的功能,并且可用作其他基于视频内存的运行时平台的基础。Direct2D 使用 DXGI 与 Direct3D 交互。为了实现Direct2D和Direct3D互操作,并显示文字,需要经历下面的准备步骤:
在
d3dApp.h添加头文件
d2d1.h和
dwrite.h,并添加静态库
d2d1.lib和
dwrite.lib
修改创建
ID3D11Device和
IDXGISwapChain时的一些配置参数
创建
ID2D1Factory
通过
IDXGISwapChain获取接口类
IDXGISurface,并通过它来创建
ID2D1RenderTarget以进行绑定。这样就可以通过该渲染目标进行具体操作了。
D3D11设备和DXGI交换链的创建属性修改
由于Direct2D需要支持BGRA的数据格式,因此在创建D3D11设备前需要修改如下部分:// 创建D3D设备 和 D3D设备上下文 UINT createDeviceFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; // Direct2D需要支持BGRA格式 #if defined(DEBUG) || defined(_DEBUG) createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; #endif
然后在创建DXGI交换链的时候也要将DXGI格式修改为
DXGI_FORMAT_B8G8R8A8_UNORM:
// 检测 MSAA支持的质量等级 md3dDevice->CheckMultisampleQualityLevels( DXGI_FORMAT_B8G8R8A8_UNORM, 4, &m4xMsaaQuality); // 注意此处DXGI_FORMAT_B8G8R8A8_UNORM assert(m4xMsaaQuality > 0); ... // 如果包含,则说明支持DX11.1 if (dxgiFactory2 != nullptr) { ... // 填充各种结构体用以描述交换链 DXGI_SWAP_CHAIN_DESC1 sd; ZeroMemory(&sd, sizeof(sd)); ... sd.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // 注意此处DXGI_FORMAT_B8G8R8A8_UNORM ... } else { // 填充DXGI_SWAP_CHAIN_DESC用以描述交换链 DXGI_SWAP_CHAIN_DESC sd; ZeroMemory(&sd, sizeof(sd)); ... sd.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // 注意此处DXGI_FORMAT_B8G8R8A8_UNORM ... }
D2D1CreateFactory函数--创建D2D工厂对象
在创建D2D渲染目标前,还需要先创建一个ID2D1Factory对象,可以用来创建各种资源:
template<class Factory> HRESULT D2D1CreateFactory( D2D1_FACTORY_TYPE factoryType, // [In]枚举值 Factory **factory // [Out]获取的工厂对象 );
创建操作如下:
HR(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, md2dFactory.GetAddressOf()));
注意这里用了
HR宏,以及
md2dFactory是
ComPtr<ID2D1Factory>类型
ID2D1Factory::CreateDxgiSurfaceRenderTarget方法--创建一个DXGI表面渲染目标
现在我们要创建的是ID2D1RenderTarget对象。
接下来的操作需要在每次窗口大小变化且调用了
IDXGISwapChain::ReSizeBuffers方法之后进行。通常建议写在
GameApp::OnReSize内调用
D3DApp::OnReSize之后。
首先释放之前创建的D2D资源(如果有的话),通过
IDXGISwapChain::GetBuffer方法来获取后备缓冲区的
IDXGISurface接口:
md2dRenderTarget.Reset(); ComPtr<IDXGISurface> surface; HR(mSwapChain->GetBuffer(0, __uuidof(IDXGISurface), reinterpret_cast<void**>(surface.GetAddressOf())));
然后填充
D2D1_RENDER_TARGET_PROPERTIES结构体属性:
typedef struct D2D1_RENDER_TARGET_PROPERTIES { D2D1_RENDER_TARGET_TYPE type; // 渲染目标类型枚举值 D2D1_PIXEL_FORMAT pixelFormat; FLOAT dpiX; // X方向每英寸像素点数,设为0.0f使用默认DPI FLOAT dpiY; // Y方向每英寸像素点数,设为0.0f使用默认DPI D2D1_RENDER_TARGET_USAGE usage; // 渲染目标用途枚举值 D2D1_FEATURE_LEVEL minLevel; // D2D最小特性等级 } D2D1_RENDER_TARGET_PROPERTIES; typedef struct D2D1_PIXEL_FORMAT { DXGI_FORMAT format; // DXGI格式 D2D1_ALPHA_MODE alphaMode; // 混合模式 } D2D1_PIXEL_FORMAT;
可以借用
D2D1::RenderTargetProperties函数来创建,这里使用默认DPI:
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties( D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED));
最后
ID2D1Factory::CreateDxgiSurfaceRenderTarget方法如下:
HRESULT ID2D1Factory::CreateDxgiSurfaceRenderTarget( IDXGISurface *dxgiSurface, // [In]DXGI表面 const D2D1_RENDER_TARGET_PROPERTIES *renderTargetProperties, // [In]D2D渲染目标属性 ID2D1RenderTarget **renderTarget // [Out]得到的D2D渲染目标 );
具体调用如下:
HR(md2dFactory->CreateDxgiSurfaceRenderTarget(surface.Get(), &props, md2dRenderTarget.GetAddressOf())); surface.Reset();
至此,Direct2D就可以和Direct3D通过DXGI实现互操作了。通过
ID2D1RenderTarget,你可以创建各种类型的颜色刷子,并进行绘制操作。但由于我们需要绘制文字,下面会介绍
DWrite。
使用DWrite显示文字
要显示文字,需要经过下面的步骤:创建
IDWriteFactory工厂对象
通过DWrite工厂对象创建
IDWriteTextFormat文本格式对象
为文本格式对象设置好文本格式
通过
ID2D1RenderTarget创建颜色刷
在绘制完3D部分后以及最终呈现之前进行文本绘制
DWriteCreateFactory函数--创建DWrite工厂对象
函数原型如下:HRESULT DWRITE_EXPORT DWriteCreateFactory( DWRITE_FACTORY_TYPE factoryType, // [In]工厂类型枚举 const IID & iid, // [In]接口标识ID IUnknown **factory // [Out]获得工厂对象 );
下面演示了创建过程:
HR(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(mdwriteFactory.GetAddressOf())));
IDWriteFactory::CreateTextFormat方法--创建文本格式对象
HRESULT IDWriteFactory::CreateTextFormat( const WCHAR * fontFamilyName, // [In]字体系列名称 IDWriteFontCollection * fontCollection, // [In]通常用nullptr来表示使用系统字体集合 DWRITE_FONT_WEIGHT fontWeight, // [In]字体粗细程度枚举值 DWRITE_FONT_STYLE fontStyle, // [In]字体样式枚举值 DWRITE_FONT_STRETCH fontStretch, // [In]字体拉伸程度枚举值 FLOAT fontSize, // [In]字体大小 const WCHAR * localeName, // [In]区域名称 IDWriteTextFormat ** textFormat); // [Out]创建的文本格式
字体系列的名称可以用中文来引用,比如
L"宋体",
L"微软雅黑"等。
字体粗细看个人喜好,用
DWRITE_FONT_WEIGHT_NORMAL就差不多了吧
字体样式如下:
枚举值 | 样式 |
---|---|
DWRITE_FONT_STYLE_NORMAL | 默认 |
DWRITE_FONT_STYLE_OBLIQUE | 斜体 |
DWRITE_FONT_STYLE_ITALIC | 意大利体 |
DWRITE_FONT_STRETCH_NORMAL就可以了
字体大小建议在Word文档提前感受一下
区域名称这里默认用
L"zh-cn"
创建演示如下:
HR(mdwriteFactory->CreateTextFormat(L"宋体", nullptr, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 20, L"zh-cn", mTextFormat.GetAddressOf()));
创建了
IDWriteTextFormat对象后,可以调用它的一系列Get方法获取文本格式的详细信息,也可以用一系列Set方法来设置。这里不展开说明。
ID2D1RenderTarget::CreateSolidColorBrush方法--创建单色刷对象
虽然ID2D1RenderTarget对象提供了多种刷子供创建,但最常用的还是创建
ID2D1SolidColorBrush单色刷。
该方法是经过重载的,现在只讨论其中一种重载方法:
HRESULT ID2D1RenderTarget::CreateSolidColorBrush( const D2D1_COLOR_F &color, // [In]颜色 ID2D1SolidColorBrush **solidColorBrush // [Out]输出的颜色刷 );
这里会默认指定
Alpha值为1.0
D2D1_COLOR_F是一个包含
r,
g,
b,
a浮点数的结构体,但其实还有一种办法可以指定颜色,就是利用它的继承类
D2D1::ColorF中的构造函数,以及
D2D1::ColorF::Enum枚举类型来指定要使用的颜色,可以进里面去查看,这里就不给出所有的颜色枚举了。
下面演示了怎么创建一个单色刷:
// 创建固定颜色刷和文本格式 HR(md2dRenderTarget->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::White), mColorBrush.GetAddressOf()))
D3DApp类、GameApp类的变化以及开始文本绘制
这里以上一个项目为例,进行修改。在
D3DApp类中,新增了
D3DApp::InitDirect2D方法用于创建D2D工厂和DWrite工厂:
bool D3DApp::InitDirect2D()
{
HR(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, md2dFactory.GetAddressOf()));
HR(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(mdwriteFactory.GetAddressOf())));
return true;
}
该方法在
D3DApp::Init中被调用。
而在
GameApp::OnReSize方法中也进行了修改:
void GameApp::OnResize()
{
assert(md2dFactory);
assert(mdwriteFactory);
// 释放D2D的相关资源
mColorBrush.Reset();
md2dRenderTarget.Reset();
// 调用父类方法
D3DApp::OnResize();
// 为D2D创建DXGI表面渲染目标
ComPtr<IDXGISurface> surface;
HR(mSwapChain->GetBuffer(0, __uuidof(IDXGISurface), reinterpret_cast<void**>(surface.GetAddressOf())));
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties( D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED));
HR(md2dFactory->CreateDxgiSurfaceRenderTarget(surface.Get(), &props, md2dRenderTarget.GetAddressOf())); surface.Reset();
// 创建固定颜色刷和文本格式 HR(md2dRenderTarget->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::White), mColorBrush.GetAddressOf()));
HR(mdwriteFactory->CreateTextFormat(L"宋体", nullptr, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 20, L"zh-cn", mTextFormat.GetAddressOf()));
}
在这里D2D的相关资源需要在D3D相关资源释放前先行释放掉,然后在D3D重设后备缓冲区后重新创建D2D渲染目标。至于D2D后续的相关资源也需要重新创建好来。
最后在
GameApp::DrawScene方法中,绘制2D部分需要在3D部分绘制完后,呈现之前进行。
首先需要调用
ID2D1RenderTarget::BeginDraw方法,开始D2D绘制。该方法没有参数
绘制完成后,就调用
ID2D1RenderTarget::EndDraw方法,结束D2D绘制。该方法的返回值为HRESULT,若之前绘制出现问题,在EndDraw才会进行反馈。可以用HR宏包住。
ID2D1RenderTarget::DrawTextW方法--绘制文本
DrawText在这里进行了宏定义:#ifdef UNICODE #define DrawText DrawTextW #else #define DrawText DrawTextA #endif // !UNICODE
我们的项目是只能使用
Unicode字符集的(
dxerr.h只允许该字符集),所以直接讨论
DrawTextW方法
该方法也经过了重载。现在只讨论其中一种,且使用默认参数:
void ID2D1RenderTarget::DrawTextW( WCHAR *string, // [In]要输出的文本 UINT stringLength, // [In]文本长度,用wcslen函数或者wstring::length方法获取即可 IDWriteTextFormat *textFormat, // [In]文本格式 const D2D1_RECT_F &layoutRect, // [In]布局区域 ID2D1Brush *defaultForegroundBrush, // [In]使用的前景刷 D2D1_DRAW_TEXT_OPTIONS options = D2D1_DRAW_TEXT_OPTIONS_NONE, DWRITE_MEASURING_MODE measuringMode = DWRITE_MEASURING_MODE_NATURAL);
D2D1_RECT_F结构体包含了
left,
top,
right,
bottom四个成员。
现给出
GameApp::DrawScene方法Direct2D部分的实现:
void GameApp::DrawScene() { assert(md3dImmediateContext); assert(mSwapChain); // 绘制Direct3D部分 ... // 绘制Direct2D部分 md2dRenderTarget->BeginDraw(); static const WCHAR* textStr = L"切换灯光类型: 1-平行光 2-点光 3-聚光灯\n" "切换模型: Q-立方体 W-球体 E-圆柱体"; md2dRenderTarget->DrawTextW(textStr, (UINT32)wcslen(textStr), mTextFormat.Get(), D2D1_RECT_F{ 0.0f, 0.0f, 400.0f, 40.0f }, mColorBrush.Get()); HR(md2dRenderTarget->EndDraw()); HR(mSwapChain->Present(0, 0)); }
最终效果如下:
DirectX11 With Windows SDK完整目录
Github项目源码
相关文章推荐
- DirectX11 With Windows SDK--17 利用几何着色器实现公告板效果
- DirectX11 With Windows SDK--10 摄像机类
- DirectX11 With Windows SDK--09 纹理映射与采样器状态
- DirectX11 With Windows SDK--22 静态天空盒的读取与实现、模型反射
- WP8 中 WIC, Direct2D, DirectWrite替代方案 (Direct3D 11 文字系统 Font Management)
- DirectX11 With Windows SDK--23 立方体映射:动态天空盒的实现
- DirectX11 With Windows SDK--02 顶点/像素着色器的创建、顶点缓冲区
- DirectX11 With Windows SDK--19 模型加载:obj格式的读取及使用二进制文件提升读取效率
- DirectX11 With Windows SDK--20 硬件实例化与视锥体裁剪
- DirectX11 With Windows SDK--13 抛弃FX11并初步实现BasicFX类
- ASP.NET中利用DataGrid控件显示图片以及在图片和文字上加超链接
- DirectX11 With Windows SDK--03 索引缓冲区、常量缓冲区
- DirectX11 With Windows SDK--14 深度测试
- DirectX11 With Windows SDK--07 添加光照与常用几何模型
- DirectX11 With Windows SDK--11 混合状态与光栅化状态
- DirectX11 With Windows SDK--14 深度测试
- DirectX11 With Windows SDK--04 使用DirectX Tool Kit帮助开发
- DirectX11 With Windows SDK--01 DirectX11初始化
- DirectX11 With Windows SDK--12 深度/模板状态
- 2 kinect for windows(k4w) sdk之提取深度图像并利用opencv显示