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

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

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

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

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

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=None)[source]

Bases: AbstractRenderer

Base Renderer Class

__init__(parent=None)[source]

Initialize the renderer.

Parameters:

state – Application state object for accessing shared data.

property dispatch_list[source]
initialize() None[source]

Initialize OpenGL resources (shaders, atoms_buffers, etc.).

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

render dispatcher

Returns:

None

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

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)