Direcxt11教程八之FontEngine(字体实现)

这个教程其实的大体实现方法是建立在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);


这样就显示出特殊颜色的字体了

最后贴出程序运行效果图:



在看看关闭混合模式下运行的图,背景色更改下(不为黑色)



打开混合模式,程序运行得到



这里可以看出混合模式的作用就是:剔除纹理图中黑色的像素,再把白色部分变色打印到屏幕上


程序源码链接:

点击打开链接


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值