CSharpGL(46)用Billboard绘制头顶文字
CSharpGL(46)用Billboard绘制头顶文字
本文介绍CSharpGL用Billboard绘制头顶文字的方法。效果如下图所示。
下载
CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL)
固定大小的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)。
- #version core
- uniform mat4 projectionMatrix;
- uniform mat4 viewMatrix;
- uniform mat4 modelMatrix;
- uniform vec2 screenSize; // screen size in pixels.
- uniform float width; // Billboard’s width in pixels
- uniform float height;// Billboard’s height in pixels.
- in vec2 inPosition;// character's quad's position relative to left bottom(0, 0).
- in vec3 inSTR;// character's quad's texture coordinate.
- out vec3 passSTR;
- void main(void) {
- vec4 position = projectionMatrix * viewMatrix * modelMatrix * vec4(, , , );
- position = position / position.w;// 代替OpenGL pipeline除以w的步骤。
- position.xy += (inPosition * height - vec2(width, height)) / screenSize;
- gl_Position = position;
- passSTR = inSTR;
- }
绘制文字
首先,你要知道如何准备文字Texture(参考这里)。
然后,根据给定的字符串Text,找到各个char的位置,更新positionBuffer和uvBuffer,更新Billboard的Width和Height。为了减少客户端的计算量,在安排char的位置时,是从左下角(0,0)开始,到右上角(width, height)结束的。不然,就该把char的位置整体移动到以(0,0)为中心了。
下图中,把一个一个字符围起来的框框,说明了文字是如何排列的。
多个Billboard的重叠问题
在Billboard中,为了显示文字,启用了OpenGL的混合(blend)功能。
- public static TextBillboardNode Create(int width, int height, int capacity, GlyphServer glyphServer = null)
- {
- var vs = new VertexShader(vertexCode);// this vertex shader has no vertex attributes.
- var fs = new FragmentShader(fragmentCode);
- var provider = new ShaderArray(vs, fs);
- var map = new AttributeMap();
- map.Add(inPosition, GlyphsModel.position);
- map.Add(inSTR, GlyphsModel.STR);
- // 启用混合功能
- var blendState = new BlendState(BlendingSourceFactor.SourceAlpha, BlendingDestinationFactor.OneMinusSourceAlpha);
- var builder = new RenderMethodBuilder(provider, map, blendState);
- var node = new TextBillboardNode(width, height, new GlyphsModel(capacity), builder, glyphServer);
- node.Initialize();
- return node;
- }
由于blend功能是与渲染顺序相关的(即渲染顺序不同,产生的结果就可能不同),所以在渲染多个Billboard时,就可能产生不好的效果:近处的Billboard可能遮挡住远处的。
为了解决这个问题,我想了一个办法:先按深度给各个Billboard排序,然后依序渲染各个Billboard。为此,需要新建一些东西。
排序动作BillboardSortAction
首先要将各个Billboard排序,并保存到数组。显然,在这里,使用二分插入排序是最快的排序方式。
- public class BillboardSortAction : DependentActionBase
- {
- private List<float> depthList = new List<float>();
- private List<TextBillboardNode> billboardList = new List<TextBillboardNode>();
- /// <summary>
- /// Sorted billboard list.
- /// </summary>
- public List<TextBillboardNode> BillboardList
- {
- get { return billboardList; }
- }
- /// <summary>
- /// Sort billboards in depth order.
- /// </summary>
- /// <param name="scene"></param>
- public BillboardSortAction(Scene scene) : base(scene) { }
- public override void Act()
- {
- this.depthList.Clear();
- this.billboardList.Clear();
- mat4 viewMatrix = this.Scene.Camera.GetViewMatrix();
- this.Sort(this.Scene.RootElement, viewMatrix);
- }
- private void Sort(SceneNodeBase sceneElement, mat4 viewMatrix)
- {
- if (sceneElement != null)
- {
- var billboard = sceneElement as TextBillboardNode;
- if (billboard != null)
- {
- Insert(billboard, viewMatrix);
- }
- foreach (var item in sceneElement.Children)
- {
- this.Sort(item, viewMatrix);
- }
- }
- }
- /// <summary>
- /// binary insertion sort.
- /// </summary>
- /// <param name="billboard"></param>
- /// <param name="camera"></param>
- /// <param name="list"></param>
- private void Insert(TextBillboardNode billboard, mat4 viewMatrix)
- {
- // viewPosition.z is depth in view/camera space.
- vec3 viewPosition = billboard.GetAbsoluteViewPosition(viewMatrix);
- int left = , right = this.depthList.Count - ;
- while (left <= right)
- {
- int middle = (left + right) / ;
- float value = this.depthList[middle];
- if (value < viewPosition.z)
- {
- left = middle + ;
- }
- else if (value == viewPosition.z)
- {
- left = middle;
- break;
- }
- else //(viewPosition.z < value)
- {
- right = middle - ;
- }
- }
- this.depthList.Insert(left, viewPosition.z);
- this.billboardList.Insert(left, billboard);
- }
- }
BillboardSortAction
渲染动作BillboardRenderAction
虽然我们有专门的渲染动作RenderAction,但是RenderAction只会按结点的树结构顺次渲染。因此,我们要新建一个专门渲染已经排序好了的Billboard数组的动作。
- /// <summary>
- /// Render sorted billboards.
- /// </summary>
- public class BillboardRenderAction : DependentActionBase
- {
- private BillboardSortAction sortAction;
- public BillboardRenderAction(Scene scene, BillboardSortAction sortAction)
- : base(scene)
- {
- this.sortAction = sortAction;
- }
- public override void Act()
- {
- var arg = new RenderEventArgs(this.Scene, this.Scene.Camera);
- foreach (var item in this.sortAction.BillboardList)
- {
- item.RenderBeforeChildren(arg);
- }
- }
- }
- }
当然,不要忘了取消Billboard在RenderAction里的渲染动作。
- var billboard = TextBillboardNode.Create(, , );
- billboard.Text = string.Format("Hello TextBillboardNode[{0}]!", index);
- // we don't render it in RenderAction. we render it in BillboardRenderAction.
- billboard.EnableRendering = ThreeFlags.None;
总结
又一次,又一次,又一次,犯了很二的错误。
TextBillboardNode.cs是复制过来的,然后我就忘记了修改里面的AttributeMap的数据。原本2个小时就能完成的东西,花了2天才找到错误所在。
这个事情告诉我,即使很类似的代码,也不要复制过来。一点一点写才是最快的。
CSharpGL(46)用Billboard绘制头顶文字的更多相关文章
- SharpGL(46)用Billboard绘制头顶文字
CSharpGL(46)用Billboard绘制头顶文字 本文介绍CSharpGL用Billboard绘制头顶文字的方法.效果如下图所示. 下载 CSharpGL已在GitHub开源,欢迎对OpenG ...
- 在Arcscene绘制管线三维横断面(AE绘制三维点阵文字)
根据数据信息动态生成三维管线及横断面表格.效果图如下: 在获取信息后,直接构造点阵进行文字绘制即可. 绘制IElement代码: /// <summary> /// 绘制三维文字 /// ...
- 使用 NGUI 实现头顶文字及血条
以下是 NGUI HUD Text 实现的: 基本原理: 1. 在角色头顶绑一个点 Pivot,用于对齐 2. 因为界面总是覆盖在人物头顶信息的上面,所以将 UIRoot 分为2个 Panel:1) ...
- 测试canvas绘制旋转文字的性能
canvas 绘制各种动画效果时,我们经常会使用画布旋转,使绘制上去的元素有旋转的效果. 最近在项目中碰到了很严重的性能问题,经常排查发现是因为绘制批量文字时使用了画布旋转,且每行文字的旋转角度是不一 ...
- canvas一周一练 -- canvas绘制立体文字(2)
运行效果: <!DOCTYPE html> <html> <head> </head> <body> <canvas id=" ...
- Canvas里绘制矩阵文字
效果如下 实现方法: [ [0,0,1,1,1,0,0], [0,1,1,0,1,1,0], [1,1,0,0,0,1,1], [1,1,0,0,0,1,1], [1,1,0,0,0,1,1], [1 ...
- C#利用GDI+绘制旋转文字等效果
C#中利用GDI+绘制旋转文本的文字,网上有很多资料,基本都使用矩阵旋转的方式实现.但基本都只提及按点旋转,若要实现在矩形范围内旋转文本,资料较少.经过琢磨,可以将矩形内旋转转化为按点旋转,不过需要经 ...
- 基本形状的绘制&添加文字
本次用opencv在图像上绘制了线,矩形,椭圆,圆的形状和放置了文字. #include<iostream> using namespace std; using namespace cv ...
- CAD绘制单行文字(网页版)
在CAD设计时,需要绘制文字,用户可以设置设置绘制文字的高度等属性. 主要用到函数说明: _DMxDrawX::DrawText 绘制一个单行文字.详细说明如下: 参数 说明 DOUBLE dPosX ...
随机推荐
- .NET开发微信小程序-Template模块开发
1.添加一个文件目录,里面放模板信息 例:我在根目录添加一个文件夹:template 然后在这个文件夹下面添加相应的页面.比如我添加一个promodel.wxml文件.主要是放商品相关的模块信息(注: ...
- C语言 > 字符串和字符串函数
输入 gets() 函数 : 1.gets() 从标准输入设备读取字符串,以回车结束读取,使用'\0'结尾,回车符'\n'被舍弃没有遗留在缓冲区. 2.可以用来输入带空格的字符串. 3.可以无限读取, ...
- define 的全部使用方法
typedef的总结,以下是引用的内容(红色部分是我自己写的内容). 用途一: 定义一种类型的别名,而不只是简单的宏替换.可以用作同时声明指针型的多个对象.比如: char* pa, pb; // 这 ...
- Alfred效率神器
下图就是Alfred的主界面我们所有的操作都在这一个界面上进行.通过热键打开主界面(本人设置的是option+command),输入一个"a"后Alfred就会为我在候选界面上显示 ...
- .NET面试常考算法
1.求质数 质数也成为素数,质数就是这个数除了1和他本身两个因数以外,没有其他因数的数,叫做质数,和他相反的是合数, 就是除了1和他本身两个因数以外,还友其他因数的数叫做合数. 1 nam ...
- mybatis整合spring获取配置文件信息出错
描述:mybatis整合spring加载jdbc.properties文件,然后使用里面配置的值来 配置数据源,后来发现用户变成了admin- jdbc.properties的配置: 加载配置: 报错 ...
- 在centos,docker中安装HeadlessChrome
在centos6中安装chrome与chrome-driver,中间走了很多弯路,遇到很多坑,现将详细步骤总结如下.参考博客链接:https://blog.csdn.net/u013849486/ar ...
- QM3_Statistics Concepts and Market Returns
Basic Concepts Terms Descriptive Statistics Describes the important aspects of large data sets. 统计 概 ...
- maven国内镜像(国内oschina的maven服务器关了)
Maven是官方的库在国外,连下载速度很慢.国内oschina的maven服务器很早之前就关了.今天发现阿里云的一个中央仓库,亲测可用. 找到E:\maven\apache-maven-3.5.2\c ...
- 【NOIP模拟赛】随
题目链接: 172.18.111.252:800/problem.php?cid=1001&pid=0 题解: 膜达神……(NOIP考这个就等爆零吧……) 显然我们得到一个结论:$ans=\s ...