Our engine is supposed to run huge scenes with tracks stretching miles across. It would mean a lot of objects would need to be passed on to the rendering engine resulting in a lot of draw calls being made despite instanced rendering being applied. Sure, the rasterizer will decide which pixel shaders need to be run but the vertex shaders will still be running unnecessarily. This will naturally cause a lot of performance issues. In order to avoid this, we had to implement Frustum Culling which helps to restrict the number of objects passed to the rendering engine to be the number of objects in the camera view space. In other words, we would draw only the objects that are being seen by the player.
In order to achieve Frustum Culling, there is a need to first divide the 3d space in our scene into partitions. For our engine, we are using an in-house dynamic octree(an octree that reallocates nodes for objects moving in the scene every frame) to achieve such a spatial partitioning.
The way a game object gets processed for frustum culling in this engine is represented as steps below:
When the rendering component is added to an entity, it calculates an axis-aligned bounding for that entity by finding the 8 extreme corners of the mesh by going through each vertex.
The octree multiplies each corner of this bounding box with the entity's transformation matrix and calculates a new axis-aligned bounding box to assign a new node.
The octree checks if the new bounding box is bound by a node and if it does then it checks for its child nodes as well. If none of the child nodes are able to contain the bounding box or if the parent node is a leaf node already then the entity is assigned the parent node. In order to avoid traversing the octree later to find the node the entity belongs to, the entity is assigned an octree ID which keeps track of the node, it belongs to.
In the update loop, the Octree does the following tasks:
Gets the view matrix for the current frame and recalculates the viewing space plane equations.
Receives a list of entities with updated positions from the scene graph and reallocates their nodes similarly.
Checks if the objects lie in the view space by doing a simple sphere intersection test with all 6 planes.
Culls objects based on the results of the intersection tests.
Potential Future Improvements:
Add functionality to cache unused nodes into a list/array and use them as new nodes where needed. This should allow for a lot of memory savings.
Comments