如何从C#调用openGL?

时间:2013-07-31 13:31:58

标签: c# winforms opengl

我正在使用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?

1 个答案:

答案 0 :(得分:3)

  

我如何自己实施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 registry自行生成C#绑定。

但...... 等待,好消息 (你很幸运):XML OpenGL API definition are publicly accessible! (!)

(!)对于那些不热心回答的人:我等了很多很多年了!!!