我正在使用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?
答案 0 :(得分:3)
我如何自己实施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 registry自行生成C#绑定。
但...... 等待,好消息 (你很幸运):XML OpenGL API definition are publicly accessible! (!)
(!)对于那些不热心回答的人:我等了很多很多年了!!!