Skip to content

MatthewZelriche/softrender

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

57 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Softrender

A simple toy software renderer written in Rust

Features

  • User-programmable vertex and fragment shaders via Rust traits
  • Ability to specify an arbitrary number of vertex attributes to shader programs. All supported Vertex Attribute types will be automatically interpolated without having to write boilerplate code, thanks to Rust's derive macros.
  • Perspective-correct vertex attribute interpolation.
  • A simple AABB is applied to triangles during rasterization to avoid traversal of unecessary pixels.

Getting Started

Construct a new Renderer:

let mut renderer = Renderer::new(800, 800);

The Renderer is interacted with primarily through its draw function, where you pass the renderer your vertex data, index data, and your shader. To create your shader, first define a struct containing your vertex attributes, for example:

struct Vertex {
    pos: glam::Vec3,
    color: glam::Vec3,
}

This struct specifies the layout of the vertex data you pass to the Renderer. You must also define a struct containing the outputs of your vertex shader. These are the values that you want to be interpolated via barycentric coordinates inbetween the vertex and fragment shader stages:

#[derive(Clone, Barycentric)]
struct VertexOut {
    color: glam::Vec3,
}

Note that you must specify the Barycentric derive macro on this struct, so that boilerplate code for interpolating your data can be generated automatically.

Once you have defined the inputs and outputs for your vertex shader, you can define your programmable shader by having it implement the Shader trait:

struct MyShader;
impl Shader<Vertex, VertexOut> for MyShader {
    fn vertex(&self, vertex: &Vertex) -> (glam::Vec4, VertexOut) {
        (
            vertex.pos.extend(1.0),
            VertexOut {
                color: vertex.color,
            },
        )
    }

    fn fragment(&self, inputs: VertexOut) -> glam::UVec3 {
        inputs.color.as_uvec3()
    }
}

Note that a vertex position is the only required output of your vertex shader. For the fragment shader, the output is a set of RGB color values from 0-255.

All that is left to do is provide the renderer with the required information to execute a single draw call:

let color_buf = renderer.draw(&mut shader, &vertices, &indices);

Providing a set of indices is currently a requirement, even if you do not re-use vertex data. This may change in the future. The draw function returns the color buffer, and you can now present the rendered frame however you'd like by accessing the raw array of pixel values. The provided examples use the softbuffer and winit crates to render to a window without requiring GPU acceleration.

For more information on using this crate, see the examples subdirectory for several complete examples, including more complicated use cases such as loading and rendering 3D model data.

Examples

1. Hello Triangle

2. Texture Rendering

3. Model Loading

Licensing Information

This project is licensed under the MIT License. See the LICENSE file for details.

Other credits are as follows:

  1. res/coyote.jpg, res/coyote.obj: alitural, CC BY 4.0
  2. res/texture.png: Generated by Test Grid Generator, Wahooney, CC0
  3. res/teapot.obj: Martin Newell, Kenzie Lamar, CC0

About

A simple toy software renderer written in Rust.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages