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: object

Holds 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

as_ribbon_args() dict[source]

Convert into arguments for setup_ribbon_buffers.

bind()[source]
unbind()[source]
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)

delete()[source]

delete to remove atoms_buffers

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: object

Stores 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]
texture_id: int | None = None[source]
mvp_matrix: ndarray[source]
model_matrix: ndarray[source]
view: ndarray[source]
eye_np: ndarray[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: RendererBase

Unified 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.

initialize_shaders()[source]

Load and compile shaders.

initialize()[source]

Create VAO and VBOs once.

render() None[source]

Dispatch render pass.

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: ObjectRenderer

Basic 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.

initialize_textures()[source]
get_texture_filename()[source]

get texture filename

Returns:

texture filename: str

set_texture_filename(file_name: str | None = None)[source]
Parameters:

file_name – str = None

Returns:

None

set_resource_path(base_path: str | Path, subdir: str)[source]
Parameters:
  • base_path – str | Path

  • subdir – str

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: object

GPU‐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]
classmethod from_mesh_data(mesh: MeshData) GLMesh[source]

Construct a GLMesh from a MeshData container.

Parameters:

mesh (MeshData) – Must have .vbo (Nx3), .ebo (Mx1), optional .cbo (Nx3), .nbo (Nx3), uvs (Nx2)

Returns:

Ready-to-upload mesh (GPU buffers are allocated only when upload() is called).

Return type:

GLMesh

upload() None[source]

Allocate & fill GPU buffers.

bind()[source]
unbind()[source]
delete()[source]

Free GPU resources.

draw() None[source]

Draw the mesh.

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: AbstractRenderer

Base Renderer Class

__init__(parent: object | None = None)[source]

Initialize the renderer.

Parameters:

state – Application state object for accessing shared data.

property dispatch_list[source]
property initialized: bool[source]
render(mvp_matrix: ndarray | None = None) None[source]

render dispatcher

Returns:

None

initialize() None[source]

initialize_rendering_buffers

Returns:

initialize_rendering_buffers()[source]

For back compatibility

set_visibility(visible: bool) None[source]

Set the visibility of the object.

The RendererBase class provides the base functionality for all renderers in PicoGL.

AbstractRenderer

class picogl.renderer.abstract.AbstractRenderer[source]

Bases: ABC

abstract initialize() None[source]

Subclasses must implement buffer setup.

set_visibility(visible: bool) None[source]

Set the visibility of the object.

abstract render()[source]

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: RendererBase

2D UV Renderer that draws a mesh using UV coordinates and indices. Follows the RendererBase interface.

__init__(parent: object | None = None, vertex_shader_file: str = 'glsl/utils/uv2d/vertex.glsl', fragment_shader_file: str = 'glsl/utils/uv2d/fragment.glsl')[source]

Initialize the renderer.

Parameters:

state – Application state object for accessing shared data.

initialize(uv_buffer: int, indices_buffer: int, index_count: int) None[source]

Bind the UV and index buffers to the renderer.

Parameters:
  • uv_buffer – OpenGL buffer ID containing UVs

  • indices_buffer – OpenGL buffer ID containing indices

  • index_count – Number of indices to draw

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:

  1. Data Preparation: Create MeshData with vertices, colors, normals, etc.

  2. Context Setup: Initialize GLContext with shaders and OpenGL state

  3. Renderer Creation: Create appropriate renderer (ObjectRenderer, TextureRenderer, etc.)

  4. Initialization: Call renderer.initialize() to set up OpenGL resources

  5. 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

  1. Choose the right renderer for your target platform

  2. Use MeshData.from_raw() for easy data creation

  3. Initialize renderers once and reuse them

  4. Handle errors gracefully with fallbacks

  5. 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)