Shaders API

The shaders module provides OpenGL shader management functionality for PicoGL, including shader compilation, program creation, and uniform management.

Core Classes

ShaderProgram

class picogl.backend.modern.core.shader.program.ShaderProgram(shader_name: str | None = None, vertex_source_file: str | None = None, fragment_source_file: str | None = None, glsl_dir: str | Path | None = None)[source]

Bases: object

OpenGL Shader program manager for vertex and fragment shaders.

__init__(shader_name: str | None = None, vertex_source_file: str | None = None, fragment_source_file: str | None = None, glsl_dir: str | Path | None = None)[source]

constructor

program_id()[source]
init_shader_from_glsl_files(vertex_source_file: str, fragment_source_file: str, glsl_dir: str | Path | None = None) None[source]
Parameters:
  • glsl_dir – directory containing vertex shaders

  • vertex_source_file – list of paths to vertex shaders

  • fragment_source_file – list of paths to fragment shaders

Returns:

None

init_shader_from_glsl(vertex_source: str, fragment_source: str) None[source]
Parameters:
  • vertex_source – list of paths to vertex shaders

  • fragment_source – list of paths to fragment shaders

Returns:

None

init_shader(vertex_source: str, fragment_source: str)[source]
Parameters:
  • vertex_source – list of paths to vertex shaders

  • fragment_source – list of paths to fragment shaders

Returns:

None

Create, compile, and link shaders into a program.

uniform(name: str, value)[source]
Parameters:
  • name – str - uniform name

  • value – value to set (float, int, vec2, vec3, vec4, mat4, or np.ndarray)

Returns:

self - for chaining

Set uniform value (auto-detect type)

create_shader_program()[source]
get_uniform_location(uniform_name)[source]
begin()[source]
end()[source]
bind()[source]

begin

unbind()[source]
release()[source]
delete()[source]

The ShaderProgram class manages OpenGL shader programs, including vertex and fragment shaders.

Example:

from picogl.backend.modern.core.shader.program import ShaderProgram

# Create shader program
shader = ShaderProgram(
    vertex_source_file="vertex.glsl",
    fragment_source_file="fragment.glsl"
)

# Use shader
with shader:
    shader.uniform("mvp_matrix", mvp_matrix)
    shader.uniform("color", [1.0, 0.0, 0.0])
    # Draw calls here

ShaderManager

The ShaderManager class provides centralized shader management for multiple shader programs.

Example:

from picogl.shaders.manager import ShaderManager

# Create shader manager
manager = ShaderManager(shader_directory="glsl/")

# Load shaders
manager.load_shader(ShaderType.BASIC, 1)
manager.load_shader(ShaderType.TEXTURE, 2)

# Use shader
shader = manager.get(ShaderType.BASIC)
with shader:
    # Draw calls here

Shader Types

ShaderType

class picogl.shaders.type.ShaderType(value)[source]

Bases: str, Enum

An enumeration.

AXIS = 'axis'[source]
ATOMS = 'atoms'[source]
BONDS = 'bonds'[source]
DEFAULT = 'default'[source]
CALPHAS = 'calphas'[source]
RIBBONS = 'ribbons'[source]
ISOSURFACE = 'isosurface'[source]
get_name()[source]
get_display_name()[source]
vertex_path()[source]
fragment_path()[source]
__format__(format_spec)[source]

Returns format using actual value type unless __str__ has been overridden.

The ShaderType enum defines the available shader types in PicoGL.

Available Types: * BASIC: Basic color shader * TEXTURE: Texture mapping shader * LIGHTING: Lighting and shading shader * NORMAL_MAPPING: Normal mapping shader * TRANSPARENT: Transparent rendering shader * TEXT: Text rendering shader

Example:

from picogl.shaders.type import ShaderType

# Use shader type
shader_type = ShaderType.BASIC
shader = manager.get(shader_type)

Shader Compilation

Compile Shaders

picogl.shaders.compile.compile_shaders(vertex_src: str, fragment_src: str, shader_name: str | None) ShaderProgram | None[source]

Compiles and links a vertex + fragment shader_manager.current_shader_program shader_program using Qt’s OpenGL API.

Parameters:
  • shader_name – str

  • vertex_src – Vertex shader_manager.current_shader_program GLSL code.

  • fragment_src – Fragment shader_manager.current_shader_program GLSL code.

Returns:

Linked PicoGLShader or None on failure.

Compiles vertex and fragment shader source code into OpenGL shaders.

Example:

from picogl.shaders.compile import compile_shaders

# Compile shaders
vertex_source = """
#version 330 core
layout(location = 0) in vec3 position;
uniform mat4 mvp_matrix;
void main() {
    gl_Position = mvp_matrix * vec4(position, 1.0);
}
"""

fragment_source = """
#version 330 core
out vec4 color;
uniform vec3 color_uniform;
void main() {
    color = vec4(color_uniform, 1.0);
}
"""

shader = compile_shaders(vertex_source, fragment_source, "basic")

Load Shaders

picogl.shaders.load.load_fragment_and_vertex_for_shader_type(shader_type_value: str, shader_directory: str) tuple[str, str][source]
Parameters:
  • shader_directory – str

  • shader_type_value – ShaderType

Returns:

None

Loads vertex and fragment shader source code from files.

Example:

from picogl.shaders.load import load_fragment_and_vertex_for_shader_type

# Load shader files
vertex_src, fragment_src = load_fragment_and_vertex_for_shader_type(
    "basic", "glsl/"
)

# Compile shaders
shader = compile_shaders(vertex_src, fragment_src, "basic")

Uniform Management

ShaderUniform

class picogl.shaders.shader_uniform.ShaderUniform(name: str, location: int, value: int | float | Sequence[float] | numpy.ndarray | bool | NoneType = None, uniform_type: picogl.shaders.shader_uniform.UniformKind | None = None)[source]

Bases: object

name: str[source]
location: int[source]
value: int | float | Sequence[float] | ndarray | bool | None = None[source]
uniform_type: UniformKind | None = None[source]
static infer_type(v: Any) UniformKind[source]
upload(gl_module=None) None[source]

Upload the current value to the bound GL program using PyOpenGL-like calls. If location < 0 (GL returns -1 when not found/used), this is a no-op.

  • gl_module: optional OpenGL.GL-like module. If None, will try to import PyOpenGL (from OpenGL import GL as GL) at call time.

__init__(name: str, location: int, value: int | float | Sequence[float] | ndarray | bool | None = None, uniform_type: UniformKind | None = None) None

The ShaderUniform class manages OpenGL shader uniforms.

Example:

from picogl.shaders.shader_uniform import ShaderUniform

# Create uniform
uniform = ShaderUniform(shader, "mvp_matrix")

# Set uniform value
uniform.set(mvp_matrix)

Uniform Types

PicoGL supports various uniform types:

Matrix Uniforms:

# 4x4 matrix
shader.uniform("mvp_matrix", mvp_matrix)

# 3x3 matrix
shader.uniform("normal_matrix", normal_matrix)

Vector Uniforms:

# 3D vector
shader.uniform("color", [1.0, 0.0, 0.0])

# 4D vector
shader.uniform("position", [0.0, 0.0, 0.0, 1.0])

Scalar Uniforms:

# Float
shader.uniform("time", 0.5)

# Integer
shader.uniform("texture_sampler", 0)

Boolean Uniforms:

# Boolean
shader.uniform("use_texture", True)

Built-in Shaders

PicoGL includes several built-in shader programs:

Basic Color Shader

Vertex Shader:

#version 330 core
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color;

uniform mat4 mvp_matrix;

out vec3 fragment_color;

void main() {
    gl_Position = mvp_matrix * vec4(position, 1.0);
    fragment_color = color;
}

Fragment Shader:

#version 330 core
in vec3 fragment_color;
out vec4 color;

void main() {
    color = vec4(fragment_color, 1.0);
}

Texture Shader

Vertex Shader:

#version 330 core
layout(location = 0) in vec3 position;
layout(location = 1) in vec2 uv;

uniform mat4 mvp_matrix;

out vec2 fragment_uv;

void main() {
    gl_Position = mvp_matrix * vec4(position, 1.0);
    fragment_uv = uv;
}

Fragment Shader:

#version 330 core
in vec2 fragment_uv;

uniform sampler2D texture0;

out vec4 color;

void main() {
    color = texture(texture0, fragment_uv);
}

Lighting Shader

Vertex Shader:

#version 330 core
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;

uniform mat4 mvp_matrix;
uniform mat4 model_matrix;

out vec3 frag_position;
out vec3 frag_normal;

void main() {
    gl_Position = mvp_matrix * vec4(position, 1.0);
    frag_position = vec3(model_matrix * vec4(position, 1.0));
    frag_normal = mat3(transpose(inverse(model_matrix))) * normal;
}

Fragment Shader:

#version 330 core
in vec3 frag_position;
in vec3 frag_normal;

uniform vec3 light_pos;
uniform vec3 view_pos;
uniform vec3 object_color;

out vec4 color;

void main() {
    vec3 norm = normalize(frag_normal);
    vec3 light_dir = normalize(light_pos - frag_position);

    float diff = max(dot(norm, light_dir), 0.0);
    vec3 diffuse = diff * object_color;

    vec3 ambient = 0.1 * object_color;
    vec3 result = ambient + diffuse;

    color = vec4(result, 1.0);
}

Fallback Shaders

PicoGL includes fallback shaders for systems with limited OpenGL support:

Fallback Vertex Shader:

#version 120
attribute vec3 in_position;
uniform mat4 mvp;

void main() {
    gl_Position = mvp * vec4(in_position, 1.0);
}

Fallback Fragment Shader:

#version 120
void main() {
    gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);  // Hot pink
}

Shader Usage

Creating Shader Programs

From Files:

from picogl.backend.modern.core.shader.program import ShaderProgram

# Create shader from files
shader = ShaderProgram(
    vertex_source_file="vertex.glsl",
    fragment_source_file="fragment.glsl",
    glsl_dir="glsl/"
)

From Source:

# Create shader from source code
shader = ShaderProgram(
    vertex_source=vertex_source,
    fragment_source=fragment_source
)

Using Shaders

Basic Usage:

# Use shader
with shader:
    shader.uniform("mvp_matrix", mvp_matrix)
    shader.uniform("color", [1.0, 0.0, 0.0])
    # Draw calls here

Advanced Usage:

# Bind shader
shader.bind()

# Set uniforms
shader.uniform("mvp_matrix", mvp_matrix)
shader.uniform("model_matrix", model_matrix)
shader.uniform("view_pos", camera_position)

# Draw
mesh.draw()

# Unbind shader
shader.unbind()

Error Handling

Shader operations include comprehensive error handling:

Compilation Errors: Check shader source code Link Errors: Check shader program compatibility Uniform Errors: Validate uniform names and types Context Errors: Check OpenGL context

Example:

try:
    shader = ShaderProgram(
        vertex_source_file="vertex.glsl",
        fragment_source_file="fragment.glsl"
    )
except ShaderCompilationError as e:
    print(f"Shader compilation error: {e}")
    # Check shader source code
except ShaderLinkError as e:
    print(f"Shader link error: {e}")
    # Check shader compatibility
except Exception as e:
    print(f"Unexpected error: {e}")
    # Handle gracefully

Performance Considerations

Modern Shaders (OpenGL 3.3+): * Best performance and features * Full shader pipeline support * Advanced uniform types * Requires modern OpenGL

Legacy Shaders (OpenGL 1.x/2.x): * Compatible with older OpenGL versions * Limited shader features * Uses fixed function pipeline * Good performance on older hardware

Fallback Shaders: * Maximum compatibility * Basic functionality only * Uses immediate mode rendering * Works on any OpenGL system

Best Practices

  1. Use appropriate shader type for your target platform

  2. Compile shaders once and reuse them

  3. Handle compilation errors gracefully

  4. Use fallback shaders for compatibility

  5. Test on multiple platforms for compatibility

Example:

# Choose shader based on OpenGL support
if has_modern_opengl():
    shader = ShaderProgram(
        vertex_source_file="vertex.glsl",
        fragment_source_file="fragment.glsl"
    )
elif has_legacy_opengl():
    shader = ShaderProgram(
        vertex_source_file="legacy_vertex.glsl",
        fragment_source_file="legacy_fragment.glsl"
    )
else:
    # Use fallback shader
    shader = ShaderProgram(
        vertex_source=fallback_vertex_source,
        fragment_source=fallback_fragment_source
    )