Top Related Projects
Code repository of all OpenGL chapters from the book and its accompanying website https://learnopengl.com
Tiny but powerful single file wavefront obj loader
Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
stb single-file public domain libraries for C/C++
Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WebGL2
One stop solution for all Vulkan samples
Quick Overview
TinyRenderer is an educational project that implements a simple software renderer from scratch. It aims to teach the fundamentals of 3D graphics programming by building a basic rendering pipeline without relying on existing libraries like OpenGL or DirectX.
Pros
- Excellent learning resource for understanding 3D graphics concepts
- Step-by-step tutorials with clear explanations and code examples
- Minimal dependencies, making it easy to set up and run
- Encourages experimentation and modification of the renderer
Cons
- Not suitable for production use or high-performance rendering
- Limited feature set compared to professional rendering engines
- May require a solid understanding of C++ and linear algebra
- Documentation is primarily in the form of lessons, which may be challenging for quick reference
Code Examples
- Drawing a line using Bresenham's algorithm:
void line(int x0, int y0, int x1, int y1, TGAImage &image, TGAColor color) {
bool steep = false;
if (std::abs(x0-x1)<std::abs(y0-y1)) {
std::swap(x0, y0);
std::swap(x1, y1);
steep = true;
}
if (x0>x1) {
std::swap(x0, x1);
std::swap(y0, y1);
}
int dx = x1-x0;
int dy = y1-y0;
int derror2 = std::abs(dy)*2;
int error2 = 0;
int y = y0;
for (int x=x0; x<=x1; x++) {
if (steep) {
image.set(y, x, color);
} else {
image.set(x, y, color);
}
error2 += derror2;
if (error2 > dx) {
y += (y1>y0?1:-1);
error2 -= dx*2;
}
}
}
- Implementing basic triangle rasterization:
void triangle(Vec2i t0, Vec2i t1, Vec2i t2, TGAImage &image, TGAColor color) {
if (t0.y>t1.y) std::swap(t0, t1);
if (t0.y>t2.y) std::swap(t0, t2);
if (t1.y>t2.y) std::swap(t1, t2);
int total_height = t2.y-t0.y;
for (int i=0; i<total_height; i++) {
bool second_half = i>t1.y-t0.y || t1.y==t0.y;
int segment_height = second_half ? t2.y-t1.y : t1.y-t0.y;
float alpha = (float)i/total_height;
float beta = (float)(i-(second_half ? t1.y-t0.y : 0))/segment_height;
Vec2i A = t0 + (t2-t0)*alpha;
Vec2i B = second_half ? t1 + (t2-t1)*beta : t0 + (t1-t0)*beta;
if (A.x>B.x) std::swap(A, B);
for (int j=A.x; j<=B.x; j++) {
image.set(j, t0.y+i, color);
}
}
}
- Implementing basic vertex shader:
Vec3f vertex_shader(const vertex_t &v) {
Vec4f gl_Vertex = embed<4>(v.position);
gl_Vertex = Projection * ModelView * gl_Vertex;
gl_Vertex = gl_Vertex / gl_Vertex[3];
return proj<3>(gl_Vertex);
}
Getting Started
- Clone the repository:
git clone https
Competitor Comparisons
Code repository of all OpenGL chapters from the book and its accompanying website https://learnopengl.com
Pros of LearnOpenGL
- Comprehensive coverage of modern OpenGL techniques
- Extensive documentation and explanations for each concept
- Includes advanced topics like PBR and shadow mapping
Cons of LearnOpenGL
- Steeper learning curve for beginners
- Relies on external libraries, increasing complexity
- Less focus on fundamental graphics algorithms
Code Comparison
LearnOpenGL (using modern OpenGL):
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
tinyrenderer (software rendering):
for (int y = t0.y; y <= t1.y; y++) {
for (int x = t0.x; x <= t1.x; x++) {
Vec3f bc_screen = barycentric(t0, t1, t2, Vec2i(x, y));
if (bc_screen.x < 0 || bc_screen.y < 0 || bc_screen.z < 0) continue;
image.set(x, y, color);
}
}
LearnOpenGL focuses on using modern OpenGL APIs, while tinyrenderer implements rendering algorithms from scratch, providing a deeper understanding of graphics fundamentals.
Tiny but powerful single file wavefront obj loader
Pros of tinyobjloader
- Focused specifically on loading OBJ files, providing a more specialized and optimized solution
- Actively maintained with regular updates and contributions from the community
- Designed as a header-only library, making it easy to integrate into existing projects
Cons of tinyobjloader
- Limited to OBJ file format, while tinyrenderer covers a broader range of 3D rendering concepts
- Lacks the educational aspect and step-by-step tutorials provided by tinyrenderer
- May require additional code for rendering and visualization of loaded models
Code Comparison
tinyobjloader:
tinyobj::ObjReader reader;
reader.ParseFromFile("model.obj");
auto& attrib = reader.GetAttrib();
auto& shapes = reader.GetShapes();
tinyrenderer:
Model model("obj/african_head.obj");
TGAImage image(width, height, TGAImage::RGB);
render(model, image);
The tinyobjloader code focuses on loading OBJ files, while tinyrenderer demonstrates a complete rendering pipeline. tinyobjloader provides more detailed access to model attributes, whereas tinyrenderer abstracts the rendering process.
Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
Pros of ImGui
- Provides a ready-to-use GUI library for immediate mode rendering
- Extensive documentation and examples for quick integration
- Cross-platform support with multiple backend options
Cons of ImGui
- Focused on GUI elements rather than low-level rendering techniques
- May have a steeper learning curve for those new to immediate mode GUIs
- Larger codebase and dependencies compared to TinyRenderer
Code Comparison
ImGui (C++):
ImGui::Begin("My Window");
ImGui::Text("Hello, world!");
if (ImGui::Button("Click me!"))
doSomething();
ImGui::End();
TinyRenderer (C++):
Vec3f light_dir(0, 0, -1);
for (int i = 0; i < width * height; i++) {
Vec3f n = model->normal(uv);
float intensity = n * light_dir;
image.set(x, y, TGAColor(intensity * 255, intensity * 255, intensity * 255, 255));
}
Summary
ImGui is a feature-rich GUI library for immediate mode rendering, offering cross-platform support and extensive documentation. It's ideal for quickly adding user interfaces to applications. TinyRenderer, on the other hand, is a minimalistic software renderer focused on teaching low-level graphics concepts. While ImGui provides ready-to-use GUI components, TinyRenderer offers a deeper understanding of rendering techniques through its simple implementation.
stb single-file public domain libraries for C/C++
Pros of stb
- Broader scope: Offers a collection of single-file libraries for various tasks, not limited to rendering
- More mature and widely used in production environments
- Designed for easy integration into existing projects
Cons of stb
- Less focused on educational purposes
- May require more effort to understand the entire codebase
- Not specifically tailored for learning computer graphics concepts
Code Comparison
tinyrenderer:
Vec3f barycentric(Vec2i *pts, Vec2i P) {
Vec3f u = cross(Vec3f(pts[2][0]-pts[0][0], pts[1][0]-pts[0][0], pts[0][0]-P[0]),
Vec3f(pts[2][1]-pts[0][1], pts[1][1]-pts[0][1], pts[0][1]-P[1]));
return Vec3f(1.f-(u.x+u.y)/u.z, u.y/u.z, u.x/u.z);
}
stb:
int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes)
{
FILE *f;
int i,j,k,padding,psize;
stbi__write_context s;
The code snippets demonstrate the different focus of each project. tinyrenderer shows a specific graphics algorithm implementation, while stb provides a more general-purpose image writing function.
Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WebGL2
Pros of Filament
- More comprehensive and production-ready rendering engine
- Supports multiple platforms (Android, iOS, Windows, macOS, Linux)
- Offers advanced features like physically-based rendering and image-based lighting
Cons of Filament
- Steeper learning curve due to its complexity
- Larger codebase, which may be overwhelming for beginners
- Less focused on educational purposes compared to Tinyrenderer
Code Comparison
Tinyrenderer (basic triangle rasterization):
void triangle(Vec2i t0, Vec2i t1, Vec2i t2, TGAImage &image, TGAColor color) {
if (t0.y > t1.y) std::swap(t0, t1);
if (t0.y > t2.y) std::swap(t0, t2);
if (t1.y > t2.y) std::swap(t1, t2);
line(t0, t1, image, color);
line(t1, t2, image, color);
line(t2, t0, image, color);
}
Filament (material definition):
material {
name : "Basic",
parameters : [
{ type : float3, name : "baseColor" }
],
requires : [
uv0
],
shadingModel : unlit,
culling : none
}
The code snippets highlight the difference in complexity and focus between the two projects. Tinyrenderer provides a simple implementation for educational purposes, while Filament offers a more sophisticated material system for production use.
One stop solution for all Vulkan samples
Pros of Vulkan-Samples
- Comprehensive showcase of Vulkan API features and best practices
- Regularly updated with new samples and improvements
- Backed by Khronos Group, ensuring high-quality and industry-standard examples
Cons of Vulkan-Samples
- Steeper learning curve due to Vulkan's complexity
- Requires more setup and boilerplate code
- May be overwhelming for beginners in computer graphics
Code Comparison
Vulkan-Samples (initialization):
VkInstanceCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
create_info.pApplicationInfo = &app_info;
VkResult result = vkCreateInstance(&create_info, nullptr, &instance);
tinyrenderer (initialization):
TGAImage image(width, height, TGAImage::RGB);
Model model("obj/african_head.obj");
Vec3f light_dir(0, 0, -1);
Vulkan-Samples offers a more comprehensive and industry-standard approach to graphics programming, while tinyrenderer provides a simpler, educational introduction to 3D rendering concepts. The code comparison illustrates the difference in complexity, with Vulkan-Samples requiring more setup and API-specific calls.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Tiny Renderer or how OpenGL works: software rendering in 500 lines of code
Check the wiki for the detailed lessons.
compilation
git clone https://github.com/ssloy/tinyrenderer.git &&
cd tinyrenderer &&
mkdir build &&
cd build &&
cmake .. &&
cmake --build . -j &&
./tinyrenderer ../obj/diablo3_pose/diablo3_pose.obj ../obj/floor.obj
The rendered image is saved to framebuffer.tga
.
You can open the project in Gitpod, a free online dev evironment for GitHub:
On open, the editor will compile & run the program as well as open the resulting image in the editor's preview. Just change the code in the editor and rerun the script (use the terminal's history) to see updated images.
The main idea
My source code is irrelevant. Read the wiki and implement your own renderer. Only when you suffer through all the tiny details you will learn what is going on.
In this series of articles, I want to show the way OpenGL works by writing its clone (a much simplified one). Surprisingly enough, I often meet people who cannot overcome the initial hurdle of learning OpenGL / DirectX. Thus, I have prepared a short series of lectures, after which my students show quite good renderers.
So, the task is formulated as follows: using no third-party libraries (especially graphic ones), get something like this picture:
Warning: this is a training material that will loosely repeat the structure of the OpenGL library. It will be a software renderer. I do not want to show how to write applications for OpenGL. I want to show how OpenGL works. I am deeply convinced that it is impossible to write efficient applications using 3D libraries without understanding this.
I will try to make the final code about 500 lines. My students need 10 to 20 programming hours to begin making such renderers. At the input, we get a test file with a polygonal wire + pictures with textures. At the output, weâll get a rendered model. No graphical interface, the program simply generates an image.
Since the goal is to minimize external dependencies, I give my students just one class that allows working with TGA files. Itâs one of the simplest formats that supports images in RGB/RGBA/black and white formats. So, as a starting point, weâll obtain a simple way to work with pictures. You should note that the only functionality available at the very beginning (in addition to loading and saving images) is the capability to set the color of one pixel.
There are no functions for drawing line segments and triangles. Weâll have to do all of this by hand. I provide my source code that I write in parallel with students. But I would not recommend using it, as this doesnât make sense. The entire code is available on github, and here you will find the source code I give to my students.
#include "tgaimage.h"
const TGAColor white = TGAColor(255, 255, 255, 255);
const TGAColor red = TGAColor(255, 0, 0, 255);
int main(int argc, char** argv) {
TGAImage image(100, 100, TGAImage::RGB);
image.set(52, 41, red);
image.write_tga_file("output.tga");`
return 0;
}
output.tga should look something like this:
Teaser: few examples made with the renderer
Top Related Projects
Code repository of all OpenGL chapters from the book and its accompanying website https://learnopengl.com
Tiny but powerful single file wavefront obj loader
Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
stb single-file public domain libraries for C/C++
Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WebGL2
One stop solution for all Vulkan samples
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot