QRasterizer

Github link

This is a software renderer/rasterizer that emulates how a graphics API work. To be more precise, it is a “clone” that mimics how OpenGL/DirectX work behind the scene by providing its own implementation. The project is built mostly from “scratch” using C++14

Introduction

The goal of this project is to unveil the black box behind the rendering pipeline. Although I only implemented a few features, the lessons I learned was numerous: grow my C++ skills, more courage to do math, the adrenaline rush of tackling challenging problems, and make me more comfortable when tackling a graphics API

Features Back-end features
Wireframe rendering Line drawing
Flat Shading Linear Algebra library
Wavefront .obj file loader Z-Buffer
  Perspective correct interpolation
  Back-face culling
  Rasterizer
  Clipping

Some pictures during the development process:

Wireframe
Wireframe rendering
z-buffer
z-buffer
flat-shading
flat shading
texture with flat shading
texture mapping
Demo
Full demo

Postmortem

C++ tidbits:

<cmath> doesn’t support constexpr, e.g., std::abs, so I could only partially apply constexpr in my math library

initializer_list trivia: given foo f = {2, 3, 4};, what would happen if I don’t provide any copy constructor?

  • It will convert the list to a temp Foo using initializer_list constructor, then it passes that as an argument to the implicitly generated copy assignment member function. Guess what happens next? If your class has pointers, boom!
  • The lesson is when using initializer_list, check if you have any pointer around and decide whether to change to smart pointers, or use RO3

Pass by value + std::move save typings, since it will optimally move or copy

Smart pointers can be used on C functions in library like SDL2 (courtesy to 1 & 2):

struct SDL_Deleter
{
   void operator()(SDL_Window *window)
   {
         SDL_DestroyWindow(window);
   }
};

// In Foo.h
std::unique_ptr<SDL_Window, decltype(&SDL_DestroyWindow)> m_window;

// In Foo.cpp
Foo() : m_window(nullptr, SDL_DestroyWindow)
{
    m_window.reset(SDL_CreateWindow(...));
}

When working with templates, we want to deactivate some functions if template type is of some type. This can be leveraged with std::enable_if_t (this link explains more in detail):

// Code excerpt: The Cross product is only generated when working with 3D vectors. If you
// accidentally use it on 2D vectors, the compiler won't generate the function andd error is caught
// at compile time
template<typename T, size_t Size, typename = std::enable_if_t<(Size == 3)>>
constexpr Vector<T, Size> Cross(const Vector<T, Size>& v1, const Vector<T, Size>& v2)
{
      return Vector<T, Size>{
          v1[1] * v2[2] - v1[2] * v2[1],
          v1[2] * v2[0] - v1[0] * v2[2],
          v1[0] * v2[1] - v1[1] * v2[0]};
}

Computer Graphics tidbits

Resources