Renderer API
The renderer module provides the core rendering functionality for PicoGL, including mesh data management, OpenGL context handling, and various renderer implementations.
Core Classes
MeshData
- class picogl.renderer.meshdata.MeshData(vbo: ndarray | None = None, nbo: ndarray | None = None, uvs: ndarray | None = None, cbo: ndarray | None = None, ebo: ndarray | None = None)[source]
Bases:
objectHolds OpenGL-related state objects for rendering.
- __init__(vbo: ndarray | None = None, nbo: ndarray | None = None, uvs: ndarray | None = None, cbo: ndarray | None = None, ebo: ndarray | None = None)[source]
set up the OpenGL context
- classmethod from_raw(vertices: ndarray | list[float], normals: ndarray | list[float] | None = None, uvs: ndarray | list[float] | None = None, colors: ndarray | list[float] | None = None, indices: ndarray | list[float] | None = None, color_per_vertex: ndarray | list[float] | None = None)[source]
Build a MeshData from raw/python inputs.
- Parameters:
vertices – np.ndarray required, list/array of x,y,z triplets
normals – np.ndarray optional, list/array of x,y,z triplets
uvs – np.ndarray optional, list/array of u,v pairs
indices – np.ndarray optional int indices
colors – np.ndarray optional per-vertex colors (flat float32 array)
color_per_vertex – np.ndarray if provided and colors is None, generate per-vertex colors
- draw(color: tuple | None = None, line_width: float = 1.0, mode: int = OpenGL.GL.GL_TRIANGLES, fill: bool = False, alpha: float = 1.0)[source]
Draw the mesh with optional colour override and transparency.
- Parameters:
color – Optional colour override. If None and vertex colors exist, uses vertex colors.
line_width – Line width for wireframe mode
mode – OpenGL drawing mode
fill – Whether to fill or use wireframe
alpha – Transparency value from 0.0 (opaque) to 1.0 (fully transparent)
The MeshData class is the central data structure for storing 3D mesh information including vertices, colors, normals, and texture coordinates.
Example:
from picogl.renderer import MeshData
import numpy as np
# Create mesh data
vertices = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0]], dtype=np.float32)
colors = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]], dtype=np.float32)
data = MeshData.from_raw(vertices=vertices, colors=colors)
GLContext
- class picogl.renderer.glcontext.GLContext(vaos: dict[str, ~picogl.backend.modern.core.vertex.array.object.VertexArrayObject] = <factory>, vertex_array: ~picogl.backend.modern.core.vertex.array.object.VertexArrayObject | None = None, shader: ~picogl.backend.modern.core.shader.program.ShaderProgram | None = None, texture_id: int | None = None, mvp_matrix: ~numpy.ndarray = <factory>, model_matrix: ~numpy.ndarray = <factory>, view: ~numpy.ndarray = <factory>, eye_np: ~numpy.ndarray = <factory>)[source]
Bases:
objectStores dynamic OpenGL-related state (VAO, shader, texture handles, etc.). Does NOT store raw vertex data.
- vaos: dict[str, VertexArrayObject][source]
- vertex_array: VertexArrayObject | None = None[source]
- shader: ShaderProgram | None = None[source]
- create_shader_program(vertex_source_file: str, fragment_source_file: str, glsl_dir: str | Path | None = None) None[source]
- Parameters:
vertex_source_file – str
fragment_source_file – str
glsl_dir – str
- Returns:
None
- __init__(vaos: dict[str, ~picogl.backend.modern.core.vertex.array.object.VertexArrayObject] = <factory>, vertex_array: ~picogl.backend.modern.core.vertex.array.object.VertexArrayObject | None = None, shader: ~picogl.backend.modern.core.shader.program.ShaderProgram | None = None, texture_id: int | None = None, mvp_matrix: ~numpy.ndarray = <factory>, model_matrix: ~numpy.ndarray = <factory>, view: ~numpy.ndarray = <factory>, eye_np: ~numpy.ndarray = <factory>) None
The GLContext class manages OpenGL-related state including VAOs, shaders, textures, and transformation matrices.
Example:
from picogl.renderer import GLContext
# Create OpenGL context
context = GLContext()
# Create shader program
context.create_shader_program(
vertex_source_file="vertex.glsl",
fragment_source_file="fragment.glsl"
)
Renderer Classes
ObjectRenderer
- class picogl.renderer.object.ObjectRenderer(context: GLContext, data: MeshData, base_dir: str | Path | None = None, glsl_dir: str | Path | None = None, use_texture: bool = False, texture_file: str | None = None, resource_subdir: str = 'tu02')[source]
Bases:
RendererBaseUnified renderer for textured and untextured objects.
- __init__(context: GLContext, data: MeshData, base_dir: str | Path | None = None, glsl_dir: str | Path | None = None, use_texture: bool = False, texture_file: str | None = None, resource_subdir: str = 'tu02')[source]
Initialize the renderer.
- Parameters:
state – Application state object for accessing shared data.
The ObjectRenderer class provides unified rendering for both textured and untextured objects using modern OpenGL.
Example:
from picogl.renderer import GLContext, MeshData
from picogl.renderer.object import ObjectRenderer
# Create renderer
context = GLContext()
data = MeshData.from_raw(vertices=vertices, colors=colors)
renderer = ObjectRenderer(
context=context,
data=data,
use_texture=False
)
# Initialize and render
renderer.initialize_shaders()
renderer.initialize()
renderer.render()
TextureRenderer
- class picogl.renderer.texture.TextureRenderer(context: GLContext, data: MeshData, base_dir: str | Path | None = None, glsl_dir: str | Path | None = None, use_texture: bool = False, texture_file: str | None = None, resource_subdir: str | None = None)[source]
Bases:
ObjectRendererBasic renderer class
- __init__(context: GLContext, data: MeshData, base_dir: str | Path | None = None, glsl_dir: str | Path | None = None, use_texture: bool = False, texture_file: str | None = None, resource_subdir: str | None = None)[source]
Initialize the renderer.
- Parameters:
state – Application state object for accessing shared data.
The TextureRenderer class extends ObjectRenderer to provide specialized texture rendering capabilities.
Example:
from picogl.renderer.texture import TextureRenderer
# Create texture renderer
renderer = TextureRenderer(
context=context,
data=data,
base_dir="path/to/resources",
use_texture=True,
texture_file="texture.png"
)
# Initialize textures
renderer.initialize_textures()
LegacyGLMesh
The LegacyGLMesh class provides OpenGL 1.x/2.x compatible mesh rendering for systems without modern OpenGL support.
Example:
from picogl.renderer.legacy_glmesh import LegacyGLMesh
# Create legacy mesh
mesh = LegacyGLMesh(
vertices=vertices,
faces=faces,
colors=colors,
normals=normals
)
# Upload to GPU and draw
mesh.upload()
mesh.draw()
GLMesh
- class picogl.renderer.glmesh.GLMesh(vertices: ndarray, faces: ndarray, colors: ndarray | None = None, normals: ndarray | None = None, uvs: ndarray | None = None, use_indices: bool = True)[source]
Bases:
objectGPU‐resident mesh: owns VAO/VBO/EBO/CBO/NBO for an indexed triangle mesh. It does not know anything about shaders or matrices.
- __init__(vertices: ndarray, faces: ndarray, colors: ndarray | None = None, normals: ndarray | None = None, uvs: ndarray | None = None, use_indices: bool = True)[source]
The GLMesh class provides modern OpenGL mesh rendering with VAO/VBO support.
Example:
from picogl.renderer.glmesh import GLMesh
# Create modern mesh
mesh = GLMesh(
vertices=vertices,
faces=faces,
colors=colors,
normals=normals
)
# Upload to GPU and draw
mesh.upload()
mesh.draw()
Base Classes
RendererBase
- class picogl.renderer.base.RendererBase(parent: object | None = None)[source]
Bases:
AbstractRendererBase Renderer Class
The RendererBase class provides the base functionality for all renderers in PicoGL.
AbstractRenderer
The AbstractRenderer class defines the interface that all renderers must implement.
Utility Classes
UvRenderer
- class picogl.renderer.uvrenderer.UvRenderer(parent: object | None = None, vertex_shader_file: str = 'glsl/utils/uv2d/vertex.glsl', fragment_shader_file: str = 'glsl/utils/uv2d/fragment.glsl')[source]
Bases:
RendererBase2D UV Renderer that draws a mesh using UV coordinates and indices. Follows the RendererBase interface.
The UvRenderer class provides 2D UV coordinate rendering for texture visualization.
Example:
from picogl.renderer.uvrenderer import UvRenderer
# Create UV renderer
renderer = UvRenderer()
# Initialize with UV data
renderer.initialize(uv_buffer, indices_buffer, index_count)
renderer.render()
Data Structures
The renderer module uses several data structures to represent 3D mesh information:
Vertices: 3D positions of mesh vertices Colors: RGB color values for each vertex Normals: Surface normal vectors for lighting UVs: Texture coordinates for texture mapping Faces: Triangle indices defining mesh topology
Example:
# Vertex data (N, 3) array
vertices = np.array([
[0, 0, 0], # Vertex 0
[1, 0, 0], # Vertex 1
[0, 1, 0] # Vertex 2
], dtype=np.float32)
# Color data (N, 3) array
colors = np.array([
[1, 0, 0], # Red
[0, 1, 0], # Green
[0, 0, 1] # Blue
], dtype=np.float32)
# Face data (M, 3) array
faces = np.array([
[0, 1, 2] # Triangle connecting vertices 0, 1, 2
], dtype=np.uint32)
Rendering Pipeline
The PicoGL rendering pipeline follows these steps:
Data Preparation: Create MeshData with vertices, colors, normals, etc.
Context Setup: Initialize GLContext with shaders and OpenGL state
Renderer Creation: Create appropriate renderer (ObjectRenderer, TextureRenderer, etc.)
Initialization: Call renderer.initialize() to set up OpenGL resources
Rendering: Call renderer.render() to draw the mesh
Example:
# 1. Prepare data
data = MeshData.from_raw(vertices=vertices, colors=colors)
# 2. Setup context
context = GLContext()
context.create_shader_program("vertex.glsl", "fragment.glsl")
# 3. Create renderer
renderer = ObjectRenderer(context=context, data=data)
# 4. Initialize
renderer.initialize_shaders()
renderer.initialize()
# 5. Render (in main loop)
renderer.render()
Error Handling
PicoGL renderers include comprehensive error handling:
OpenGL Context Errors: Fallback to legacy rendering Shader Compilation Errors: Use fallback shaders Texture Loading Errors: Skip texture rendering Mesh Upload Errors: Use immediate mode rendering
Example:
try:
renderer.initialize()
except OpenGLError as e:
print(f"OpenGL error: {e}")
# Fallback to legacy rendering
except Exception as e:
print(f"Unexpected error: {e}")
# Handle gracefully
Performance Considerations
Modern Renderers (ObjectRenderer, TextureRenderer): * Best performance with modern OpenGL * Requires OpenGL 3.3+ support * Uses VAO/VBO for efficient rendering * Supports advanced features
Legacy Renderers (LegacyGLMesh): * Compatible with older OpenGL versions * Uses immediate mode or legacy VBOs * Good performance on older hardware * Limited feature set
Minimal Renderers (Immediate mode): * Maximum compatibility * Uses glBegin/glEnd for rendering * Lower performance but works everywhere * No advanced features
Best Practices
Choose the right renderer for your target platform
Use MeshData.from_raw() for easy data creation
Initialize renderers once and reuse them
Handle errors gracefully with fallbacks
Test on multiple platforms for compatibility
Example:
# Choose renderer based on OpenGL support
if has_modern_opengl():
renderer = ObjectRenderer(context, data)
elif has_legacy_opengl():
renderer = LegacyGLMesh(vertices, faces, colors)
else:
# Use immediate mode fallback
renderer = ImmediateModeRenderer(vertices, colors)