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
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:
![]() |
![]() |
|
![]() |
![]() |
![]() |
<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?
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!initializer_list
, check if you have any pointer around and decide
whether to change to smart pointers, or use RO3Pass 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]};
}