使用FreeType实现DirectX11的文本输入

来源:互联网 发布:华讯网络是大公司么 编辑:程序博客网 时间:2024/05/22 00:48

           使用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_FacemFTFace;FT_GlyphSlotmFTSlot;unsignedmCharWidth;unsignedmCharHeight;ID3D11Buffer*pVBBuffer;ID3D11Buffer*pIndexBuffer;ID3DX11EffectShaderResourceVariable* pFontTextureVariable;floatmFontSize;// 字体大小boolbIsAntiAliased;  // 反走样//==========================================// 下面几个FreeType变量用于排版字体//==========================================floatmAscender;floatmDescender;float       mFontHeight;floatmAddance;float       mToplineHeigh;mj::MStringmFontName;   // 字体名称MDx11LayoutManage*pLayoutManage;ID3D11InputLayout*pLayout;ID3DX11Effect*pEffect;MDx11BufferManage*pBufferManage;ID3D11ShaderResourceView*pFontTexture{ nullptr };std::unordered_map<wchar_t, CharBitmapType> mCharTextMap;MRectFmWindowRect;    // 保存渲染目标的窗口大小MRectFmRendArear;     // 渲染区域MPointFmPointPen; // 开始渲染位置FontVertexmFontVectex[4]; // 字体所处的矩形区域XMFLOAT4mFontColor;     // 字体颜色};


          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 static const char* ft_errors[] = {#define FT_ERROR_END_LIST 0};#include FT_ERRORS_HMFTFont::MFTFont(const mj::MString& fontname, const float size, const bool antialiased) :mFontName(fontname), mFontSize(size), bIsAntiAliased(antialiased){if (!Ft_Usage_Count++)FT_Init_FreeType(&ft_lib);UpdateFont();mWindowRect = MRectF(0, 0, 130, 60);        // 定义一个默认渲染窗口大小mFontColor = XMFLOAT4(1.f, 0.f, 0.f, 1.f);  // 定义一个默认颜色//=============================================// 设定纹理坐标//=============================================mFontVectex[0].tex = XMFLOAT2(0.f, 0.f);mFontVectex[1].tex = XMFLOAT2(1.f, 0.f);mFontVectex[2].tex = XMFLOAT2(0.f, 1.f);mFontVectex[3].tex = XMFLOAT2(1.f, 1.f);mPointPen = MPointF(0, 40);  // 默认开始为,字体的渲染使用窗口坐标     // 所以在真正渲染的时候需要转换为世界坐标}MFTFont::~MFTFont(){Free();if (!--Ft_Usage_Count)FT_Done_FreeType(ft_lib);safe_delete(pLayoutManage);safe_delete(pBufferManage);}


          在构造函数中加载一次FreeType库,然后将字体初始化,初始化函数字体如下:

        

void MFTFont::UpdateFont(){Free();if (mFontName.find(".") == -1){mFontName += ".ttf";}if (!file::fileisexist(mFontName)){int n = mFontName.find_last("/");if (n == -1){n = mFontName.find_last("\\\\");}mj::MString newStr;if (n != -1){newStr = mFontName.copy<int>(n, mFontName.length());}mj::MString oldString = mFontName;mj::MString header = "C:\\Windows\\Fonts\\";if (n != -1){mFontName = header + newStr;}else{mFontName = header + mFontName;}if (!file::fileisexist(mFontName)){mj::HParseString par;par("%1 File isn't exist", oldString);throw std::runtime_error(par);}}FT_Error error;if ((error = FT_New_Face(ft_lib, mFontName.c_str(),0, &mFTFace)) != 0){mj::HParseString par;par("Failed to create face from font file ' %1 ' error was: %2 " ,mFontName ,((error < FT_Err_Max) ? ft_errors[error]:"unknown error" ));throw std::runtime_error(par);}FT_Select_Charmap(mFTFace, FT_ENCODING_UNICODE);FT_Set_Pixel_Sizes(mFTFace, mFontSize, mFontSize);if (!mFTFace->charmap){FT_Done_Face(mFTFace);mFTFace = nullptr;mj::HParseString par;par("The font ' %1 ' does not have a Unicode charmap, and cannot be used.", mFontName);throw std::runtime_error(par);}//===================================================// DPI//===================================================unsigned horzdpi = 96;unsigned vertdpi = 96;float hps = mFontSize * 64;float vps = mFontSize * 64;if (FT_Set_Char_Size(mFTFace, FT_F26Dot6(hps), FT_F26Dot6(vps), horzdpi, vertdpi)){float ptSize_72 = (mFontSize * 72.0f) / vertdpi;float best_delta = 99999;float best_size = 0;for (int i = 0; i < mFTFace->num_fixed_sizes; i++){float size = mFTFace->available_sizes[i].size * float(FT_POS_COEF);float delta = fabs(size - ptSize_72);if (delta < best_delta){best_delta = delta;best_size = size;}}if ((best_size <= 0) ||FT_Set_Char_Size(mFTFace, 0, FT_F26Dot6(best_size * 64), 0, 0)){mj::HParseString par;par("The font ' %1 ' cannot be ""rasterised at a size of %2  points, and cannot be ""used.", mFontName,mFontSize);throw std::runtime_error(par);}}if (mFTFace->face_flags & FT_FACE_FLAG_SCALABLE){float y_scale = mFTFace->size->metrics.y_scale * float(FT_POS_COEF) * (1.0f / 65536.0f);mAscender = mFTFace->ascender * y_scale;mDescender = mFTFace->descender * y_scale;mFontHeight = mFTFace->height * y_scale;}else{mAscender = mFTFace->size->metrics.ascender * float(FT_POS_COEF);mDescender = mFTFace->size->metrics.descender * float(FT_POS_COEF);mFontHeight = mFTFace->size->metrics.height * float(FT_POS_COEF);}}

            在更换字体或者重新设置字体大小时都必须要调用UpdateFont(),因为他会重新计算排版信息,Free()将旧字体信息清除。

           

void MFTFont::Free(){if (!mFTFace)return;FT_Done_Face(mFTFace);mFTFace = nullptr;for (auto & m : mCharTextMap){auto it = std::get<4>(m.second);safe_release(it);}mCharTextMap.clear();}

          初始化字体后,我们要将他和D3D11结合起来,那就是InitFont(ID3D11Device* pDevice, ID3D11DeviceContext* pContext)函数。

        

void MFTFont::InitFont(ID3D11Device* pDevice, ID3D11DeviceContext* pContext){if (pDevice == 0 || pContext == 0)throw std::runtime_error("Device invalid");p_Device = pDevice;p_DeviceContex = pContext;//=================================================// 创建布局管理器//=================================================if (pLayoutManage == nullptr){pLayoutManage = new MDx11LayoutManage(pDevice);}//=====================================================// 创建Effect//=====================================================try{pEffect = pLayoutManage->dx_FxComplieFile("./FX/Font.fx");}catch (std::runtime_error e){mj::HParseString par;par("\nFILE:%1\nFun:%2\nLine:%3", __FILE__, __FUNCTION__, __LINE__);box::Error_MessageBox(std::string(e.what()) + par.getresult());}D3D11_INPUT_ELEMENT_DESC solidColorLayout[] ={{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT,0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT,0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 28, D3D11_INPUT_PER_VERTEX_DATA, 0 },};unsigned len = ARRAYSIZE(solidColorLayout);try{pLayout = pLayoutManage->dx_CreatInputLayout(solidColorLayout, len,"FontRenderTech");}catch (std::runtime_error e){mj::HParseString par;par("\nFILE:%1\nFun:%2\nLine:%3", __FILE__, __FUNCTION__, __LINE__);box::Error_MessageBox(std::string(e.what()) + par.getresult());return;}pFontTextureVariable = pLayoutManage->dx_GetShaderResourceFromEF("FontTexture");// 默认情况下也会渲染一个矩形出来的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 } };pBufferManage = new MDx11BufferManage(pDevice);//-----------------------------------// 创建纹理资源//-----------------------------------try{pFontTexture = pBufferManage->dx_CreateTexTureResouceOnly("./Res/test2.bmp");}catch (std::runtime_error e){mj::HParseString par;par("\nFILE:%1\nFun:%2\nLine:%3", __FILE__, __FUNCTION__, __LINE__);box::Error_MessageBox(std::string(e.what()) + par.getresult());return;}//========================================================// 创建一个动态的顶点buffer//========================================================D3D11_SUBRESOURCE_DATA pointssubdata;pointssubdata.pSysMem = points;D3D11_BUFFER_DESC pointBufDesc;pointBufDesc.ByteWidth = sizeof(FontVertex)* 4;pointBufDesc.Usage = D3D11_USAGE_DYNAMIC;pointBufDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;pointBufDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;pointBufDesc.MiscFlags = 0;HRESULT hr = pDevice->CreateBuffer(&pointBufDesc, &pointssubdata, &pVBBuffer);if (FAILED(hr)){throw std::runtime_error("Create Vertex Buffer fail.......");}//============================================================// 创建一个索引buffer//============================================================unsigned index[6] = { 0, 1, 2, 2, 1, 3 };D3D11_SUBRESOURCE_DATA subdata{ 0 };subdata.pSysMem = index;D3D11_BUFFER_DESC BufDesc{ 0 };BufDesc.ByteWidth = sizeof(unsigned)* 6;BufDesc.Usage = D3D11_USAGE_DEFAULT;BufDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;BufDesc.MiscFlags = 0;hr = pDevice->CreateBuffer(&BufDesc, &subdata, &pIndexBuffer);if (FAILED(hr)){throw std::runtime_error("Create Index Buffer fail.......");}}


            Font.fx 文件待会儿给出。在InitFont函数中主要创建一个输入布局和一个动态顶点缓冲buffer,还有一个索引buffer。
           不过在正式进行渲染前还有一个重量级的函数,GetShaderView(wchar_t ch),参数是我们要打印的字符,返回的是该字符的纹理资源,他的实现如下:

          

ID3D11ShaderResourceView* MFTFont::GetShaderView(wchar_t ch){ID3D11ShaderResourceView* __result{ nullptr };if (FT_Load_Char(mFTFace, ch, FT_LOAD_RENDER))return __result;mFTSlot = mFTFace->glyph;FT_Bitmap& bitmap = mFTSlot->bitmap;mCharHeight = bitmap.rows;mCharWidth = bitmap.width;D3D11_SUBRESOURCE_DATA __subData;__subData.pSysMem = bitmap.buffer;__subData.SysMemPitch = mCharWidth;__subData.SysMemSlicePitch = mCharWidth*mCharHeight ; // 只对3D纹理有效ID3D11Texture2D*      Tex2D;D3D11_TEXTURE2D_DESC  Tex2Dtdesc;Tex2Dtdesc.Width = mCharWidth;Tex2Dtdesc.Height = mCharHeight;Tex2Dtdesc.MipLevels = 1;Tex2Dtdesc.ArraySize = 1;Tex2Dtdesc.SampleDesc.Count = 1;Tex2Dtdesc.SampleDesc.Quality = 0;Tex2Dtdesc.Usage = D3D11_USAGE_DEFAULT;Tex2Dtdesc.Format = DXGI_FORMAT_A8_UNORM; Tex2Dtdesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;Tex2Dtdesc.CPUAccessFlags = 0;Tex2Dtdesc.MiscFlags = 0;if (FAILED(p_Device->CreateTexture2D(&Tex2Dtdesc, &__subData, &Tex2D))){return __result;}D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc;viewDesc.Format = Tex2Dtdesc.Format;viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;viewDesc.Texture2D.MipLevels = Tex2Dtdesc.MipLevels;viewDesc.Texture2D.MostDetailedMip = 0;HRESULT hr = p_Device->CreateShaderResourceView(Tex2D, &viewDesc, &__result);if (FAILED(hr)){mj::HParseString par;par("CreateShaderResourceView Failed\nFILE:%1\nFun:%2\nLine:%3", __FILE__, __FUNCTION__, __LINE__);throw std::runtime_error(par);}safe_release(Tex2D);//==================================================// 获取该字符的排版信息//==================================================mToplineHeigh = mFTSlot->metrics.horiBearingY >> 6;float h = mFTSlot->metrics.height>>6;mToplineHeigh = h - mToplineHeigh;mAddance = mFTSlot->metrics.horiAdvance >> 6;return __result;}

           其实最需要说明的就是这个函数,这里是关键,FT_Load_Char(mFTFace, ch, FT_LOAD_RENDER)渲染后得到的位图是一张灰度图,他只有8位,我们要做的就是用这个8bit的灰度图创建出我们想要的2D纹理图。所以,我们将该8bit的数据作为我们创建纹理的alpha通道,所以我们使用DXGI_FORMAT_A8_UNORM作为2D纹理格式,而这种格式生成的纹理的rgb通道应该都是0,因为打印出来的字体是黑色的(^_^,我又开始连蒙带猜了,有木有大神出来解惑啊?)因为一开始使用如下方式但是居然没有显示出文字,我也是醉了:

          

int* buffer = new int[mCharHeight*mCharWidth];unsigned char *__buffer = reinterpret_cast<unsigned char*>(buffer);;for (int i = 0; i < bitmap.rows; ++i){unsigned char *src = bitmap.buffer + (i *bitmap.pitch);switch (bitmap.pixel_mode){case FT_PIXEL_MODE_GRAY:for (int j = 0; j < bitmap.width; ++j){// RGBA*__buffer++ = 0xFF;*__buffer++ = 0xFF;*__buffer++ = 0xFF;*__buffer++ = *src++;}break;case FT_PIXEL_MODE_MONO:for (int j = 0; j < bitmap.width; ++j)buffer[j] = (src[j / 8] & (0x80 >> (j & 7))) ? 0xFFFFFFFF : 0x00000000;break;default:throw std::runtime_error("The glyph could not be drawn because the pixel mode is ""unsupported.");break;}}D3D11_SUBRESOURCE_DATA __subData;__subData.pSysMem = buffer*4;__subData.SysMemPitch = mCharWidth;__subData.SysMemSlicePitch = mCharWidth*mCharHeight*4 ;ID3D11Texture2D*      Tex2D;D3D11_TEXTURE2D_DESC  Tex2Dtdesc;Tex2Dtdesc.Width = mCharWidth;Tex2Dtdesc.Height = mCharHeight;Tex2Dtdesc.MipLevels = 1;Tex2Dtdesc.ArraySize = 1;Tex2Dtdesc.SampleDesc.Count = 1;Tex2Dtdesc.SampleDesc.Quality = 0;Tex2Dtdesc.Usage = D3D11_USAGE_DEFAULT;Tex2Dtdesc.Format = DXGI_FORMAT_A8_UNORM; 

            有没有D3D11大神帮忙解释下为什么吗?这对一个业余的人来说实在是太纠结了,主要是关于D3D11的书籍国内还没多少。好吧,不纠结,我们使用DXGI_FORMAT_A8_UNORM格式一样很easy的达到我们想要的目的,纠结那么多干嘛呢?因为我们只是想要能够输入文本就好了,有没啥需求。
 
           好吧,现在我们可以开始渲染了:

         

void MFTFont::RenderText(const std::wstring& text){UINT stride = sizeof(FontVertex);UINT offset = 0;MPointF oldPoint = mPointPen;  // 渲染之前我们需要保存笔的位置p_DeviceContex->IASetInputLayout(pLayout);p_DeviceContex->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);p_DeviceContex->IASetVertexBuffers(0, 1, &pVBBuffer, &stride, &offset);p_DeviceContex->IASetIndexBuffer(pIndexBuffer, DXGI_FORMAT_R32_UINT, 0);D3DX11_TECHNIQUE_DESC techDesc = pLayoutManage->dx_GetTichDescFromEF("FontRenderTech");//===============================================// 每个顶点的颜色都一样//===============================================mFontVectex[0].color = mFontColor;mFontVectex[1].color = mFontColor;mFontVectex[2].color = mFontColor;mFontVectex[3].color = mFontColor;//=====================================================================// 开始循环渲染字体//=====================================================================int n = text.length();for (int i = 0; i < n; ++i){//==============================================// 对空格和回车进行处理//==============================================if (text[i] == L' '){float step = SPACE_STEP;mPointPen.X(mPointPen.X() + step);continue;}if (text[i] == L'\n'){mPointPen.X(oldPoint.X());mPointPen.Y(mPointPen.Y() + mFontHeight);continue;}//========================================================// 获取字符纹理资源以及该字符的相关尺寸信息//========================================================if (mCharTextMap.count(text[i])){CharBitmapType& chartype = mCharTextMap.at(text[i]);mCharWidth = std::get<0>(chartype);mCharHeight = std::get<1>(chartype);mToplineHeigh = std::get<2>(chartype);mAddance = std::get<3>(chartype);pFontTexture = std::get<4>(chartype);}else{auto it = GetShaderView(text[i]);if (it){pFontTexture = it;CharBitmapType chartype(mCharWidth, mCharHeight, mToplineHeigh, mAddance, it);mCharTextMap[text[i]] = chartype;}}if (mPointPen.X() + mCharWidth >= mWindowRect.X() + mWindowRect.Width() - 10){mPointPen.X(oldPoint.X());mPointPen.Y(mPointPen.Y() + mFontHeight);}MRectF rect(mPointPen.X(), mPointPen.Y() - mAscender + mToplineHeigh, mCharWidth, mCharHeight);//======================================================// 变换坐标//======================================================mRendArear = LocalSpaceToWorldSpace(mWindowRect, rect);//======================================================// 设置字符的渲染区域//=======================================================mFontVectex[0].pos = XMFLOAT3(mRendArear.X(), mRendArear.Y() + mRendArear.Height(), 1.0);mFontVectex[1].pos = XMFLOAT3(mRendArear.X() + mRendArear.Width(), mRendArear.Y() + mRendArear.Height(), 1.0);mFontVectex[2].pos = XMFLOAT3(mRendArear.X(), mRendArear.Y(), 1.0);mFontVectex[3].pos = XMFLOAT3(mRendArear.X() + mRendArear.Width(), mRendArear.Y(), 1.0);mPointPen.X(mPointPen.X() + mAddance);//======================================================// 更新顶点坐标信息//======================================================FontVertex* data = pBufferManage->dx_GetNeedUpdateBuffer<FontVertex>(p_DeviceContex, pVBBuffer);for (int i = 0; i < 4; ++i){data[i] = mFontVectex[i];}pBufferManage->dx_EndUpdateBuffer(p_DeviceContex, pVBBuffer);pFontTextureVariable->SetResource(pFontTexture);for (unsigned int p = 0; p < techDesc.Passes; p++){ID3DX11EffectPass* pass = pLayoutManage->dx_GetPassFromEF("FontRenderTech", p);if (pass != 0){pass->Apply(0, p_DeviceContex);p_DeviceContex->DrawIndexed(6, 0, 0);}}}mPointPen = oldPoint;}

           剩下的就是一些小函数了,没啥好说:

          

//==================================================// 在窗体size改变的时候调用该函数以保证字体不会模糊//===================================================void MFTFont::OnSizeChanged(const MRectF& size){mWindowRect = size;}//===================================================// 设置开始渲染位置//===================================================void MFTFont::StartPoint(const MPointF& pt){mPointPen = pt;}//===================================================// 设置字体颜色//===================================================void MFTFont::SetFontColor(const XMFLOAT4& color){mFontColor = color;}//======================================================// 设置字体名称//======================================================void MFTFont::SetFontName(const mj::MString& fontname){mFontName = fontname;UpdateFont();}//=====================================================// 设置字体大小//=====================================================void MFTFont::SetFontSize(unsigned fontsize){mFontSize = fontsize;UpdateFont();}//=======================================================// 对字体名称和大小同时设置//=======================================================void MFTFont::SetFont(const mj::MString& fontname, const unsigned fontsize){mFontName = fontname;mFontSize = fontsize;UpdateFont();}
        最后就是Font.fx文件:

//--------------------Font.fx---------------------------------------------//Texture2D FontTexture;struct VSSceneIn{float3 pos : POSITION;float4 colour : COLOR;float2 tex : TEXCOORD;};struct PSSceneIn{float4 pos : SV_Position;float4 colour : COLOR;float2 tex : TEXCOORD;};DepthStencilState DisableDepth{DepthEnable = FALSE;DepthWriteMask = ZERO;};BlendState Font_Blend{AlphaToCoverageEnable = false;BlendEnable[0] = true;SrcBlendAlpha = INV_DEST_ALPHA;DestBlendAlpha = ONE;SrcBlend = SRC_ALPHA;DestBlend = INV_SRC_ALPHA;};SamplerState FontSampler{Filter = MIN_MAG_MIP_LINEAR;AddressU = Border;AddressV = Border;};RasterizerState clipRasterstate{DepthClipEnable = false;FillMode = Solid;CullMode = None;ScissorEnable = true;};// Vertex shaderPSSceneIn VSMain(VSSceneIn input){PSSceneIn output = (PSSceneIn)0.0;   output.pos = float4(input.pos, 1);   output.tex = input.tex;output.colour = input.colour;return output;}// Pixel shaderfloat4 PSMain(PSSceneIn input) : SV_Target{float4 colour = FontTexture.Sample(FontSampler, input.tex);colour.rgb = 1;  // 这里如果不这么做,将永远是黑色,如果上面所说的rgb全是0return colour* input.colour;}// Techniquestechnique11 FontRenderTech{pass P0{SetVertexShader(CompileShader(vs_5_0, VSMain()));SetGeometryShader(NULL);SetPixelShader(CompileShader(ps_5_0, PSMain()));SetDepthStencilState(DisableDepth, 0);SetBlendState(Font_Blend, float4(0.0f, 0.0f, 0.0f, 0.0f), 0xFFFFFFFF);SetRasterizerState(clipRasterstate);}}

          到此大功告成,我们可以对比一下他和使用贴图纹理(DXUT使用的方法)的效果差异:

          首先是使用我们上面的类进行渲染的字体,这个图片上有两个画面是D3D11渲染的,一个按钮和一个子窗口,其中按钮使用的是中文字体,所以显示英文变成等宽显得有些丑,子窗口使用的是Arial字体,大小为12号。:

    





             接下来我们使用图片纹理渲染方式的:



         可以明显的看到有些模糊,字体越大越模糊,下面都使用我们上面的类进行14号Arial字体渲染,可以看到按钮上面的英文字符变得很漂亮:



         最后我们和CEGUI的文本对比一下,发现都是一样一样的,button上面的绿色字体是上面的类进行渲染的,子窗口上的控件是CEGUI控件:




0 0
原创粉丝点击