A real-time 3D engine implemented entirely in .ps1 files — no modules, no
native dependencies, no NuGet packages. It does software rasterization of
triangle meshes into a depth-buffered ASCII/ANSI frame buffer and draws it in
Windows Terminal (or any ANSI-capable console) at interactive frame rates.
- 3D math pipeline (
src/PS3D.Math.ps1) —Vec3/Vec4/Mat4classes: dot/cross/lerp, 4×4 multiply, translation/rotation/scale, perspective projection, yaw/pitch camera with a view matrix and movement basis. - Rendering pipeline (
src/PS3D.Renderer.ps1) — software rasterizer:- view-space backface culling and flat-shaded directional lighting
- near-plane triangle clipping (Sutherland–Hodgman, view space)
- perspective divide → NDC → screen space
- barycentric triangle fill into a z-buffered char/light buffer
- 11-step ASCII brightness ramp (
.:;-=+*#%@) or ANSI true-colour output - optional depth-tested wireframe mode
- Meshes (
src/PS3D.Mesh.ps1) — built-inscube,pyramid,gem(icosahedron),torus,sphere, plus a Wavefront OBJ loader (v/f, n-gon fan triangulation, negative indices, auto-recentre/scale, auto-orient). - Interactive demo loop (
Start-PS3D.ps1) — fly-around camera, toggles, FPS counter, HUD, and a headless render mode for CI/validation. - Tests (
tests/Invoke-SmokeTest.ps1) — 30 assertions covering the math, OBJ parsing, clipping, and end-to-end rasterization.
| Requirement | Notes |
|---|---|
| Windows 10/11 | any edition |
| Windows Terminal (recommended) | full ANSI + UTF-8 support, best colours |
PowerShell 7 (pwsh) — recommended |
much faster than 5.1; get it via winget install Microsoft.PowerShell |
| or Windows PowerShell 5.1 | ships with Windows; works, just slower |
Classic
conhost.exealso works on Windows 10 1709+ (it understands the ANSI escapes used here), but Windows Terminal looks and feels better.
# PowerShell 7 (recommended)
pwsh -File .\Start-PS3D.ps1
# Windows PowerShell 5.1
powershell -ExecutionPolicy Bypass -File .\Start-PS3D.ps1
# or just double-click
PS3D.batExamples:
pwsh -File .\Start-PS3D.ps1 -Mesh sphere -Width 120 -Height 44
pwsh -File .\Start-PS3D.ps1 -Mesh .\examples\shuttle.obj -Fov 75
pwsh -File .\Start-PS3D.ps1 -Mesh cube -Wireframe -NoColor
pwsh -File .\Start-PS3D.ps1 -Mesh C:\models\teapot.obj| Key | Action |
|---|---|
| ← → ↑ ↓ | look around (yaw / pitch) |
W S |
move forward / back |
A D |
strafe left / right |
R F |
move up / down |
+ - |
dolly in / out |
space |
toggle auto-rotate |
V |
toggle wireframe / filled |
C |
toggle ANSI colour |
L |
toggle lighting |
N P |
next / previous built-in mesh |
H |
help screen |
Q / Esc |
quit |
-Mesh <name|path> builtin mesh or .obj file (default: torus)
-Width -Height framebuffer size in chars (default: console size)
-Fov <deg> vertical field of view (default: 60)
-Dist <units> initial camera distance (default: 4.2)
-Speed <rad/s> auto-rotate speed (default: 0.8)
-Fps <n> frame-rate cap (default: 30)
-Wireframe depth-tested wireframe mode
-NoColor disable ANSI true-colour shading
-NoHud hide the status line
-NoCulling show back faces (for inside-out OBJ files)
-SpinX <factor> tumble rate around X (default: 0.7)
-Headless render without a console UI
-Frames <n> headless frame count
-OutFile <path> headless: write final frame text
vertices ──► model·view matrix ──► view space
──► backface cull + directional lighting (face normal)
──► near-plane clip
──► perspective matrix → w-divide → NDC → screen space
──► barycentric fill: char + z-buffer + light level
──► one StringBuilder → a single Console::Write per frame
Per-pixel cost is the bottleneck (this is interpreted PowerShell, not a GPU): expect roughly 8–20 fps at 90×40 depending on triangle count and host. Tips for speed:
- use PowerShell 7, not 5.1 (it is several times faster in these loops)
- shrink the buffer:
-Width 80 -Height 32 -Wireframemode is much cheaper than filled triangles- fewer polygons help: built-ins are tuned to be light; huge OBJ files will crawl
Renders frames without needing a console and writes the last frame as text — used by the test suite and handy on CI machines:
pwsh -File .\Start-PS3D.ps1 -Headless -Frames 30 -Mesh torus -OutFile frame.txt
pwsh -File .\tests\Invoke-SmokeTest.ps1 # 30-assertion smoke test- Flat shading only (no per-vertex normals, no Gouraud/Phong, no textures).
- Near-plane clipping only; triangles are not clipped against the screen edges (they are cheaply bounding-box clamped instead).
- OBJ loader reads
v/frecords only —vn/vt/materials are ignored. - Interactive input requires a real console; use
-Headlesson CI/redirected output. - Console glyphs are ~2× taller than wide; the projection compensates, but extreme aspect ratios still look stretched.
- It's PowerShell: frame times are tens of milliseconds, not microseconds.
ps3d/
├── Start-PS3D.ps1 entry point + interactive/headless loop
├── PS3D.bat double-click launcher (Windows PowerShell)
├── src/
│ ├── PS3D.Math.ps1 Vec3/Vec4/Mat4, projection, camera
│ ├── PS3D.Mesh.ps1 Mesh class, built-ins, OBJ loader
│ └── PS3D.Renderer.ps1 z-buffer rasterizer, ANSI composer
├── examples/ cube.obj, shuttle.obj
└── tests/Invoke-SmokeTest.ps1