Skip to main content

Real-time 3D rendering engine with ECS architecture, built on pure wgpu

Project description

ManifoldX

PyPI version Python versions License Tests

A real-time 3D rendering engine built on pure wgpu with an Entity Component System (ECS) architecture. Written in Python with numpy for high-performance data handling.

⚠️ Beta / Academic Project — This is an experimental proof-of-concept exploring the extent to which Python can be used for high-performance graphics via wgpu. Not recommended for production use. Expect bugs, breaking changes, and missing features.

Motivation

Can Python + numpy reasonably power a modern real-time rendering pipeline? This project tests that question by building:

  • An ECS with Structure-of-Arrays (SoA) layout for cache-efficient data access
  • Instanced GPU rendering with material-specific pipelines
  • PBR (Physically Based Rendering) with GGX BRDF
  • A Pythonic API for 3D graphics

Spoiler: Python is surprisingly capable, but there are trade-offs. The ECS overhead is minimal (~microseconds per frame), but the rendering loop must be carefully optimized to avoid Python overhead.

Installation

# Install from PyPI
pip install manifold-gfx

# Or install from source
pip install manifold-gfx

Requirements:

  • Python 3.13+
  • GPU with WebGPU support (via wgpu backend)
    • Vulkan on Linux
    • Metal on macOS
    • D3D12 on Windows

Quick Start

import manifoldx as mx
import numpy as np

from manifoldx.components import Transform, Mesh, Material
from manifoldx.resources import StandardMaterial, PointLight, cube, sphere

# Create engine with default settings
engine = mx.Engine("My First Scene")

# Create a cube and sphere
cube_geo = cube(1, 1, 1)
sphere_geo = sphere(0.7, 32)

# Create PBR materials (roughness: 0-1, metallic: 0-1)
red_shiny = StandardMaterial(color="#ff3333", roughness=0.15, metallic=0.9)
blue_dull = StandardMaterial(color="#3366ff", roughness=0.8, metallic=0.0)

# Spawn entities
engine.spawn(
    Mesh(cube_geo),
    Material(red_shiny),
    Transform(pos=(-1.5, 0, 0)),
)

engine.spawn(
    Mesh(sphere_geo),
    Material(blue_dull),
    Transform(pos=(1.5, 0, 0)),
)

# Add an orbiting light
light = PointLight(color="#ffffff", intensity=15.0, position=(5, 5, 5))
engine.set_lights([light])

# Animate
@engine.system
def animate_lights(query: mx.Query[Transform], dt: float):
    t = engine.elapsed
    light.position = (
        5 * np.cos(t * 0.7),
        3 + np.sin(t * 0.5) * 2,
        5 * np.sin(t * 0.7),
    )

# Auto-fit camera to view the scene
engine.camera.fit(radius=5.0, azimuth=30, elevation=35)

# Run!
engine.run()

Save as my_scene.py and run:

python my_scene.py

Examples

Example Description
hello_world.py Minimal empty window
cube.py Rotating cube with Phong material
pbr_demo.py 3×2 grid demonstrating PBR materials + 3 orbiting lights
spheres.py Many spheres with physics-like behavior

Run an example:

python -m examples.pbr_demo

Features

ECS Architecture

  • Structure of Arrays (SoA) layout for each component
  • Vectorized numpy operations for batch transforms
  • Free-list for efficient entity reuse
  • Component view with operator overloads (+=, *=, etc.)

Rendering

  • Instanced drawing — single draw call per (geometry, material) batch
  • Material-specific pipelines — each material type compiles its own WGSL shader
  • Transform caching — dirty-flag optimization to avoid recomputing matrices
  • Shared transform buffer — all instance transforms uploaded once per frame

Materials & Lighting

  • BasicMaterial — unlit flat color with simple diffuse
  • StandardMaterial — full PBR with GGX BRDF
    • Roughness/metallic workflow
    • Multiple point lights with inverse-square attenuation
    • Reinhard tonemapping + gamma correction
  • External lights — passed to engine like camera (not in ECS)

Camera

  • Perspective projection (WebGPU NDC)
  • Spherical coordinate orbit controls
  • Fit/fit_bounds for automatic framing

Geometries

  • Cube (with normals)
  • UV Sphere (with normals, CCW winding)
  • Plane (with normals)

Architecture Highlights

The ECS uses numpy arrays for all component data. When you call query[Transform].pos += velocity * dt, it's a single vectorized numpy operation spanning thousands of entities.

Limitations (Known)

  • ❌ No shadows
  • ❌ No texture support
  • ❌ No environment/IBL mapping
  • ❌ Single material params per draw call (not per-instance)
  • ❌ Only point lights in PBR shader
  • ❌ Limited to ~100k entities

Future Ideas

This is an academic/experimental project. Ideas for future development:

  1. Per-instance material data — Storage buffer for varying roughness/metallic per instance in a single draw
  2. Shadow mapping — Shadow pass + PCF sampling
  3. Texture maps — Diffuse, normal, roughness textures via storage buffers
  4. Spot/Directional lights — Extend PBR shader
  5. Environment mapping — IBL with prefiltered radiance
  6. Skinned animation — Bone transforms in vertex shader
  7. Post-processing — Bloom, DOF, TAA
  8. Deferred rendering — Forward+ / clustered lighting for many lights

Contributing

Contributions welcome! This is an educational project — all skill levels encouraged.

Areas needing work:

  • Bug fixes and stability improvements
  • Additional geometry types (torus, cylinder, etc.)
  • More material types (toon, unlit with texture)
  • Shadow implementation
  • Performance profiling and optimization

Getting started:

# Clone and set up
git clone https://github.com/apiad/manifoldx.git
cd manifoldx
pip install -e ".[dev]"

# Run tests
make test

# Run an example
python -m examples.cube

Testing

# Run all tests
make test

# Run specific test file
python -m pytest tests/test_ecs.py -v

Current test coverage: 150+ tests covering ECS operations, components, materials, rendering, and camera.

License

MIT License — See LICENSE file.

Credits

  • wgpu — Pure Python WebGPU bindings
  • PyGfx — Reference for WGSL shader patterns
  • rendercanvas — Window management

Disclaimer: This project is for educational and research purposes. Not optimized for production use. Performance characteristics will vary by hardware and Python version.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

manifold_gfx-0.1.0.tar.gz (159.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

manifold_gfx-0.1.0-py3-none-any.whl (33.7 kB view details)

Uploaded Python 3

File details

Details for the file manifold_gfx-0.1.0.tar.gz.

File metadata

  • Download URL: manifold_gfx-0.1.0.tar.gz
  • Upload date:
  • Size: 159.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for manifold_gfx-0.1.0.tar.gz
Algorithm Hash digest
SHA256 df2f8a806ffa23025b69eb675105ff3e18b77098328ec77805b03b0299af1949
MD5 73595f85400e019554a87b7f8b219890
BLAKE2b-256 81a35695d2b5c4af72058207b8d57218429703e400b2b11de76bcb6e90226f4e

See more details on using hashes here.

File details

Details for the file manifold_gfx-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: manifold_gfx-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 33.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for manifold_gfx-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 19aa48b3f5f437591a8713b4ac0360b12b91ea15ed5d9df941dc7604352b6df8
MD5 1530b5e4faf0d052a689e4ad63bf7e7f
BLAKE2b-256 8e48d3dfeec7b730399b8a0d9d1e3a5294e57a39851e7a6d31f623387c4e9cc7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page