A Python framework for high-performance simulation and graphics programming that JIT compiles Python functions to efficient GPU/CPU kernel code.
Warp provides rendering capabilities for visualizing simulation results and creating graphics output. The rendering module supports both OpenGL-based real-time rendering and USD-based offline rendering for production workflows.
Real-time OpenGL-based renderer for interactive visualization and debugging.
class OpenGLRenderer:
"""OpenGL-based real-time renderer."""
def __init__(self, width: int, height: int, headless: bool = False):
"""
Create OpenGL renderer with specified resolution.
Args:
width: Render target width in pixels
height: Render target height in pixels
headless: Enable headless rendering (no window)
"""
def render(self, mesh: Mesh, camera_pos: vec3, camera_target: vec3) -> None:
"""
Render mesh with specified camera parameters.
Args:
mesh: Triangle mesh to render
camera_pos: Camera position in world space
camera_target: Camera look-at target
"""
def render_points(self,
positions: array,
colors: array = None,
point_size: float = 1.0) -> None:
"""
Render point cloud.
Args:
positions: Array of 3D point positions
colors: Array of point colors (optional)
point_size: Size of rendered points
"""
def render_lines(self,
positions: array,
indices: array,
colors: array = None,
line_width: float = 1.0) -> None:
"""
Render line segments.
Args:
positions: Array of vertex positions
indices: Array of line segment indices
colors: Array of line colors (optional)
line_width: Width of rendered lines
"""
def set_camera(self,
pos: vec3,
target: vec3,
up: vec3 = vec3(0, 1, 0)) -> None:
"""Set camera position and orientation."""
def set_projection(self,
fov: float = 45.0,
near: float = 0.1,
far: float = 1000.0) -> None:
"""Set camera projection parameters."""
def clear(self, color: vec4 = vec4(0, 0, 0, 1)) -> None:
"""Clear render target with specified color."""
def present(self) -> None:
"""Present rendered frame to display."""
def save_image(self, filename: str) -> None:
"""Save rendered frame to image file."""
def get_pixels(self) -> array:
"""Get rendered pixels as array."""
def close(self) -> None:
"""Clean up renderer resources."""Universal Scene Description (USD) renderer for production-quality output and film workflows.
class UsdRenderer:
"""USD-based renderer for production workflows."""
def __init__(self, stage_path: str):
"""
Create USD renderer with specified stage.
Args:
stage_path: Path to USD stage file
"""
def add_mesh(self,
mesh: Mesh,
transform: mat44 = None,
material_path: str = None) -> str:
"""
Add mesh to USD stage.
Args:
mesh: Triangle mesh to add
transform: World transformation matrix
material_path: Path to material definition
Returns:
USD prim path for added mesh
"""
def add_points(self,
positions: array,
radii: array = None,
colors: array = None) -> str:
"""
Add point instances to USD stage.
Args:
positions: Array of point positions
radii: Array of point radii (optional)
colors: Array of point colors (optional)
Returns:
USD prim path for point instances
"""
def add_camera(self,
pos: vec3,
target: vec3,
up: vec3 = vec3(0, 1, 0),
fov: float = 45.0) -> str:
"""
Add camera to USD stage.
Returns:
USD prim path for camera
"""
def add_light(self,
light_type: str,
intensity: float = 1.0,
color: vec3 = vec3(1, 1, 1),
transform: mat44 = None) -> str:
"""
Add light to USD stage.
Args:
light_type: Type of light ('distant', 'sphere', 'rect')
intensity: Light intensity
color: Light color
transform: Light transformation
Returns:
USD prim path for light
"""
def set_time_sample(self, frame: int, time: float) -> None:
"""Set time sample for animation."""
def save(self, output_path: str = None) -> None:
"""
Save USD stage to file.
Args:
output_path: Output file path (uses stage_path if None)
"""
def render_frame(self,
camera_path: str,
output_image: str,
width: int = 1920,
height: int = 1080) -> None:
"""
Render single frame to image file.
Args:
camera_path: USD path to camera
output_image: Output image file path
width: Image width in pixels
height: Image height in pixels
"""Utility functions for color mapping and visualization helpers.
def bourke_color_map(value: float, min_val: float, max_val: float) -> vec3:
"""
Map scalar value to Bourke color scheme.
Args:
value: Scalar value to map
min_val: Minimum value in range
max_val: Maximum value in range
Returns:
RGB color as vec3
"""import warp as wp
import warp.render as render
import numpy as np
# Create renderer
renderer = render.OpenGLRenderer(width=800, height=600)
# Create simple mesh (triangle)
vertices = wp.array([
[0.0, 1.0, 0.0], # Top vertex
[-1.0, -1.0, 0.0], # Bottom left
[1.0, -1.0, 0.0] # Bottom right
], dtype=wp.vec3, device='cuda')
indices = wp.array([
[0, 1, 2] # Single triangle
], dtype=wp.int32, device='cuda')
mesh = wp.Mesh(vertices, indices)
# Set up camera
camera_pos = wp.vec3(0, 0, 5)
camera_target = wp.vec3(0, 0, 0)
renderer.set_camera(camera_pos, camera_target)
# Render loop
for frame in range(100):
renderer.clear()
renderer.render(mesh, camera_pos, camera_target)
renderer.present()
# Rotate camera
angle = frame * 0.1
camera_pos = wp.vec3(5 * np.sin(angle), 0, 5 * np.cos(angle))
# Save final frame
renderer.save_image("output.png")
renderer.close()import warp as wp
import warp.render as render
# Generate random point cloud
num_points = 10000
positions = wp.array(np.random.randn(num_points, 3).astype(np.float32),
device='cuda')
# Color points based on height
@wp.kernel
def color_by_height(positions: wp.array(dtype=wp.vec3),
colors: wp.array(dtype=wp.vec3)):
i = wp.tid()
pos = positions[i]
# Map Y coordinate to color
height = pos[1]
color = render.bourke_color_map(height, -3.0, 3.0)
colors[i] = color
colors = wp.zeros(num_points, dtype=wp.vec3, device='cuda')
wp.launch(color_by_height, dim=num_points, inputs=[positions, colors])
# Render point cloud
renderer = render.OpenGLRenderer(1024, 768)
renderer.set_camera(wp.vec3(5, 5, 5), wp.vec3(0, 0, 0))
renderer.clear()
renderer.render_points(positions, colors, point_size=2.0)
renderer.save_image("point_cloud.png")import warp as wp
import warp.render as render
# Create USD renderer
usd_renderer = render.UsdRenderer("animation.usda")
# Add camera
camera_path = usd_renderer.add_camera(
pos=wp.vec3(10, 10, 10),
target=wp.vec3(0, 0, 0)
)
# Add light
usd_renderer.add_light(
light_type='distant',
intensity=2.0,
color=wp.vec3(1, 0.9, 0.8)
)
# Simulate and render animation
num_frames = 120
dt = 1.0 / 60.0
# Initialize particle system
positions = wp.zeros(1000, dtype=wp.vec3, device='cuda')
velocities = wp.zeros(1000, dtype=wp.vec3, device='cuda')
@wp.kernel
def update_particles(positions: wp.array(dtype=wp.vec3),
velocities: wp.array(dtype=wp.vec3),
dt: float):
i = wp.tid()
# Simple gravity simulation
gravity = wp.vec3(0, -9.81, 0)
velocities[i] = velocities[i] + gravity * dt
positions[i] = positions[i] + velocities[i] * dt
# Bounce off ground
if positions[i][1] < 0.0:
positions[i] = wp.vec3(positions[i][0], 0.0, positions[i][2])
velocities[i] = wp.vec3(velocities[i][0], -0.8 * velocities[i][1], velocities[i][2])
# Animation loop
for frame in range(num_frames):
# Update simulation
wp.launch(update_particles, dim=1000, inputs=[positions, velocities, dt])
# Set time sample
time = frame * dt
usd_renderer.set_time_sample(frame, time)
# Add particles for this frame
point_path = usd_renderer.add_points(
positions,
radii=wp.full(1000, 0.05, device='cuda')
)
# Save USD file
usd_renderer.save("particle_animation.usda")
# Render frames
for frame in range(0, num_frames, 5): # Every 5th frame
usd_renderer.render_frame(
camera_path,
f"frame_{frame:04d}.png",
width=1920,
height=1080
)import warp as wp
import warp.render as render
# Create complex mesh (sphere)
def create_sphere_mesh(radius: float, resolution: int):
# Generate sphere vertices and indices
vertices = []
indices = []
for i in range(resolution + 1):
for j in range(resolution + 1):
theta = np.pi * i / resolution
phi = 2 * np.pi * j / resolution
x = radius * np.sin(theta) * np.cos(phi)
y = radius * np.cos(theta)
z = radius * np.sin(theta) * np.sin(phi)
vertices.append([x, y, z])
# Generate triangle indices
for i in range(resolution):
for j in range(resolution):
v0 = i * (resolution + 1) + j
v1 = v0 + 1
v2 = (i + 1) * (resolution + 1) + j
v3 = v2 + 1
indices.append([v0, v2, v1])
indices.append([v1, v2, v3])
return wp.array(vertices, dtype=wp.vec3), wp.array(indices, dtype=wp.int32)
# Create sphere mesh
vertices, indices = create_sphere_mesh(radius=2.0, resolution=32)
sphere_mesh = wp.Mesh(vertices, indices)
# OpenGL rendering with lighting
renderer = render.OpenGLRenderer(1200, 800)
renderer.set_camera(wp.vec3(0, 0, 8), wp.vec3(0, 0, 0))
renderer.set_projection(fov=60.0)
# Render with rotation
for frame in range(360):
renderer.clear(wp.vec4(0.1, 0.1, 0.2, 1.0))
# Rotate mesh
angle = frame * np.pi / 180
transform = wp.mat44(
np.cos(angle), 0, np.sin(angle), 0,
0, 1, 0, 0,
-np.sin(angle), 0, np.cos(angle), 0,
0, 0, 0, 1
)
renderer.render(sphere_mesh, renderer.camera_pos, renderer.camera_target)
if frame % 30 == 0: # Save every 30 frames
renderer.save_image(f"sphere_{frame:03d}.png")
renderer.close()import warp as wp
import warp.render as render
# Set up simulation
@wp.kernel
def cloth_simulation(positions: wp.array(dtype=wp.vec3),
velocities: wp.array(dtype=wp.vec3),
forces: wp.array(dtype=wp.vec3),
dt: float):
i = wp.tid()
# Apply forces and integrate
vel = velocities[i] + forces[i] * dt
pos = positions[i] + vel * dt
# Simple constraints
if pos[1] < 0.0: # Ground constraint
pos = wp.vec3(pos[0], 0.0, pos[2])
vel = wp.vec3(vel[0], 0.0, vel[2])
positions[i] = pos
velocities[i] = vel
# Initialize cloth mesh
cloth_res = 32
cloth_positions = wp.zeros(cloth_res * cloth_res, dtype=wp.vec3, device='cuda')
cloth_velocities = wp.zeros_like(cloth_positions)
cloth_forces = wp.zeros_like(cloth_positions)
# Create renderer
renderer = render.OpenGLRenderer(1024, 768)
renderer.set_camera(wp.vec3(5, 5, 5), wp.vec3(0, 0, 0))
# Real-time simulation loop
dt = 1.0 / 60.0
running = True
while running:
# Update simulation
wp.launch(cloth_simulation,
dim=cloth_res * cloth_res,
inputs=[cloth_positions, cloth_velocities, cloth_forces, dt])
# Render frame
renderer.clear()
renderer.render_points(cloth_positions, point_size=3.0)
renderer.present()
# Check for exit condition
# running = check_user_input() # Implementation dependent# Renderer types
class Renderer:
"""Base renderer interface."""
def render(self, *args, **kwargs) -> None:
"""Render scene objects."""
def save(self, filename: str) -> None:
"""Save rendered output."""
# Mesh type for rendering
class RenderMesh:
"""Mesh optimized for rendering."""
vertices: array # Vertex positions
indices: array # Triangle indices
normals: array # Vertex normals (optional)
uvs: array # Texture coordinates (optional)
colors: array # Vertex colors (optional)
# Camera parameters
class Camera:
"""Camera configuration."""
position: vec3 # Camera position
target: vec3 # Look-at target
up: vec3 # Up vector
fov: float # Field of view (degrees)
near: float # Near clipping plane
far: float # Far clipping plane
# Light configuration
class Light:
"""Light source configuration."""
type: str # Light type ('distant', 'point', 'spot')
position: vec3 # Light position
direction: vec3 # Light direction
intensity: float # Light intensity
color: vec3 # Light color
# Material properties
class Material:
"""Rendering material properties."""
diffuse_color: vec3 # Base color
metallic: float # Metallic factor
roughness: float # Surface roughness
emission: vec3 # Emissive colorInstall with Tessl CLI
npx tessl i tessl/pypi-warp-lang