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只是进行最基本的变换操作,并负责传递纹理大小。

 #version  core

 in vec3 in_Position;

 uniform mat4 MVP;
uniform float pointSize; void main(void) {
gl_Position = MVP * vec4(in_Position, 1.0);
gl_PointSize = pointSize;
}

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

 #version  core

 out vec4 out_Color;

 uniform sampler2D tex;
uniform vec3 textColor; void main(void) {
float transparency = texture2D(tex, gl_PointCoord).r;
if (transparency == 0.0f)
{
discard;
}
else
{
out_Color = vec4(, , , transparency) * vec4(textColor, 1.0f);
}
}

设定VAO

模型只需一个Point。

         private void InitVAO()
{
GL.GenVertexArrays(, vao);
GL.BindVertexArray(vao[]); // Create a vertex buffer for the vertex data.
{
UnmanagedArray<vec3> in_Position = new UnmanagedArray<vec3>();
in_Position[] = this.position; uint[] ids = new uint[];
GL.GenBuffers(, ids);
GL.BindBuffer(BufferTarget.ArrayBuffer, ids[]);
GL.BufferData(BufferTarget.ArrayBuffer, in_Position, BufferUsage.StaticDraw);
GL.VertexAttribPointer(attributeIndexPosition, , GL.GL_FLOAT, false, , IntPtr.Zero);
GL.EnableVertexAttribArray(attributeIndexPosition);
} // Unbind the vertex array, we've finished specifying data for it.
GL.BindVertexArray();
}

程序生成文字贴图

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

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

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

         /// <summary>
/// 为指定的字符串生成贴图。
/// </summary>
/// <param name="fontResource"></param>
/// <param name="content"></param>
/// <param name="fontSize"></param>
/// <param name="maxRowWidth"></param>
/// <returns></returns>
public static System.Drawing.Bitmap GenerateBitmapForString(this FontResource fontResource,
string content, int fontSize, int maxRowWidth)
{
// step 1: get totalLength
int totalLength = ;
{
int glyphsLength = ;
for (int i = ; i < content.Length; i++)
{
char c = content[i];
CharacterInfo cInfo;
if (fontResource.CharInfoDict.TryGetValue(c, out cInfo))
{
int glyphWidth = cInfo.width;
glyphsLength += glyphWidth;
}
//else
//{ throw new Exception(string.Format("Not support for display the char [{0}]", c)); }
} //glyphsLength = (glyphsLength * this.fontSize / FontResource.Instance.FontHeight);
int interval = fontResource.FontHeight / ; if (interval < ) { interval = ; }
totalLength = glyphsLength + interval * (content.Length - );
} // step 2: setup contentBitmap
System.Drawing.Bitmap contentBitmap = null;
{
int interval = fontResource.FontHeight / ; if (interval < ) { interval = ; }
//int totalLength = glyphsLength + interval * (content.Length - 1);
int currentTextureWidth = ;
int currentWidthPos = ;
int currentHeightPos = ;
if (totalLength * fontSize > maxRowWidth * fontResource.FontHeight)// 超过1行能显示的内容
{
currentTextureWidth = maxRowWidth * fontResource.FontHeight / fontSize; int lineCount = (totalLength - ) / currentTextureWidth + ;
// 确保整篇文字的高度在贴图中间。
currentHeightPos = (currentTextureWidth - fontResource.FontHeight * lineCount) / ;
//- FontResource.Instance.FontHeight / 2;
}
else//只在一行内即可显示所有字符
{
if (totalLength >= fontResource.FontHeight)
{
currentTextureWidth = totalLength; // 确保整篇文字的高度在贴图中间。
currentHeightPos = (currentTextureWidth - fontResource.FontHeight) / ;
//- FontResource.Instance.FontHeight / 2;
}
else
{
currentTextureWidth = fontResource.FontHeight; currentWidthPos = (currentTextureWidth - totalLength) / ;
//glyphsLength = fontResource.FontHeight;
}
} //this.textureWidth = textureWidth * this.fontSize / FontResource.Instance.FontHeight;
//currentWidthPosition = currentWidthPosition * this.fontSize / FontResource.Instance.FontHeight;
//currentHeightPosition = currentHeightPosition * this.fontSize / FontResource.Instance.FontHeight; contentBitmap = new Bitmap(currentTextureWidth, currentTextureWidth);
Graphics gContentBitmap = Graphics.FromImage(contentBitmap);
Bitmap bigBitmap = fontResource.FontBitmap;
for (int i = ; i < content.Length; i++)
{
char c = content[i];
CharacterInfo cInfo;
if (fontResource.CharInfoDict.TryGetValue(c, out cInfo))
{
if (currentWidthPos + cInfo.width > contentBitmap.Width)
{
currentWidthPos = ;
currentHeightPos += fontResource.FontHeight;
} gContentBitmap.DrawImage(bigBitmap,
new Rectangle(currentWidthPos, currentHeightPos, cInfo.width, fontResource.FontHeight),
new Rectangle(cInfo.xoffset, cInfo.yoffset, cInfo.width, fontResource.FontHeight),
GraphicsUnit.Pixel); currentWidthPos += cInfo.width + interval;
}
}
gContentBitmap.Dispose();
//contentBitmap.Save("PointSpriteStringElement-contentBitmap.png");
System.Drawing.Bitmap bmp = null;
if (totalLength * fontSize > maxRowWidth * fontResource.FontHeight)// 超过1行能显示的内容
{
bmp = (System.Drawing.Bitmap)contentBitmap.GetThumbnailImage(
maxRowWidth, maxRowWidth, null, IntPtr.Zero);
}
else//只在一行内即可显示所有字符
{
if (totalLength >= fontResource.FontHeight)
{
bmp = (System.Drawing.Bitmap)contentBitmap.GetThumbnailImage(
totalLength * fontSize / fontResource.FontHeight,
totalLength * fontSize / fontResource.FontHeight,
null, IntPtr.Zero); }
else
{
bmp = (System.Drawing.Bitmap)contentBitmap.GetThumbnailImage(
fontSize, fontSize, null, IntPtr.Zero);
}
}
contentBitmap.Dispose();
contentBitmap = bmp;
//contentBitmap.Save("PointSpriteStringElement-contentBitmap-scaled.png");
} return contentBitmap; }

计算字符串贴图

缺陷

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

总结

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

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

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

    C#+OpenGL+FreeType显示3D文字(1) - 从TTF文件导出字形贴图 +BIT祝威+悄悄在此留下版了个权的信息说: 最近需要用OpenGL绘制文字,这是个很费时费力的事.一般的思路就是 ...

  2. C#+OpenGL+FreeType显示3D文字(2) - 用GLSL+VBO绘制文字

    C#+OpenGL+FreeType显示3D文字(2) - 用GLSL+VBO绘制文字 +BIT祝威+悄悄在此留下版了个权的信息说: 上一篇得到了字形贴图及其位置字典(可导出为XML).本篇就利用此贴 ...

  3. Android 使用Canvas在图片上绘制文字

    一个小应用,在图片上绘制文字,以下是绘制文字的方法,并且能够实现自动换行,字体自动适配屏幕大小 private void drawNewBitmap(ImageView imageView, Stri ...

  4. [OpenGL ES 03]3D变换:模型,视图,投影与Viewport

    [OpenGL ES 03]3D变换:模型,视图,投影与Viewport 罗朝辉 (http://blog.csdn.net/kesalin) 本文遵循“署名-非商业用途-保持一致”创作公用协议 系列 ...

  5. OPENGL绘制文字

    OPENGL没有提供直接绘制文字的功能,需要借助于操作系统. 用OPENGL绘制文字比较常见的方法是利用显示列表.创建一系列显示列表,每个字符对应一个列表编号.例如,'A'对应列表编号1000+'A' ...

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

    [iTyran原创]iPhone中OpenGL ES显示3DS MAX模型之二:lib3ds加载模型 作者:u0u0 - iTyran 在上一节中,我们分析了OBJ格式.OBJ格式优点是文本形式,可读 ...

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

    [iTyran原创]iPhone中OpenGL ES显示3DS MAX模型之一:OBJ文件格式分析作者:yuezang - iTyran     在iOS的3D开发中常常需要导入通过3DS MAX之类 ...

  8. Windows MFC 两个OpenGL窗口显示与线程RC问题

    问题为:背景界面是一个OpenGL窗口(对话框),在其上弹出一个OpenGL窗口(模态对话框)时, 1.上方的OpenGL窗口能响应鼠标操作等并刷新: 2.当移动或放大缩小上方的OpenGL窗口时,其 ...

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

    一直以来很多Stage3D开发者都在为3D模型在2DUI上显示的问题头疼.Stage3D一直是在 Stage2D下面.为了做到3D模型在2DUI上显示通常大家有几种实现方式,下面来说说这几种实现方式吧 ...

随机推荐

  1. 安装ganglia

    安装ganglia 1.默认已经配置好相关的主机名和Ip地址映射关系 2.默认已经安装好ssh密码登陆 3.默认已经配置好yum源和相关网络配置(如hosts 可在墙外) 4.服务器端安装(除了yum ...

  2. for in 遍历json

    获取对象属性的语法: 1.obj.attr 2.obj["attr"] for in 遍历json的时候, 1.for(attr in json)中的attr获取到的是json中属 ...

  3. Code Complete 笔记—— 第二章 用隐喻来更充分理解软件开发

    在这章里面,提到的隐喻,类同于比喻(建模)的方法的去理解软件开发. 隐喻的优点在于其可预期的效果能被所有人所理解.不必要的沟通和误解也因此大为减低,学习与教授更为快速,实际上,隐喻是对概念进行内在化和 ...

  4. 利用DelegatingHandler实现Web Api 的Api key校验

    客户端在请求Web Api时可以有以下两种方式提供API key 基于Querystring提供Api key http://localhost:57967/Api/Values?key=12345 ...

  5. 批量设置select为默认项

    app.$form.find("select").prop("selectedIndex", 0);

  6. 图片Base64编码

    我们经常在做Jquery插件的时候需要插入一些自定义的控件,比如说按钮,而我们自己又觉着button标签很丑,而且不同浏览器显示的效果还不一样,这个时候我们需要用到图片,当然,我们可以通过img标签添 ...

  7. Easyui之datagrid实现点击单元格修改单元格背景颜色

    前段时间有个需求中有点击datagrid的单元格实现某种事件,调用datagrid的onclickCell这个方法很容易实现,但是体验不好啊,完全不知道自己刚才点击的是哪个单元格,然后就尝试单击单元格 ...

  8. POJ 3233Matrix Power Series

    妈妈呀....这简直是目前死得最惨的一次. 贴题目: http://poj.org/problem?id=3233 Matrix Power Series Time Limit: 3000MS Mem ...

  9. BZOJ4262: Sum

    Description   Input 第一行一个数 t,表示询问组数. 第一行一个数 t,表示询问组数. 接下来 t 行,每行四个数 l_1, r_1, l_2, r_2.   Output 一共 ...

  10. Object-C与Swift混编

    1.在Object-C项目中调用Swift 1.1.创建一个Object-C project(项目名例如:YYDemo) 如下图 1.2.创建一个Swift Class,如下图 这里会显示是否需要创建 ...