博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
CSharpGL(46)用Billboard绘制头顶文字
阅读量:6888 次
发布时间:2019-06-27

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

CSharpGL(46)用Billboard绘制头顶文字

本文介绍CSharpGL用Billboard绘制头顶文字的方法。效果如下图所示。

下载

CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入()

 

固定大小的Billboard

在OpenGL的渲染流水线上,描述顶点位置的坐标,依次要经过object space, world space, view/camera space, clip space, normalized device space, Screen/window space这几个状态。下表列出了各个状态的特点。

Space

Coordinate

feature

object

(x, y, z, 1)

从模型中读取的原始位置(x,y,z),可在shader中编辑

world

(x, y, z, w)

可在shader中编辑

view/camera

(x, y, z, w)

可在shader中编辑

clip

(x, y, z, w)

vertex shader中,赋给gl_Position的值

normalized device

(x, y, z, 1)

上一步的(x, y, z, w)同时除以w。OpenGL自动完成。x, y, z的绝对值小于1时,此顶点在窗口可见范围内。即可见范围为[-1, -1, -1]到[1, 1, 1]。

screen/window

glViewport(x, y, width, height);

glDepthRange(near, far)

窗口左下角为(0, 0)。

上一步的顶点为(-1, -1, z)时,screen上的顶点为(x, y)。

上一步的顶点为(1, 1, z)时,screen上的顶点为(width, height)。

为了让Billboard保持他应有的位置深度值,object space, world space, view space这三步是必须照常进行的。

在normalized device space这个状态下,[-1,-1,-1]和[1,1,1]之间就是Billboard能显示出来的部分。例如,如果一个Billboard矩形的四个角落,恰好落在(-1,-1)和(1,1)上,那么这个Billboard就会恰好覆盖整个画布。所以,如果知道了Billboard和画布的尺寸(像素值),就可以按比例计算出Billboard在此状态时应有的尺寸了。

这两段分析就是下面的vertex shader的精髓。Billboard的位置,由一个位于矩形中心的表示。在object space里,这个点自然要位于(0, 0, 0, 1)。

1 #version 330 core 2  3 uniform mat4 projectionMatrix; 4 uniform mat4 viewMatrix; 5 uniform mat4 modelMatrix; 6 uniform vec2 screenSize; // screen size in pixels. 7  8 uniform float width; // Billboard’s width in pixels 9 uniform float height;// Billboard’s height in pixels.10 11 in vec2 inPosition;// character's quad's position relative to left bottom(0, 0).12 in vec3 inSTR;// character's quad's texture coordinate.13 14 out vec3 passSTR;15 16 void main(void) {17     vec4 position = projectionMatrix * viewMatrix * modelMatrix * vec4(0, 0, 0, 1);18     position = position / position.w;// 代替OpenGL pipeline除以w的步骤。19     position.xy += (inPosition * height - vec2(width, height)) / screenSize;20     gl_Position = position;21 22     passSTR = inSTR;23 }

绘制文字

首先,你要知道如何准备文字Texture()。

然后,根据给定的字符串Text,找到各个char的位置,更新positionBuffer和uvBuffer,更新Billboard的Width和Height。为了减少客户端的计算量,在安排char的位置时,是从左下角(0,0)开始,到右上角(width, height)结束的。不然,就该把char的位置整体移动到以(0,0)为中心了。

下图中,把一个一个字符围起来的框框,说明了文字是如何排列的。

多个Billboard的重叠问题

在Billboard中,为了显示文字,启用了OpenGL的混合(blend)功能。

1         public static TextBillboardNode Create(int width, int height, int capacity, GlyphServer glyphServer = null) 2         { 3             var vs = new VertexShader(vertexCode);// this vertex shader has no vertex attributes. 4             var fs = new FragmentShader(fragmentCode); 5             var provider = new ShaderArray(vs, fs); 6             var map = new AttributeMap(); 7             map.Add(inPosition, GlyphsModel.position); 8             map.Add(inSTR, GlyphsModel.STR); 9             // 启用混合功能10             var blendState = new BlendState(BlendingSourceFactor.SourceAlpha, BlendingDestinationFactor.OneMinusSourceAlpha);11             var builder = new RenderMethodBuilder(provider, map, blendState);12             var node = new TextBillboardNode(width, height, new GlyphsModel(capacity), builder, glyphServer);13             node.Initialize();14 15             return node;16         }

由于blend功能是与渲染顺序相关的(即渲染顺序不同,产生的结果就可能不同),所以在渲染多个Billboard时,就可能产生不好的效果:近处的Billboard可能遮挡住远处的。

为了解决这个问题,我想了一个办法:先按深度给各个Billboard排序,然后依序渲染各个Billboard。为此,需要新建一些东西。

排序动作BillboardSortAction

首先要将各个Billboard排序,并保存到数组。显然,在这里,使用二分插入排序是最快的排序方式。

1     public class BillboardSortAction : DependentActionBase 2     { 3         private List
depthList = new List
(); 4 private List
billboardList = new List
(); 5 6 ///
7 /// Sorted billboard list. 8 /// 9 public List
BillboardList10 {11 get { return billboardList; }12 }13 14 ///
15 /// Sort billboards in depth order.16 /// 17 ///
18 public BillboardSortAction(Scene scene) : base(scene) { }19 20 public override void Act()21 {22 this.depthList.Clear();23 this.billboardList.Clear();24 25 mat4 viewMatrix = this.Scene.Camera.GetViewMatrix();26 this.Sort(this.Scene.RootElement, viewMatrix);27 }28 29 private void Sort(SceneNodeBase sceneElement, mat4 viewMatrix)30 {31 if (sceneElement != null)32 {33 var billboard = sceneElement as TextBillboardNode;34 if (billboard != null)35 {36 Insert(billboard, viewMatrix);37 }38 39 foreach (var item in sceneElement.Children)40 {41 this.Sort(item, viewMatrix);42 }43 }44 }45 46 ///
47 /// binary insertion sort.48 /// 49 ///
50 ///
51 ///
52 private void Insert(TextBillboardNode billboard, mat4 viewMatrix)53 {54 // viewPosition.z is depth in view/camera space.55 vec3 viewPosition = billboard.GetAbsoluteViewPosition(viewMatrix);56 int left = 0, right = this.depthList.Count - 1;57 while (left <= right)58 {59 int middle = (left + right) / 2;60 float value = this.depthList[middle];61 if (value < viewPosition.z)62 {63 left = middle + 1;64 }65 else if (value == viewPosition.z)66 {67 left = middle;68 break;69 }70 else //(viewPosition.z < value)71 {72 right = middle - 1;73 }74 }75 76 this.depthList.Insert(left, viewPosition.z);77 this.billboardList.Insert(left, billboard);78 }79 }
BillboardSortAction

渲染动作BillboardRenderAction

虽然我们有专门的渲染动作RenderAction,但是RenderAction只会按结点的树结构顺次渲染。因此,我们要新建一个专门渲染已经排序好了的Billboard数组的动作。

1     ///  2     /// Render sorted billboards. 3     ///  4     public class BillboardRenderAction : DependentActionBase 5     { 6         private BillboardSortAction sortAction; 7         public BillboardRenderAction(Scene scene, BillboardSortAction sortAction) 8             : base(scene) 9         {10             this.sortAction = sortAction;11         }12 13         public override void Act()14         {15             var arg = new RenderEventArgs(this.Scene, this.Scene.Camera);16             foreach (var item in this.sortAction.BillboardList)17             {18                 item.RenderBeforeChildren(arg);19             }20         }21     }22 }

当然,不要忘了取消Billboard在RenderAction里的渲染动作。

1     var billboard = TextBillboardNode.Create(200, 40, 100);2     billboard.Text = string.Format("Hello TextBillboardNode[{0}]!", index);3     // we don't render it in RenderAction. we render it in BillboardRenderAction.4     billboard.EnableRendering = ThreeFlags.None;

总结

又一次,又一次,又一次,犯了很二的错误。

TextBillboardNode.cs是复制过来的,然后我就忘记了修改里面的AttributeMap的数据。原本2个小时就能完成的东西,花了2天才找到错误所在。

这个事情告诉我,即使很类似的代码,也不要复制过来。一点一点写才是最快的。

 

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

你可能感兴趣的文章
文件的拷贝
查看>>
使用MTL库求解矩阵特征值和特征向量
查看>>
UVa10006 Carmichael Numbers【素数判定+快速模幂】
查看>>
HDU1319 POJ1595 UVA406 UVALive5490 ZOJ1312 Prime Cuts【素数筛选+打表】
查看>>
【基础篇】DatePickerDialog日期控件的基本使用(二) ——分别获取年、月、日、时、分...
查看>>
理解 Android Build 系统
查看>>
hdu4632
查看>>
DotNetCore跨平台~功能测试TestHost的使用
查看>>
ASP.NET动态生成静态页面(C#)
查看>>
Python 数据清洗--处理Nan
查看>>
条件变量pthread_cond_wait()和pthread_cond_signal()详解
查看>>
内核中的多点触摸协议文档 Multi【转】
查看>>
linux下获取微秒级精度的时间【转】
查看>>
2012年1月,拥有131年历史的柯达申请破产
查看>>
一个常见的错误
查看>>
python基础学习-列表
查看>>
初学redis(1)--windows下安装redis
查看>>
纯Js ——文字上下左右滚动
查看>>
大学:自由学术的制度保障
查看>>
----uni-app之解密二维码----
查看>>