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

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

+BIT祝威+悄悄在此留下版了个权的信息说:

上一篇得到了字形贴图及其位置字典(可导出为XML)。本篇就利用此贴图和位置字典,把文字绘制到OpenGL窗口。

基本流程

+BIT祝威+悄悄在此留下版了个权的信息说:

有了贴图,绘制文字和绘制普通纹理的过程是一样的。我们需要用glTexImage2D设定纹理,然后用GLSL+VBO设置一个长方形,把纹理的某个字形所占据的位置贴到长方形上,就可以绘制一个字符。连续设置多个长方形,就可以显示字符串了。

当然,用legacy opengl里的glVertex和glTexCoord来设置长方形和贴图也可以,不过本文推荐用modern opengl的GLSL+VBO的方式来实现。

 

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

 

编辑GLSL

我们只需vertex shader和fragment shader。

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

 1 #version 120
 2 
 3 attribute vec3 in_Position;
 4 attribute vec2 in_TexCoord;
 5 varying vec2 texcoord;
 6 uniform mat4 projectionMatrix;
 7 uniform mat4 viewMatrix;
 8 uniform mat4 modelMatrix;
 9 
10 void main(void) {
11   gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(in_Position, 1);
12   texcoord = in_TexCoord;
13 }

 

Fragment shader根据纹理坐标所在位置的纹理颜色决定此位置是否显示(透明与否)。这就绘制出了一个字形。 

1 #version 120
2 
3 varying vec2 texcoord;
4 uniform sampler2D tex;
5 uniform vec4 color;
6 
7 void main(void) {
8   gl_FragColor = vec4(1, 1, 1, texture2D(tex, texcoord).r) * color;
9 }

设定VAO

每个字符的宽度是不同的,所以每个长方形都要据此调整宽度。下面是根据字符串生成VAO/VBO的片段。

 1         private void InitVAO(string value)
 2         {
 3             if (value == null) { value = string.Empty; }
 4 
 5             this.mode = PrimitiveModes.Quads;
 6             this.vertexCount = 4 * value.Length;
 7 
 8             //  Create a vertex buffer for the vertex data.
 9             UnmanagedArray<vec3> in_Position = new UnmanagedArray<vec3>(this.vertexCount);
10             UnmanagedArray<vec2> in_TexCoord = new UnmanagedArray<vec2>(this.vertexCount);
11             Bitmap bigBitmap = this.ttfTexture.BigBitmap;
12             vec3[] tmpPositions = new vec3[this.vertexCount];
13             float totalLength = 0;
14             for (int i = 0; i < value.Length; i++)
15             {
16                 char c = value[i];
17                 CharacterInfo cInfo;
18                 if (this.ttfTexture.CharInfoDict.TryGetValue(c, out cInfo))
19                 {
20                     float glyphWidth = (float)cInfo.width / (float)this.ttfTexture.FontHeight;
21                     if (i == 0)
22                     {
23                         tmpPositions[i * 4 + 0] = new vec3(0, 0, 0);
24                         tmpPositions[i * 4 + 1] = new vec3(glyphWidth, 0, 0);
25                         tmpPositions[i * 4 + 2] = new vec3(glyphWidth, 1, 0);
26                         tmpPositions[i * 4 + 3] = new vec3(0, 1, 0);
27                     }
28                     else
29                     {
30                         tmpPositions[i * 4 + 0] = tmpPositions[i * 4 + 0 - 4 + 1];
31                         tmpPositions[i * 4 + 1] = tmpPositions[i * 4 + 0] + new vec3(glyphWidth, 0, 0);
32                         tmpPositions[i * 4 + 3] = tmpPositions[i * 4 + 3 - 4 - 1];
33                         tmpPositions[i * 4 + 2] = tmpPositions[i * 4 + 3] + new vec3(glyphWidth, 0, 0);
34                     }
35                     totalLength += glyphWidth;
36                 }
37 
38             }
39             for (int i = 0; i < value.Length; i++)
40             {
41                 char c = value[i];
42                 CharacterInfo cInfo;
43                 float x1 = 0;
44                 float x2 = 1;
45                 float y1 = 0;
46                 float y2 = 1;
47                 if (this.ttfTexture.CharInfoDict.TryGetValue(c, out cInfo))
48                 {
49                     x1 = (float)cInfo.xoffset / (float)bigBitmap.Width;
50                     x2 = (float)(cInfo.xoffset + cInfo.width) / (float)bigBitmap.Width;
51                     y1 = (float)cInfo.yoffset / (float)bigBitmap.Height;
52                     y2 = (float)(cInfo.yoffset + this.ttfTexture.FontHeight) / (float)bigBitmap.Height;
53                 }
54 
55                 in_Position[i * 4 + 0] = tmpPositions[i * 4 + 0] - new vec3(totalLength / 2, 0, 0);
56                 in_Position[i * 4 + 1] = tmpPositions[i * 4 + 1] - new vec3(totalLength / 2, 0, 0);
57                 in_Position[i * 4 + 2] = tmpPositions[i * 4 + 2] - new vec3(totalLength / 2, 0, 0);
58                 in_Position[i * 4 + 3] = tmpPositions[i * 4 + 3] - new vec3(totalLength / 2, 0, 0);
59 
60                 in_TexCoord[i * 4 + 0] = new vec2(x1, y2);
61                 in_TexCoord[i * 4 + 1] = new vec2(x2, y2);
62                 in_TexCoord[i * 4 + 2] = new vec2(x2, y1);
63                 in_TexCoord[i * 4 + 3] = new vec2(x1, y1);
64             }
65 
66             GL.GenVertexArrays(1, vao);
67             GL.BindVertexArray(vao[0]);
68 
69             GL.GenBuffers(2, vbo);
70 
71             uint in_PositionLocation = shaderProgram.GetAttributeLocation(strin_Position);
72             GL.BindBuffer(BufferTarget.ArrayBuffer, vbo[0]);
73             GL.BufferData(BufferTarget.ArrayBuffer, in_Position, BufferUsage.StaticDraw);
74             GL.VertexAttribPointer(in_PositionLocation, 3, GL.GL_FLOAT, false, 0, IntPtr.Zero);
75             GL.EnableVertexAttribArray(in_PositionLocation);
76 
77             uint in_TexCoordLocation = shaderProgram.GetAttributeLocation(strin_TexCoord);
78             GL.BindBuffer(BufferTarget.ArrayBuffer, vbo[1]);
79             GL.BufferData(BufferTarget.ArrayBuffer, in_TexCoord, BufferUsage.StaticDraw);
80             GL.VertexAttribPointer(in_TexCoordLocation, 2, GL.GL_FLOAT, false, 0, IntPtr.Zero);
81             GL.EnableVertexAttribArray(in_TexCoordLocation);
82 
83             GL.BindVertexArray(0);
84 
85             in_Position.Dispose();
86             in_TexCoord.Dispose();
87         }
根据字符串生成VAO/VBO

 

其它 

在上一篇,我们通过TTF文件得到了贴图文件及其位置信息(XML文件)。此时其实不再需要借助freetype就可以直接使用这些贴图了。

另外,本文所给的demo已经包含了perspective和ortho两种透视的camera功能,固定在窗口左下角显示坐标系的功能,感兴趣的话通过反编译即可得到。

 

总结

现在能够绘制文字了,但是换行之类的高级功能还没有实现。这已经不熟悉opengl的研究范围,而是更高层的功能了,所以暂时不再深入考虑。

+BIT祝威+悄悄在此留下版了个权的信息说:
posted @ 2015-08-15 16:31  BIT祝威  阅读(2748)  评论(0编辑  收藏  举报