使用 OpenTK 在 C# 中设计的模拟时钟
一个使用 C# 中的 OpenTK 和 WinForms 设计的 2D 模拟时钟。
引言
Open Toolkit 是一个高级的、底层的 C# 库,它封装了 OpenGL、OpenCL 和 OpenAL。它适用于游戏、科学应用程序以及任何需要 3D 图形、音频或计算功能的项目。简而言之,它被称为 OpenTK。
背景
它不是 OpenGL 的快速 C# 实现,但它是最好的。以前有一些,比如 CSGL、TAO framework 等,但它们不完整并受到一些限制。而现在,它们已经完全无法跟上 .NET Framework 的步伐了。
我们可以通过 GL 类调用每一个 OpenGL 函数,就像 OpenGL 中使用的 glVertex2f(-2.0,-2.0)
在 OpenTk 中被用作 GL.Vertex2(-2.0,-2.0)
。它拥有一个非常漂亮且灵活的 GUI 选项,跨平台的 GLControl (Windows.Forms) 可以轻松添加到 Visual Studio 工具箱中,GLWidget 是另一个丰富且有用的组件 (用于 GTK#)以及 WPFControl
类。还有一个原生、高性能的 GameWindow,它是专门为游戏设计的。你可以比你想象的更快地开发游戏。
它还拥有一个非常有用的 API 集合,例如 3D Math Toolkit,它提供了 Vector、Matrix、Quaternion 和 Bezier 结构。 Input API 提供了键盘、鼠标和操纵杆接口。 Display API 帮助处理多显示器。OpenTK.Compatibility
支持 TAO framework 应用程序。
如果你想考虑平台独立性,它支持 Windows、Linux 和 Mac OS X 的 32 位和 64 位版本。无需混乱的库 - 编译一次,随处运行。最棒的是,你可以自由使用、修改和分发 源代码。它适用于开源和闭源项目。
使用代码
你不需要任何 OpenGL 或 C# 图形方面的先验知识来学习 OpenTK。只需要一些 Windows Form 设计的基础知识就足够了。
要使用 OpenTK,你需要在 Visual Studio 中添加两个 DLL 作为引用。它们是 OpenTK.dll 和 OpenTK.Graphics.OpenGL.dll。两者都可以在这里轻松找到。
我们不打算使用众所周知的游戏窗口作为 OpenTK 的窗口,因为我猜你对 Windows Form 设计很熟悉。我们只是使用一个普通的 Windows Form。OpenTK 提供了一个非常好的控件/工具。为此,你需要将它添加到 Visual Studio 工具箱中,它叫做 Glcontrol
,你可以在 这里找到。为此,首先点击工具箱并选择“项”,然后浏览并添加这个控件的 DLL。现在你就可以使用它了。
首先,创建一个 Form,你将在上面放置你的 GLControl
。在工具箱的空白处右键单击,选择“选择项…”,然后浏览 OpenTK.GLControl.dll。确保你可以在 .NET Framework 组件中找到 GLControl
,如下图所示。
然后,你可以像添加任何 .NET 控件一样将 GLControl
添加到你的 Form 中。一个名为 glControl1
的 GLControl
将被添加到你的 Form
中。
所以,首先将这个命名为 glControl1
的控件添加到你的端口,并编写下面的代码。还要将此方法添加到此控件的加载事件中。有关更多详细信息,你可以 点击这里。
private void glControl1_Resize(object sender, EventArgs e)
{
int w = glControl1.Width;
int h = glControl1.Height;
glControl1.MakeCurrent();
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.ClearColor(Color.SkyBlue);
GL.Ortho(-w / 2, w / 2, -h / 2, h / 2, -1, 1);
GL.Viewport(0, 0, w, h);
GL.End();
glControl1.SwapBuffers();
}
如果我们运行它,我们会发现一个如下所示的窗口
那么这是怎么实现的呢?很简单。首先,我们看到
glControl1.MakeCurrent();
它使接下来所有针对此控件的 GL 命令都生效。
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
我们将矩阵模式设置为投影,然后加载单位矩阵。对于 2D 投影,有四种模式,如果你想了解更多关于这些模式的信息,你可以 点击这里查看。
下一个命令非常重要,需要理解。为此,我们首先需要成为好的 OpenGL 开发者,并使用 GL.Ortho()
设置一个正射投影矩阵。我们还需要调用 GL.Viewport()
。
GL.Ortho(-w / 2, w / 2, -h / 2, h / 2, -1, 1);
这使得 GL 框的中心在 x、y 轴上为 0,0。因为 w 是 GL 框的宽度,h 是高度。如果你想让左下角像素点为 0,0,你可以这样写
GL.Ortho(0, w, 0, h, -1, 1)
GL.Viewport(0, 0, w, h);
Viewport
用于选择控件中的绘图区域。
GL.ClearColor(Color.SkyBlue);
它将其设置为蓝色。为了命令的清理和窗口缓冲区,我们必须写
GL.End();
glControl1.SwapBuffers();
现在,首先,我们必须在我们的 glcontrol
中绘制一个圆。对于 draw
方法,我们必须使用 glcontrol
中 paint
事件的 paint
方法,如下所示
private void glControl1_Paint(object sender, PaintEventArgs e)
{
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit)
drawclock();
glControl1.SwapBuffers();
}
void Draw_clock()
{
drawCircle(80);//80 is radius of the circle
Draw_digit();
}
对于 drawcircle
,我使用了以下代码
void drawCircle(float radius)
{
GL.Color3(Color.White);
GL.Begin(BeginMode.TriangleFan);
for (int i = 0; i < 360; i++)
{
double degInRad = i * 3.1416/180;
GL.Vertex2(Math.Cos(degInRad) * radius, Math.Sin(degInRad) * radius);
}
GL.End;
}
现在,看起来一切都如此轻松。
GL.Begin(BeginMode.TriangleFan);
此模式使用提供的顶点绘制一个三角形并用白色填充。所有这些三角形构成了圆,就像三角形披萨块组成了一个圆形的披萨 。
如果你想了解 GL.Begin
,请参阅 这里。你必须记住在 GL.Begin();
之后写 GL.End();
否则什么也不会发生,编译器也不会给你任何错误。现在我们需要绘制一个数字以及分钟和小时的两条线。
编写简单的代码来进行普通的顶点操作。记住圆的半径是 80。
void Draw_digit()
{
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Color3(Color.Red);
//for hour
GL.Begin(BeginMode.TriangleFan);
GL.Vertex2(0, +5);
GL.Vertex2(0, -5);
GL.Vertex2(70, 0);
GL.Vertex2(70, 0);
GL.Color3(Color.Red);
GL.End();
//for minute
GL.Begin(BeginMode.TriangleFan);
GL.Vertex2(+5, 0);
GL.Vertex2(-5, 0);
GL.Vertex2(-65, 40);
GL.Vertex2(-65, 40);
GL.End();
GL.Color3(Color.Black);
//for draw digit III
GL.Begin(BeginMode.Lines);
GL.Vertex2(5, 60);
GL.Vertex2(5, 70);
GL.Vertex2(0, 60);
GL.Vertex2(0, 70);
GL.Vertex2(-5, 70);
GL.Vertex2(-15, 60);
GL.Vertex2(-15, 70);
GL.Vertex2(-5, 60);
GL.End();
GL.Color3(Color.Black);
//for draw digit XII
GL.Begin(BeginMode.Lines);
GL.Vertex2(60,0);
GL.Vertex2(60,8);
GL.Vertex2(70,0);
GL.Vertex2(70,8);
GL.Vertex2(65, 0);
GL.Vertex2(65, 8);
GL.End();
GL.Color3(Color.Black);
//for draw digit IV
GL.Begin(BeginMode.Lines);
GL.Vertex2(10, -60);
GL.Vertex2(10, -70);
GL.Vertex2(0, -60);
GL.Vertex2(0, -70);
GL.Vertex2(5, -60);
GL.Vertex2(0, -70);
GL.Vertex2(5, -60);
GL.Vertex2(0, -70);
GL.End();
GL.Color3(Color.Black);
//for draw digit IX
GL.Begin(BeginMode.Lines);
GL.Vertex2(-75,-5);
GL.Vertex2(-75,-15);
GL.Vertex2( -70,-5);
GL.Vertex2(-60,-15);
GL.Vertex2(- 70,-15);
GL.Vertex2(-60,-5);
}
这是连接一个顶点到另一个顶点的代码。有关详细信息,你可以 这里查看类似的 OpenGL 函数。
绘制完成后,我们看到输出窗口如下所示

现在我们需要为此创建一个计时器事件,所以首先添加一个计时器,将其间隔设置为 1000(1秒),启用它,然后将此事件添加到 timer tick 中
private void timer1_Tick(object sender, EventArgs e)
{
glControl2.MakeCurrent();
PaintEventArgs p = null;
glControl2_Paint(sender,p);
GL.End();
}
现在 paint
方法将在 1 秒后被调用。你需要在那里绘制秒针,所以 paint
事件将是
private void glControl2_Paint(object sender, PaintEventArgs e)
{
glControl2.MakeCurrent();
GL.End();
glControl2.SwapBuffers();
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
Draw_clock();
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.End();
glControl2.SwapBuffers();
drawsecond();
glControl2.SwapBuffers();
}
这里绘制秒针需要一个 static
变量。为了改变秒针顶点位置的坐标,我们取
static int i = 0;
现在是 drawsecond()
函数
void drawsecond()
{
GL.Color3(Color.Red);
GL.Begin(BeginMode.Quads);
GL.Vertex2(5, 0);
GL.Vertex2(-5, 5);
double degInRad = i * 3.1416 / 180;
GL.Vertex2(Math.Cos(degInRad) * 80, Math.Sin(degInRad) * 80);
GL.Vertex2(Math.Cos(degInRad) * 85, Math.Sin(degInRad) * 85);
i = i - 6;
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.End();
}
这里 static
变量被减去是为了顺时针旋转,并减去 6,因为 360/6 等于 60,我们需要这个来圆整圆。
所以秒针正在旋转
关注点
对于 C#,我确信你找不到比 OpenTK 更好的 OpenGL 替代品了。但是,如果你想开发一个真正专业的游戏,我必须告诉你,你走的不是正确的方向。C++ 比 C# 更适合。如果你想直接用 C# 进行开发,请使用 DirectX 或 XNA。
历史
- 2010 年 12 月 23 日:初次发布。