Skip to content

DataBergin/rasterizer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Software Rasterizer

A 3D software rasterizer written from scratch in C++. No OpenGL, no DirectX. SDL2 opens a window and blits a pixel buffer; every pixel of every triangle is computed by CPU code in this repo.

What this is

A GPU does three things when rendering a 3D scene:

  1. Vertex transform — model/view/projection matrices move geometry into screen space.
  2. Rasterization — determine which screen pixels each triangle covers.
  3. Shading — color those pixels with lighting and textures.

This project implements all three on the CPU so every step is readable code. The goal is understanding, not performance.

Pipeline

Mesh (positions, normals, UVs)
  │
  ▼ Mat4: model × view × projection
  │
  ├─ back-face cull (dot(face_normal, to_camera) ≤ 0 → skip)
  ├─ near-plane cull (clip.w ≤ 0 → skip)
  │
  ▼ perspective divide (÷ clip.w) → NDC [-1,+1]
  │
  ▼ viewport transform → screen pixels
  │
  ▼ triangle_shaded()
     ├─ barycentric fill of bounding box
     ├─ z-buffer depth test (depth_test_and_set)
     ├─ perspective-correct UV interpolation (u/w, 1/w trick)
     └─ Lambertian diffuse + ambient → final pixel color

Build (Windows + CLion + vcpkg)

  1. Install SDL2 via vcpkg:
    .\vcpkg install sdl2:x64-windows
    
  2. CLion: Settings → CMake → CMake options, add:
    -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake
    
  3. Reset CMake cache (Tools → CMake → Reset Cache and Reload Project), then Run.

Loading your own model

Drop files into models/ and the app picks them up automatically:

File Purpose
models/scene.obj Wavefront OBJ mesh
models/scene.png Texture (PNG, JPG, BMP, TGA ok)
models/scene.jpg Fallback texture

If neither file is found the app renders a rotating textured cube.

Supported OBJ features: v, vt, vn, f (tris, quads, convex polygons). Flat normals are auto-computed when vn data is absent.

Project layout

rasterizer/
├── CMakeLists.txt
├── vendor/
│   └── stb_image.h        Header-only image loader (PNG/JPG/BMP/TGA)
├── models/                Drop scene.obj + scene.png here
├── include/
│   ├── rmath.h            Vec2, Vec3, Vec4, Mat4, Point2i, color helpers
│   ├── framebuffer.h      Pixel buffer + z-buffer
│   ├── draw.h             2D primitives + 3D shaded triangle
│   ├── mesh.h             Triangle/Vertex structs, OBJ loader, cube generator
│   └── texture.h          stb_image wrapper, checkerboard fallback
└── src/
    ├── main.cpp           SDL setup, asset loading, render loop
    ├── rmath.cpp          Mat4 implementations
    ├── framebuffer.cpp    set_pixel, depth_test_and_set, clear
    ├── draw.cpp           Bresenham, barycentric fill, triangle_shaded
    ├── mesh.cpp           OBJ parser, make_cube
    └── texture.cpp        stb_image integration

Key concepts

Perspective-correct UV (draw.cpptriangle_shaded): Interpolating UVs linearly in screen space produces distortion. The fix: store u/w and v/w at each vertex, interpolate those and 1/w linearly, then recover u = (u/w) / (1/w). One extra division per pixel.

Z-buffer (framebuffer.cppdepth_test_and_set): Each pixel stores the depth of the closest fragment drawn so far. A new fragment only writes if its depth is strictly less than the stored value. clear() resets depth to 1.0 (far) each frame.

Barycentric fill (draw.cpptriangle_shaded): Walk every pixel in the triangle's bounding box. For each, compute three weights that sum to 1 and describe "closeness to each vertex." All weights ≥ 0 → inside. The same weights interpolate depth, UVs, and normals for free.

Back-face culling (main.cpprender): In world space, dot the face normal with the vector from the surface to the camera. ≤ 0 means the face points away → skip, halving draw calls for closed meshes.

Mat4 / look_at / perspective (rmath.cpp): Standard right-handed OpenGL conventions.

  • look_at: camera looks along −Z in view space.
  • perspective: maps z=−near→NDC−1, z=−far→NDC+1.
  • Normal matrix: for rotation-only model transforms, the model matrix itself is correct for normals (a rotation's inverse-transpose equals itself).

Controls

  • ESC or close window: quit

Implemented

  • Vec2, Vec3, Vec4, Mat4 — full math library with dot, cross, normalize, perspective, look_at, rotate_x/y/z, translate, scale
  • Framebuffer — pixel buffer + z-buffer, safe bounds checking
  • Draw::line — Bresenham, all 8 octants
  • Draw::triangle_wireframe / triangle_filled — 2D primitives
  • Draw::triangle_shaded — z-buffered, perspective-correct UV, diffuse lighting
  • Mesh::load_obj — OBJ parser (tris, quads, polygons, auto-normals)
  • Mesh::make_cube — procedural unit cube with UVs
  • Texture::load — stb_image (PNG/JPG/BMP/TGA)
  • Texture::checkerboard — procedural fallback

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors