Civet Engine Dev Diary #1
I didn’t manage to land a meaningful internship this summer of 2022 so I decided to work on a large project that I was always interested in, alongside the computer vision research project I was still a part of at Georgia Tech. I just called it Civet because it might end up being poop but hopefully someone (aside from me) finds it to be useful in some way. That being said, this is mostly going to be a learning experience for me.
I’ve decided to finally write this because the engine is now in a largely functional state.
That is, it can load models from .obj
files and display them correctly in the world space with the correct shading.
So ideally, this series of writings will keep track of what gets implemented and current plans for the project.
Overview
The Civet Engine is a rendering engine, with both ray-tracing and real-time rendering features. The ray-tracer is based on the PBRTv3 path-tracer with LuxCoreRender (also originally based on PBRT) as a reference as well. The main material import method will convert materials into Disney PBR. With that said, I don’t expect to be implementating everything presented in the PBRT book, and so will begin with what I consider to be the bare minimum to rendering a basic scene.
Meanwhile, the real-time renderer uses rasterization techniques with the aim to produce images that are as physically accurate as the ray-traced images. This would likely be based on Epic Games’ adaptation of Disney PBR for their Unreal Engine 4, which should reduce number of steps in the translation of the scene and materials between rasterization and ray-tracing. I expect to be writing the GLSL shaders for rendering the scene using both forward and deferred shading methods, both as learning material and as points of comparison.
Working with OpenGL
There was a bit of difficulty when implementing the vector math classes (vectors, matrices, transforms, etc.) and getting them to work with OpenGL. The decision to completely bypass GLM and use the same vector math classes for both the raytracer and OpenGL side seemed pretty sensible in the beginning, since it meant not needing to convert vectors constantly between GLM and our own implementations.
That is how I came to learn about the specifics in OpenGL.
It uses a right-handed (RH) coordinate system and GLSL takes column-major matrices.
This led to a lot of confusion when first trying to get a proper cube drawn on the screen.
Now you’ll find that the 4x4 matrix class implementation is row-major, so when sending matrices as a uniform to the shader,
we set the GLboolean transpose
in glUniformMatrix4fv
to GL_TRUE
.
void glUniformMatrix4fv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value);
Similarly, the lookAt
function originally implemented (adapted from PBRT) was for a left-handed coordinate system.
That had to be adapted to a RH coordinate system for OpenGL (so both variants of lookAt
are now available).
The thing to keep in mind now is in building the scene from OpenGL for ray-tracing,
since following the PBRT implementation requires using the LH system.
I mentioned that the engine now is at minimum functional, being able to display a loaded model. Currently, that includes a simple shader that renders the model in shades of gray to show the contours of the mesh surface, similar to the default solid view in Blender.
The immediate item in development right now is a ambient/diffuse/specular shader that can handle both color and texture inputs, with support for up to 32 (maybe 64) light sources. This would be a simple forward shading process, just to test out the simple capabilities before transitioning to something that can handle theoretically an infinite amount of lights, like deferred shading or forward+ methods. Ideally, I’d also like to make it possible to use a PBR material with this shader as well, that can hopefully match the ray-tracing results.
CUDA support
As of now, CUDA support has been almost baked in to many of the math classes and ray-tracing classes. With that said, it is still not going to be utilized for rendering until I’ve fully implemented the CPU rendering side. I figured it would be easier to make the changes needed for CUDA later on, rather than including them right now and attempting to debug two compiled (and untested) processes simultaneously.
That means I expect the CUDA support to take quite a while, however, because much of the current project structure
use C++ standard library objects that are not compatible with CUDA, including vector
and smart pointers (unique_ptr
and shared_ptr
).
With to my limited knowledge in writing CUDA programs, I will likely need to reference LuxCoreRender to implement this eventually, since that also supports GPU rendering.
Eventual GUI and other notes
I’d like to include a GUI eventually, especially to be able to load models dynamically and edit scenes and other properties before rendering. I’m looking at using ImGui since it’s supposed to be quite easy and quick to implement into such an application. Maybe a UI upgrade to using Qt, or even a custom OpenGL UI library, in the future but not for a long time. Right now though, it’s not a huge priority.
I think a big issue I have to mention is in compiling assimp.
For whatever reason that CLion refuses to show, the resource .rc
file always fails to build on Windows.
So I’ve had to comment out all the contents of that file. Hopefully that doesn’t cause any issues in the future.