All Articles

Sloth, A one-of-a-kind 3D Renderer for the CLI, Written in Rust

Pika-char
Pika-char (Rendered in real time to the terminal)

With over 2000 Reddit upvotes, 250 stars on Github, half a dozen forks, I present to you, a detailed overview of sloth – a 3D software-rasterizing, OBJ, and STL loading, Pikachu and Suzanne rotating, command-line app!

Let’s begin with the name. It’s called sloth because it’s slow, for two reasons. One it’s a software rasterizer, so the actual creation of pixel data is innately slow, as it’s doing hundreds of triangle-pixel intersect calculations in milliseconds, this is because of the command line being attached to the CPU, so we couldn’t use a real graphics library like OpenGL (at least not easily, and reliably). Two it’s printing to the console, which is almost like copying a book by hand.

It’s written rust because of a few reasons. Rust was voted as the most loved programming language in 2018 by StackOverflow because it’s changing the game of what it means to have low-level power in a language. What we all associate with C/C++ is a wild-west style ecosystem of old libraries, hard-to-read commercial code and docs, and hard to debug errors such as dead pointers. Rust’s community has simplified many things in a wholesome way. It has a repository of user made libraries called crates which are maintained in an open source and automated way, unified docs style, and a really simple collection of memory rules that makes it impossible to be ambiguous in development. Rust has a long way to go before it’s commercially viable, but it’s already proven to pique the interest of many hobbyist coders like myself (and John Carmack).

Challenges of software rasterization in a command line setting

Doubling Characters

There are various reasons why this project was a little more challenging than I thought it would be. But most definitely the most challenging was making it look good.

Before and After – Cube
Before and After – Cube

You can see above, I was originally using one pixel = one character, and adding an empty space as characters are longer than they are wide. After filling in the empty space with the character next to it, you can see a vast difference in clarity, it’s almost staggering.

Colors, making it faster

Another challenge I encountered related colors, and speed. Below are two renders of Blender Group’s “Suzanne, after 3-weeks of code. The progression from the left picture to the right involves a lot more than changing a few lines of code to make it red and spin smoother.

After
After
Before
Before

I posted both to /r/blender and received 60 upvotes from one, but 1500 from the other. It’s not a mystery which one turned out better. The version on the left is extra slow because rust’s swiss-army-knife for compiling and running development code called cargo is set to debug when actually executing code from source files. This is so rust can give a detailed overview of the stack when something goes wrong. When using the release flag, the code runs incredibly fast.

I also changed the model loading code, so that meshes were broken down into groups based on their materials and each triangle is rendered with a unique color. This sounds easy, but it required a fundamental change to the entire program. Characters are another name for letters and someone a long time ago thought up a standard for them called Unicode. Originally, the framebuffer (another name for the grid of characters) was made up of a grid of Unicode values, which were concatenated into one large print to the screen command. This is perfectly fine, and very fast, but it doesn’t offer the most flexibility in ANSI escape codes, like the ones for FG and BG colors in terminals. So I had to switch to strings and define a color for each pixel separately.

Just a subtle brag, the \n character isn’t used to break lines into rows. It only saves a little bit of memory, but I’m proud of it.

Future Sloth (Robosloth? Slothinator? Sloth to the Future?)

I have a lot of ideas for where this program could go. For one, I would love to create some tests. I’ve never used CI before, and I’d love some help with that! Once that’s set up, we can hopefully publish it as a crate, and perhaps a push to the Archlinux, Fedora, and Ubuntu repositories. I could even make windows executables, somehow.

Before tests are written, there needs to be infrastructure for rendering stills, and not realtime terminal output. This is to ensure that no code in the future changes the render result. The stills could also be used to generate portable animations mentioned below.

A more interesting endeavor would be to create a portable-sloth. Once you have rendered your 3D model into frames, the rust program could export those frames as Javascript objects and generate a script so you can take your rendered model to the web! No rasterization, model loading, or issues with cross-term, as it would be updating a paragraph tag in the div at the desired framerate.

Thank you contributors!

Thank you to the people listed below, and their contributions to Sloth!

Maxgy – Rustfmt lint

donbright – STL model loading added, Rustfmt lint

jonathandturner – Crossterm port