OpenTK是一个开源的C#工具包,它封装了OpenGL、OpenCL和OpenAL。它适用于游戏、科学应用以及任何需要3D图形、音频或计算功能的项目。简而言之,它被称为OpenTK。
OpenTK不仅仅是一个快速的C#实现OpenGL,它是最好的。尽管之前有一些如CSGL、TAO框架等实现,但它们是不完整的,并且受到一些限制。现在,它们完全无法跟上.NET Framework的发展。
在OpenTK中,每个OpenGL函数都可以通过GL类调用,例如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数学工具包,提供Vector、Matrix、Quaternion和Bezier结构。输入API提供键盘、鼠标和游戏杆接口。显示API有助于多显示器支持。OpenTK.Compatibility支持TAO框架应用程序。
如果想要思考平台独立性,它支持Windows、Linux和Mac OS X的32位和64位版本。不需要误管理库——一次编译,到处运行。而且,可以自由使用、修改和重新分发源代码。它适合开源和闭源项目。
使用代码,不需要有任何OpenGL或C#图形的先验知识来学习OpenTK。只需要一些Windows表单设计的基础知识就足够了。
要使用OpenTK,需要在Visual Studio引用中添加两个DLL:OpenTK.dll和OpenTK.Graphics.OpenGL.dll。两者都可以在这里轻松找到。
不会使用OpenTK的众所周知的游戏窗口,假设熟悉Windows表单设计。只是使用一个普通的Windows表单。OpenTK提供了一个非常好的控件/工具。为此,需要将其添加到Visual Studio工具箱中,它被称为GLControl,可以在这里找到它。首先点击工具箱并选择项目,然后浏览并添加这个控件的DLL。现在可以使用它了。
首先,创建一个表单,将在其中放置GLControl。右键单击工具箱中的一些空白区域,选择“选择项目…”,然后浏览OpenTK.GLControl.dll。确保可以在.NET Framework组件中找到GLControl,如下所示。然后可以像任何.NET控件一样将GLControl添加到表单中。一个名为glControl1的GLControl将被添加到表单中。
首先添加这个控件并在端口中命名为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();
将投影矩阵模式设置为投影,并加载身份。如果想了解更多关于这些模式的信息,可以在这里查看。
下一个命令非常重要,理解它之前,首先需要成为一个好的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);
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);
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();
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);
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);
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);
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);
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秒,启用它,并在计时器滴答时添加这个事件:
private void timer1_Tick(object sender, EventArgs e)
{
glControl2.MakeCurrent();
PaintEventArgs p = null;
glControl2_Paint(sender, p);
GL.End();
}
现在paint方法将在一秒钟后被调用。必须在那里绘制一条线,用于秒,所以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 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();
}