博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
CSharpGL(15)用GLSL渲染2种类型的文字
阅读量:6224 次
发布时间:2019-06-21

本文共 14682 字,大约阅读时间需要 48 分钟。

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

2016-08-13

由于CSharpGL一直在更新,现在这个教程已经不适用最新的代码了。CSharpGL源码中包含10多个独立的Demo,更适合入门参考。

为了尽可能提升渲染效率,CSharpGL是面向Shader的,因此稍有难度。

下载

这个示例是CSharpGL的一部分,CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入()

血条

玩家头顶的血条、名字随着玩家在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         ///  2         ///  3         ///  4         /// camera的Position和Target之间的距离 5         /// 血条高度 6         /// 
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上就行了。

贴图

所以要准备好贴图。这一步我已经在()详细叙述过了。

模型

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

1     ///   2     /// 用于渲染一段文字  3     ///   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 GlyphTexCoord119         {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
139 {140 public PositionBuffer(string varNameInShader)141 : base(varNameInShader, 2, GL.GL_FLOAT, BufferUsage.StaticDraw)142 { }143 }144 class ColorBuffer : PropertyBuffer
145 {146 public ColorBuffer(string varNameInShader)147 : base(varNameInShader, 4, GL.GL_FLOAT, BufferUsage.StaticDraw)148 { }149 }150 151 class TexCoordBuffer : PropertyBuffer
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     else16     {17         outputColor = vec4(1, 1, 1, transparency) * passColor;18     }19 }

根据字符串创建模型

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

1     public static class DummyStringModelFactory 2     { 3         ///  4         /// 简单地生成一行文字。 5         ///  6         ///  7         /// 
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 1519 * ------- ------- ------- -------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 center45 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的基础上,只需借助即可实现opengl里的标签控件。

1     public class DummyLabel : RendererBase, IUILayout 2     { 3         public StringRenderer renderer; 4  5         ///  6         ///  7         ///  8         /// the edges of the viewport to which a SimpleUIRect is bound and determines how it is resized with its parent. 9         /// 
something like AnchorStyles.Left | AnchorStyles.Bottom.
10 /// 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 IUILayout25 26 public IUILayoutParam Param { get; set; }27 28 #endregion IUILayout29 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 }

总结

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

 

转载地址:http://apyna.baihongyu.com/

你可能感兴趣的文章
选项卡
查看>>
【深度学习】一文读懂机器学习常用损失函数(Loss Function)
查看>>
php判断数据类型
查看>>
css 定位标签设置格式
查看>>
js-实现鼠标滑轮滚动实现换页
查看>>
windows下通过.bat运行java程序
查看>>
Zabbix二次开发_01基础
查看>>
几何画板演示多边形滚动动画的方法
查看>>
指令处理的数据的长度
查看>>
短信验证倒计时
查看>>
iOS11里判断Safari浏览器是无痕模式还是正常模式?
查看>>
Vue.js 与 ActiveX 控件
查看>>
DVWA学习笔记
查看>>
C语言
查看>>
匈牙利表示法
查看>>
hiho一下115周 网络流
查看>>
python之装饰器
查看>>
java笔记高级部分
查看>>
PostgreSQL 查看单表大小
查看>>
apex透视自瞄无后子弹追踪飞天加速辅助
查看>>