OpenGL渲染管线学习—文字模糊问题

发表于2016-05-17
评论2 1.17w浏览

OpenGL渲染管线学习文字模糊问题

在cocos3.x框架中,采用freetype进行文字展示,但是在一些情况下文字展示是模糊的。针对模糊的问题,花了一点时间学习opengl。


一、  OpenGL渲染管线

首先介绍一下opengl的渲染管线,主要包括(如图1 )

  准备顶点数据(VBO,VAO和Vertex attribute 来传递数据给OpenGL)

  顶点处理(主要是Vertex Shader,顶点坐标归一化和viewport变换)

  Primitive组装(根据向量,采用基本图元组装)

  光栅化生成一个一个像素

  使用Fragment shader 来处理这些像素

  采样处理(主要包括Scissor Test, Depth Test, Blending, Stencil Test等) 


              


其中蓝色区域是可编程的阶段(从别处参考的流程图,具体见参考文档)。

 

二、  结合实际问题,渲染管线的流程

以下以渲染文字,“您的钻石不足”为例。

下图为程序渲染展示出来的文字(放大效果):



PS下对应字号的文字(放大效果)



对比一下单独的文字,以“钻”为例:



可以明显看出本地渲染出来的文字是模糊的。


准备顶点数据:

其中顶点的数据(以“钻”为例):



顶点处理:

在顶点数据进入到vertex shader之前需要对当前的坐标进行转换。

其中3D物体从建模到最终显示屏幕上要经历以下几个阶段:



(从别处参考的效果图,具体见参考文档)

  局部坐标是对象相对局部远点的坐标,也是对象开始的坐标;以上述的字符串为例 (0,0,0)

  局部坐标转换为世界坐标,世界坐标是作为一个更大控件范围的坐标系统,这些坐标是相对世界的原点。

  世界坐标转换为观察坐标,观察坐标是指以摄像机或者观察者的角度观察的坐标。

  在将坐标处理到观察控件后,我们需要将起投影到裁切坐标,裁切坐标是处理(-1.0, 1.0)范围内并判断那些顶点会出现在屏幕上。

  裁切坐标转换为屏幕坐标,这一过程称为视口变换,视口变换将位于 -1.0 到1.0 范围的坐标转换到由glviewport函数所定义的坐标范围内,最后转换的坐标将会送到光栅器,由光栅器将其转换为片段。

在实际的开发过程中,往往会把MV变换放在一起,一般的做法是:

gl_position=P*MV*ObJectionPosition             (公式1)

其中投影变换与视口变换是相互对应的,投影变换的效果是建立一个裁剪用的视镜体,投影位于其中的物体,对视镜体外的物品进行阶段,投影变换之后的坐标反映的是物品在视镜体内的位置属性,比如同样大小的面(在全局坐标系统中),透视投影时,靠近视镜体前端的占起响应的截面比例更大,因此在变换后的面积也更大。 视口变换,作用是将最终确定的图像大小,并且帧缓冲区域内的数据转变为能显示在屏幕的像素。由于这两个变换属于全局变换,所以在研究的时候着重考虑MV的坐标变换。

其中的MV变换,在Opengl中主要指的是nodeToParent的变换,发现在程序中糊的字展示获得的MV矩阵非整数,而不糊的文字展示,MV矩阵参数都是整数。可以推断文字的效果与MV矩阵数据有关。

查看Vertex Shader的内容:



文字的位置为:

gl_Postion = CC_MVPMatrix  * a_position             (公式2)

根据公式1 上公式可以变换为:

glPostion=CC_PMatrix* CC_MVMatrix  * a_position             (公式3)

以“钻“为例,顶点数据带入,发现文字在处理转换的时候,对顶点的信息做了限制。



返回的顶点信息,一定是整数。如果此时*MV矩阵,若MV矩阵中数据为带小数的delta(比如0.5),则结果有原来位置偏移delta,那么在后续坐标转换结束,光栅化的会将原本一个像素展示的信息,变成两个像素进行展示,则展示的内容在视觉效果下,感觉是模糊的。

查看下MV矩阵的计算过程,若文字的位置或者锚点的位置坐标或者存在旋转拉伸参数等,则计算出来的结果就会出现小数的情况。在光栅化的时候可能会导致产生模糊的情况。


Primitive组装(根据向量,采用基本图元组装)


glDrawElements(GL_TRIANGLES, (GLsizei) numberOfQuads*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(_indices[0])) );


采用GL_TRIANGLES基本图元来进行展示


 光栅化生成一个一个像素 (可编程)


使用Fragment shader 来处理这些像素



通过CC_Texture0的采样器和向量属性中的纹理坐标来计算对应的alpha值。

纹理(以钻“为例)




上图发现,纹理和PS的相似,并且无糊边的效果,表明当前纹理是没有问题的。

 Uniform设置当前文字颜色:


glprogram->setUniformLocationWith4f(_uniformTextColor,

            _textColorF.r,_textColorF.g,_textColorF.b,_textColorF.a);

(从别处参考的效果图,具体见参考文档)


采样处理(主要包括Scissor Test, Depth Test, Blending, Stencil Test等)


GL::blendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );


其他的属性参数,保持与全局使用相同,不进行赘述。


 展示结果



以上对比可以看出,矫正后的结果更贴近与PS的展示结果,实际运行视觉效果也变得清晰了,问题圆满解决。

 

三、  参考文档:

https://www.opengl.org/wiki/Rendering_Pipeline_Overview opengl管线基本介绍

https://en.wikipedia.org/wiki/OpenGL

http://www.flakor.cn/2014-05-15-384.html opengl坐标转换

http://blog.sina.com.cn/s/blog_537cc4d9010172o9.html

http://blog.db-in.com/cameras-on-opengl-es-2-x/

http://blog.csdn.net/honghaier/article/details/7931914 红孩儿--Cocos2d-x中图字原理之深入分析

等等

  学习,研究,交流,有异议可以互相交流~~

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引