Welcome to ModernGL’s documentation!

Start here.

ModernGL

ModernGL on Github

If you have less time to read this then go to the TL;DR section.

ModernGL and OpenGL

OpenGL is a great environment for developing portable, platform independent, interactive 2D and 3D graphics applications. The API implementation in Python is cumbersome, resulting in applications with high latency. To solve this problem we have developed ModernGL, a wrapper over OpenGL that simplifies the creation of simple graphics applications like scientific simulations, small games or user interfaces. Usually, acquiring in-depth knowledge of OpenGL requires a steep learning curve. In contrast, ModernGL is easy to learn and use, moreover it is capable of rendering with the same performance and quality, with less code written.

How to install?

pip install ModernGL

How to create a window?

ModernGL encapsulates the use of the OpenGL API, a separate module must be used for creating a window. ModernGL can be integrated easily in GLWindow, PyQt5, pyglet, pygame, GLUT and many more.

How to create a context?

ModernGL

ModernGL: PyOpenGL alternative

Context

create_context(require=None) → Context

Create a ModernGL context by loading OpenGL functions from an existing OpenGL context. An OpenGL context must exists. If rendering is done without a window please use the create_standalone_context() instead.

Keyword Arguments:
 require (Version) – OpenGL version.
Returns:context
Return type:Context
create_standalone_context(size=(256, 256), require=None) → Context

Create a standalone ModernGL context. This method will create a hidden window with the initial size given in the parameters. This will set the initial viewport as well.

It is reccommanded to use a separate framebuffer when rendering.

The size is not really important for the default framebuffer. It is only useful to get a viewport with a visible size. Size (1, 1) is great to save memory, however makes harder to detect when the viewport was forgotten to set.

Keyword Arguments:
 
  • size (tuple) – Initial framebuffer size.
  • require (Version) – OpenGL version.
Returns:

context

Return type:

Context

class Context

Create a Context using:

The create_context() must be used when rendering in a window. The create_standalone_context() must be used when rendering without a window.

Members:

clear(red=0, green=0, blue=0, alpha=0, viewport=None)

Clear the framebuffer.

Values must be in (0, 255) range. If the viewport is not None then scrissor test will be used to clear the given viewport.

If the viewport is a 2-tuple it will clear the (0, 0, width, height) where (width, height) is the 2-tuple.

If the viewport is a 4-tuple it will clear the given viewport.

Parameters:
  • red (int) – color component.
  • green (int) – color component.
  • blue (int) – color component.
  • alpha (int) – alpha component.
Keyword Arguments:
 

viewport (tuple) – The viewport.

enable(flag)

Enable flags.

Valid flags are:

Parameters:flag – The flag to enable.
disable(flag)

Disable flags.

Valid flags are:

Parameters:flag – The flag to disable.
finish()

Wait for all drawing commands to finish.

copy_buffer(dst, src, size=-1, read_offset=0, write_offset=0)

Copy buffer content.

Parameters:
  • dst (Buffer) – Destination buffer.
  • src (Buffer) – Source buffer.
  • size (int) – Size to copy.
Keyword Arguments:
 
  • read_offset (int) – Read offset.
  • write_offset (int) – Write offset.
copy_framebuffer(dst, src)

Copy framebuffer content. Use this method to blit framebuffers. Use this method to copy framebuffer content into a texture. Use this method to downsample framebuffers, it will allow to read the framebuffer’s content. Use this method to downsample a framebuffer directly to a texture.

Parameters:
buffer(data=None, reserve=0, dynamic=False) → Buffer

Create a Buffer.

Parameters:

data (bytes) – Content of the new buffer.

Keyword Arguments:
 
  • reserve (int) – The number of bytes to reserve.
  • dynamic (bool) – Treat buffer as dynamic.
Returns:

buffer

Return type:

Buffer

texture(size, components, data=None, samples=0, floats=False) → Texture

Create a Texture.

Parameters:
  • size (tuple) – Width, height.
  • components (int) – The number of components 1, 2, 3 or 4.
  • alignment (int) – The byte alignment 1, 2, 4 or 8.
  • data (bytes) – Content of the image.
Keyword Arguments:
 

floats (bool) – Use floating point precision.

Returns:

texture

Return type:

Texture

depth_texture(size, data=None, samples=0) → Texture

Create a Texture.

Parameters:
  • size (tuple) – Width, height.
  • alignment (int) – The byte alignment 1, 2, 4 or 8.
  • data (bytes) – Content of the image.
Returns:

depth texture

Return type:

Texture

vertex_array(program, content, index_buffer=None) → VertexArray

Create a VertexArray.

Parameters:
  • program (Program) – The program used by render and transform.
  • content (list) – A list of (buffer, format, attributes).
  • index_buffer (Buffer) – An index buffer.
Returns:

vertex array

Return type:

VertexArray

simple_vertex_array(program, buffer, attributes) → VertexArray

Create a VertexArray.

This is an alias for:

format = detect_format(program, attributes) vertex_array(program, [(buffer, format, attributes)])
Parameters:
  • program (Program) – The program used by render and transform.
  • buffer (Buffer) – The buffer.
  • attributes (list) – A list of attribute names.
Returns:

vertex array

Return type:

VertexArray

program(shaders, varyings=()) → Program

Create a Program object. Only linked programs will be returned.

For more information please see: Program and Shader

A single shader in the shaders parameter is also accepted. The varyings are only used when a transform program is created.

Parameters:
  • shaders (list) – A list of Shader objects.
  • varyings (list) – A list of varying names.
Returns:

program

Return type:

Program

Examples

A simple program designed for rendering:

>>> my_render_program = ctx.program([
...         ctx.vertex_shader('''
...                 #version 330
...
...                 in vec2 vert;
...
...                 void main() {
...                         gl_Position = vec4(vert, 0.0, 1.0);
...                 }
...         '''),
...         ctx.fragment_shader('''
...                 #version 330
...
...                 out vec4 color;
...
...                 void main() {
...                         color = vec4(0.3, 0.5, 1.0, 1.0);
...                 }
...         '''),
... ])

A simple program designed for transforming:

>>> my_vertex_shader = ctx.vertex_shader('''
...     #version 330
...
...     in vec4 vert;
...     out float vert_length;
...
...     void main() {
...         vert_length = length(vert);
...     }
... ''')

>>> my_transform_program = ctx.program(my_vertex_shader, ['vert_length'])
vertex_shader(source) → Shader

The Vertex Shader is the programmable Shader stage in the rendering pipeline that handles the processing of individual vertices.

Vertex shaders are fed Vertex Attribute data, as specified from a vertex array object by a drawing command. A vertex shader receives a single vertex from the vertex stream and generates a single vertex to the output vertex stream.

Parameters:source (str) – The source code in GLSL.
Returns:vertex shader
Return type:Shader

Examples

Create a simple vertex shader:

>>> my_vertex_shader = ctx.vertex_shader('''
...     #version 330
...
...     in vec2 vert;
...
...     void main() {
...         gl_Position = vec4(vert, 0.0, 1.0);
...     }
... ''')
fragment_shader(source) → Shader

A Fragment Shader is the Shader stage that will process a Fragment generated by the Rasterization into a set of colors and a single depth value.

Parameters:source (str) – The source code in GLSL.
Returns:fragment shader
Return type:Shader

Examples

Create a simple fragment shader:

>>> my_fragment_shader = ctx.fragment_shader('''
...     #version 330
...
...     out vec4 color;
...
...     void main() {
...         color = vec4(0.3, 0.5, 1.0, 1.0);
...     }
... ''')
geometry_shader(source) → Shader

Create a Shader.

Parameters:source (str) – The source code in GLSL.
Returns:geometry shader
Return type:Shader
tess_evaluation_shader(source) → Shader

Create a Shader.

Parameters:source (str) – The source code in GLSL.
Returns:tesselation evaluation shader
Return type:Shader
tess_control_shader(source) → Shader

Create a Shader.

Parameters:source (str) – The source code in GLSL.
Returns:tesselation control shader
Return type:Shader
framebuffer(color_attachments, depth_attachment) → Framebuffer

Create a Framebuffer.

Parameters:
  • color_attachments (list) – A list of Texture or Renderbuffer objects.
  • depth_attachment (Renderbuffer or Texture) – A Texture or Renderbuffer object.
Returns:

framebuffer

Return type:

Framebuffer

renderbuffer(size, components=4, samples=0, floats=True) → Renderbuffer

Create a Renderbuffer.

Parameters:
  • size (tuple) – The width and height.
  • components (int) – The number of components 1, 2, 3 or 4.
Keyword Arguments:
 
  • samples – The number of samples. Value 0 means no multisample format.
  • floats – Use floating point precision.
Returns:

renderbuffer

Return type:

Renderbuffer

depth_renderbuffer(size, samples=0) → Renderbuffer

Create a Renderbuffer.

Parameters:size (tuple) – The width and height.
Keyword Arguments:
 samples – The number of samples. Value 0 means no multisample format.
Returns:depth renderbuffer
Return type:Renderbuffer
compute_shader(source) → ComputeShader

Create a ComputeShader.

Parameters:source (str) – The source of the compute shader.
Returns:compute shader program
Return type:ComputeShader
line_width

float – Set the default line width.

point_size

float – Set the default point size.

viewport

tuple – The viewport.

Reading this property may force the GPU to sync. Use this property to set the viewport only.

default_texture_unit

int – The default texture unit.

max_texture_units

int – The max texture units.

default_framebuffer

Framebuffer – The default framebuffer.

vendor

str – The vendor.

renderer

str – The renderer.

version

str – The OpenGL version.

Shaders and Programs

Context.vertex_shader(source) → Shader

The Vertex Shader is the programmable Shader stage in the rendering pipeline that handles the processing of individual vertices.

Vertex shaders are fed Vertex Attribute data, as specified from a vertex array object by a drawing command. A vertex shader receives a single vertex from the vertex stream and generates a single vertex to the output vertex stream.

Parameters:source (str) – The source code in GLSL.
Returns:vertex shader
Return type:Shader

Examples

Create a simple vertex shader:

>>> my_vertex_shader = ctx.vertex_shader('''
...     #version 330
...
...     in vec2 vert;
...
...     void main() {
...         gl_Position = vec4(vert, 0.0, 1.0);
...     }
... ''')
Context.fragment_shader(source) → Shader

A Fragment Shader is the Shader stage that will process a Fragment generated by the Rasterization into a set of colors and a single depth value.

Parameters:source (str) – The source code in GLSL.
Returns:fragment shader
Return type:Shader

Examples

Create a simple fragment shader:

>>> my_fragment_shader = ctx.fragment_shader('''
...     #version 330
...
...     out vec4 color;
...
...     void main() {
...         color = vec4(0.3, 0.5, 1.0, 1.0);
...     }
... ''')
Context.geometry_shader(source) → Shader

Create a Shader.

Parameters:source (str) – The source code in GLSL.
Returns:geometry shader
Return type:Shader
Context.tess_evaluation_shader(source) → Shader

Create a Shader.

Parameters:source (str) – The source code in GLSL.
Returns:tesselation evaluation shader
Return type:Shader
Context.tess_control_shader(source) → Shader

Create a Shader.

Parameters:source (str) – The source code in GLSL.
Returns:tesselation control shader
Return type:Shader
Context.program(shaders, varyings=()) → Program

Create a Program object. Only linked programs will be returned.

For more information please see: Program and Shader

A single shader in the shaders parameter is also accepted. The varyings are only used when a transform program is created.

Parameters:
  • shaders (list) – A list of Shader objects.
  • varyings (list) – A list of varying names.
Returns:

program

Return type:

Program

Examples

A simple program designed for rendering:

>>> my_render_program = ctx.program([
...         ctx.vertex_shader('''
...                 #version 330
...
...                 in vec2 vert;
...
...                 void main() {
...                         gl_Position = vec4(vert, 0.0, 1.0);
...                 }
...         '''),
...         ctx.fragment_shader('''
...                 #version 330
...
...                 out vec4 color;
...
...                 void main() {
...                         color = vec4(0.3, 0.5, 1.0, 1.0);
...                 }
...         '''),
... ])

A simple program designed for transforming:

>>> my_vertex_shader = ctx.vertex_shader('''
...     #version 330
...
...     in vec4 vert;
...     out float vert_length;
...
...     void main() {
...         vert_length = length(vert);
...     }
... ''')

>>> my_transform_program = ctx.program(my_vertex_shader, ['vert_length'])
class Shader

Shader objects represent compiled GLSL code for a single shader stage.

A Shader object cannot be instantiated directly, it requires a context. Use the following methods to create one:

source

str – The source code of the shader.

class Program

A Program object represents fully processed executable code in the OpenGL Shading Language, for one or more Shader stages.

In ModernGL, a Program object can be assigned to VertexArray objects. The VertexArray object is capable of binding the Program object once the VertexArray.render() or VertexArray.transform() is called.

Program objects has no method called use(), VertexArrays encapsulate this mechanism.

A Program object cannot be instantiated directly, it requires a context. Use Context.program() to create one.

uniforms

UniformMap – The uniforms of the program. The return value is a dictinary like object. It can be used to access Uniform objects by name.

Examples

Set the value of the uniforms:

# uniform vec3 eye_pos;
>>> program.uniforms['eye_pos'].value = (10.0, 20.0, 0.0)

# uniform sampler2D my_textures[3];
>>> program.uniforms['my_texture'].value = [0, 3, 2]

# The values of `my_textures` will be:
my_textures[0] = GL_TEXTURE0             # GL_TEXTURE0
my_textures[1] = GL_TEXTURE0 + 3         # GL_TEXTURE3
my_textures[2] = GL_TEXTURE0 + 2         # GL_TEXTURE2

Get information about the uniforms:

>>> program.uniforms['eye_pos'].location
0

>>> program.uniforms['eye_pos'].dimension
3

>>> program.uniforms['eye_pos'].value
(10.0, 20.0, 0.0)

>>> program.uniforms['my_textures'].dimension
1

>>> program.uniforms['my_textures'].array_length
3
uniform_blocks

UniformBlockMap – The uniform blocks of the program. The return value is a dictinary like object. It can be used to access UniformBlock objects by name.

Examples

Get the location of the uniform block:

# uniform custom_material {
#     ...
# };

>>> program.uniform_blocks['custom_material'].location
16
attributes

AttributeMap – The attributes of the program. The return value is a dictinary like object. It can be used to access Attribute objects by name.

Examples

Set the default value for the attributes:

# in vec3 normal;
>>> program.attributes['normal'].default = (0.0, 0.0, 1.0)

# in vec2 texture_coordinates[3];
>>> program.attributes['texture_coordinates'].default = [
...     (0.0, 0.0),
...     (0.0, 0.0),
...     (0.0, 0.0),
... ]

Get information about the attributes:

>>> program.attributes['normal'].default
(0.0, 0.0, 1.0)

>>> program.attributes['normal'].dimension
3

>>> program.attributes['texture_coordinates'].default
[(0.0, 0.0), (0.0, 0.0), (0.0, 0.0)]

>>> program.attributes['texture_coordinates'].array_length
3

>>> program.attributes['texture_coordinates'].dimension
2
varyings

VaryingMap – The varyings of the program. The return value is a dictinary like object. It can be used to access Varying objects by name.

The only reason varyings were added to allow verifying varyings programatically, they do not hold any other information.

Examples

Check varyings:

>>> 'vertex_out' in program.varyings
True
geometry_input

Primitive – The geometry input primitive. The GeometryShader’s input primitive if the GeometryShader exists. The geometry input primitive will be used for validation.

geometry_output

Primitive – The geometry output primitive. The GeometryShader’s output primitive if the GeometryShader exists.

geometry_vertices

int – The maximum number of vertices that the geometry shader will output.

vertex_shader

ProgramStage – The vertex shader program stage.

The return value is NOT a Shader.

fragment_shader

ProgramStage – The fragment shader program stage.

The return value is NOT a Shader.

geometry_shader

ProgramStage – The geometry shader program stage.

The return value is NOT a Shader.

tess_evaluation_shader

ProgramStage – The tesselation evaluation shader program stage.

The return value is NOT a Shader.

tess_control_shader

ProgramStage – The tesselation control shader program stage.

The return value is NOT a Shader.

Uniform

class Uniform

A uniform is a global GLSL variable declared with the “uniform” storage qualifier. These act as parameters that the user of a shader program can pass to that program.

In ModernGL, Uniforms can be accessed using Program.uniforms

name

str – The name of the uniform. The name does not contain leading [0]. The name may contain [ ] when the uniform is part of a struct.

location

int – The location of the uniform.

dimension

int – The dimension of the uniform.

GLSL type dimension
sampler2D 1
sampler2DCube 1
sampler2DShadow 1
bool 1
bvec2 2
bvec3 3
bvec4 4
int 1
ivec2 2
ivec3 3
ivec4 4
uint 1
uvec2 2
uvec3 3
uvec4 4
float 1
vec2 2
vec3 3
vec4 4
double 1
dvec2 2
dvec3 3
dvec4 4
mat2 4
mat2x3 6
mat2x4 8
mat3x2 6
mat3 9
mat3x4 12
mat4x2 8
mat4x3 12
mat4 16
dmat2 4
dmat2x3 6
dmat2x4 8
dmat3x2 6
dmat3 9
dmat3x4 12
dmat4x2 8
dmat4x3 12
dmat4 16
array_length

int – The length of the array of the uniform. The array_length is 1 for non array uniforms.

value

The value of the uniform. Reading the value of the uniform may force the GPU to sync.

The value must be a tuple for non array uniforms. The value must be a list of tuples for array uniforms.

class UniformMap
__getitem__(key) → Uniform

Uniform Blocks

class UniformBlock
name

str – name

class UniformBlockMap
__getitem__(key) → UniformBlock

Attributes

class Attribute
name

str – The attribute name. The name will be filtered to have no array syntax on it’s end. Attribute name without '[0]' ending if any.

location

int – The location of the attribute. The result of the glGetAttribLocation.

array_length

int – If the attribute is an array the array_length is the length of the array otherwise 1.

dimension

int – The attribute dimension.

GLSL type dimension
int 1
ivec2 2
ivec3 3
ivec4 4
uint 1
uvec2 2
uvec3 3
uvec4 4
float 1
vec2 2
vec3 3
vec4 4
double 1
dvec2 2
dvec3 3
dvec4 4
mat2 4
mat2x3 6
mat2x4 8
mat3x2 6
mat3 9
mat3x4 12
mat4x2 8
mat4x3 12
mat4 16
dmat2 4
dmat2x3 6
dmat2x4 8
dmat3x2 6
dmat3 9
dmat3x4 12
dmat4x2 8
dmat4x3 12
dmat4 16
class AttributeMap
__getitem__(key) → Attribute

Varyings

class Varying
name

str – The name of the varying.

number

int – The number of the varying.

class VaryingMap
__getitem__(key) → Varying

Program Stages

class ProgramStage
subroutines

SubroutineMap – The subroutines of the program stage.

subroutine_uniforms

SubroutineUniformMap – The subroutine uniforms of the program stage.

Subroutines
class Subroutine
name

str – The name of the subroutine.

index

int – The index of the subroutine.

class SubroutineMap
__getitem__(key) → Subroutine
SubroutineUniforms
class SubroutineUniform
name

str – The name of the subroutine uniform.

location

int – The location of the subroutine uniform.

class SubroutineUniformMap
__getitem__(key) → SubroutineUniform

Buffers

Context.buffer(data=None, reserve=0, dynamic=False) → Buffer

Create a Buffer.

Parameters:

data (bytes) – Content of the new buffer.

Keyword Arguments:
 
  • reserve (int) – The number of bytes to reserve.
  • dynamic (bool) – Treat buffer as dynamic.
Returns:

buffer

Return type:

Buffer

class Buffer

Buffer Objects are OpenGL Objects that store an array of unformatted memory allocated by the OpenGL context, (data allocated on the GPU). These can be used to store vertex data, pixel data retrieved from images or the framebuffer, and a variety of other things.

A Buffer object cannot be instantiated directly, it requires a context. Use Context.buffer() to create one.

Copy buffer content using Context.copy_buffer().

access(size=-1, offset=0, readonly=False) → BufferAccess

Create a BufferAccess object.

Keyword Arguments:
 
  • size (int) – The size. Value -1 means all.
  • offset (int) – The offset.
  • readonly (bool) – The readonly.

Examples

Simple with statement:

# The buffer will be mapped once and accessed multiple times.

>>> with buffer.access() as access:
...     access.read(...)
...     access.write(...)
read(size=-1, offset=0) → bytes

Read the content.

Parameters:size (int) – The size. Value -1 means all.
Keyword Arguments:
 offset (int) – The offset.
Returns:The content of the buffer.
Return type:bytes
write(data, offset=0)

Write the content.

Parameters:data – The data.
Keyword Arguments:
 offset – The offset.
orphan()

Orphan the buffer.

It is also called buffer re-specification.

Reallocate the buffer object before you start modifying it.

Since allocating storage is likely faster than the implicit synchronization, you gain significant performance advantages over synchronization.

The old storage will still be used by the OpenGL commands that have been sent previously. It is likely that the GL driver will not be doing any allocation at all, but will just be pulling an old free block off the unused buffer queue and use it, so it is likely to be very efficient.

Examples

Simple orphaning example:

# For simplicity the VertexArray creation is omitted

>>> vbo = ctx.buffer(reserve=1024)

# Fill the buffer

>>> vbo.write(some_temorary_data)

# Issue a render call that uses the vbo

>>> vao.render(...)

# Orphan the buffer

>>> vbo.orphan()

# Issue another render call without waiting for the previous one

>>> vbo.write(some_temorary_data)
>>> vao.render(...)
bind_to_uniform_block(location=0)

Bind the buffer to a uniform block.

Parameters:location – The uniform block location.
bind_to_storage_buffer(location=0)

Bind the buffer to a shader storage buffer.

Parameters:location – The shader storage location.
class BufferAccess

BufferAccess objects are designed to access a Buffer object’s content inside a with statement. The buffer is mapped and unmapped only once.

Use Buffer.access() to get a BufferAccess object.

open()

Map the buffer. This method is called by __enter__.

close()

Unmap the buffer. This method is called by __exit__.

read(size=-1, offset=0) → bytes

Read the content.

Parameters:size – The size. Value -1 means all.
Keyword Arguments:
 offset – The offset.
Returns:binary data
Return type:bytes
write(data, offset=0)

Write the content.

Parameters:size – The data.
Keyword Arguments:
 offset – The offset.

VertexArray

Context.simple_vertex_array(program, buffer, attributes) → ModernGL.vertex_arrays.VertexArray

Create a VertexArray.

This is an alias for:

format = detect_format(program, attributes) vertex_array(program, [(buffer, format, attributes)])
Parameters:
  • program (Program) – The program used by render and transform.
  • buffer (Buffer) – The buffer.
  • attributes (list) – A list of attribute names.
Returns:

vertex array

Return type:

VertexArray

Context.vertex_array(program, content, index_buffer=None) → ModernGL.vertex_arrays.VertexArray

Create a VertexArray.

Parameters:
  • program (Program) – The program used by render and transform.
  • content (list) – A list of (buffer, format, attributes).
  • index_buffer (Buffer) – An index buffer.
Returns:

vertex array

Return type:

VertexArray

class VertexArray

A VertexArray object is an OpenGL object that stores all of the state needed to supply vertex data. It stores the format of the vertex data as well as the Buffer objects providing the vertex data arrays.

In ModernGL, the VertexArray object also stores a reference for a Program object, and some Subroutine information.

A VertexArray object cannot be instantiated directly, it requires a context. Use Context.vertex_array() or Context.simple_vertex_array() to create one.

render(mode=TRIANGLES, vertices=-1, first=0, instances=1)

The render primitive (mode) must be the same as the input primitive of the GeometryShader.

Parameters:
  • mode – By default TRIANGLES will be used.
  • vertices – The number of vertices to transform.
Keyword Arguments:
 
  • first – The index of the first vertex to start with.
  • instances – The number of instances.
transform(buf, mode=POINTS, vertices=-1, *, first=0, instances=1)

Transform vertices. Stores the output in a single buffer. The transform primitive (mode) must be the same as the input primitive of the GeometryShader.

Parameters:
  • buffer – The buffer to store the output.
  • mode – By default TRIANGLES will be used.
  • vertices – The number of vertices to transform.
Keyword Arguments:
 
  • first – The index of the first vertex to start with.
  • instances – The number of instances.
program

Program – The program assinged to the VertexArray. The program used when rendering or transforming primitives.

attributes

VertexArrayAttributeMap – Individual vertex attributes. Use the VertexArrayAttribute.bind() method to assign vertex attributes to buffers.

index_buffer

Buffer – The index buffer if the index_buffer is set, otherwise None.

vertices

int – The number of vertices detected. This is the minimum of the number of vertices possible per Buffer. The size of the index_buffer determines the number of vertices. Per instance vertex attributes does not affect this number.

VertexArrayAttributes

class VertexArrayAttribute
bind(buffer, offset=0, stride=0, divisor=0)

bind

enable()

enable

disable()

disable

location
divisor
stride
enabled

Textures

Context.texture(size, components, data=None, samples=0, floats=False) → Texture

Create a Texture.

Parameters:
  • size (tuple) – Width, height.
  • components (int) – The number of components 1, 2, 3 or 4.
  • alignment (int) – The byte alignment 1, 2, 4 or 8.
  • data (bytes) – Content of the image.
Keyword Arguments:
 

floats (bool) – Use floating point precision.

Returns:

texture

Return type:

Texture

Context.depth_texture(size, data=None, samples=0) → Texture

Create a Texture.

Parameters:
  • size (tuple) – Width, height.
  • alignment (int) – The byte alignment 1, 2, 4 or 8.
  • data (bytes) – Content of the image.
Returns:

depth texture

Return type:

Texture

class Texture

A Texture is an OpenGL object that contains one or more images that all have the same image format. A texture can be used in two ways. It can be the source of a texture access from a Shader, or it can be used as a render target.

A Texture object cannot be instantiated directly, it requires a context. Use Context.texture() or Context.depth_texture() to create one.

write(self, data, viewport=None)

Update the content of the texture.

use(location=0)

Bind the texture.

Parameters:location (int) – The texture location. Same as the integer value that is used for sampler2D uniforms in the shaders. The value 0 will bind the texture to the GL_TEXTURE0 binding point.
width

int – The width of the texture.

height

int – The height of the texture.

size

tuple – The size of the texture.

samples

int – The number of samples of the texture.

components

int – The number of components of the texture.

depth

bool – Is the texture a depth texture?

Framebuffers

Context.framebuffer(color_attachments, depth_attachment) → Framebuffer

Create a Framebuffer.

Parameters:
  • color_attachments (list) – A list of Texture or Renderbuffer objects.
  • depth_attachment (Renderbuffer or Texture) – A Texture or Renderbuffer object.
Returns:

framebuffer

Return type:

Framebuffer

class Framebuffer

Create a Framebuffer using Context.framebuffer().

use()

Bind the framebuffer. Set the target for the VertexArray.render or VertexArray.transform methods.

Renderbuffers

Context.renderbuffer(size, components=4, samples=0, floats=True) → Renderbuffer

Create a Renderbuffer.

Parameters:
  • size (tuple) – The width and height.
  • components (int) – The number of components 1, 2, 3 or 4.
Keyword Arguments:
 
  • samples – The number of samples. Value 0 means no multisample format.
  • floats – Use floating point precision.
Returns:

renderbuffer

Return type:

Renderbuffer

Context.depth_renderbuffer(size, samples=0) → Renderbuffer

Create a Renderbuffer.

Parameters:size (tuple) – The width and height.
Keyword Arguments:
 samples – The number of samples. Value 0 means no multisample format.
Returns:depth renderbuffer
Return type:Renderbuffer
class Renderbuffer

Renderbuffer objects are OpenGL objects that contain images. They are created and used specifically with Framebuffer objects. They are optimized for use as render targets, while Texture objects may not be, and are the logical choice when you do not need to sample from the produced image. If you need to resample, use Textures instead. Renderbuffer objects also natively accommodate multisampling.

A Renderbuffer object cannot be instantiated directly, it requires a context. Use Context.renderbuffer() or Context.depth_renderbuffer() to create one.

width

int – The width of the renderbuffer.

height

int – The height of the renderbuffer.

size

tuple – The size of the renderbuffer.

samples

int – The samples of the renderbuffer.

components

int – The components of the renderbuffer.

depth

bool – Is the renderbuffer a depth renderbuffer?

ComputeShaders

Context.compute_shader(source) → ModernGL.programs.ComputeShader

Create a ComputeShader.

Parameters:source (str) – The source of the compute shader.
Returns:compute shader program
Return type:ComputeShader
class ComputeShader
run(size_x=1, size_y=1, size_z=1)

Run the compute shader.

source

str – The source code of the compute shader.

InvalidObject

class InvalidObject

A ModernGL object turns into an InvalidObject once the release() method is successfully called.

Constants

Enable Flags

BLEND = ModernGL.BLEND

GL_BLEND

DEPTH_TEST = ModernGL.DEPTH_TEST

GL_DEPTH_TEST

CULL_FACE = ModernGL.CULL_FACE

GL_CULL_FACE

MULTISAMPLE = ModernGL.MULTISAMPLE

GL_MULTISAMPLE

Versions

class Version(major, minor)
CORE_330 = ModernGL.CORE_330

OpenGL 3.3

CORE_400 = ModernGL.CORE_400

OpenGL 4.0

CORE_410 = ModernGL.CORE_410

OpenGL 4.1

CORE_420 = ModernGL.CORE_420

OpenGL 4.2

CORE_430 = ModernGL.CORE_430

OpenGL 4.3

CORE_440 = ModernGL.CORE_440

OpenGL 4.4

CORE_450 = ModernGL.CORE_450

OpenGL 4.5

Primitives

class Primitive
TRIANGLES = ModernGL.TRIANGLES

GL_TRIANGLES

TRIANGLE_STRIP = ModernGL.TRIANGLE_STRIP

GL_TRIANGLE_STRIP

TRIANGLE_FAN = ModernGL.TRIANGLE_FAN

GL_TRIANGLE_FAN

LINES = ModernGL.LINES

GL_LINES

LINE_STRIP = ModernGL.LINE_STRIP

GL_LINE_STRIP

LINE_LOOP = ModernGL.LINE_LOOP

GL_LINE_LOOP

POINTS = ModernGL.POINTS

GL_POINTS

LINE_STRIP_ADJACENCY = ModernGL.LINE_STRIP_ADJACENCY

GL_LINE_STRIP_ADJACENCY

LINES_ADJACENCY = ModernGL.LINES_ADJACENCY

GL_LINES_ADJACENCY

TRIANGLE_STRIP_ADJACENCY = ModernGL.TRIANGLE_STRIP_ADJACENCY

GL_TRIANGLE_STRIP_ADJACENCY

TRIANGLES_ADJACENCY = ModernGL.TRIANGLES_ADJACENCY

GL_TRIANGLES_ADJACENCY

Error

exception Error

ModernGL Error

Examples

01. Hello World!

01_hello_world

Hello World

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import struct

import GLWindow
import ModernGL

# Window & Context

wnd = GLWindow.create_window()
ctx = ModernGL.create_context()

# Shaders & Program

prog = ctx.program([
    ctx.vertex_shader('''
        #version 330

        in vec2 vert;

        void main() {
            gl_Position = vec4(vert, 0.0, 1.0);
        }
    '''),
    ctx.fragment_shader('''
        #version 330

        out vec4 color;

        void main() {
            color = vec4(0.3, 0.5, 1.0, 1.0);
        }
    '''),
])

# Buffer

vbo = ctx.buffer(struct.pack(
    '6f',
    0.0, 0.8,
    -0.6, -0.8,
    0.6, -0.8,
))

# Put everything together

vao = ctx.simple_vertex_array(prog, vbo, ['vert'])

# Main loop

while wnd.update():
    ctx.viewport = wnd.viewport
    ctx.clear(0.9, 0.9, 0.9)
    vao.render()

02. Uniforms and Attributes

02_uniforms_and_attributes

Uniforms and Attributes

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import struct

import GLWindow
import ModernGL

# Window & Context

wnd = GLWindow.create_window()
ctx = ModernGL.create_context()

# Shaders & Program

prog = ctx.program([
    ctx.vertex_shader('''
        #version 330

        in vec2 vert;

        in vec3 vert_color;
        out vec3 frag_color;

        uniform vec2 scale;
        uniform float rotation;

        void main() {
            frag_color = vert_color;
            mat2 rot = mat2(
                cos(rotation), sin(rotation),
                -sin(rotation), cos(rotation)
            );
            gl_Position = vec4((rot * vert) * scale, 0.0, 1.0);
        }
    '''),
    ctx.fragment_shader('''
        #version 330

        in vec3 frag_color;
        out vec4 color;

        void main() {
            color = vec4(frag_color, 1.0);
        }
    '''),
])

# Uniforms

scale = prog.uniforms['scale']
rotation = prog.uniforms['rotation']

width, height = wnd.size
scale.value = (height / width * 0.75, 0.75)

# Buffer

vbo = ctx.buffer(struct.pack(
    '15f',

    1.0, 0.0,
    1.0, 0.0, 0.0,

    -0.5, 0.86,
    0.0, 1.0, 0.0,

    -0.5, -0.86,
    0.0, 0.0, 1.0,
))

# Put everything together

vao = ctx.simple_vertex_array(prog, vbo, ['vert', 'vert_color'])

# Main loop

while wnd.update():
    ctx.viewport = wnd.viewport
    ctx.clear(0.9, 0.9, 0.9)
    rotation.value = wnd.time
    vao.render()

03. Blending

03_alpha_blending

Alpha Blending

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import struct

import GLWindow
import ModernGL

# Window & Context

wnd = GLWindow.create_window()
ctx = ModernGL.create_context()

# Shaders & Program

prog = ctx.program([
    ctx.vertex_shader('''
        #version 330

        in vec2 vert;

        in vec4 vert_color;
        out vec4 frag_color;

        uniform vec2 scale;
        uniform float rotation;

        void main() {
            frag_color = vert_color;
            mat2 rot = mat2(
                cos(rotation), sin(rotation),
                -sin(rotation), cos(rotation)
            );
            gl_Position = vec4((rot * vert) * scale, 0.0, 1.0);
        }
    '''),
    ctx.fragment_shader('''
        #version 330

        in vec4 frag_color;
        out vec4 color;

        void main() {
            color = vec4(frag_color);
        }
    '''),
])

# Uniforms

scale = prog.uniforms['scale']
rotation = prog.uniforms['rotation']

width, height = wnd.size
scale.value = (height / width * 0.75, 0.75)

# Buffer

vbo = ctx.buffer(struct.pack(
    '18f',

    1.0, 0.0,
    1.0, 0.0, 0.0, 0.5,

    -0.5, 0.86,
    0.0, 1.0, 0.0, 0.5,

    -0.5, -0.86,
    0.0, 0.0, 1.0, 0.5,
))

# Put everything together

vao = ctx.simple_vertex_array(prog, vbo, ['vert', 'vert_color'])

# Main loop

while wnd.update():
    ctx.viewport = wnd.viewport
    ctx.clear(0.9, 0.9, 0.9)
    ctx.enable(ModernGL.BLEND)
    rotation.value = wnd.time
    vao.render(instances=10)

04. Texture

04_textures

Textures

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import os
import struct

import GLWindow
import ModernGL

from PIL import Image

# Window & Context

wnd = GLWindow.create_window()
ctx = ModernGL.create_context()

# Shaders & Program

prog = ctx.program([
    ctx.vertex_shader('''
        #version 330

        in vec2 vert;
        in vec2 tex_coord;
        out vec2 v_tex_coord;

        uniform vec2 scale;
        uniform float rotation;

        void main() {
            float r = rotation * (0.5 + gl_InstanceID * 0.05);
            mat2 rot = mat2(cos(r), sin(r), -sin(r), cos(r));
            gl_Position = vec4((rot * vert) * scale, 0.0, 1.0);
            v_tex_coord = tex_coord;
        }
    '''),
    ctx.fragment_shader('''
        #version 330

        uniform sampler2D texture;

        in vec2 v_tex_coord;
        out vec4 color;

        void main() {
            color = vec4(texture2D(texture, v_tex_coord).rgb, 1.0);
        }
    '''),
])

# Uniforms

scale = prog.uniforms['scale']
rotation = prog.uniforms['rotation']

width, height = wnd.size
scale.value = (height / width * 0.75, 0.75)

# Buffer

vbo = ctx.buffer(struct.pack(
    '12f',
    1.0, 0.0, 0.5, 1.0,
    -0.5, 0.86, 1.0, 0.0,
    -0.5, -0.86, 0.0, 0.0,
))

# Put everything together

vao = ctx.simple_vertex_array(prog, vbo, ['vert', 'tex_coord'])

# Texture

img = Image.open(os.path.join(os.path.dirname(__file__), '..', 'data', 'noise.jpg'))
texture = ctx.texture(img.size, 3, img.tobytes(), alignment=4)
texture.use()

# Main loop

while wnd.update():
    ctx.viewport = wnd.viewport
    ctx.clear(0.9, 0.9, 0.9)
    rotation.value = wnd.time
    vao.render()

05. Perspective

05_perspective_projection

Perspective Projection

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import struct

import GLWindow
import ModernGL

# Window & Context

wnd = GLWindow.create_window()
ctx = ModernGL.create_context()

vert = ctx.vertex_shader('''
    #version 330

    in vec3 vert;

    uniform float znear;
    uniform float zfar;
    uniform float fovy;
    uniform float ratio;

    uniform vec3 center;
    uniform vec3 eye;
    uniform vec3 up;

    mat4 perspective() {
        float zmul = (-2.0 * znear * zfar) / (zfar - znear);
        float ymul = 1.0 / tan(fovy * 3.14159265 / 360);
        float xmul = ymul / ratio;

        return mat4(
            xmul, 0.0, 0.0, 0.0,
            0.0, ymul, 0.0, 0.0,
            0.0, 0.0, -1.0, -1.0,
            0.0, 0.0, zmul, 0.0
        );
    }

    mat4 lookat() {
        vec3 forward = normalize(center - eye);
        vec3 side = normalize(cross(forward, up));
        vec3 upward = cross(side, forward);
        return mat4(
            side.x, upward.x, -forward.x, 0,
            side.y, upward.y, -forward.y, 0,
            side.z, upward.z, -forward.z, 0,
            -dot(eye, side), -dot(eye, upward), dot(eye, forward), 1
        );
    }

    void main() {
        gl_Position = perspective() * lookat() * vec4(vert, 1.0);
    }
''')

frag = ctx.fragment_shader('''
    #version 330

    out vec4 color;

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

width, height = wnd.size

prog = ctx.program([vert, frag])

prog.uniforms['znear'].value = 0.1
prog.uniforms['zfar'].value = 1000.0
prog.uniforms['ratio'].value = width / height
prog.uniforms['fovy'].value = 60

prog.uniforms['eye'].value = (3, 3, 3)
prog.uniforms['center'].value = (0, 0, 0)
prog.uniforms['up'].value = (0, 0, 1)

grid = bytes()

for i in range(0, 65):
    grid += struct.pack('6f', i - 32, -32.0, 0.0, i - 32, 32.0, 0.0)
    grid += struct.pack('6f', -32.0, i - 32, 0.0, 32.0, i - 32, 0.0)

vbo = ctx.buffer(grid)
vao = ctx.simple_vertex_array(prog, vbo, ['vert'])

while wnd.update():
    ctx.viewport = wnd.viewport
    ctx.clear(0.9, 0.9, 0.9)
    vao.render(ModernGL.LINES, 65 * 4)

Julia Fractal

julia_fractal

Julia Fractal

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import struct

import GLWindow
import ModernGL

# Window & Context

wnd = GLWindow.create_window()
ctx = ModernGL.create_context()

vert = ctx.vertex_shader('''
    #version 330

    in vec2 vert;
    out vec2 tex;

    void main() {
        gl_Position = vec4(vert, 0.0, 1.0);
        tex = vert / 2.0 + vec2(0.5, 0.5);
    }
''')

frag = ctx.fragment_shader('''
    #version 330

    in vec2 tex;
    out vec4 color;

    uniform vec2 center;
    uniform int iter;

    void main() {
        vec2 z = vec2(5.0 * (tex.x - 0.5), 3.0 * (tex.y - 0.5));
        vec2 c = center;

        int i;
        for(i = 0; i < iter; i++) {
            vec2 v = vec2(
                (z.x * z.x - z.y * z.y) + c.x,
                (z.y * z.x + z.x * z.y) + c.y
            );
            if (dot(v, v) > 4.0) break;
            z = v;
        }

        float cm = fract((i == iter ? 0.0 : float(i)) * 10 / iter);
        color = vec4(
            fract(cm + 0.0 / 3.0),
            fract(cm + 1.0 / 3.0),
            fract(cm + 2.0 / 3.0),
            1.0
        );
    }
''')

prog = ctx.program([vert, frag])

vbo = ctx.buffer(struct.pack('8f', -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0))
vao = ctx.simple_vertex_array(prog, vbo, ['vert'])

prog.uniforms['iter'].value = 100

x, y = (0.49, 0.32)

wnd.grab_mouse(True)

while wnd.update():
    ctx.viewport = wnd.viewport
    ctx.clear(0.9, 0.9, 0.9)
    mx, my = wnd.mouse_delta
    x -= mx / 100
    y -= my / 100

    prog.uniforms['center'].value = (y, x)
    vao.render(ModernGL.TRIANGLE_STRIP)

Particle System

particle_system

Particle System

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import math
import random
import struct

import GLWindow
import ModernGL

# Window & Context

wnd = GLWindow.create_window()
ctx = ModernGL.create_context()

tvert = ctx.vertex_shader('''
    #version 330

    uniform vec2 acc;

    in vec2 in_pos;
    in vec2 in_prev;

    out vec2 out_pos;
    out vec2 out_prev;

    void main() {
        out_pos = in_pos * 2.0 - in_prev + acc;
        out_prev = in_pos;
    }
''')

vert = ctx.vertex_shader('''
    #version 330

    in vec2 vert;

    void main() {
        gl_Position = vec4(vert, 0.0, 1.0);
    }
''')

frag = ctx.fragment_shader('''
    #version 330

    out vec4 color;

    void main() {
        color = vec4(0.30, 0.50, 1.00, 1.0);
    }
''')

prog = ctx.program([vert, frag])

transform = ctx.program(tvert, ['out_pos', 'out_prev'])


def particle():
    a = random.uniform(0.0, math.pi * 2.0)
    r = random.uniform(0.0, 0.001)

    return struct.pack('2f2f', 0.0, 0.0, math.cos(a) * r - 0.003, math.sin(a) * r - 0.008)


vbo1 = ctx.buffer(b''.join(particle() for i in range(1024)))
vbo2 = ctx.buffer(reserve=vbo1.size)

vao1 = ctx.simple_vertex_array(transform, vbo1, ['in_pos', 'in_prev'])
vao2 = ctx.simple_vertex_array(transform, vbo2, ['in_pos', 'in_prev'])

render_vao = ctx.vertex_array(prog, [
    (vbo1, '2f8x', ['vert']),
])

transform.uniforms['acc'].value = (0, -0.0001)

idx = 0

ctx.point_size = 5.0

while wnd.update():
    ctx.viewport = wnd.viewport
    ctx.clear(0.9, 0.9, 0.9)

    for i in range(8):
        vbo1.write(particle(), offset=idx * struct.calcsize('2f2f'))
        idx = (idx + 1) % 1024

    render_vao.render(ModernGL.POINTS, 1024)
    vao1.transform(vbo2, ModernGL.POINTS, 1024)
    ctx.copy_buffer(vbo1, vbo2)

Contributing

TL;DR

Indices and tables