字体,如果按照Programming.Role.Playing.Games.with.DirectX书上的作做法,使用ID3DXFont会很简单,完全无脑,但是DX9太老了,在学习DX11的时候,就会发现没有ID3DXFont,需要自己利用绘制2D的方式来实现字体。还是老规则,左边的是用固定管线实现的,右边是用Shader实现的,而且这次会非常明显,有文字提示。
Programming.Role.Playing.Games.with.DirectX书上用的DX9版本比较老,实现的方式有点不一样,所以就没有用书的源码。右边的就是使用贴图加构建顶点的方式来实现的,类似我之前学习DX11学习的font工程,也是我之前学习卡住的地方,显示有点模糊,之前以为是采样方式的问题,但在DX9上完全没问题,还的抽个时间再去学习下。
来看下渲染的代码,代码是在Draw2D上修改的:
void FontClass::Render(IDirect3DDevice9* device, TCHAR* content, int strLength, RECT* rect, DWORD format, D3DCOLOR color)
{
m_font->DrawTextW(nullptr, content, strLength, rect, format, color);
}
是不是很简单,再加上创建ID3DXFont的函数,也没几句代码。
bool FontClass::Initialize(IDirect3DDevice9* device)
{
D3DXFONT_DESC fontDesc;
HRESULT result;
::ZeroMemory(&fontDesc, sizeof(D3DXFONT_DESC));
fontDesc.CharSet = DEFAULT_CHARSET;
::lstrcpyn(fontDesc.FaceName, TEXT("Arial"), 20);
fontDesc.Height = 25;
fontDesc.Italic = false;
fontDesc.MipLevels = D3DX_DEFAULT;
fontDesc.OutputPrecision = 0;
fontDesc.PitchAndFamily = 0;
fontDesc.Quality = 0;
fontDesc.Weight = 500;
fontDesc.Width = 12;
result = ::D3DXCreateFontIndirect(device, &fontDesc, &m_font);
if (FAILED(result))
return false;
return true;
}
这些接口都可以查到,而且更加仔细,就不细讲了。接着就上重点了,用渲染2D的方式来实现,其实也是很简单,就是一个字,一个正方形,两个三角面,6个顶点,其实也可以是4个顶点,但需要索引缓存。可以在Unity引擎里测试下,就会发现1个字就是2个三角面,4个顶点。
Shader渲染,其实就是用Draw2D的代码,这里就不贴了,就贴构建顶点缓存的代码:
bool ShaderFontClass::BuildVertexArray(IDirect3DDevice9* device, char* sentence, float drawX, float drawY)
{
VertexType* vertices;
int numLetters;
int index;
HRESULT result;
VertexType* verticesPtr;
int width, height;
numLetters = (int)::strlen(sentence);
vertices = new VertexType[numLetters * 6];
if (!vertices)
return false;
index = 0;
width = 15;
height = 25;
for (int i = 0; i < numLetters; ++i)
{
int letter = (int)sentence[i] - 32;
if (0 == letter)
drawX += 3.0f;
else
{
// First triangle
vertices[index]._x = drawX;// Top left
vertices[index]._y = drawY;
vertices[index]._z = 0.0f;
vertices[index]._rhw = 1.0f;
vertices[index]._u = m_font[letter].left;
vertices[index]._v = 0.0f;
++index;
vertices[index]._x = drawX + width;// Bottom right
vertices[index]._y = drawY + height;
vertices[index]._z = 0.0f;
vertices[index]._rhw = 1.0f;
vertices[index]._u = m_font[letter].right;
vertices[index]._v = 1.0f;
++index;
vertices[index]._x = drawX;// Bottom left
vertices[index]._y = drawY + height;
vertices[index]._z = 0.0f;
vertices[index]._rhw = 1.0f;
vertices[index]._u = m_font[letter].left;
vertices[index]._v = 1.0f;
++index;
// Second triangle
vertices[index]._x = drawX;// Top left
vertices[index]._y = drawY;
vertices[index]._z = 0.0f;
vertices[index]._rhw = 1.0f;
vertices[index]._u = m_font[letter].left;
vertices[index]._v = 0.0f;
++index;
vertices[index]._x = drawX + width;// Top right
vertices[index]._y = drawY;
vertices[index]._z = 0.0f;
vertices[index]._rhw = 1.0f;
vertices[index]._u = m_font[letter].right;
vertices[index]._v = 0.0f;
++index;
vertices[index]._x = drawX + width;// Bottom right
vertices[index]._y = drawY + height;
vertices[index]._z = 0.0f;
vertices[index]._rhw = 1.0f;
vertices[index]._u = m_font[letter].right;
vertices[index]._v = 1.0f;
++index;
drawX += m_font[letter].size + width;
}
}
m_vertexCount = index;
result = device->CreateVertexBuffer(
sizeof(VertexType),
D3DUSAGE_WRITEONLY,
VERTEX_FVF,
D3DPOOL_DEFAULT,
&m_vertexBuffer,
nullptr);
if (FAILED(result))
return false;
result = m_vertexBuffer->Lock(0, 0, (void**)&verticesPtr, 0);
if (FAILED(result))
return false;
::memcpy(verticesPtr, (void*)vertices, (sizeof(VertexType) * m_vertexCount));
result = m_vertexBuffer->Unlock();
if (FAILED(result))
return false;
return true;
}
核心思想就是,根据一个字符,构建6个顶点,顶点位置没什么好讲的,重点就是uv的位置,因为dx是以左上角为原点的,而且uv的也是以左上角为原点的,topleft翻转一下其实就是bottomleft。
源码下载:下载地址