如何从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

如何从C#调用openGL

我如何自己实现GLControl?

OpenTK或多或少是怎么做的;既然它是开源的,你可以研究它!

我警告你:这并不容易。您必须将窗口/控件的创建与OpenGL上下文的创建同步(覆盖CreateHandleOnHandleCreatedOnHandleDestroyed),并覆盖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定义可以公开访问!(!)

(!)对于那些不理解的人,热情地回答:我等了好多好多年了!!!