C#+OpenGL+FreeType显示3D文字(3) - 用PointSprite绘制文字

C#+OpenGL+FreeType显示3D文字(3) - 用PointSprite绘制文字

上一篇实现了把文字绘制到OpenGL窗口,但实质上只是把含有文字的贴图贴到矩形模型上。本篇我们介绍用PointSprite绘制文字,这可以只用1个点绘制文字,并确保文字始终面相窗口。用PointSprite绘制的文字,其大小范围有限,本篇提供的Demo中,Max Row Width最大只有256。现在能够绘制少量的文字,为其指定的位置的过程与为一个点指定位置的过程是相同的,所以此方式的应用范围还是比较广的。

基本流程

与前文相同的是仍然用GLSL+VBO+贴图来绘制。PointSprite只是Enable了一些OpenGL开关,然后把贴图贴到一个Point图元上。

您可以在此下载查看上图所示的demo。为节省空间,此demo只能显示ASCII范围内的字符。实际上它具有显示所有Unicode字符的能力。

编辑GLSL

我们只需vertex shader和fragment shader。

Vertex shader只是进行最基本的变换操作,并负责传递纹理大小。

 1 #version 150 core
 2
 3 in vec3 in_Position;
 4
 5 uniform mat4 MVP;
 6 uniform float pointSize;
 7
 8 void main(void) {
 9     gl_Position = MVP * vec4(in_Position, 1.0);
10     gl_PointSize = pointSize;
11 }

Fragment shader根据纹理坐标所在位置的纹理颜色决定此位置是否显示(透明与否)。这就绘制出了一个字形。还可以顺便用一个uniform vec3 textColor指定文字颜色。

 1 #version 150 core
 2
 3 out vec4 out_Color;
 4
 5 uniform sampler2D tex;
 6 uniform vec3 textColor;
 7
 8 void main(void) {
 9     float transparency = texture2D(tex, gl_PointCoord).r;
10     if (transparency == 0.0f)
11     {
12         discard;
13     }
14     else
15     {
16         out_Color = vec4(1, 1, 1, transparency) * vec4(textColor, 1.0f);
17     }
18 }

设定VAO

模型只需一个Point。

 1         private void InitVAO()
 2         {
 3             GL.GenVertexArrays(1, vao);
 4             GL.BindVertexArray(vao[0]);
 5
 6             //  Create a vertex buffer for the vertex data.
 7             {
 8                 UnmanagedArray<vec3> in_Position = new UnmanagedArray<vec3>(1);
 9                 in_Position[0] = this.position;
10
11                 uint[] ids = new uint[1];
12                 GL.GenBuffers(1, ids);
13                 GL.BindBuffer(BufferTarget.ArrayBuffer, ids[0]);
14                 GL.BufferData(BufferTarget.ArrayBuffer, in_Position, BufferUsage.StaticDraw);
15                 GL.VertexAttribPointer(attributeIndexPosition, 3, GL.GL_FLOAT, false, 0, IntPtr.Zero);
16                 GL.EnableVertexAttribArray(attributeIndexPosition);
17             }
18
19             //  Unbind the vertex array, we‘ve finished specifying data for it.
20             GL.BindVertexArray(0);
21         }

程序生成文字贴图

要想显示任意的字符串,必须借助前文的贴图来一个字符一个字符地拼接出需要的字符串贴图并用之生成Texture。

这是从上面的图片中计算出的"bithuwei.cnblogs.com"的贴图。

由于PointSprite支持的贴图大小有限(最大256),所以计算字符串贴图的程序有点繁琐。

  1         /// <summary>
  2         /// 为指定的字符串生成贴图。
  3         /// </summary>
  4         /// <param name="fontResource"></param>
  5         /// <param name="content"></param>
  6         /// <param name="fontSize"></param>
  7         /// <param name="maxRowWidth"></param>
  8         /// <returns></returns>
  9         public static System.Drawing.Bitmap GenerateBitmapForString(this FontResource fontResource,
 10             string content, int fontSize, int maxRowWidth)
 11         {
 12             // step 1: get totalLength
 13             int totalLength = 0;
 14             {
 15                 int glyphsLength = 0;
 16                 for (int i = 0; i < content.Length; i++)
 17                 {
 18                     char c = content[i];
 19                     CharacterInfo cInfo;
 20                     if (fontResource.CharInfoDict.TryGetValue(c, out cInfo))
 21                     {
 22                         int glyphWidth = cInfo.width;
 23                         glyphsLength += glyphWidth;
 24                     }
 25                     //else
 26                     //{ throw new Exception(string.Format("Not support for display the char [{0}]", c)); }
 27                 }
 28
 29                 //glyphsLength = (glyphsLength * this.fontSize / FontResource.Instance.FontHeight);
 30                 int interval = fontResource.FontHeight / 10; if (interval < 1) { interval = 1; }
 31                 totalLength = glyphsLength + interval * (content.Length - 1);
 32             }
 33
 34             // step 2: setup contentBitmap
 35             System.Drawing.Bitmap contentBitmap = null;
 36             {
 37                 int interval = fontResource.FontHeight / 10; if (interval < 1) { interval = 1; }
 38                 //int totalLength = glyphsLength + interval * (content.Length - 1);
 39                 int currentTextureWidth = 0;
 40                 int currentWidthPos = 0;
 41                 int currentHeightPos = 0;
 42                 if (totalLength * fontSize > maxRowWidth * fontResource.FontHeight)// 超过1行能显示的内容
 43                 {
 44                     currentTextureWidth = maxRowWidth * fontResource.FontHeight / fontSize;
 45
 46                     int lineCount = (totalLength - 1) / currentTextureWidth + 1;
 47                     // 确保整篇文字的高度在贴图中间。
 48                     currentHeightPos = (currentTextureWidth - fontResource.FontHeight * lineCount) / 2;
 49                     //- FontResource.Instance.FontHeight / 2;
 50                 }
 51                 else//只在一行内即可显示所有字符
 52                 {
 53                     if (totalLength >= fontResource.FontHeight)
 54                     {
 55                         currentTextureWidth = totalLength;
 56
 57                         // 确保整篇文字的高度在贴图中间。
 58                         currentHeightPos = (currentTextureWidth - fontResource.FontHeight) / 2;
 59                         //- FontResource.Instance.FontHeight / 2;
 60                     }
 61                     else
 62                     {
 63                         currentTextureWidth = fontResource.FontHeight;
 64
 65                         currentWidthPos = (currentTextureWidth - totalLength) / 2;
 66                         //glyphsLength = fontResource.FontHeight;
 67                     }
 68                 }
 69
 70                 //this.textureWidth = textureWidth * this.fontSize / FontResource.Instance.FontHeight;
 71                 //currentWidthPosition = currentWidthPosition * this.fontSize / FontResource.Instance.FontHeight;
 72                 //currentHeightPosition = currentHeightPosition * this.fontSize / FontResource.Instance.FontHeight;
 73
 74                 contentBitmap = new Bitmap(currentTextureWidth, currentTextureWidth);
 75                 Graphics gContentBitmap = Graphics.FromImage(contentBitmap);
 76                 Bitmap bigBitmap = fontResource.FontBitmap;
 77                 for (int i = 0; i < content.Length; i++)
 78                 {
 79                     char c = content[i];
 80                     CharacterInfo cInfo;
 81                     if (fontResource.CharInfoDict.TryGetValue(c, out cInfo))
 82                     {
 83                         if (currentWidthPos + cInfo.width > contentBitmap.Width)
 84                         {
 85                             currentWidthPos = 0;
 86                             currentHeightPos += fontResource.FontHeight;
 87                         }
 88
 89                         gContentBitmap.DrawImage(bigBitmap,
 90                             new Rectangle(currentWidthPos, currentHeightPos, cInfo.width, fontResource.FontHeight),
 91                             new Rectangle(cInfo.xoffset, cInfo.yoffset, cInfo.width, fontResource.FontHeight),
 92                             GraphicsUnit.Pixel);
 93
 94                         currentWidthPos += cInfo.width + interval;
 95                     }
 96                 }
 97                 gContentBitmap.Dispose();
 98                 //contentBitmap.Save("PointSpriteStringElement-contentBitmap.png");
 99                 System.Drawing.Bitmap bmp = null;
100                 if (totalLength * fontSize > maxRowWidth * fontResource.FontHeight)// 超过1行能显示的内容
101                 {
102                     bmp = (System.Drawing.Bitmap)contentBitmap.GetThumbnailImage(
103                         maxRowWidth, maxRowWidth, null, IntPtr.Zero);
104                 }
105                 else//只在一行内即可显示所有字符
106                 {
107                     if (totalLength >= fontResource.FontHeight)
108                     {
109                         bmp = (System.Drawing.Bitmap)contentBitmap.GetThumbnailImage(
110                             totalLength * fontSize / fontResource.FontHeight,
111                             totalLength * fontSize / fontResource.FontHeight,
112                             null, IntPtr.Zero);
113
114                     }
115                     else
116                     {
117                         bmp = (System.Drawing.Bitmap)contentBitmap.GetThumbnailImage(
118                             fontSize, fontSize, null, IntPtr.Zero);
119                     }
120                 }
121                 contentBitmap.Dispose();
122                 contentBitmap = bmp;
123                 //contentBitmap.Save("PointSpriteStringElement-contentBitmap-scaled.png");
124             }
125
126             return contentBitmap;
127
128         }

计算字符串贴图

缺陷

用PointSprite绘制的文字,其大小范围有限,本篇提供的Demo中,Max Row Width最大只有256。

总结

现在能够绘制少量的文字,为其指定的位置的过程与为一个点指定位置的过程是相同的,所以此方式的应用范围还是比较广的。

时间: 2024-11-10 08:05:34

C#+OpenGL+FreeType显示3D文字(3) - 用PointSprite绘制文字的相关文章

C#+OpenGL+FreeType显示3D文字(1) - 从TTF文件导出字形贴图

C#+OpenGL+FreeType显示3D文字(1) - 从TTF文件导出字形贴图 +BIT祝威+悄悄在此留下版了个权的信息说: 最近需要用OpenGL绘制文字,这是个很费时费力的事.一般的思路就是解析TTF文件从而得到字形的贴图,然后通过OpenGL绘制贴图的方式显示文字. 本篇记录了解析TTF文件并把所有字形安排到一张大贴图上的过程. 使用FreeType 想从零开始解析TTF文件是一个比较大的工程,所以目前就借助FreeType.FreeType是一个开源的跨平台的TTF文件解析器.当然

[iTyran原创]iPhone中OpenGL ES显示3DS MAX模型之一:OBJ格式分析

[iTyran原创]iPhone中OpenGL ES显示3DS MAX模型之一:OBJ文件格式分析作者:yuezang - iTyran 在iOS的3D开发中常常需要导入通过3DS MAX之类的3D设计软件生成的模型.因为OpenGL ES是不能直接读取这些文件的,所以常常需要开发人员增加接口来导入.通常的做法是在建模软件中建立3D模型之后在OpenGL ES中导入并进行控制.    3DS MAX通常的保存格式有*.max(现在生成的版本的格式),*.3ds(低版本的3ds Max生成的格式)

[iTyran原创]iPhone中OpenGL ES显示3DS MAX模型之二:lib3ds加载模型

[iTyran原创]iPhone中OpenGL ES显示3DS MAX模型之二:lib3ds加载模型 作者:u0u0 - iTyran 在上一节中,我们分析了OBJ格式.OBJ格式优点是文本形式,可读性好,缺点也很明显,计算机解析文本过程会比解析二进制文件慢很多.OBJ还有个问题是各种3D建模工具导出的布局格式还不太一样,face还有多边形(超过三边形),不利于在OpenGL ES里面加载. .3ds文件是OBJ的二进制形式,并且多很多信息.有一个C语言写的开源库可以用来加.3ds文件,这就是l

ArcGIS API for JavaScript 4.2学习笔记[2] 显示3D地图

3D地图又叫场景. 由上一篇可知, require入口函数的第一个参数是字符串数组 ["esri/Map", "esri/views/MapView", "dojo/domReady!"] 这里,MapView就是2D的地图视图. 所以,将2D地图改为3D地图,其实很简单,只需要把这个字符串数组的第二个引用改为"esri/views/SceneView" 然后在第二个参数中修改对应的view代码即可. 下面给出第二个参数(函数

Flash Stage3D 在2D UI 界面上显示3D模型问题完美解决

一直以来很多Stage3D开发者都在为3D模型在2DUI上显示的问题头疼.Stage3D一直是在 Stage2D下面.为了做到3D模型在2DUI上显示通常大家有几种实现方式,下面来说说这几种实现方式吧. 实现方式1: 在2DUI上挖个洞透过去显示3D层.这种做法的缺陷在于如果两个UI界面同时打开就会UI错层显示错乱.为了解决这问题很多程序员选择了当挖洞显示3D的UI打时把其他界面隐藏掉,用户体验超差. 实现方式2: 利用Context3D 的 drawToBitmapData API 实时将3D

Qt 3D研究(三):显示3D模型

Qt 3D研究(三):显示3D模型 上一篇文章介绍了如何使用最少的代码创建一个Qt 3D的应用.和大家最初接触的glut一样,对于3D应用来说,需要做的准备工作还真不少,不过呢,Qt 3D把一些窗口相关的琐碎事情解决了,剩下的,该由我们完成重要的渲染部分了,可以说,带来了某种程度的方便. 蒋彩阳原创文章,首发地址:http://blog.csdn.net/gamesdev/article/details/43964499.欢迎同行前来探讨. 我们接下来要使用Qt 3D将一个模型显示出来.Qt 3

QT使用painter绘制文字时的居中显示

在窗体上绘制文字时,在paintEvent()方法里用QPainter进行绘制. 主要获取对字符串打印在屏幕上时占用的像素大小 QPainter p(this); QFont font("宋体",13,75); p.setFont(font); QString title("标题"); int widthOfTitle = p.fontMetrics().width(title);//字符串显示的像素大小 p.drawText(this->width()/2-

Qt 3D教程(二)初步显示3D的内容

Qt3D教程(二)初步显示3D的内容 前一篇很easy,全然就没有牵涉到3D的内容,它仅仅是我们搭建3D应用的基本框架而已,而这一篇.我们将要利用它来初步地显示3D的内容了! 本次目的是将程序中间的内容替换成3D的视图,而这一切也只是十几行代码.要不我们来试一试吧! 蒋彩阳原创文章,首发地址:http://blog.csdn.net/gamesdev/article/details/47131099.欢迎同行前来探讨. 首先我们须要对我们的MainWindow.cpp文件进行改动,在此基础上加入

用TextPaint来绘制文字

TextPaint是paint的子类,用它可以很方便的进行文字的绘制,一般情况下遇到绘制文字的需求时,我们一般用TextPaint所提供的方法.开始学习如何绘制文字之前,我们必须要先了解下android中文字是怎么绘制到屏幕上的,文字的格式又是怎么样的. 一.FontMetrics 1.1 理论知识 它是一个Paint的内部类,作用是“字体测量”.它里面呢就定义了top,ascent,descent,bottom,leading五个成员变量其他什么也没有,和rect很相似.如果你不信,我们可以去