CSharpGL(45)自制控件的思路

本文介绍CSharpGL实现自制控件的方法。

所谓自制控件,就是用纯OpenGL模仿WinForm里的Button、Label、TextBox、CheckBox等控件,支持布局、修改大小和文字等功能。

 

如上图所示,左下角就是一个显示二维图片的类似PictureBox的控件,我称之为CtrlImage。(所有的CSharpGL自制控件类型,都继承自GLControl,都添加前缀Ctrl)CtrlImage上方分别是一个CtrlButton和一个CtrlLabel。

下载

CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL

 

控件是什么?

一个控件,最基本的属性包括这几条:隶属关系(Parent、Children),布局属性(Location、Size、Anchor),渲染(Initialize、Render、BackgroundColor等),事件(Click、Resize等)。

隶属关系

WinForm里的控件们构成了一个形关系,CSharpGL也是这样。有了这样的隶属关系,就可以以相对于Parent的位置来记录自己的位置。

而且,当我做好了CtrlLabel,就可以直接放到CtrlButton.Children里,于是CtrlButton上显示文字的功能就瞬间实现了(当然还要设置一下文字位置,但工作量已经可以忽略不计了)。

  View Code

布局属性

首先要有Location和Size。然后,在Parent的Size改变时,自己要相应的改变Location和Size,那么就需要Anchor来指定“是不是维持与某一边的距离不变”。

如何计算自己更新后的Location和Size?这是个简单的算法问题。

  View Code

为了降低对.net库的依赖,我根据.net自带的Size、Point、Anchor等基础的数据结构,复制了GUISize、GUIPoint、GUIAnchor……

渲染

用OpenGL渲染控件,实际上是如何在固定位置以固定大小画图的问题。

在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)。

根据上表来看,object space, world space, view space三步可以省略跳过,而normalized device space是无法跳过的,所以我们在shader中给控件指定的坐标,就应该在[-1,-1,-1]和[1,1,1]之间。然后通过glViewport(x, y, width, height);指定控件的位置(x, y)和大小(width, height)。

为了避免影响到控件范围外的东西,要启用GL_SCISSOR_TEST

复制代码
 1 using System;
 2 
 3 namespace CSharpGL
 4 {
 5     public abstract partial class GLControl
 6     {
 7         public virtual void RenderGUIBeforeChildren(GUIRenderEventArgs arg)
 8         {
 9             GL.Instance.Enable(GL.GL_SCISSOR_TEST);
10             GL.Instance.Scissor(this.absLeft, this.absBottom, this.width, this.height);
11             GL.Instance.Viewport(this.absLeft, this.absBottom, this.width, this.height);
12 
13             if (this.RenderBackground)
14             {
15                 vec4 color = this.BackgroundColor;
16                 GL.Instance.ClearColor(color.x, color.y, color.z, color.w);
17                 GL.Instance.Clear(GL.GL_COLOR_BUFFER_BIT);
18             }
19         }
20     }
21 }
复制代码

事件

事件这个东西太复杂,我们来一点一点的说清楚其设计思路。

WinGLCanvas是一个WinForm控件,所http://blog.sina.com.cn/u/6272335754
http://blog.sina.com.cn/s/blog_175dc3f8a0102xgr6.html
http://blog.sina.com.cn/s/blog_175dc3f8a0102xgr5.html
http://blog.sina.com.cn/s/blog_175dc3f8a0102xgpo.html
http://blog.sina.com.cn/s/blog_175dc3f8a0102xgpp.html
http://blog.sina.com.cn/s/blog_175dc3f8a0102xgpr.html
http://blog.sina.com.cn/s/blog_175dc3f8a0102xgqa.html
http://blog.sina.com.cn/s/blog_175dc3f8a0102xgqb.html
http://blog.sina.com.cn/s/blog_175dc3f8a0102xgqc.html
http://blog.sina.com.cn/s/blog_175dc3f8a0102xgr2.html
http://blog.sina.com.cn/s/blog_175dc3f8a0102xgr4.html有的OpenGL渲染的内容都在此显示。

当我的WinForm控件WinGLCanvas收到一个消息(以鼠标按下mouse down为例)时,他会遍历所有的GLControl,告诉他们“有mouse down消息来了”。每个控件都会调用自己关联的mouseDown事件(如果有的话)。

然而细想一下,只有鼠标所在位置的那个GLControl才应该响应mouse Down消息。所以,在WinGLCanvas遍历GLControl时,要分辨出哪个控件在mouse Down的位置,然后通知它;不通知其他控件。类似的,只有得到Focus的控件才会收到key down消息,从而调用自己的KeyDown事件。

绘制文字

做一个CtrlLabel,核心工作就是要在指定的位置绘制文字。

大致思路是这样的:

首先,做出这样的文字贴图。当要绘制的文字比较多的时候,就会出现不止一张贴图。这里为了便于演示,我故意把贴图尺寸设定得比较小,从而出现了第二张贴图;并且用金色边框把贴图的边沿描绘出来,用红色或绿色边框把各个Glyph的位置和大小都表示出来。

 

 

然后,用OpenGL创建一个GL_TEXTURE_2D_ARRAY的纹理Texture,把上面这些贴图都放进去。

最后,用一个Dictionary<char, GlyphInfo>字典记录每个字符的字形信息(在Texture中的位置、大小)。

思路就这三步,详情直接看代码比较好(GlyphMap)。

需要注意的是,之前规定了“控件的顶点范围应该在[-1,-1,-1]和[1,1,1]之间”。所以在给CtrlLabel设定好各个字形的位置后,还要按比例缩放到[-1,-1,-1]和[1,1,1]中,并且调整CtrlLabel的Width属性。这样才能正常显示文字。这其实就是WinForm里的Label的AutoSize=true。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值