How to save an OpenGL rendering to disk

You can use the following code to read back an OpenGL rendering to a System.Drawing.Bitmap. You can then use the Save() method to save this to disk.

Hints:

  • Don't forget to call Dispose() on the returned Bitmap once you are done with it. Otherwise, you will run out of memory rapidly. If you wish to save a video, rather than a single screenshot, consider modifying this method to reuse the same Bitmap.
  • Call GrabScreenshot() from your main rendering thread, i.e. the thread which contains your GraphicsContext.
  • Make sure you have bound the correct framebuffer object before calling GrabScreenshot().
  • You can improve performance significantly by removing the bmp.RotateFlip() call and saving the resulting image as a BMP rather than a PNG file. This is especially important if you wish to record a video - it is the difference between a real-time recording and a slideshow.
  • This code can record 720p/30Hz video relatively easily, given suitable hardware and a little optimization (as outlined above). There are many programs that can encode a stream of consecutive BMP files into a high definition video.
using System;
using System.Drawing;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
 
static class GraphicsHelpers
{
        // Returns a System.Drawing.Bitmap with the contents of the current framebuffer
        public static Bitmap GrabScreenshot()
        {
            if (GraphicsContext.CurrentContext == null)
                throw new GraphicsContextMissingException();
 
            Bitmap bmp = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
            System.Drawing.Imaging.BitmapData data =
                bmp.LockBits(this.ClientRectangle, System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            GL.ReadPixels(0, 0, this.ClientSize.Width, this.ClientSize.Height, PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
            bmp.UnlockBits(data);
 
            bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
            return bmp;
        }
}