Screen capture in DirectX is an inconspicuous element of the rendering pipeline that can provide many auxiliary benefits to 3D applications. Through creative use of the screen capture functionality, the operation can be extended to not just video creation, but also multi-stage graphics and post-processing. At its core is the code that enables capture of the current frame in the rendering process.
When programming screenshots, the most important element is placing the code at the correct point in the render loop. A poorly integrated screenshot function can cause instability and system crashes in the 3D application due to race conditions or memory overwrite.
DirectX applications generally have two flavors of user interface interaction: single-threaded and isolated UI. Single-threaded applications have a fully integrated render loop, where the input elements are handled on each iteration of the render loop. Graphics are first drawn sequentially, and then inputs such as the mouse cursor and keyboard are checked for any state updates. Although this works well for fully-DirectX applications, it is often necessary to integrate a 3D application with external UI elements. External UI can simplify development by leveraging the large base of controls offered by Windows and third-party vendors. In this situation, the render loop runs parallel to the UI loop, and the two threads require a synchronized data structure for communication.
The render loop itself can be split into three stages – pre-rendering, the primary rendering process, and post-rendering. Pre-rendering handles either keyboard and mouse inputs, or interfacing with the synchronized UI data structure. The primary render loop sets render settings as necessary and draws the scene. Finally, post-rendering can apply post-processing to the frame. The screenshot functionality is integrated into the post-processing step, or as an element at the end of the primary render process.
Taking the screenshot itself is relatively straightforward. Once the scene has been rendered, the Direct3D device offers a GetRenderTarget function that returns a pointer to a Direct3D surface with the current scene. Using a SurfaceToBitmap function, the RenderTarget can be converted to a Bitmap object, which can then be saved as either a JPEG or PNG to disk. Below is an example of the code involved:
Bitmap screenshot = Funcs.SurfaceToBitmap(device.GetRenderTarget(0));
screenshot.Save(Frame.ToString() + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
In order to create a video from the saved screenshots, it’s important to virtualize the system animation. Screenshots result in a much lower frame rate than usual for the application, so that animations must be frame-referenced as opposed to time-referenced. Screenshots should then be saved into a folder and named by their absolute frame number. With all of the screenshots organized, an open-source tool such as MakeAVI can then be used to combine all the screenshots into a video.
The most opportune aspect of screenshots, however, is in graphics post-processing. By accessing the RenderTarget as a surface, the image can then be saved and resent through a second rendering pipeline for applying operations such as localized blur, specular amplification, or shadow enhancement. Post-processing effects can compensate for efficiency optimizations in the render loop, and provide for a more photo-realistic image quality.
With all of their versatility, screenshots are an essential element of many 3D applications. When programmed correctly, screenshots can serve as a foundation for the implementation of advanced creative effects in the later stages of 3D project development.
C# DirectX – Capturing Frame Screenshots
Written by Andrew Palczewski
About the Author
Andrew Palczewski is CEO of apHarmony, a Chicago software development company. He holds a Master's degree in Computer Engineering from the University of Illinois at Urbana-Champaign and has over ten years' experience in managing development of software projects.