About
Whisper Engine is a rendering engine I began after hitting a wall with chamoix-engine. I quickly realized that for a game engine to be useful it needs to cooperate with other tools, and to support a specific type of game. Trying to make a general game engine as a solo dev is a fun exercise but ultimately doomed.
Context
As I said I had a specific game in mind behind this engine: An open world survival horror game with forests and snow, think "The Long Dark" but without the well thought out artistic direction. This means few light sources and strong ambient lighting. Something else to keep in mind is I was about to begin an internship in which I would be tasked with making a vulkan visualization tool. Pragmatic, and frustrated with my previous workflow for render passes, I created a render-graph which could be reused between the two projects to handle pass and resource lifetimes/dependencies.
3D modelers don't want to reimplement models one primitive at a time with tinyobj, assigning the materials manually. No, they want to be able to export a file in blender, import it in game, and expect it to look the same. I focused on the gltf format for its near universal support of complex scenes, primitives, and materials. Clicking a gltf asset in the asset browser imports all the referenced images, textures, materials and samplers while avoiding any duplicate (which honestly is probably unnecessary and something I wouldn't bother with again.) It then renders said scene to the screen. I used Tracy to profile my code with this project and realized that holding and multiplying transforms hierarchicaly for a static scene was a little too much, so the import also generates a table of tightly packed, precomputed transforms, leaving the renderer to only reference an idea. This lead to a moderate speed up.
Rendering
Due to the hypothetically low light source count, I went with forward+ over deferred rendering. '+' stands for a depth and normals prepass which we use for basic culling as well as a Screen Space Ambient Occlusion effect. Having profiled my project with Tracy, I can say the prepass neither added nor removed to rendering time, while allowing screen space rendering techniques. I render PBR materials and supports pretty much all such materials but transparency. I also make use of precomputed textures for speed such as a brdfLUT and a noise texture to better scatter the SSAO and shadows.
Environment-based lighting
Using HDRs for ambient lighting means scenes better integrate in their environment. Due to the supposed open-world snowy exterior, convincing ambient lighting was essential. The HDR aslo leads to better reflections, using mip levels to sample reflections at different roughness. One of the issues with this technique is that occlusion isnt calculated for these reflections. It would be better to presample various HDRs across the scene (I tried bent normals and it wasnt much better.) This weakness really shows in interior scenes which not every game needs to have but regardless, SSAO helps dim the effect on smaller objects, and shadow maps are also included.
As the project became less "game-engine" and more "renderer of static scenes" I didn't necessarily see a point with integrated CSM like I do in my current rewrite of this project. Generally speaking however I really want to see how far one can push shadow maps in the future. In a world of Ray Tracing and Global Illumination. shadow maps still manage to be used everywhere to render highly convincing scene with minimal VRam costs.
Adam Head, Unity Technologies (Sketchfab) — Lieutenant Head, Unity Technologies (Sketchfab) — 3D model courtesy of Santiago Piedrahita Bello
Frost
Frost is my reflection program written in Python which parses my code looking for markers. It's essentially a clone and exploration of Epic Games UPROPERTY system. This felt pretty essential after the chore that was "overriding the save, load, edit functions for all new actors and components" in my previous engine. However having to write such a tool is what lead me to move to Jai and grow more frustrated with C++. It is essentially an impossible task. The parser can never be fully accurate or it would be a compiler and be too slow. So you have to see clang compile it all, parsing beautiful trees of your code, before throwing all that golden information away and giving you no access to it. Why oh why... (I heard some form of introspection is arriving in future Cpp releases so we will see. In the meantime Jai just let's me access that stuff instantly.)
Example of code being turned to editor by Frost
Acknowledgements
Alien creature 3D model courtesy of Santiago Piedrahita Bello.