如何从C#调用openGL
本文关键字:调用 openGL | 更新日期: 2023-09-27 18:25:44
我正在使用Winforms for GUI用C#编程一个应用程序。我想创建一个使用OpenGL显示3D图形的控件。
我对OpenTK进行了一些实验,发现使用它的GLControl来显示OpenGL图形非常简单;我只需要实现一个处理GLControl的Paint事件的方法,并在此方法中调用OpenTK包装器方法,例如,而不是
glVertex2d(0.0, 0.0);
我会打电话给
GL.Vertex2(0.0, 0.0);
我还发现,可以使用p/Invoke:将OpenTK与直接对OpenGL的调用混合使用
using System;
using System.Runtime.InteropServices;
public class gl
{
[DllImport("opengl32")]
public static extern void glVertex2d(double x, double y);
};
...
GL.Vertex2(0.0, 0.0); // OpenTK
gl.glVertex2d(1.0, 1.0); // OpenGL directly using Pinvoke
然而,我希望一起消除对OpenTK的依赖。我不想使用任何外部库,而是直接调用OpenGL。
如何让OpenGL在winform上"绘制"?换句话说我如何自己实现GLControl
我如何自己实现GLControl?
OpenTK或多或少是怎么做的;既然它是开源的,你可以研究它!
我警告你:这并不容易。您必须将窗口/控件的创建与OpenGL上下文的创建同步(覆盖CreateHandle
、OnHandleCreated
、OnHandleDestroyed
),并覆盖OnPaint
例程,以便使OpenGL上下文成为当前上下文(实际上允许绘制阶段的图形)。
只是给你一个想法:这是我的UserControl(部分)实现:
protected override void CreateHandle()
{
// Create the render window
mRenderWindow = new RenderWindow(this);
mRenderWindow.Width = (uint)base.ClientSize.Width;
mRenderWindow.Height = (uint)base.ClientSize.Height;
// "Select" device pixel format before creating control handle
switch (Environment.OSVersion.Platform) {
case PlatformID.Win32Windows:
case PlatformID.Win32NT:
mRenderWindow.PreCreateObjectWgl(SurfaceFormat);
break;
case PlatformID.Unix:
mRenderWindow.PreCreateObjectX11(SurfaceFormat);
break;
}
// OVerride default swap interval
mRenderWindow.SwapInterval = SwapInterval;
// Base implementation
base.CreateHandle();
}
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.HandleCreated"/> event.
/// </summary>
/// <param name="e">
/// An <see cref="T:System.EventArgs"/> that contains the event data.
/// </param>
protected override void OnHandleCreated(EventArgs e)
{
if (DesignMode == false) {
// Finalize control handle creation
// - WGL: SetPixelFormat
// - GLX: store FBConfig and XVisualInfo selected in CreateHandle()
// ...
// - Setup swap interval, if supported
mRenderWindow.Create((RenderContext)null);
// Create the render context (before event handling)
mRenderContext = new RenderContext(mRenderWindow.GetDeviceContext(), mRenderContextFlags);
}
// Base implementation
base.OnHandleCreated(e);
// Raise CreateContext event
if (DesignMode == false) {
mRenderContext.MakeCurrent(true);
RaiseCreateContextEvent(new RenderEventArgs(mRenderContext, mRenderWindow));
mRenderContext.MakeCurrent(false);
}
}
/// <summary>
///
/// </summary>
/// <param name="e"></param>
protected override void OnHandleDestroyed(EventArgs e)
{
if (DesignMode == false) {
if (mRenderContext != null) {
// Raise DestroyContext event
mRenderContext.MakeCurrent(true);
RaiseDestroyContextEvent(new RenderEventArgs(mRenderContext, mRenderWindow));
mRenderContext.MakeCurrent(false);
// Dispose the renderer context
mRenderContext.Dispose();
mRenderContext = null;
}
// Dispose the renderer window
if (mRenderWindow != null) {
mRenderWindow.Dispose();
mRenderWindow = null;
}
}
// Base implementation
base.OnHandleDestroyed(e);
}
/// <summary>
///
/// </summary>
/// <param name="e"></param>
protected override void OnPaint(PaintEventArgs e)
{
if (DesignMode == false) {
if (mRenderContext != null) {
// Render the UserControl
mRenderContext.MakeCurrent(true);
// Define viewport
OpenGL.Gl.Viewport(0, 0, ClientSize.Width, ClientSize.Height);
// Derived class implementation
try {
RenderThis(mRenderContext);
} catch (Exception exception) {
}
// Render event
RaiseRenderEvent(new RenderEventArgs(mRenderContext, mRenderWindow));
// Swap buffers if double-buffering
Surface.SwapSurface();
// Base implementation
base.OnPaint(e);
mRenderContext.MakeCurrent(false);
} else {
e.Graphics.DrawLines(mFailurePen, new Point[] {
new Point(e.ClipRectangle.Left, e.ClipRectangle.Bottom), new Point(e.ClipRectangle.Right, e.ClipRectangle.Top),
new Point(e.ClipRectangle.Left, e.ClipRectangle.Top), new Point(e.ClipRectangle.Right, e.ClipRectangle.Bottom),
});
// Base implementation
base.OnPaint(e);
}
} else {
e.Graphics.Clear(Color.Black);
e.Graphics.DrawLines(mDesignPen, new Point[] {
new Point(e.ClipRectangle.Left, e.ClipRectangle.Bottom), new Point(e.ClipRectangle.Right, e.ClipRectangle.Top),
new Point(e.ClipRectangle.Left, e.ClipRectangle.Top), new Point(e.ClipRectangle.Right, e.ClipRectangle.Bottom),
});
// Base implementation
base.OnPaint(e);
}
}
protected override void OnClientSizeChanged(EventArgs e)
{
if (mRenderWindow != null) {
mRenderWindow.Width = (uint)base.ClientSize.Width;
mRenderWindow.Height = (uint)base.ClientSize.Height;
}
// Base implementation
base.OnClientSizeChanged(e);
}
private static readonly Pen mFailurePen = new Pen(Color.Red, 1.5f);
private static readonly Pen mDesignPen = new Pen(Color.Green, 1.0f);
#endregion
(RenderWindow是一个用于选择设备像素格式的实用程序类)。实现的关键是,您需要模拟与经典C++程序相同的调用,并与实际的System.Windows.Forms实现(.NET和Mono)集成。
但最艰巨的任务是使用p/Invokes定义OpenGL API:OpenGL定义了大量枚举(常量)和入口点(数千…);作为人类,您不能手动编写所有这些声明(主要是在有限的时间内,但也容易出错)。如果你不喜欢OpenTK,你可以从TAO框架开始。如果您有时间浪费和大量的耐心,您可以使用OpenGL注册表自己生成C#绑定。
但是WAIT,BIG GOOD NEWS(您很幸运):XML OpenGL API定义可以公开访问!(!)
(!)对于那些不理解的人,热情地回答:我等了好多好多年了!!!