使用FreeType实现DirectX11的文本输入
D3D11取消了文本输入的接口(DX9有提供D3DX9Font接口),这使得在D3D11中想要输入文本变成一件棘手的事,当然这个棘手仅仅只是对于娱乐玩家来说,因为真正从事游戏编程的大牛们自然有自己的解决方案,但是我那奇怪了,为什么网上搜了不少就是找不到一个想要的解决方案呢?大多都是说使用FreeType来渲染字体而且还是针对OpenGL的,好吧,搜了很多关于怎么在DirecteX11里使用FreeType的方案却也没有找到多少有用的信息,要知道,很多人研究D3D11并非真正的拿去工作,也许只是工作之余想要研究一下,于是对D3D11或者是FreeType并没有了解多少,又或许只极限于搭一个框架,又或者仅构架一个自己方便娱乐研究使用的小引擎而已——像我这种蛋疼的人,所以这时候遇到问题可能不能很快的解决,然后网上寻找解决方案,但是怎么在D3D11里面渲染字体的文章相对很少,所以这里提供一种简单的方法,以供大家参考。
如果不想要输入显示中文文本的话,那么有一个简单的方案:准备一张拥有所有英文字符和标点符号等等可打印字符的一个图,按照纹理坐标的定义[0,1]提取每个字符的纹理坐标,然后使用正方形对纹理坐标进行贴图,最后得到我们想要的结果,当然这不是这里要说的主题,因为这个方式相对来最简单,因为我们不可能仅使用英文字符,很多时候还是愿意输入中文的。
好吧,下面开始进入正题,首先定义一个顶点结构,该结构保存顶点位置,颜色以及纹理坐标。
struct FontVertex{
XMFLOAT3 pos;
XMFLOAT4 color;
XMFLOAT2 tex;
};
在DirectX里面使用4个顶点再加6个索引便可构成一个矩形:
FontVertex points[] = {
XMFLOAT3(-0.5f, 0.5f, 0.5f), XMFLOAT4{ 1.f, 0.f, 0.f, 1.f }, XMFLOAT2{ 0, 0 },
XMFLOAT3(0.5f, 0.5f, 0.5f), XMFLOAT4{ 1.f, 0.f, 0.f, 1.f }, XMFLOAT2{ 1, 0 },
XMFLOAT3(-0.5f, -0.5f, 0.5f), XMFLOAT4{ 1.f, 0.f, 0.f, 1.f }, XMFLOAT2{ 0, 1 },
XMFLOAT3(0.5f, -0.5f, 0.5f), XMFLOAT4{ 1.f, 0.f, 0.f, 1.f }, XMFLOAT2{ 1, 1 }
};
unsigned index[6] = { 0, 1, 2, 2, 1, 3 };
那么现在再加一字体纹理图就渲染上一个字体。只是这个字体纹理怎么获取呢?当然是使用FreeType生成bitmap,然后装载成D3D11纹理(因为本人不是从事游戏开发的,对D3D11的了解也只是懂点而已,所以要是有些说得不对的地方或者是说不清楚的地方还望大家多多指教)。
对于FreeType的使用这里不作详细介绍,因为网上可以搜到很多相关资料,只要查阅一下那些资料就算不懂FreeType的也能够看懂这里的代码。那么这里就详细的说一下怎么在D3D11里面使用他的细节,先来看看头文件:
#pragma once
#include <MString.h>
#include "MSize.h"
#include <unordered_map>
#include "D3D11/MDx11BufferManage.h"
#include "D3D11/MDx11LayoutManage.h"
#include <tuple>
#include <HContainer.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#define MCheneseFontTTF "simhei.ttf" // 预定义一个中文的字体
#define MEnglishFontTTF "Arial.ttf" // 预定义一个英文的字体
struct FontVertex{
XMFLOAT3 pos;
XMFLOAT4 color;
XMFLOAT2 tex;
};
typedef std::tuple<float, float, float, float, ID3D11ShaderResourceView*> CharBitmapType;
class MFTFont
{
public:
MFTFont(const mj::MString& fontname, const float size, const bool antialiased);
~MFTFont();
void InitFont(ID3D11Device* pDevice, ID3D11DeviceContext* pContext);
void RenderText(const std::wstring& text);
void OnSizeChanged(const MRectF& size);
void StartPoint(const MPointF& pt);
void SetFontColor(const XMFLOAT4& color);
void SetFontName(const mj::MString& fontname);
void SetFontSize(unsigned fontsize);
void SetFont(const mj::MString& fontname, const unsigned fontsize);
MFTFont(const MFTFont& other) = delete;
MFTFont& operator=(const MFTFont& other) = delete;
protected:
void UpdateFont();
void Free();
ID3D11ShaderResourceView* GetShaderView(wchar_t ch);
private:
ID3D11Device* p_Device;
ID3D11DeviceContext* p_DeviceContex;
FT_Face mFTFace;
FT_GlyphSlot mFTSlot;
unsigned mCharWidth;
unsigned mCharHeight;
ID3D11Buffer* pVBBuffer;
ID3D11Buffer* pIndexBuffer;
ID3DX11EffectShaderResourceVariable* pFontTextureVariable;
float mFontSize; // 字体大小
bool bIsAntiAliased; // 反走样
//==========================================
// 下面几个FreeType变量用于排版字体
//==========================================
float mAscender;
float mDescender;
float mFontHeight;
float mAddance;
float mToplineHeigh;
mj::MString mFontName; // 字体名称
MDx11LayoutManage* pLayoutManage;
ID3D11InputLayout* pLayout;
ID3DX11Effect* pEffect;
MDx11BufferManage* pBufferManage;
ID3D11ShaderResourceView* pFontTexture{ nullptr };
std::unordered_map<wchar_t, CharBitmapType> mCharTextMap;
MRectF mWindowRect; // 保存渲染目标的窗口大小
MRectF mRendArear; // 渲染区域
MPointF mPointPen; // 开始渲染位置
FontVertex mFontVectex[4]; // 字体所处的矩形区域
XMFLOAT4 mFontColor; // 字体颜色
};
MRectF 保存一个矩形的左上角顶点和宽高,类型为float,MPointF保存的是一个float类型的xy,MString是一个字符串处理类,可以使用std::string来代替。MDx11LayoutManage管理D3D11的输入布局以及ID3DX11Effect的框架信息。MDx11BufferManage管理D3D11常用的buffer信息。mCharTextMap用于缓存字符纹理。该类禁止复制和赋值。
下面来看看实现:
#include "MFTFont.h"
#include <HPath_File.h>
#include "MSize.cpp"
#ifdef _DEBUG | __DEBUG
# pragma comment(lib,"FreeType_D")
#else
# pragma comment(lib,"FreeType")
#endif
#define INTER_GLYPH_PAD_SPACE 2
#define INTER_FONT_STEP 4
#define SPACE_STEP 10.0
#define FT_POS_COEF (1.0/64.0)
static FT_Library ft_lib;
static int Ft_Usage_Count = 0;
#undef __FTERRORS_H__
#define FT_ERRORDEF( e, v, s ) s,
#define FT_ERROR_START_LIST