文章出處

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祝威+悄悄在此留下版了個權的信息說:

文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()