Kandria
Another deep dive!
This week I want to give you another look into the deeper systems involved in game development. Before that, though, another brief update on Kandria:
We're steadily reaching completion. All content is in and playable, and all that's left now is to test, fix, and polish. We're still doing that, and will be for the remaining time. If you're someone with beta access from the Kickstarter, please give the game a shot and let us know if you encounter any issues.
And now, on to the technical details!
Rendering Subsystem
One of the most important parts of a game engine is its rendering subsystem. This system is responsible for taking the scene you put together, and figuring out how to put it onto the screen.
In Trial, this is divided up into several parts:
The "shader pipeline", which defines the different phases of rendering a frame, and the dependencies between these phases
The "shader passes", which each encapsulate one render phase, and define which objects they render and how
The scene, which defines the objects that are visible, and for each object, how it is rendered
I'll spare you the details of how this system used to work before this rewrite I did, and instead just outline how it works now. The third part still works the same as it used to, however:
Each renderable object type can define a method on the render
function, which takes the object itself and the "shader program" instance. The shader program is an OpenGL object that lets you put data to the GPU. The render
function then typically sets various variables on the shader program, binds some textures, and tells OpenGL to render a mesh with the shader program active.
The shader program itself is derived from shaders defined by the object type, but also optionally the shader pass that renders the object. This allows control from both sides – often the object will "know" better how to render itself, but the pass might need to override or change parts of that behaviour.
The second part is what changed. It now works like this: there's two types of passes, ones that care about the scene, and thus render individual objects, and ones that do not, and instead render stuff completely on their own. The latter is mostly used for post-processing effects, and they're pretty straight-forward as they're self-contained.
For passes that care about the scene, they operate in three steps:
Figure out which objects to render for this frame. To do this, it asks the camera to tell it which objects are visible, automatically performing "frustum culling". We don't need to render objects that can't be seen anyhow.
Sort the objects so that we render things that are behind first. This is important to make sure transparency works correctly.
Step through the objects in order and call
render
.
There's a few more details around preparing textures and shader programs and such, but this is the basic idea. One could also devise ways to eliminate steps 1 and 2 if we can know that the objects didn't change since the previous frame, speeding things up.
Each of these steps can also be customised, which is very useful. For instance, in Kandria the default behaviour is for the camera to eliminate everything that is not immediately visible. However, for shadows, we need to draw things that are slightly out of frame, too, so that tall buildings can cast shadows onto adjacent places, for instance.
The ordering also needs to be customised slightly. In Kandria everything has a "layer index" to separate the objects, but sometimes that's not fine-grained enough. For instance, we don't want water to be drawn on top of the base terrain layer, but we *do* want it to be drawn on top of the player, so just knowing that each of them are on layer 2 is not enough. We customise the sorting to ensure these objects are properly ordered.
All in all this system is a lot easier to understand, use, and best of all, also performs better than the previous one. It shot up the framerate by about 4x on my home workstation. Nice!
Besides this effort I've also started rewriting and, most importantly, documenting other parts of Trial. This should make it easier for others to start using the engine, and also to understand how to mod Kandria later. If you're interested in the development process, keep an eye on the GitHub repository.
Anyway, now I have to get on back to working out those last few bugs in Kandria. Getting real close to zero feedback items now!