The OpenTK bindings for OpenGL are clean, lightweight, and complete. Definitely recommended for any .NET developer interested in taking a swing at 3D graphics programming under OpenGL.
// Draw a gradient quad...
GL.Begin(BeginMode.Quads);
GL.Color4(Color4.Black);
GL.Vertex3(-1, -1, -1);
GL.Color4(Color4.Red);
GL.Vertex3(1, -1, -1);
GL.Color4(Color4.Black);
GL.Vertex3(1, 1, -1);
GL.Color4(Color4.LimeGreen);
GL.Vertex3(-1, 1, -1);
GL.End();
OpenTK exposes the entire OpenGL frequency spectrum to your .NET app, including deprecated functions, extensions, and satellite libraries like GLUT. With few exceptions, if it exists in OpenGL, you can call it effortlessly via OpenTK.
And when I say effortlessly, I mean that when you expect a certain function to be there, it’s there, usually in a type-safe way. And I also mean that certain things that are a pain in C++ plus OpenGL become very, very easy under C# plus OpenTK.
For example: those pesky OpenGL extensions. Here’s a bit of code I have from an old C++ OpenGL project. This code attempts to enable v-sync, using the WGL_EXT_swap_control extension. It ends up having to do some string comparisons, look up and store a raw function pointer, and then call the pointed-to function.
//Funcs to set up vsync
void GraphicsManager::InitVSync()
{
char* extensions = (char*)glGetString(GL_EXTENSIONS);
if (strstr(extensions,"WGL_EXT_swap_control"))
{
wglSwapIntervalEXT = (PFNWGLEXTSWAPCONTROLPROC) wglGetProcAddress("wglSwapIntervalEXT");
wglGetSwapIntervalEXT = (PFNWGLEXTGETSWAPINTERVALPROC) wglGetProcAddress("wglGetSwapIntervalEXT");
}
}
void GraphicsManager::SetVSync(bool VSync)
{
if (wglSwapIntervalEXT)
wglSwapIntervalEXT(VSync ? 1 : 0);
}
bool GraphicsManager::GetVSync() const
{
if (wglGetSwapIntervalEXT)
return wglGetSwapIntervalEXT()==0 ? false : true;
return false;
}
So that’s the C++ version. Meanwhile in OpenTK:
VSync = VSyncMode.On;
Syntactic sugar, yes. And yet, OpenTK doesn’t conceal anything from you. You could, if you wanted, call the extension functions directly. If you’d rather not, there are convenience properties like this to save you some time. Or this one, to set the app into full-screen mode (something that’s always been a little messy under C++ OpenGL):
WindowState = WindowState.Fullscreen;
Speaking of windows, I like the way OpenTK handles windows and rendering contexts. There are basically three ways to set things up:
- Use the built-in
GameWindow
class (or a class derived from it), designed for raw speed, and typical game-loop (update/render) behaviors. - Use WinForms-friendly
GLControl
class, which allows you to host an OpenGL-friendly window as a standard .NET WinForms control. - Use an external window/context created elsewhere.
So whether you want to handle all aspects of window and context creation yourself, or have OpenTK handle them for you, your choice. For the CardsFall visualizer (implemented in C# and OpenTK)…
…I started off with a standard GameWindow
, shown here in its basic skeleton form.
class MainWindow : GameWindow
{
public MainWindow( GraphicsMode mode )
: base(1280, 720, mode, "My OpenTK Demo App")
{
VSync = VSyncMode.On;
WindowState = WindowState.Fullscreen;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// Initialize OpenGL properties
// Subscribe to mouse events
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
// Set up OpenGL viewport
// Set up projection/modelview matrices
}
// Called when it's time to update the state of our game
protected override void OnUpdateFrame(FrameEventArgs e)
{
base.OnUpdateFrame(e);
if (Keyboard[Key.Escape])
Exit();
// Update logic goes here.
}
// Called when it's time to render the game
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
// Painting logic goes here.
SwapBuffers();
}
// The application entry point
[STAThread]
public static void Main(string[] args)
{
GraphicsMode mode = new GraphicsMode(new OpenTK.Graphics.ColorFormat(32), 24, 0, 2, new OpenTK.Graphics.ColorFormat(32));
// Request 60 UpdateFrame events per second and as many RenderFrame events as will fit
using (MainWindow game = new MainWindow(mode))
{
game.Run(60.0);
}
}
}
That should look more or less familiar to you WinForms/Win32 cats. The big difference is that the GameWindow enforces a classic update/render cycle:
OnUpdateFrame
is called to give you a chance to update the state of your game.OnRenderFrame
is called to give you a chance to draw your game to the screen.
You’ll get frame time indicators with both of those, so you can figure out where your game objects and particles and explosions need to be. And if you don’t like how any of this works, again, you can always build your window (and your game loop) yourself.
Now, speaking for myself, I still prefer native C++ and DirectX/OpenGL for intensive, fry-your-graphics-card stuff.
And if I’m developing in .NET, I’ll often go with XNA by default, which is a world unto itself, and has the energy of an entire world, and a “fun” factor that’s through the roof.
That said, OpenTK offers an amazingly clean 3D programming experience. Maybe the cleanest ever.
No, it’s not going to give you all the bells and whistles that XNA gives you. No, it’s not going to be quite as performant as native OpenGL (although I was surprised at just how small the differences were). No, it doesn’t have all the built-ins that for example Processing has. But OpenTK is unequivocally tidy, and provided you’re familiar with OpenGL, completely intuitive.
Which makes it an extraordinarily productive stack. In the words of the guy who introduced me to OpenTK:
Anything you can do in OpenGL, you can do in OpenTK in half the time.
Depending on what you’re building, and whether platform independence is a goal, that may be worth more than bells and whistles.