0
# Rendering
1
2
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.
3
4
## Capabilities
5
6
### OpenGL Renderer
7
8
Real-time OpenGL-based renderer for interactive visualization and debugging.
9
10
```python { .api }
11
class OpenGLRenderer:
12
"""OpenGL-based real-time renderer."""
13
14
def __init__(self, width: int, height: int, headless: bool = False):
15
"""
16
Create OpenGL renderer with specified resolution.
17
18
Args:
19
width: Render target width in pixels
20
height: Render target height in pixels
21
headless: Enable headless rendering (no window)
22
"""
23
24
def render(self, mesh: Mesh, camera_pos: vec3, camera_target: vec3) -> None:
25
"""
26
Render mesh with specified camera parameters.
27
28
Args:
29
mesh: Triangle mesh to render
30
camera_pos: Camera position in world space
31
camera_target: Camera look-at target
32
"""
33
34
def render_points(self,
35
positions: array,
36
colors: array = None,
37
point_size: float = 1.0) -> None:
38
"""
39
Render point cloud.
40
41
Args:
42
positions: Array of 3D point positions
43
colors: Array of point colors (optional)
44
point_size: Size of rendered points
45
"""
46
47
def render_lines(self,
48
positions: array,
49
indices: array,
50
colors: array = None,
51
line_width: float = 1.0) -> None:
52
"""
53
Render line segments.
54
55
Args:
56
positions: Array of vertex positions
57
indices: Array of line segment indices
58
colors: Array of line colors (optional)
59
line_width: Width of rendered lines
60
"""
61
62
def set_camera(self,
63
pos: vec3,
64
target: vec3,
65
up: vec3 = vec3(0, 1, 0)) -> None:
66
"""Set camera position and orientation."""
67
68
def set_projection(self,
69
fov: float = 45.0,
70
near: float = 0.1,
71
far: float = 1000.0) -> None:
72
"""Set camera projection parameters."""
73
74
def clear(self, color: vec4 = vec4(0, 0, 0, 1)) -> None:
75
"""Clear render target with specified color."""
76
77
def present(self) -> None:
78
"""Present rendered frame to display."""
79
80
def save_image(self, filename: str) -> None:
81
"""Save rendered frame to image file."""
82
83
def get_pixels(self) -> array:
84
"""Get rendered pixels as array."""
85
86
def close(self) -> None:
87
"""Clean up renderer resources."""
88
```
89
90
### USD Renderer
91
92
Universal Scene Description (USD) renderer for production-quality output and film workflows.
93
94
```python { .api }
95
class UsdRenderer:
96
"""USD-based renderer for production workflows."""
97
98
def __init__(self, stage_path: str):
99
"""
100
Create USD renderer with specified stage.
101
102
Args:
103
stage_path: Path to USD stage file
104
"""
105
106
def add_mesh(self,
107
mesh: Mesh,
108
transform: mat44 = None,
109
material_path: str = None) -> str:
110
"""
111
Add mesh to USD stage.
112
113
Args:
114
mesh: Triangle mesh to add
115
transform: World transformation matrix
116
material_path: Path to material definition
117
118
Returns:
119
USD prim path for added mesh
120
"""
121
122
def add_points(self,
123
positions: array,
124
radii: array = None,
125
colors: array = None) -> str:
126
"""
127
Add point instances to USD stage.
128
129
Args:
130
positions: Array of point positions
131
radii: Array of point radii (optional)
132
colors: Array of point colors (optional)
133
134
Returns:
135
USD prim path for point instances
136
"""
137
138
def add_camera(self,
139
pos: vec3,
140
target: vec3,
141
up: vec3 = vec3(0, 1, 0),
142
fov: float = 45.0) -> str:
143
"""
144
Add camera to USD stage.
145
146
Returns:
147
USD prim path for camera
148
"""
149
150
def add_light(self,
151
light_type: str,
152
intensity: float = 1.0,
153
color: vec3 = vec3(1, 1, 1),
154
transform: mat44 = None) -> str:
155
"""
156
Add light to USD stage.
157
158
Args:
159
light_type: Type of light ('distant', 'sphere', 'rect')
160
intensity: Light intensity
161
color: Light color
162
transform: Light transformation
163
164
Returns:
165
USD prim path for light
166
"""
167
168
def set_time_sample(self, frame: int, time: float) -> None:
169
"""Set time sample for animation."""
170
171
def save(self, output_path: str = None) -> None:
172
"""
173
Save USD stage to file.
174
175
Args:
176
output_path: Output file path (uses stage_path if None)
177
"""
178
179
def render_frame(self,
180
camera_path: str,
181
output_image: str,
182
width: int = 1920,
183
height: int = 1080) -> None:
184
"""
185
Render single frame to image file.
186
187
Args:
188
camera_path: USD path to camera
189
output_image: Output image file path
190
width: Image width in pixels
191
height: Image height in pixels
192
"""
193
```
194
195
### Rendering Utilities
196
197
Utility functions for color mapping and visualization helpers.
198
199
```python { .api }
200
def bourke_color_map(value: float, min_val: float, max_val: float) -> vec3:
201
"""
202
Map scalar value to Bourke color scheme.
203
204
Args:
205
value: Scalar value to map
206
min_val: Minimum value in range
207
max_val: Maximum value in range
208
209
Returns:
210
RGB color as vec3
211
"""
212
```
213
214
## Usage Examples
215
216
### Basic OpenGL Rendering
217
```python
218
import warp as wp
219
import warp.render as render
220
import numpy as np
221
222
# Create renderer
223
renderer = render.OpenGLRenderer(width=800, height=600)
224
225
# Create simple mesh (triangle)
226
vertices = wp.array([
227
[0.0, 1.0, 0.0], # Top vertex
228
[-1.0, -1.0, 0.0], # Bottom left
229
[1.0, -1.0, 0.0] # Bottom right
230
], dtype=wp.vec3, device='cuda')
231
232
indices = wp.array([
233
[0, 1, 2] # Single triangle
234
], dtype=wp.int32, device='cuda')
235
236
mesh = wp.Mesh(vertices, indices)
237
238
# Set up camera
239
camera_pos = wp.vec3(0, 0, 5)
240
camera_target = wp.vec3(0, 0, 0)
241
renderer.set_camera(camera_pos, camera_target)
242
243
# Render loop
244
for frame in range(100):
245
renderer.clear()
246
renderer.render(mesh, camera_pos, camera_target)
247
renderer.present()
248
249
# Rotate camera
250
angle = frame * 0.1
251
camera_pos = wp.vec3(5 * np.sin(angle), 0, 5 * np.cos(angle))
252
253
# Save final frame
254
renderer.save_image("output.png")
255
renderer.close()
256
```
257
258
### Point Cloud Visualization
259
```python
260
import warp as wp
261
import warp.render as render
262
263
# Generate random point cloud
264
num_points = 10000
265
positions = wp.array(np.random.randn(num_points, 3).astype(np.float32),
266
device='cuda')
267
268
# Color points based on height
269
@wp.kernel
270
def color_by_height(positions: wp.array(dtype=wp.vec3),
271
colors: wp.array(dtype=wp.vec3)):
272
i = wp.tid()
273
pos = positions[i]
274
275
# Map Y coordinate to color
276
height = pos[1]
277
color = render.bourke_color_map(height, -3.0, 3.0)
278
colors[i] = color
279
280
colors = wp.zeros(num_points, dtype=wp.vec3, device='cuda')
281
wp.launch(color_by_height, dim=num_points, inputs=[positions, colors])
282
283
# Render point cloud
284
renderer = render.OpenGLRenderer(1024, 768)
285
renderer.set_camera(wp.vec3(5, 5, 5), wp.vec3(0, 0, 0))
286
287
renderer.clear()
288
renderer.render_points(positions, colors, point_size=2.0)
289
renderer.save_image("point_cloud.png")
290
```
291
292
### Animation with USD
293
```python
294
import warp as wp
295
import warp.render as render
296
297
# Create USD renderer
298
usd_renderer = render.UsdRenderer("animation.usda")
299
300
# Add camera
301
camera_path = usd_renderer.add_camera(
302
pos=wp.vec3(10, 10, 10),
303
target=wp.vec3(0, 0, 0)
304
)
305
306
# Add light
307
usd_renderer.add_light(
308
light_type='distant',
309
intensity=2.0,
310
color=wp.vec3(1, 0.9, 0.8)
311
)
312
313
# Simulate and render animation
314
num_frames = 120
315
dt = 1.0 / 60.0
316
317
# Initialize particle system
318
positions = wp.zeros(1000, dtype=wp.vec3, device='cuda')
319
velocities = wp.zeros(1000, dtype=wp.vec3, device='cuda')
320
321
@wp.kernel
322
def update_particles(positions: wp.array(dtype=wp.vec3),
323
velocities: wp.array(dtype=wp.vec3),
324
dt: float):
325
i = wp.tid()
326
327
# Simple gravity simulation
328
gravity = wp.vec3(0, -9.81, 0)
329
velocities[i] = velocities[i] + gravity * dt
330
positions[i] = positions[i] + velocities[i] * dt
331
332
# Bounce off ground
333
if positions[i][1] < 0.0:
334
positions[i] = wp.vec3(positions[i][0], 0.0, positions[i][2])
335
velocities[i] = wp.vec3(velocities[i][0], -0.8 * velocities[i][1], velocities[i][2])
336
337
# Animation loop
338
for frame in range(num_frames):
339
# Update simulation
340
wp.launch(update_particles, dim=1000, inputs=[positions, velocities, dt])
341
342
# Set time sample
343
time = frame * dt
344
usd_renderer.set_time_sample(frame, time)
345
346
# Add particles for this frame
347
point_path = usd_renderer.add_points(
348
positions,
349
radii=wp.full(1000, 0.05, device='cuda')
350
)
351
352
# Save USD file
353
usd_renderer.save("particle_animation.usda")
354
355
# Render frames
356
for frame in range(0, num_frames, 5): # Every 5th frame
357
usd_renderer.render_frame(
358
camera_path,
359
f"frame_{frame:04d}.png",
360
width=1920,
361
height=1080
362
)
363
```
364
365
### Mesh Visualization with Materials
366
```python
367
import warp as wp
368
import warp.render as render
369
370
# Create complex mesh (sphere)
371
def create_sphere_mesh(radius: float, resolution: int):
372
# Generate sphere vertices and indices
373
vertices = []
374
indices = []
375
376
for i in range(resolution + 1):
377
for j in range(resolution + 1):
378
theta = np.pi * i / resolution
379
phi = 2 * np.pi * j / resolution
380
381
x = radius * np.sin(theta) * np.cos(phi)
382
y = radius * np.cos(theta)
383
z = radius * np.sin(theta) * np.sin(phi)
384
385
vertices.append([x, y, z])
386
387
# Generate triangle indices
388
for i in range(resolution):
389
for j in range(resolution):
390
v0 = i * (resolution + 1) + j
391
v1 = v0 + 1
392
v2 = (i + 1) * (resolution + 1) + j
393
v3 = v2 + 1
394
395
indices.append([v0, v2, v1])
396
indices.append([v1, v2, v3])
397
398
return wp.array(vertices, dtype=wp.vec3), wp.array(indices, dtype=wp.int32)
399
400
# Create sphere mesh
401
vertices, indices = create_sphere_mesh(radius=2.0, resolution=32)
402
sphere_mesh = wp.Mesh(vertices, indices)
403
404
# OpenGL rendering with lighting
405
renderer = render.OpenGLRenderer(1200, 800)
406
renderer.set_camera(wp.vec3(0, 0, 8), wp.vec3(0, 0, 0))
407
renderer.set_projection(fov=60.0)
408
409
# Render with rotation
410
for frame in range(360):
411
renderer.clear(wp.vec4(0.1, 0.1, 0.2, 1.0))
412
413
# Rotate mesh
414
angle = frame * np.pi / 180
415
transform = wp.mat44(
416
np.cos(angle), 0, np.sin(angle), 0,
417
0, 1, 0, 0,
418
-np.sin(angle), 0, np.cos(angle), 0,
419
0, 0, 0, 1
420
)
421
422
renderer.render(sphere_mesh, renderer.camera_pos, renderer.camera_target)
423
424
if frame % 30 == 0: # Save every 30 frames
425
renderer.save_image(f"sphere_{frame:03d}.png")
426
427
renderer.close()
428
```
429
430
### Real-time Simulation Visualization
431
```python
432
import warp as wp
433
import warp.render as render
434
435
# Set up simulation
436
@wp.kernel
437
def cloth_simulation(positions: wp.array(dtype=wp.vec3),
438
velocities: wp.array(dtype=wp.vec3),
439
forces: wp.array(dtype=wp.vec3),
440
dt: float):
441
i = wp.tid()
442
443
# Apply forces and integrate
444
vel = velocities[i] + forces[i] * dt
445
pos = positions[i] + vel * dt
446
447
# Simple constraints
448
if pos[1] < 0.0: # Ground constraint
449
pos = wp.vec3(pos[0], 0.0, pos[2])
450
vel = wp.vec3(vel[0], 0.0, vel[2])
451
452
positions[i] = pos
453
velocities[i] = vel
454
455
# Initialize cloth mesh
456
cloth_res = 32
457
cloth_positions = wp.zeros(cloth_res * cloth_res, dtype=wp.vec3, device='cuda')
458
cloth_velocities = wp.zeros_like(cloth_positions)
459
cloth_forces = wp.zeros_like(cloth_positions)
460
461
# Create renderer
462
renderer = render.OpenGLRenderer(1024, 768)
463
renderer.set_camera(wp.vec3(5, 5, 5), wp.vec3(0, 0, 0))
464
465
# Real-time simulation loop
466
dt = 1.0 / 60.0
467
running = True
468
469
while running:
470
# Update simulation
471
wp.launch(cloth_simulation,
472
dim=cloth_res * cloth_res,
473
inputs=[cloth_positions, cloth_velocities, cloth_forces, dt])
474
475
# Render frame
476
renderer.clear()
477
renderer.render_points(cloth_positions, point_size=3.0)
478
renderer.present()
479
480
# Check for exit condition
481
# running = check_user_input() # Implementation dependent
482
```
483
484
## Types
485
486
```python { .api }
487
# Renderer types
488
class Renderer:
489
"""Base renderer interface."""
490
491
def render(self, *args, **kwargs) -> None:
492
"""Render scene objects."""
493
494
def save(self, filename: str) -> None:
495
"""Save rendered output."""
496
497
# Mesh type for rendering
498
class RenderMesh:
499
"""Mesh optimized for rendering."""
500
501
vertices: array # Vertex positions
502
indices: array # Triangle indices
503
normals: array # Vertex normals (optional)
504
uvs: array # Texture coordinates (optional)
505
colors: array # Vertex colors (optional)
506
507
# Camera parameters
508
class Camera:
509
"""Camera configuration."""
510
511
position: vec3 # Camera position
512
target: vec3 # Look-at target
513
up: vec3 # Up vector
514
fov: float # Field of view (degrees)
515
near: float # Near clipping plane
516
far: float # Far clipping plane
517
518
# Light configuration
519
class Light:
520
"""Light source configuration."""
521
522
type: str # Light type ('distant', 'point', 'spot')
523
position: vec3 # Light position
524
direction: vec3 # Light direction
525
intensity: float # Light intensity
526
color: vec3 # Light color
527
528
# Material properties
529
class Material:
530
"""Rendering material properties."""
531
532
diffuse_color: vec3 # Base color
533
metallic: float # Metallic factor
534
roughness: float # Surface roughness
535
emission: vec3 # Emissive color
536
```