Skip to main content

The Quark renderer, desktop OpenGL 4.0

· 4 min read

The 2012 engine was built for OpenGL ES on Symbian. Symbian died in 2013, so I ported everything to desktop OpenGL and started again from scratch. The new renderer got the name Quark. This post collects the notes from August 2013 until March 2015.

Why a rewrite and not a port​

The 2012 engine emulated OpenGL ES 2.0 on the desktop. That emulation did not support threading, and threading is what I needed on the desktop side. So I dropped the emulation and moved to real desktop OpenGL. I also wrote my own math classes. They passed 1500 lines of code at 50 percent completion, because I wanted code that I could extend myself. SFML handled window creation and window events.

The code almost used no external libraries. The only exceptions were freeGlut, GLEW and Qt, and I avoided most of the Qt API.

The deferred pipeline​

A deferred lighting pass went in within two days. The picture below shows the point lights.

Deferred lighting pass with many point lights.

Ambient light and a directional light came next. Both are additive, and both can be used multiple times.

Ambient and directional light in the deferred pipeline.

Deferred scene with additive lights.

The frustum culling had a fault, and I fixed it together with some smaller light bugs.

Frustum culling result.

Renderer with global directional ambient light.

The GPU problem of 2014​

My laptop GPU died in spring 2014. The remaining Intel HD4000 supports OpenGL 3.3 only, and the engine targeted OpenGL 4.0. Two options existed:

  • bring the OpenGL target down to 3.3
  • get a new machine

A downgrade would have meant a shader rewrite and changed rendering procedures. I bought a desktop machine instead. The engine code then failed to render the loaded textures on the new machine. That fault was small, and I fixed it in August 2014.

Scene management​

The biggest gain came from spatial subdivision. I implemented two octrees, one for static objects and one for dynamic objects. Under average conditions the octrees raised performance enormously.

Loading models into an octree.

Octree debug view in wireframe.

Alongside the octrees these changes happened:

  • restructured most classes
  • a CompositeObject class as container for all GameObject related parts
  • graphs to measure performance
  • memory leaks fixed by moving to smart pointers, and the new bugs caused by that move fixed as well
  • bugs in the event system fixed
  • octree related bugs fixed
  • wireframe and vertex render modes back in the render pipeline
  • plans for debug output of light geometry and octree geometry

Wireframe and vertex debug modes.

Debugging the GPU side used AMD CodeXL.

Debugging the engine with CodeXL.

Asset loading and sky​

My hand written OBJ loader got replaced by the Assimp library. The classes were already prepared for multiple formats, so the swap was easy.

Assimp loading OBJ files.

Skyboxes add a lot to a 3D scene. Not everything worked correct at that point, but it was close.

Skybox test scene.

Second skybox test scene.

Feature list of the renderer in 2015​

  • entity management system, work in progress
  • post processing render passes
  • FXAA, NFAA, sharpening filter, Gauss blur, SSAO
  • gamma correction
  • Assimp model import
  • sky cubes
  • improved texture map handling
  • octrees
  • early view frustum culling
  • unlimited amount of dynamic point lights
  • directional lights
  • ambient light
  • deferred rendering pipeline

The shader work lives in its own posts, because those posts contain the code: gamma correction, post processing aa with FXAA and NFAA.

What came after​

The renderer never got a new mobile port. The desktop version reached a usable state, and after that the work moved to other projects. The projects that came before this engine are documented in the old page archive.