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:
objectOpenGL 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
- 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.
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
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- 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.
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
Use appropriate shader type for your target platform
Compile shaders once and reuse them
Handle compilation errors gracefully
Use fallback shaders for compatibility
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
)