1

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 を直接呼び出します。

WinformでOpenGLを「ペイント」するにはどうすればよいですか? 言い換えると; GLControl を自分で実装するにはどうすればよいですか?

4

1 に答える 1

3

GLControl を自分で実装するにはどうすればよいですか?

多かれ少なかれ OpenTK のやり方。ソースオープンなので勉強できます!

警告しますが、簡単ではありません。ウィンドウ/コントロールの作成を OpenGL コンテキストの作成と同期させ ( CreateHandleOnHandleCreatedOnHandleDestroyedをオーバーライド)、OnPaintOpenGL コンテキストを現在の状態にするためにルーチンをオーバーライドする必要があります (実際、ペイント フェーズでの描画を許可します)。

アイデアを提供するだけです:これは私の 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 は、デバイスのピクセル形式を選択するためのユーティリティ クラスです)。実装の鍵は、実際の System.Windows.Forms 実装 (.NET および Mono) と統合された、従来の C++ プログラムと同じ呼び出しを模倣する必要があることです。

しかし、最も大きなタスクは、P/Invokes を使用した OpenGL API の定義です。OpenGL は、多数の列挙型 (定数) とエントリ ポイント (数千...) を定義します。人間として、これらすべての宣言を手動で書くことはできません(主に限られた時間ですが、エラーが発生しやすくなります)。OpenTK が気に入らない場合は、TAO フレームワークから始めることができます。無駄な時間があり、多くの忍耐力がある場合は、OpenGL レジストリを使用して C# バインディングを自分で生成できます。

しかし...待ってください、大きな朗報です(あなたはとても幸運です):XML OpenGL API定義は公開されています!(!)

(!) 熱心な回答でわからない方へ: 何年も何年も待ってます!!!

于 2013-07-31T19:26:40.843 に答える