As our game engine gets more complex, it's important to have an easy way to change and update the rendering pipeline without having to update everything. To do this, I wrote a basic render pass framework for our game engine. Each render pass holds on to a list of objects that it should draw, and calls the relevant OpenGL commands to prepare the pipeline for drawing. Adding to the rendering pipeline usually requires the creation of two new files: Render Pass, and a Renderable.
Renderables:
The Renderable class acts as a way to keep track of what type of data and buffers a renderable object has. Since not all objects have the same data, the Renderable class acts as a way to group together different types of data for each type of renderable. For example: mesh objects can keep all the vertices and their information (UV coords, normals, etc), while a particle object can keep track of information relating to the particle, such as size and opacity. This allows each object uses the same basic interface when called for drawing (bind, draw, unbind), but store whatever relevant data we need for each pass.
Render Pass Class: Render passes are handled through an abstract class that each type of render pass inherits from. The base class contains a list of renderable objects that will be drawn that frame as well as an abstract draw method and some method for setting OpenGL properties. This means that creating a child class for the Render Pass only really needs two things: information about what OpenGL properties to set / enable, and how the actual draw function in the class should work. For most render passes, you can simply set the properties in the constructor, then iterate through the renderable objects and call the right draw functions on them. For more complex use, the QueueRenderable function can be overridden to customize how objects are queued for rendering. This is usefully for rendering passes that require order, such as transparent passes, as the basic rendering queue is a set.
With these classes, theres only a few more steps. First, the render pass object needs to be created and added to the list of render passes. By adding it to the list, the render pass is automatically called during the rendering loop, meaning if you have objects to render, they will be rendered. The only thing now is to add the actual renderables. This is mostly done during the update pass, were entities are filtered and passed to the relevant rendering passes.
Current Uses:
Since render passes are in our engine, we've used them for several different areas. The previously discussed particle systems have been moved to a render pass, and additional passes have been added including: a UI pass, a transparency pass, a skybox pass, and a debug line pass.
Usage for waypoints:
For our game, it is necessary to have a waypoint system for the AI. For this, I implemented a simple way point that allows for the use of bezier curves. A waypoint system can be created, and waypoints can either be pushed back as a basic point, or as a quadratic bezier curve. After adding a point, the system regenerates the points and places them in an easier to use vector for rendering and pathfinding. While this does mean that the vector would have to be regenerated each time a point is added, the list of points for each race will be decided beforehand, so this won't be an issue. during gameplay.
For waypoints, the game currently uses a render pass in debug mode to display the path of the waypoints. It's a simple line render pass with a few customization options, but it allows us to visualize the waypoints, which is important for debugging purposes.
Here is an example of the debug line with a bezier curve in it, along with other renderable around.
Comments