RusterizerS - Software Rasterizer

Mar 22, 2024 min read
👥Solo learning project 💻Platforms: Windows ⌛Duration: 1 week

What I learned

  • Working with Rust language features
  • How the graphics pipeline is implemented under the hood
  • Triangle math and barycentric coordinates
  • Implementing triangle clipping
  • Some popular Rust libraries for math, windowing, image loading…

The project

The Rusterizer is a part of a collection of masterclasses that happen yearly at BUAS in between class blocks. The aim of this masterclass is to learn a bit about Rust and the math that happens behind the scenes in our GPUs.

This is my final product at the end of the week. Full code can be found in my GitHub

It supports:

  • Triangle drawing with texture UVs and vertex colours.
  • Correct near and far plane clipping.

The code is not very optimized for runtime performance and I prioritized making every shader stage as decoupled from the others (instead of placing everything in a nested for loop). This was quite helpful when implementing clipping (that has the chance to generate more triangles from one) and for understanding the entire system.

// SETUP
let mut output_surface = Texture::new(RESOLUTION_WIDTH, RESOLUTION_HEIGHT);
let mut depth_attachment = DepthTexture::new(RESOLUTION_WIDTH, RESOLUTION_HEIGHT);

//Camera Setup
let mut camera = Camera::default();
camera.aspect_ratio = (RESOLUTION_WIDTH as f32) / (RESOLUTION_HEIGHT as f32);

//Shader abstractions
let mut vertex_shader = vertex::VertexShader::default();
let mut frag_shader = fragment::FragmentShader::default();

// Texture
frag_shader.mesh_texture = load_image_file(std::path::Path::new("assets/icon.jpeg")).unwrap();

// MAIN LOOP
while window.is_open() 
{
    let (view, projection) = camera.generate_view_projection();
    vertex_shader.view = view;
    vertex_shader.projection = projection;

    //clear
    output_surface.clear(colour::f32_to_hex(1.0, 0.0, 0.0, 0.0));
    depth_attachment.clear(1.0);

    // Draw all cube faces
    for model in MODEL_MATRICES 
    { 
        vertex_shader.model = model;  
        let (vertex_output, indices) = vs.dispatch(&PLANE_VERTEX_DATA, &PLANE_INDEX_DATA);
        frag_shader.dispatch(&mut output_surface, &mut depth_attachment, &vertex_output, &indices);
    }

    window.update_with_buffer(output_surface.as_slice(), RESOLUTION_WIDTH, RESOLUTION_HEIGHT).unwrap();
}

Link to vertex shader code

Link to fragment shader code