这个教程其实的大体实现方法是建立在D3D教程七之2D Rendering这个教程的实现思想之上的,
先看看大体的架构:
第一,这里就说说TexClass,FontClass,FontShaderClass,
在说之前先看点好东西,就是符号纹理图,和对于符号的文字查找表
符号纹理图:
对应的符号纹理查找表:
32 0.0 0.0 0
33 ! 0.0 0.000976563 1
34 " 0.00195313 0.00488281 3
35 # 0.00585938 0.0136719 8
36 $ 0.0146484 0.0195313 5
37 % 0.0205078 0.0302734 10
38 & 0.03125 0.0390625 8
39 ' 0.0400391 0.0410156 1
40 ( 0.0419922 0.0449219 3
41 ) 0.0458984 0.0488281 3
42 * 0.0498047 0.0546875 5
43 + 0.0556641 0.0625 7
44 , 0.0634766 0.0644531 1
45 - 0.0654297 0.0683594 3
46 . 0.0693359 0.0703125 1
47 / 0.0712891 0.0751953 4
48 0 0.0761719 0.0820313 6
49 1 0.0830078 0.0859375 3
50 2 0.0869141 0.0927734 6
51 3 0.09375 0.0996094 6
52 4 0.100586 0.106445 6
53 5 0.107422 0.113281 6
54 6 0.114258 0.120117 6
55 7 0.121094 0.126953 6
56 8 0.12793 0.133789 6
57 9 0.134766 0.140625 6
58 : 0.141602 0.142578 1
59 ; 0.143555 0.144531 1
60 < 0.145508 0.151367 6
61 = 0.152344 0.15918 7
62 > 0.160156 0.166016 6
63 ? 0.166992 0.171875 5
64 @ 0.172852 0.18457 12
65 A 0.185547 0.194336 9
66 B 0.195313 0.202148 7
67 C 0.203125 0.209961 7
68 D 0.210938 0.217773 7
69 E 0.21875 0.225586 7
70 F 0.226563 0.232422 6
71 G 0.233398 0.241211 8
72 H 0.242188 0.249023 7
73 I 0.25 0.250977 1
74 J 0.251953 0.256836 5
75 K 0.257813 0.265625 8
76 L 0.266602 0.272461 6
77 M 0.273438 0.282227 9
78 N 0.283203 0.290039 7
79 O 0.291016 0.298828 8
80 P 0.299805 0.306641 7
81 Q 0.307617 0.31543 8
82 R 0.316406 0.323242 7
83 S 0.324219 0.331055 7
84 T 0.332031 0.338867 7
85 U 0.339844 0.34668 7
86 V 0.347656 0.356445 9
87 W 0.357422 0.370117 13
88 X 0.371094 0.37793 7
89 Y 0.378906 0.385742 7
90 Z 0.386719 0.393555 7
91 [ 0.394531 0.396484 2
92 \ 0.397461 0.401367 4
93 ] 0.402344 0.404297 2
94 ^ 0.405273 0.410156 5
95 _ 0.411133 0.417969 7
96 ` 0.418945 0.420898 2
97 a 0.421875 0.426758 5
98 b 0.427734 0.432617 5
99 c 0.433594 0.438477 5
100 d 0.439453 0.444336 5
101 e 0.445313 0.450195 5
102 f 0.451172 0.455078 4
103 g 0.456055 0.460938 5
104 h 0.461914 0.466797 5
105 i 0.467773 0.46875 1
106 j 0.469727 0.472656 3
107 k 0.473633 0.478516 5
108 l 0.479492 0.480469 1
109 m 0.481445 0.490234 9
110 n 0.491211 0.496094 5
111 o 0.49707 0.501953 5
112 p 0.50293 0.507813 5
113 q 0.508789 0.513672 5
114 r 0.514648 0.517578 3
115 s 0.518555 0.523438 5
116 t 0.524414 0.527344 3
117 u 0.52832 0.533203 5
118 v 0.53418 0.539063 5
119 w 0.540039 0.548828 9
120 x 0.549805 0.554688 5
121 y 0.555664 0.560547 5
122 z 0.561523 0.566406 5
123 { 0.567383 0.570313 3
124 | 0.571289 0.572266 1
125 } 0.573242 0.576172 3
126 ~ 0.577148 0.583984 7
查找表的每一行为第一个为相应的符号ASCII码,第二个为对应的符号,第三个和第四个为左纹理和右纹理(只需在符号的左右边界纹理就行,后面剔除黑色像素),第五个为符号大小
FontClass有两个作用:
第一是:加载符号纹理图(这个我不细说了,前面教程有)
第二是:构建纹理查找表
struct Font
{
float left, right;
int size;
};
Font* mFont;//字体结构数组
bool FontClass::LoadFontData(string FontFileName)
{
//创建字体数组,存放字体数据,要读取的文件有95个
mFont = new Font[95];
if (!mFont)
{
return false;
}
ifstream in(FontFileName);
string line, word;
for (int i = 0; i < 95; ++i)
{
getline(in, line);
istringstream record(line);
record >> word;
record >> word;
//可直接移位,而无需进行字符串到其他类型的转换在赋值
record >> mFont[i].left;
record >> mFont[i].right;
record >> mFont[i].size;
}
return true;
}
TextClass 包含 FontClass指针和FontShader指针,以及句子结构体
//句子类型,type表示结构体
struct SentenceType
{
ID3D11Buffer* vertexBuffer;
ID3D11Buffer* indexBuffer;
int vertexCount;
int indexCount;
int maxLength;
float red, blue, green;
};
//在本个教程中使用两个句子
SentenceType* mSentence1;
SentenceType* mSentence2;
FontClass对象建立查找表通过FontClass的接口建立起每个字符串的SentenceType的顶点缓存,
void FontClass::BuildVertexArray(void* vertexs, string sentence, float drawX, float drawY)
{
VertexType* VertexPtr;
//转换指针
VertexPtr = (VertexType*)vertexs;
//获取在句子中字符的数量
int numLetters =(int)sentence.size();
//初始化索引
int index = 0;
//读取句子的每个字符,识别出读出的字符在mFont的位置以及其属性
for (int i = 0; i < numLetters; ++i)
{
//字符在mFont表的位置
int LetterPos = ((int)sentence[i]) - 32;
//如果字符是空格,往右移动位置三个像素
if (LetterPos == 0)
{
drawX += 3.0f;
}
else
{
//构成该字符第一个三角形
VertexPtr[index].pos = XMFLOAT3(drawX, drawY, 0.0f); //左上角
VertexPtr[index].tex = XMFLOAT2(mFont[LetterPos].left, 0.0f);
++index;
VertexPtr[index].pos = XMFLOAT3((drawX + mFont[LetterPos].size), (drawY - 16.0f), 0.0f); //右下角
VertexPtr[index].tex = XMFLOAT2(mFont[LetterPos].right, 1.0f);
++index;
VertexPtr[index].pos = XMFLOAT3(drawX, (drawY - 16.0f), 0.0f); //左下角
VertexPtr[index].tex = XMFLOAT2(mFont[LetterPos].left, 1.0f);
++index;
//构成该字符的第二个三角形
VertexPtr[index].pos = XMFLOAT3(drawX, drawY, 0.0f); //左上角
VertexPtr[index].tex = XMFLOAT2(mFont[LetterPos].left, 0.0f);
++index;
VertexPtr[index].pos = XMFLOAT3((drawX + mFont[LetterPos].size), drawY , 0.0f); //右上角
VertexPtr[index].tex = XMFLOAT2(mFont[LetterPos].right, 0.0f);
++index;
VertexPtr[index].pos = XMFLOAT3((drawX+ mFont[LetterPos].size), drawY - 16.0f, 0.0f); //右下角
VertexPtr[index].tex = XMFLOAT2(mFont[LetterPos].right, 1.0f);
++index;
//更新句子新的字符的新开始的位置
drawX +=(mFont[LetterPos].size + 1.0f);
}
}
}
最终构建的Sentence1和Stentence2对象的顶点缓存进行设置,用FontShaderClass建立并设置VertexShader,PixelShader进行绘制,注意常量缓存
struct CBPixelColor
{
XMFLOAT4 PixelColor;
};
这个常量缓存的值来源与Sentence结构体的
float red, blue, green;
第二,说说为什么关闭Zbuffer,这是2D Rendering教程忘记说的,关闭ZBuffer,因为先进行StenilTest,在进行ZTest,如果ZBuffer关闭,并且stencilFun=D3D11_COMPARISON_ALWAYS即StencilTest总能成功通过,此时不存在ZTest,则绘制的字体(2D Render)一定能覆盖backbuffer的相应位置的像素,即一定能绘制成功.
第三点,为什么开启alpha混合来绘制字体,这点得结合PixelShader代码来说了,先看看PixelShader代码
float4 PS(VertexOut outa) : SV_Target
{
float4 color;
//对文字纹理进行采样,有一个大张的DDS纹理图,集合了各种符号纹理在其上面
color = ShaderTexture.Sample(SampleType, outa.Tex);
//如果在纹理上的颜色是黑色的,则像素是透明的 r=0,g=0,b=0
if (color.r == 0.0f)
{
color.a = 0.0f;
}
else
{
color.rgb = PixelColor.rgb;
color.a = 1.0f;
}
return color;
}
首先符号纹理图( Font.dds)上只存在两种颜色,即黑色(0.0f,0.0f,0.0f)和白色(1.0f,1.0f,1.0f),白色为真正的字体部分
对符号纹理(Font.dds)进行采样时 如果采样像素的为黑色(r=g=b=0.0f),此时设置color.a = 0.0f;
在看看 BlendState的设置
blendStateDescription.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
blendStateDescription.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
blendStateDescription.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
此时由于目标像素颜色(背景的颜色)的混合因子为源像素的alpha的逆反值即(1-Alpha源),源像素混合因子为1.0f
1,.0f*(0.0f,0.0f,0.0f)*(1-0.0f)+(r目标,g目标,b目标)=(r目标,g目标,b目标)
对符号纹理(Font.dds)进行采样时 如果采样像素的为白色(r=g=b=0.0f),此时设置color.a = 1.0f;
1,.0f*(PixelColor.r,PixelColor.g,PixelColor.b)+(1.0f-1.0f)*(r目标,g目标,b目标)=(PixelColor.r,PixelColor.g,PixelColor.b);
这样就显示出特殊颜色的字体了
最后贴出程序运行效果图:
在看看关闭混合模式下运行的图,背景色更改下(不为黑色)
打开混合模式,程序运行得到
这里可以看出混合模式的作用就是:剔除纹理图中黑色的像素,再把白色部分变色打印到屏幕上
程序源码链接: