最近开始在啃Game Programming Gem 8.里面有一些不错的文章,及时咀嚼及时反思 记录于此。
一直以为字体渲染是件简单的事 ,因为电脑上这么多字体显示么,但是今天看了这文章才知道3D技术里的字体渲染是两回事。平时win里面看到的文字这些基本都是通过GDI在cpu上运算绘制(或者也加入了显卡绘制)的,不过在3D游戏里面看到的字体包括GUI等则不是这个流程的,它们都跟其他图形的渲染一样,都是一些多边形,经过渲染管线在显卡上绘制的,跟多边形的渲染时一样的,甚至有时是一个比较耗的过程。
通常字体的渲染有两种方式:
1 是把turetype字头的每个字符图像(英文称glyph)编码为三角形集合,然后就想渲染三角形一样在屏幕上渲染,这通常会带来大量的渲染数据。
2 把所有的字符画在一张贴图上,然后在渲染的时候,每个字符就是一个正方形(俩三角形),然后编码每个找到合适的贴图坐标给这个正方形贴图,使得贴的图就是那个大字符图上这个字符的图像,这种方法需要为每个字符预先编码他们的贴图坐标。显然这种方法更好一些,三角数目会少很多。
一般的字符渲染包括GUI的渲染一样都是采用第二种方法吗,但是他有一些效率不高的地方,比如字符可能会要经常变化,那么向管线要频繁提交定点数据。因此针对这个就有一些优化的余地:
1.将对显存back buffer的lock方式设为discard方式,这种方式是D3D支持的一种方式,一种解决cpu的提交和GPU的渲染对backbuffer的访问冲突的策略,正常情况当cpu提交给gpu的backbuffer要对其修改时,如果发现这块缓存恰巧正在gpu用于渲染给前缓存,那么提交会被打回,也就是这帧渲染不成功,会卡帧,这个discard标记在频繁的vertex buffer提交时使用,它的意思是如果发现这块缓存恰巧正在gpu用于渲染给前缓存,那么标记这个缓存为valid,这时GPU会立即将这片缓存重建,并且会马上允许cpu的提交,并渲染。这会最大化的允许cpu对字体这种字符GUI这种可能频繁变化VB的物体的提交。
2.正常包括字符数据的vb(vertex buffer)要包括位置、贴图坐标、颜色信息。但是其实不用每次都提交这么多信息,可以把一个估计的最大的字符串的VB用一个流通道a(D3D的stream channel)提交,每次变化字符时只要提交一个唯位移或者合并缩放信息到另一个通道b就行了。也就是频繁提交的只是B通道的较少的数据。
3还可以利用D3D的instance技术,即把字符做成instance 那么每次更改字符信息,只提交字符的位置就行了,当然23都要使用shader实现。
4.有时为了实现字符或GUI的上下层效果,需要有一个层数的信息,这个可以利用位置向量的z支来表示。
5.为了节省渲染资源提高效率,字符和GUI这种不涉及到3D变换的表层物件,通常直接提交的是clip空间的坐标,屏幕坐标XY对应的剪裁坐标计算公式为cx=-1+x*(2/screen_width) cy=1-y*(2/screen_height).在D3D的固定管线中就等于直接提交D3DFVF_XYZRHW格式的vb,他不执行定点变换。