CSharpGL(15)用GLSL渲染2种类型的文字

CSharpGL(15)用GLSL渲染2种类型的文字

下载

这个示例是CSharpGL的一部分,CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL

血条

玩家头顶的血条、名字随着玩家在3D世界移动,但始终朝向摄像机,且在屏幕上的大小不变。

始终朝向camera

如何使模型始终朝向camera?

对模型进行坐标变换,使模型的顶点坐标从物体坐标系变换到世界坐标系再到屏幕坐标系。这过程需要三个矩阵。

1 gl_Position = projectionMatrix * viewMatrix * modelMatrix * position;

其中经过viewMatrix后,模型就变换到了世界坐标空间里。那么,只需调整viewMatrix(去掉旋转变换),就不会改变模型的朝向了。

在4x4矩阵中,旋转变换由左上角的3x3矩阵实现。所以,只要把viewMatrix的3x3矩阵变为单位矩阵即可。

1 mat4 translateView = mat4(1.0f);//单位矩阵
2
3 for (int t = 0; t < 3; t++)
4 { translateView[t].w = viewMatrix[t].w; }
5
6 translateView[3] = viewMatrix[3];
7
8 gl_Position = projectionMatrix * translateView * (modelMatrix * position);

当然,也可以在C#里直接计算translateView。

 1         private mat4 AlwaysFaceCamera(mat4 viewMatrix)
 2         {
 3             mat4 result = mat4.identity();
 4             for (int i = 0; i < 3; i++)
 5             {
 6                 vec4 v = result[i];
 7                 v.w = viewMatrix[i].w;
 8                 result[i] = v;
 9             }
10             result[3] = viewMatrix[3];
11
12             return result;
13         }

在屏幕上的大小不变

上面解决了始终朝向camera的问题。但是此时的血条仍会在远离camera时和其他模型一样缩小。我希望血条大小保持不变。

虽然还不能完全说明原理,不过还是做出来了。只需将血条的modelMatrix设置为下面值即可。

 1         /// <summary>
 2         ///
 3         /// </summary>
 4         /// <param name="length">camera的Position和Target之间的距离</param>
 5         /// <param name="height">血条高度</param>
 6         /// <returns></returns>
 7         private mat4 AlwaysSameSize(float length, float height)
 8         {
 9             mat4 result = glm.translate(glm.scale(mat4.identity(),
10                 new vec3(length, length, 1)),
11                 new vec3(0, height / length, 0));
12
13             return result;
14         }

大体原理是:血条模型本身必须是中心对称的;摄像机远离其Target时,应该放大血条(glm.scale());之后向上移动一段距离,这段距离与摄像机到Target的距离是减函数关系(我就认为是成反比,具体原因我说不清)。这样才可能保持其大小不变。经过试验,上述AlwaysSameSize()方法是正确的。

demo

您可以在下图的例子中观察血条类型的模型是如何实现的。此demo顺便加上了后面要介绍的血条型文字(teapot)和UI文字(Hello Label!)。

字符串模型

渲染文字的基本思想很简单:字符都是贴图,贴到Quad上就行了。

贴图

所以要准备好贴图。这一步我已经在(http://www.cnblogs.com/bitzhuwei/p/generate-bitmap-from-ttf.html)详细叙述过了。

模型

文字模型是在同一平面内的若干个Quad。所以定义其模型如下。

  1     /// <summary>
  2     /// 用于渲染一段文字
  3     /// </summary>
  4     public class StringModel : IModel
  5     {
  6         public sampler2D glyphTexture { get; set; }
  7         public GlyphPosition[] positions { get; set; }
  8         public GlyphColor[] colors { get; set; }
  9         public GlyphTexCoord[] texCoords { get; set; }
 10
 11         public Objects.VertexBuffers.BufferRenderer GetPositionBufferRenderer(string varNameInShader)
 12         {
 13             using (var buffer = new PositionBuffer(varNameInShader))
 14             {
 15                 buffer.Alloc(positions.Length);
 16                 unsafe
 17                 {
 18                     var array = (GlyphPosition*)buffer.FirstElement();
 19                     for (int i = 0; i < positions.Length; i++)
 20                     {
 21                         array[i] = positions[i];
 22                     }
 23                 }
 24
 25                 return buffer.GetRenderer();
 26             }
 27         }
 28
 29         public Objects.VertexBuffers.BufferRenderer GetColorBufferRenderer(string varNameInShader)
 30         {
 31             using (var buffer = new ColorBuffer(varNameInShader))
 32             {
 33                 buffer.Alloc(colors.Length);
 34                 unsafe
 35                 {
 36                     var array = (GlyphColor*)buffer.FirstElement();
 37                     for (int i = 0; i < colors.Length; i++)
 38                     {
 39                         array[i] = colors[i];
 40                     }
 41                 }
 42
 43                 return buffer.GetRenderer();
 44             }
 45         }
 46
 47         public Objects.VertexBuffers.BufferRenderer GetTexCoordBufferRenderer(string varNameInShader)
 48         {
 49             using (var buffer = new TexCoordBuffer(varNameInShader))
 50             {
 51                 buffer.Alloc(texCoords.Length);
 52                 unsafe
 53                 {
 54                     var array = (GlyphTexCoord*)buffer.FirstElement();
 55                     for (int i = 0; i < texCoords.Length; i++)
 56                     {
 57                         array[i] = texCoords[i];
 58                     }
 59                 }
 60
 61                 return buffer.GetRenderer();
 62             }
 63         }
 64
 65         public Objects.VertexBuffers.BufferRenderer GetNormalBufferRenderer(string varNameInShader)
 66         {
 67             return null;
 68         }
 69
 70         public Objects.VertexBuffers.BufferRenderer GetIndexes()
 71         {
 72             using (var buffer = new ZeroIndexBuffer(DrawMode.Quads, 0, this.positions.Length * 4))
 73             {
 74                 return buffer.GetRenderer();
 75             }
 76         }
 77
 78         public struct GlyphPosition
 79         {
 80             public vec2 leftUp;
 81             public vec2 leftDown;
 82             public vec2 rightUp;
 83             public vec2 rightDown;
 84
 85             public GlyphPosition(
 86                 vec2 leftUp,
 87                 vec2 leftDown,
 88                 vec2 rightUp,
 89                 vec2 rightDown)
 90             {
 91                 this.leftUp = leftUp;
 92                 this.leftDown = leftDown;
 93                 this.rightUp = rightUp;
 94                 this.rightDown = rightDown;
 95             }
 96         }
 97
 98         public struct GlyphColor
 99         {
100             public vec4 leftUp;
101             public vec4 leftDown;
102             public vec4 rightUp;
103             public vec4 rightDown;
104
105             public GlyphColor(
106                 vec4 leftUp,
107                 vec4 leftDown,
108                 vec4 rightUp,
109                 vec4 rightDown)
110             {
111                 this.leftUp = leftUp;
112                 this.leftDown = leftDown;
113                 this.rightUp = rightUp;
114                 this.rightDown = rightDown;
115             }
116         }
117
118         public struct GlyphTexCoord
119         {
120             public vec2 leftUp;
121             public vec2 leftDown;
122             public vec2 rightUp;
123             public vec2 rightDown;
124
125             public GlyphTexCoord(
126                 vec2 leftUp,
127                 vec2 leftDown,
128                 vec2 rightUp,
129                 vec2 rightDown)
130             {
131                 this.leftUp = leftUp;
132                 this.leftDown = leftDown;
133                 this.rightUp = rightUp;
134                 this.rightDown = rightDown;
135             }
136         }
137
138         class PositionBuffer : PropertyBuffer<GlyphPosition>
139         {
140             public PositionBuffer(string varNameInShader)
141                 : base(varNameInShader, 2, GL.GL_FLOAT, BufferUsage.StaticDraw)
142             { }
143         }
144         class ColorBuffer : PropertyBuffer<GlyphColor>
145         {
146             public ColorBuffer(string varNameInShader)
147                 : base(varNameInShader, 4, GL.GL_FLOAT, BufferUsage.StaticDraw)
148             { }
149         }
150
151         class TexCoordBuffer : PropertyBuffer<GlyphTexCoord>
152         {
153             public TexCoordBuffer(string varNameInShader)
154                 : base(varNameInShader, 2, GL.GL_FLOAT, BufferUsage.StaticDraw)
155             { }
156         }
157     }

StringModel

Shader

vertex shader如下。

 1 #version 150 core
 2
 3 in vec2 position;
 4 in vec4 color;
 5 out vec4 passColor;
 6 in vec2 texCoord;
 7 out vec2 passTexCoord;
 8 uniform mat4 mvp;
 9
10 void main(void)
11 {
12     gl_Position = mvp * vec4(position, 0.0f, 1.0f);
13     passColor = color;
14     passTexCoord = texCoord;
15 }

fragment shader如下。

 1 #version 150 core
 2
 3 in vec4 passColor;
 4 in vec2 passTexCoord;
 5 uniform sampler2D glyphTexture;
 6 out vec4 outputColor;
 7
 8 void main(void)
 9 {
10     float transparency = texture(glyphTexture, passTexCoord).r;
11     if (transparency == 0.0f)
12     {
13         discard;
14     }
15     else
16     {
17         outputColor = vec4(1, 1, 1, transparency) * passColor;
18     }
19 }

根据字符串创建模型

给定一个字符串,我们可以计算出相应的模型。

 1     public static class DummyStringModelFactory
 2     {
 3         /// <summary>
 4         /// 简单地生成一行文字。
 5         /// </summary>
 6         /// <param name="content"></param>
 7         /// <returns></returns>
 8         public static StringModel GetModel(this string content)
 9         {
10             StringModel model = new StringModel();
11
12             var glyphPositions = new StringModel.GlyphPosition[content.Length];
13             FontResource fontResource = CSharpGL.GlyphTextures.FontResource.Default;
14             var glyphTexCoords = new StringModel.GlyphTexCoord[content.Length];
15             //fontResource.GenerateBitmapForString(content, 10, 10000);
16             int currentWidth = 0; int currentHeight = 0;
17             /*
18              * 0     3  4     6 8     11 12   15
19              * -------  ------- -------  -------
20              * |     |  |     | |     |  |     |
21              * |     |  |     | |     |  |     |
22              * |     |  |     | |     |  |     |
23              * -------  ------- -------  -------
24              * 1     2  5     6 9     10 13   14
25              */
26             for (int i = 0; i < content.Length; i++)
27             {
28                 char ch = content[i];
29                 CharacterInfo info = fontResource.CharInfoDict[ch];
30                 glyphPositions[i] = new StringModel.GlyphPosition(
31                     new GLM.vec2(currentWidth, currentHeight + fontResource.FontHeight),
32                     new GLM.vec2(currentWidth, currentHeight),
33                     new GLM.vec2(currentWidth + info.width, currentHeight),
34                     new GLM.vec2(currentWidth + info.width, currentHeight + fontResource.FontHeight));
35                 const int shrimp = 2;
36                 glyphTexCoords[i] = new StringModel.GlyphTexCoord(
37                     new GLM.vec2((float)(info.xoffset + shrimp) / (float)fontResource.FontBitmap.Width, (float)(currentHeight) / (float)fontResource.FontBitmap.Height),
38                     new GLM.vec2((float)(info.xoffset + shrimp) / (float)fontResource.FontBitmap.Width, (float)(currentHeight + fontResource.FontHeight) / (float)fontResource.FontBitmap.Height),
39                     new GLM.vec2((float)(info.xoffset - shrimp + info.width) / (float)fontResource.FontBitmap.Width, (float)(currentHeight + fontResource.FontHeight) / (float)fontResource.FontBitmap.Height),
40                     new GLM.vec2((float)(info.xoffset - shrimp + info.width) / (float)fontResource.FontBitmap.Width, (float)(currentHeight) / (float)fontResource.FontBitmap.Height)
41                     );
42                 currentWidth += info.width + 10;
43             }
44             // move to center
45             for (int i = 0; i < content.Length; i++)
46             {
47                 StringModel.GlyphPosition position = glyphPositions[i];
48
49                 position.leftUp.x -= currentWidth / 2;
50                 position.leftDown.x -= currentWidth / 2;
51                 position.rightUp.x -= currentWidth / 2;
52                 position.rightDown.x -= currentWidth / 2;
53                 position.leftUp.y -= (currentHeight + fontResource.FontHeight) / 2;
54                 position.leftDown.y -= (currentHeight + fontResource.FontHeight) / 2;
55                 position.rightUp.y -= (currentHeight + fontResource.FontHeight) / 2;
56                 position.rightDown.y -= (currentHeight + fontResource.FontHeight) / 2;
57
58                 position.leftUp.x /= (currentHeight + fontResource.FontHeight);
59                 position.leftDown.x /= (currentHeight + fontResource.FontHeight);
60                 position.rightUp.x /= (currentHeight + fontResource.FontHeight);
61                 position.rightDown.x /= (currentHeight + fontResource.FontHeight);
62                 position.leftUp.y /= (currentHeight + fontResource.FontHeight);
63                 position.leftDown.y /= (currentHeight + fontResource.FontHeight);
64                 position.rightUp.y /= (currentHeight + fontResource.FontHeight);
65                 position.rightDown.y /= (currentHeight + fontResource.FontHeight);
66                 glyphPositions[i] = position;
67             }
68
69             var glyphColors = new StringModel.GlyphColor[content.Length];
70             for (int i = 0; i < glyphColors.Length; i++)
71             {
72                 glyphColors[i] = new StringModel.GlyphColor(
73                     new GLM.vec4(0, 0, 0, 1),
74                     new GLM.vec4(0, 0, 0, 1),
75                     new GLM.vec4(0, 0, 0, 1),
76                     new GLM.vec4(0, 0, 0, 1)
77                     );
78             }
79
80             model.positions = glyphPositions;
81             model.texCoords = glyphTexCoords;
82             model.colors = glyphColors;
83             model.glyphTexture = FontTextureManager.Instance.GetTexture2D(fontResource.FontBitmap);
84
85             return model;
86         }
87     }

public static StringModel GetModel(this string content);

标签(Label)

在OpenGL场景中,像Winform里的标签(Label)一样的控件如何实现?如何在UI空间内渲染大量文字?

之前实现过的"标签"是用point sprite做的,其大小范围有限,最多到256x256个像素。当时不用GLSL+VBO,是因为那会还不知道如何使模型始终朝向camera。

现在在StringModel的基础上,只需借助IUILayout机制即可实现opengl里的标签控件。

 1     public class DummyLabel : RendererBase, IUILayout
 2     {
 3         public StringRenderer renderer;
 4
 5         /// <summary>
 6         ///
 7         /// </summary>
 8         /// <param name="param">the edges of the viewport to which a SimpleUIRect is bound and determines how it is resized with its parent.
 9         /// <para>something like AnchorStyles.Left | AnchorStyles.Bottom.</para></param>
10         /// <param name="content"></param>
11         public DummyLabel(IUILayoutParam param, string content)
12         {
13             this.renderer = new StringRenderer(content.GetModel());
14
15             IUILayout layout = this;
16             layout.Param = param;
17         }
18
19         protected override void DisposeUnmanagedResources()
20         {
21             this.renderer.Dispose();
22         }
23
24         #region IUILayout
25
26         public IUILayoutParam Param { get; set; }
27
28         #endregion IUILayout
29
30         protected override void DoInitialize()
31         {
32             this.renderer.Initialize();
33         }
34
35         protected override void DoRender(RenderEventArgs e)
36         {
37             mat4 projectionMatrix, viewMatrix, modelMatrix;
38             {
39                 IUILayout element = this as IUILayout;
40                 element.GetMatrix(out projectionMatrix, out viewMatrix, out modelMatrix, null);
41             }
42             this.renderer.mvp = projectionMatrix * viewMatrix * modelMatrix;
43
44             this.renderer.Render(e);
45
46         }
47     }

总结

本文还没有彻底解决血条型文字"在屏幕上大小不变"的问题。

时间: 2024-10-13 23:56:41

CSharpGL(15)用GLSL渲染2种类型的文字的相关文章

HTML之DocType的几种类型 -转载

HTML之DocType的几种类型转载 doctype类型详细doctype的几种类型html之doctype 分类: 前端文摘  在默认情况下,FF和IE的解释标准是不一样的,也就是说,如果一个网页没有声明DOCTYPE,它就会以默认的DOCTYPE解释下面的HTML.在同一种标准下,不同浏览器的解释模型都有所差异,如果声明标准不同,不用我说,您自己想就可以了.学习网页标准,浏览器兼容,从哪里开始您自己决定,但是,请认识DOCTYPE: 一.什么是DOCTYPE DOCTYPE是Documen

五种类型的程序员

我在代码之路上曾经遇到过很多奇怪的对手,也遇到过奇怪的队友.我至少接触了五种不同的"代码斗士".其中一些有才的战友有助于开发工作的进行,而另一些看起来阻碍了我的每一个计划. 然而,他们全都在软件开发的"万神殿"中拥有一席之地.如果不能将这些不同风格的程序员协调好的话,你会发现你的项目会花费很多时间.不够稳定或者代码难以读懂等问题. 补漏灵型 补好了! 该死,代码虽然不够完美,但是能工作就行了! 这种人是你公司的基础.当哪里出现差错的时候他会迅速的修补,在某种程度上,

块级标签与预格式化文本标签----------大多数XHTML可以表示为两种类型的标签:块标签(block tag)和内联标签(inline tag)

1 <html> 2 <head> 3 <meta charset="utf-8"> 4 <title>块级标签</title> 5 </head> 6 7 <body> 8 9 块级标签 10 就像标题.段落一样,需要在页面上占据一块的位置的标签. 11 <h1> 最大的标题 <h2> - <h6> 大小依次减小的标题 <p> 段落 12 13 <

设计模式之总结篇(设计模式六大原则以及设计模式三种类型)

经过这半年的学习,自己对设计模式这门课程也有了一定的认知.前面也发了关于23个设计模式的博客.现在对这些设计模式进行一个整体的总结. 首先,设计模式有六大原则: 原则一.单一职责原则 定义:There should never be more than one reason for a class to change(不要存在多于一个导致类变更的原因).通俗的说,即一个类只负责一项职责. 好处: 1.降低类的复杂度. 2.提高可读性. 3.提高可维护性. 4.变更引起的风险降低. 原则二.里氏替

Ext.js 中 25种类型的Ext.panel.Tool

通过Ext.panel.Panel的tools配置项来设置Ext.panel.Tool实例. 要注意的一点是,Ext框架提供的Ext.panel.Tool仅包含按钮图标而具体的点击事件处理函数需要我们自定义. ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51

陈松松:制作视频优先选择这5种类型,总有一个适合你

这是我写的第88篇原创视频营销文章 陈松松,6年视频营销实战经验 万事开头难! 就看谁先能挺住! 很多朋友发现制作视频也有很多类型,不知道选择哪种最适合自己,今天我就跟大家详细分享下,当你学习之后,你根据自己的情况去选择和深入学习,也相当于给了你一个参考方向: 第一种:套视频模板 难度系数:三星 只需要修改文字.替换掉自己的图片,一般这种模板类的视频在10秒-2分钟左右,只要你的视频模板多,都可以制作出非常多的视频. 虽然套视频模板的视频非常简单,任何没有基础的人都可以操作,你用哪种视频软件的模

swift中变量的几种类型

swift中变量分为 optional,non-optional 以及 implicitly unwrapped optional 这几种类型 var nullableProperty : AnyObject? // optional var nonNullProperty : AnyObject // non-optional var unannotatedProperty : AnyObject! // implicitly unwrapped optional 其中,optional (如

C#中方法的参数的四种类型

C#中方法的参数有四种类型: 1. 值参数类型  (不加任何修饰符,是默认的类型) 2. 引用型参数  (以ref 修饰符声明) 3. 输出型参数  (以out 修饰符声明) 4. 数组型参数  (以params 修饰符声明) =================================================== 1. 值传递: 值类型是方法默认的参数类型,采用的是值拷贝的方式.也就是说,如果使用的是值类型,则可以在方法中更改该值,但当控制传递回调用过程时,不会保留更改的值.使用

正则表达式中原子的5种类型

原子是正则表达式的最基本的组成单位,而且在每个模式中最少包含一个原子.原子是由所有那些未显示指定为元字符的打印和非打印字符组成,所以在这里将其详细划分为五类进行介绍. ①普通字符作为原子 普通字符是编写正则表达式时最常见的原子了,包括所有的大写和小写字母字符.所有数字等.例如,a——z.A——Z.0——9. ’/5/’        –用于匹配字符串中是否有5这个字符出现 ’/php/’      –用于匹配字符串中是否有PHP字符串出现 ②一些特殊字符和元字符作为原子 任何一个符号都可以作为原