High-performance GPU visibility computation using NVIDIA OptiX, CUDA, and pybind11. This package computes line-of-sight visibility and occlusion statistics between indexed vertex pairs over animated mesh frames using hardware-accelerated ray tracing.
- GPU-accelerated ray tracing via OptiX 8
- Fast dynamic mesh updates using GAS refitting
- Python bindings via pybind11
- Batched multi-frame processing
- Disk-sampled endpoint visibility integration
- CUDA reduction kernel for per-pair score aggregation
- Embedded PTX (no runtime shader files)
Given:
- A triangle mesh (vertices + faces)
- A subset of vertex indices
- Disk sampling parameters
- Multiple animated vertex frames
The system:
- Builds an OptiX acceleration structure (GAS)
- Generates all pairwise visibility rays between selected vertices
- Casts disk-jittered rays per pair
- Computes per-pair occlusion statistics
- Reduces results on GPU
- Returns per-frame visibility scores to Python
The mesh topology is static, but vertex positions may change every frame (fast refit path).
- NVIDIA GPU with RTX support recommended
- Compute Capability ≥ 7.0 (Volta or newer)
| Component | Required |
|---|---|
| Linux | Ubuntu 20.04+ recommended |
| Python | 3.10+ |
| CMake | ≥ 3.24 |
| CUDA Toolkit | ≥ 11.8 |
| NVIDIA Driver | R535+ recommended |
| OptiX SDK | 8.x |
| Compiler | GCC 9+ |
Download from NVIDIA:
https://developer.nvidia.com/designworks/optix/download
Extract somewhere, for example:
~/NVIDIA-OptiX-SDK-8.0.0Then export:
export OPTIX_ROOT=~/NVIDIA-OptiX-SDK-8.0.0This is required at build time only.
This project builds using scikit-build-core (CMake backend).
pip install -U pip scikit-build-core pybind11 buildFrom project root:
pip install .Editable dev mode:
pip install -e .python -m build -w
pip install dist/optix_visibility-*.whlIf not specified, CMake automatically detects:
nvccfrom PATH/usr/local/cuda/bin/nvccCUDA_HOMECUDACXX
Override manually:
pip install . -C cmake.args="-DCMAKE_CUDA_COMPILER=/opt/cuda/bin/nvcc"OptiX device programs are compiled to PTX and JIT compiled at runtime by the NVIDIA driver.
Default:
compute_70
You may override:
pip install . -C cmake.args="-DOPTIX_PTX_ARCH=compute_89"| GPU | Arch |
|---|---|
| RTX 20xx | compute_75 |
| RTX 30xx | compute_86 |
| RTX 40xx | compute_89 |
Using lower PTX targets improves portability.
import optix_visibilityOptixVisibility(
vertices0,
num_vertices,
faces_flat,
index_list,
disk_samples,
disk_radius,
device_id=0,
endpoint_eps=1e-4,
t_eps=1e-6,
occupancy_nsamples=3,
occupancy_jitter=1e-4,
max_intersections=256,
validation=False
)| Parameter | Description |
|---|---|
vertices0 |
Initial vertex array (float32, shape Nx3) |
faces_flat |
Triangle indices flattened (uint32 length multiple of 3) |
index_list |
Vertex indices to test pairwise visibility between |
| Parameter | Description |
|---|---|
disk_samples |
Number of disk jitter samples per pair |
disk_radius |
Radius of disk sampling at endpoints |
| Parameter | Description |
|---|---|
endpoint_eps |
Ray origin offset epsilon |
t_eps |
Intersection epsilon |
occupancy_nsamples |
Secondary stochastic samples |
occupancy_jitter |
Jitter radius |
max_intersections |
Safety cap for traversal loops |
validation |
Enable OptiX validation layer (slow) |
process_frames(
vertices_frames,
num_frames,
num_vertices,
out_scores
)vertices_frames:
shape = (num_frames, num_vertices, 3)
dtype = float32
out_scores:
shape = (num_frames, num_pairs, 3)
dtype = float32
Per pair:
[ visibility_ratio , hit_ratio , average_distance ]
(Exact meaning depends on your CUDA reduction kernel logic.)
import numpy as np
from optix_visibility import OptixVisibility
vertices0 = mesh_vertices.astype(np.float32)
faces_flat = mesh_faces.flatten().astype(np.uint32)
idx_list = np.array([10, 50, 100, 300], dtype=np.uint32)
frames = animated_vertices.astype(np.float32)
out = np.zeros((frames.shape[0],
len(idx_list)*(len(idx_list)-1)//2,
3), dtype=np.float32)
vis = OptixVisibility(
vertices0,
vertices0.shape[0],
faces_flat,
idx_list,
disk_samples=32,
disk_radius=0.002
)
vis.process_frames(
frames,
frames.shape[0],
vertices0.shape[0],
out
)
print(out)- GAS is built once, then refitted per frame
- Only vertex buffers are updated per frame
- OptiX pipeline and SBT are persistent
- CUDA streams are reused
- Reduction runs entirely on GPU
This makes it suitable for real-time or batch animation visibility analysis.
Could not find optix.h
Fix:
export OPTIX_ROOT=/path/to/NVIDIA-OptiX-SDKCheck:
nvcc --versionIf needed:
export PATH=/usr/local/cuda/bin:$PATHTry:
validation=TrueThis enables OptiX debug checks.
| Platform | Status |
|---|---|
| Linux | ✅ Fully supported |
| Windows | ⚠ Possible with minor changes |
| MacOS | ❌ Not supported (no OptiX) |
This project is released under the MIT Licence.