Skip to content

Repository files navigation

rasterminal

CI Release License: MIT C++17 Platforms Dependencies Last commit

A fast 3D model viewer in the terminal.

rasterminal spinning a model

rasterminal renders 3D models on the CPU and displays them in your terminal in real time, so you can view OBJ, PLY, STL, and glTF files anywhere you have a terminal, including over SSH with no display or GPU.

Contents

Quick start

# 1. Clone and build (release build, GCC or Clang)
git clone https://github.com/PavolUlicny/rasterminal.git
cd rasterminal
make

# 2. Grab a model to look at
curl -fsSL -o Duck.glb \
  https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/Duck/glTF-Binary/Duck.glb

# 3. Render it
./rasterminal Duck.glb

Drag with the mouse to orbit, scroll to zoom, press Space to spin, 1 to 3 to switch shading modes, Q to quit.

For a denser model, try the Stanford bunny:

curl -fsSL -o bunny.stl \
  https://raw.githubusercontent.com/reprap-io/reprapio_stanford_bunny/master/bunny.stl
./rasterminal bunny.stl

More test assets live in the Khronos glTF Sample Assets repository; any .glb, .gltf, .obj, .ply, or .stl file works.

Gallery

Wireframe Flat Phong
wireframe shading flat shading phong shading

How it works

rasterminal implements the same rasterization pipeline a GPU runs, entirely in CPU code. Each frame, every triangle is transformed from model space through the view and projection matrices into clip space. A world-space backface test rejects roughly half of all triangles before any projection work, with double-sided materials opting out of the test and flipping their normals instead. Triangles that cross the near plane are clipped so nothing renders behind the camera, and the remainder are conservatively rejected against the view frustum.

Surviving triangles go through the perspective divide and are scan-converted into fragments, with color, texture coordinates, world position, and normals all interpolated in a perspective-correct way across the triangle. A z-buffer keeps the nearest fragment at each pixel. Shading runs per fragment: flat shading evaluates Blinn-Phong lighting once per face, Phong shading evaluates it per pixel, and both are modulated by texture sampling and baked ambient occlusion.

Transparent surfaces (glTF BLEND materials, MTL d/Tr, or per-vertex alpha) take a separate path. Their fragments are gathered into a per-pixel list, sorted back to front, and composited over the finished opaque image, so the result is correct even where transparent geometry interpenetrates or is double-sided. Fully opaque models skip this path entirely.

The finished framebuffer is then written to the terminal. Each character cell represents two vertically stacked pixels, drawn as a half-block glyph whose foreground color is the top pixel and background color is the bottom, both in 24-bit ANSI color (perceptually quantized to the xterm-256 palette on terminals without truecolor support). The whole frame is assembled in a single buffer and flushed in one write.

Rendering is multi-threaded with a work-stealing scheduler. Each worker claims a chunk of triangles and rasterizes it end to end, committing opaque fragments through a per-pixel 64-bit atomic that packs depth and color into one slot, with no separate depth pre-pass. Transparency adds two further work-stealing phases, accumulate then resolve, but only for models that actually use blended materials.

Prebuilt binaries

Prebuilt binaries for Linux, macOS, and Windows are attached to each release. They use portable codegen (no -march=native, so they run on any CPU of the target architecture) and are self-contained: the Linux build statically links libstdc++/libgcc, and the Windows build statically links the C runtime, so neither needs extra runtime packages installed.

Download the archive for your platform, then:

# Linux / macOS
tar xzf rasterminal-<version>-<platform>.tar.gz
cd rasterminal-<version>-<platform>
chmod +x rasterminal
./rasterminal <model>

On Windows, extract the .zip and run rasterminal.exe from a terminal that supports ANSI escapes and UTF-8 (Windows Terminal is recommended; legacy cmd.exe is not supported).

Each release includes a checksums.txt; verify your download with sha256sum -c checksums.txt (Linux), shasum -a 256 -c checksums.txt (macOS), or Get-FileHash (Windows). checksums.txt lists all platforms, so check the line for the archive you downloaded; -c reports the other archives as missing, which is expected. To build from source instead, see Build.

Build

Two build systems are provided. Each has a release variant (-march=native, fastest on the build machine) and a portable variant (no -march=native, runs on any CPU of the target architecture). All other speed flags (-O3 -ffast-math -funroll-loops, LTO, and so on) apply to both.

Linux / macOS: Make (GCC or Clang)

make                     # release: -O3 -march=native + all speed flags
make CXX=clang++         # same with Clang
make portable            # distributable binary, no -march=native
make dist                # release artifact: portable + static libstdc++/libgcc (Linux)
make debug               # -O0 -g
make test                # build and run test suite

dist is the binary to ship: same portable codegen, but it statically links libstdc++/libgcc so it runs on older distros without a matching GLIBCXX symbol. glibc stays dynamic, so build on the oldest distro you need to support.

All platforms: CMake

The configurations below are also available as presets (CMake ≥ 3.21):

cmake --preset release      # or: portable | dist | debug | reldbg | clang
cmake --build --preset release
ctest --preset release      # release and dist define test presets

Equivalent explicit invocations:

# Release
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j

# Debug
cmake -B build-debug -DCMAKE_BUILD_TYPE=Debug
cmake --build build-debug -j

# Portable (runs on any x86-64, not just the build machine)
cmake -B build-portable -DCMAKE_BUILD_TYPE=Release -DRASTERMINAL_PORTABLE=ON
cmake --build build-portable -j

# Dist (portable + static libstdc++/libgcc, the release artifact)
cmake -B build-dist -DCMAKE_BUILD_TYPE=Release -DRASTERMINAL_PORTABLE=ON -DRASTERMINAL_STATIC_LIBSTDCXX=ON
cmake --build build-dist -j

# Tests
cmake --build build --target rasterminal_tests -j
ctest --test-dir build --output-on-failure

# Clang
cmake -B build -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release
cmake --build build -j

# MSVC (Developer PowerShell or cmd with vcvars)
cmake -B build-msvc
cmake --build build-msvc --config Release -j
ctest --test-dir build-msvc -C Release --output-on-failure

Install

Both build systems install the binary, the man page, and the license/notices following the GNU directory layout (defaulting to /usr/local). Build first, then install:

make                     # build whichever variant you want to ship (or: make dist)
sudo make install        # binary -> /usr/local/bin, man page -> .../share/man/man1, docs -> .../share/doc/rasterminal
sudo make uninstall      # remove everything install added

# CMake equivalent (after configuring/building a build dir):
sudo cmake --install build

Override the prefix to install without root, or stage into a fakeroot for packaging:

make install PREFIX=~/.local              # no sudo; ensure ~/.local/bin is on PATH
make install DESTDIR=/tmp/pkg PREFIX=/usr # staged install for packagers
cmake --install build --prefix ~/.local   # CMake prefix override

Usage

rasterminal [options] <model>
Flag Short Default Description
--shading -s phong wireframe, flat, phong
--bg -b black black, gray, white
--lighting -l dual dual, single, flat
--wireframe-color -w white white, red, green, yellow, cyan, magenta
--yaw none 0 Initial camera yaw in degrees [-180, 180]; positive turns the model left on screen
--pitch none -17.2 Initial camera pitch in degrees [-180, 180]; negative looks down from above
--zoom none 1 Initial zoom [0.2, 100] as a size multiplier of the auto-fit framing; 2 = twice as close; the bounds equal the interactive scroll range
--cull / --no-cull none on Backface culling initial state
--texture / --no-texture none on Texture rendering initial state
--spin / --no-spin -S off Auto-rotation initial state
--threads [N] -j [N] min(cores, 4) Worker threads; bare -j uses all cores; N above the CPU thread count is clamped
--fps [N] -f [N] 60 Frame cap; bare -f uncaps
--smooth-angle none 60 Crease angle in degrees [0, 180] for computed normals; 0 = faceted, 180 = fully smooth (ignored when an OBJ authors smoothing groups)
--color none auto truecolor/24bit, 256, auto
--spin-speed none 45 Auto-rotation speed in degrees per second (positive number); applies whenever spinning is active
--spin-direction none left left, right: the way the model's front face moves on screen
--bench [N] -B [N] 200 Headless benchmark over N frames; prints a startup/runtime report to stderr and exits
--bench-size none 200x120 Bench framebuffer size in pixels (WxH); requires --bench
--bench-warmup none 20 Warmup frames discarded before measurement; requires --bench
--ao / --no-ao none on Baked ambient occlusion
--hud / --no-hud none shown HUD status line
--help -h none Print usage and exit
--version -V none Print version and exit

String values are case-insensitive. Long flags that take a value accept --flag value or --flag=value; short flags accept -f value or -fvalue. Boolean flags take no value: --cull=on is an error, not a synonym for --cull. The paired flags above (for example --cull and --no-cull) select the two states directly, and where both appear the later one on the command line wins.

Controls

Key Action
W A S D / arrow keys Orbit camera
+ / - Zoom in / out
Left-button drag Orbit camera
Scroll wheel Zoom
1 2 3 Wireframe / flat / Phong shading
L Cycle lighting (dual → single → flat)
B Cycle background (black → gray → white)
C Cycle wireframe color
T Toggle texture rendering
K Toggle backface culling
Space Toggle auto-rotation
R Reset to the state set by the command-line flags (their defaults when not passed)
Q / Esc Quit

Supported formats

Format Notes
OBJ / MTL Triangles, quads, n-gons; diffuse (map_Kd), specular (map_Ks), and normal (map_Bump / norm) maps
PLY ASCII and binary (LE/BE); vertex and face colors
STL ASCII and binary; the format's conventional Z-up orientation is remapped so models load upright
glTF 2.0 External and embedded (GLB); PBR materials, vertex colors, double-sided, second UV set (TEXCOORD_1); KHR_draco_mesh_compression, EXT_meshopt_compression/KHR_meshopt_compression, KHR_texture_basisu (KTX2), EXT_texture_webp, KHR_materials_unlit, KHR_texture_transform

Requirements

Any terminal with:

  • UTF-8 support
  • ANSI color: 24-bit (truecolor) where supported, with an automatic 256-color fallback elsewhere, detected from COLORTERM/TERM and overridable with --color (a dumb terminal, or a Windows console that cannot enable ANSI escape processing, is rejected outright)
  • Mouse reporting (for drag-to-orbit and scroll-to-zoom)

Interactive rendering also requires that both standard input and standard output be the terminal: if either is piped or redirected, rasterminal exits with an error instead of emitting escape sequences into the stream. The headless --bench mode is exempt.

Works well with: iTerm2, kitty, WezTerm, Windows Terminal, and most modern Linux terminals.

Project status

Pre-1.0 and under active development. rasterminal works today, but it is still maturing: expect rough edges, and CLI flags or controls may change before 1.0.

Third-party libraries

Vendored under vendor/; see THIRD_PARTY_NOTICES for full license texts.

Library Version License Use
cgltf master (post-v1.15) MIT glTF / GLB parsing
stb_image v2.30 MIT / Unlicense Image loading
stl_reader v2.0 BSD-2 STL parsing
tinyobjloader v2.0.0rc13 MIT OBJ / MTL parsing
tinyply 3.0 Public domain PLY parsing
meshoptimizer v1.1 MIT Vertex cache / overdraw / fetch optimization
draco 1.5.7 Apache-2.0 Draco mesh decompression (KHR_draco_mesh_compression)
basis_universal v2_1_0r Apache-2.0 KTX2 / Basis Universal texture transcoding (KHR_texture_basisu)
zstd bundled w/ basis_universal v2_1_0r BSD-3 Zstd decompression for KTX2 UASTC payloads
libwebp v1.6.0 BSD-3 + PATENTS WebP texture decoding (EXT_texture_webp)

License

rasterminal is released under the MIT License; see LICENSE. Vendored third-party libraries retain their own licenses, reproduced in full in THIRD_PARTY_NOTICES.