Welcome to ModernGL’s documentation!

ModernGL

Start here.

ModernGL

ModernGL on Github

If you have less time to read docs 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: 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.0, green=0.0, blue=0.0, alpha=0.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 (float) – color component.
  • green (float) – color component.
  • blue (float) – color component.
  • alpha (float) – alpha component.
Keyword Arguments:
 

viewport (tuple) – The viewport.

enable(flag)

Enable flags.

Valid flags are:

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

Disable flags.

Valid flags are:

Parameters:flag (EnableFlag) – 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.
  • copy framebuffer content into a texture.
  • downsample framebuffers. (it will allow to read the framebuffer’s content)
  • 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) – The width and height of the texture.
  • components (int) – The number of components 1, 2, 3 or 4.
  • data (bytes) – Content of the texture.
Keyword Arguments:
 
  • samples (int) – The number of samples. Value 0 means no multisample format.
  • alignment (int) – The byte alignment 1, 2, 4 or 8.
  • floats (bool) – Use floating point precision.
Returns:

texture

Return type:

Texture

texture3d(size, components, data=None, floats=False) → Texture3D

Create a Texture3D.

Parameters:
  • size (tuple) – The width, height and depth of the texture.
  • components (int) – The number of components 1, 2, 3 or 4.
  • data (bytes) – Content of the texture.
Keyword Arguments:
 
  • alignment (int) – The byte alignment 1, 2, 4 or 8.
  • floats (bool) – Use floating point precision.
Returns:

texture

Return type:

Texture3D

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

Create a Texture.

Parameters:
  • size (tuple) – The width and height of the texture.
  • data (bytes) – Content of the texture.
Keyword Arguments:
 
  • samples (int) – The number of samples. Value 0 means no multisample format.
  • alignment (int) – The byte alignment 1, 2, 4 or 8.
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:
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 a 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

A Geometry Shader is a Shader program written in GLSL that governs the processing of Primitives. Geometry shaders reside between the Vertex Shaders (or the optional Tessellation stage) and the fixed-function Vertex Post-Processing stage.

A geometry shader is optional and does not have to be used.

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

Tessellation is the Vertex Processing stage in the OpenGL rendering pipeline where patches of vertex data are subdivided into smaller Primitives.

The Tessellation Evaluation Shader takes the tessellated patch and computes the vertex values for each generated vertex.

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

The Tessellation Control Shader (TCS) determines how much tessellation to do. It can also adjust the actual patch data, as well as feed additional patch data to later stages. The Tessellation Control Shader is optional.

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

A Framebuffer is a collection of buffers that can be used as the destination for rendering. The buffers for Framebuffer objects reference images from either Textures or Renderbuffers.

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=False) → Renderbuffer

Renderbuffer objects are OpenGL objects that contain images. They are created and used specifically with Framebuffer objects.

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

renderbuffer

Return type:

Renderbuffer

depth_renderbuffer(size, samples=0) → Renderbuffer

Renderbuffer objects are OpenGL objects that contain images. They are created and used specifically with Framebuffer objects.

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

A ComputeShader is a Shader Stage that is used entirely for computing arbitrary information. While it can do rendering, it is generally used for tasks not directly related to drawing.

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.

max_samples

int – The max samples.

max_integer_samples

int – The max integer samples.

max_texture_units

int – The max texture units.

default_texture_unit

int – The default texture unit.

default_framebuffer

Framebuffer – The default framebuffer.

wireframe

bool – The default framebuffer.

error

str – The result of glGetError() but human readable. This values is provided for debug purposes only.

vendor

str – The vendor.

renderer

str – The renderer.

version

str – The OpenGL version.

version_code

int – The OpenGL version.

info

dict – The result of multiple glGet.

Shaders and Programs

Context.vertex_shader(source) → Shader

The Vertex Shader is a 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

A Geometry Shader is a Shader program written in GLSL that governs the processing of Primitives. Geometry shaders reside between the Vertex Shaders (or the optional Tessellation stage) and the fixed-function Vertex Post-Processing stage.

A geometry shader is optional and does not have to be used.

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

Tessellation is the Vertex Processing stage in the OpenGL rendering pipeline where patches of vertex data are subdivided into smaller Primitives.

The Tessellation Evaluation Shader takes the tessellated patch and computes the vertex values for each generated vertex.

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

The Tessellation Control Shader (TCS) determines how much tessellation to do. It can also adjust the actual patch data, as well as feed additional patch data to later stages. The Tessellation Control Shader is optional.

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

read() → bytes

Read the value of the uniform.

write(value)

Write the value of the uniform.

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

UniformMap is a dictionary like object.

__getitem__(key) → Uniform

Uniform Blocks

class UniformBlock
name

str – name

index

int – The index of the uniform block.

size

int – The size of the uniform block.

binding

int – The binding of the uniform block.

class UniformBlockMap

UniformBlockMap is a dictionary like object.

__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

AttributeMap is a dictionary like object.

__getitem__(key) → Attribute

Varyings

class Varying
name

str – The name of the varying.

number

int – The number of the varying.

class VaryingMap

VaryingMap is a dictionary like object.

__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

SubroutineMap is a dictionary like object.

__getitem__(key) → Subroutine
SubroutineUniforms
class SubroutineUniform
name

str – The name of the subroutine uniform.

location

int – The location of the subroutine uniform.

class SubroutineUniformMap

SubroutineUniformMap is a dictionary like object.

__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
read_into(buffer, size=-1, offset=0, write_offset=0)

Read the content into a buffer.

Parameters:
  • buffer (bytarray) – The buffer that will receive the content.
  • size (int) – The size. Value -1 means all.
Keyword Arguments:
 
  • offset (int) – The read offset.
  • write_offset (int) – The write offset.
Returns:

The content of the buffer.

Return type:

bytes

write(data, offset=0)

Write the content.

Parameters:data (bytes) – The data.
Keyword Arguments:
 offset (int) – 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(binding=0)

Bind the buffer to a uniform block.

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

Bind the buffer to a shader storage buffer.

Parameters:binding (int) – The shader storage binding.
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 (int) – The size. Value -1 means all.
Keyword Arguments:
 offset (int) – The offset.
Returns:binary data
Return type:bytes
read_into(buffer, size=-1, offset=0, write_offset=0)

Read the content.

Parameters:
  • buffer (bytarray) – The buffer that will receive the content.
  • size (int) – The size. Value -1 means all.
Keyword Arguments:
 
  • offset (int) – The read offset.
  • write_offset (int) – The write offset.
Returns:

binary data

Return type:

bytes

write(data, offset=0)

Write the content.

Parameters:size (int) – The data.
Keyword Arguments:
 offset (int) – The offset.

VertexArray

Context.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:
Returns:

vertex array

Return type:

VertexArray

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

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 (Primitive) – By default TRIANGLES will be used.
  • vertices (int) – The number of vertices to transform.
Keyword Arguments:
 
  • first (int) – The index of the first vertex to start with.
  • instances (int) – 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 (Buffer) – The buffer to store the output.
  • mode (Primitive) – By default TRIANGLES will be used.
  • vertices (int) – The number of vertices to transform.
Keyword Arguments:
 
  • first (int) – The index of the first vertex to start with.
  • instances (int) – 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.

subroutines

tuple – The subroutines assinged to the VertexArray. The subroutines used when rendering or transforming primitives.

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) – The width and height of the texture.
  • components (int) – The number of components 1, 2, 3 or 4.
  • data (bytes) – Content of the texture.
Keyword Arguments:
 
  • samples (int) – The number of samples. Value 0 means no multisample format.
  • alignment (int) – The byte alignment 1, 2, 4 or 8.
  • 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) – The width and height of the texture.
  • data (bytes) – Content of the texture.
Keyword Arguments:
 
  • samples (int) – The number of samples. Value 0 means no multisample format.
  • alignment (int) – The byte alignment 1, 2, 4 or 8.
Returns:

depth texture

Return type:

Texture

Context.texture3d(size, components, data=None, floats=False) → Texture3D

Create a Texture3D.

Parameters:
  • size (tuple) – The width, height and depth of the texture.
  • components (int) – The number of components 1, 2, 3 or 4.
  • data (bytes) – Content of the texture.
Keyword Arguments:
 
  • alignment (int) – The byte alignment 1, 2, 4 or 8.
  • floats (bool) – Use floating point precision.
Returns:

texture

Return type:

Texture3D

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.

read(alignment=1) → bytes

Read the content of the texture into a buffer.

Keyword Arguments:
 alignment (int) – The byte alignment of the pixels.
Returns:the pixels
Return type:bytes
read_into(buffer, alignment=1, write_offset=0)

Read the content of the texture into a buffer.

Parameters:

buffer (bytearray) – The buffer that will receive the pixels.

Keyword Arguments:
 
  • alignment (int) – The byte alignment of the pixels.
  • write_offset (int) – The write offset.
write(data, viewport=None, alignment=1)

Update the content of the texture.

build_mipmaps(base=0, max_level=1000)

Generate mipmaps.

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

bool – The repeat_x of the texture.

repeat_y

bool – The repeat_y of the texture.

filter

TextureFilter – The filter of the texture.

swizzle

str – The swizzle of the texture.

width

int – The width of the texture.

height

int – The height of the texture.

size

tuple – The size of the texture.

components

int – The number of components of the texture.

samples

int – The number of samples of the texture.

floats

bool – Is the texture using floats?

depth

bool – Is the texture a depth texture?

class Texture3D

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 Texture3D object cannot be instantiated directly, it requires a context. Use Context.texture3d() to create one.

read(alignment=1) → bytes

Read the content of the texture into a buffer.

Parameters:
  • buffer (bytearray) – The buffer that will receive the pixels.
  • viewport (tuple) – The viewport.
Keyword Arguments:
 

alignment (int) – The byte alignment of the pixels.

Returns:

the pixels

Return type:

bytes

read_into(buffer, alignment=1, write_offset=0)

Read the content of the texture into a buffer.

Parameters:
  • buffer (bytearray) – The buffer that will receive the pixels.
  • viewport (tuple) – The viewport.
Keyword Arguments:
 
  • alignment (int) – The byte alignment of the pixels.
  • write_offset (int) – The write offset.
write(data, viewport=None, alignment=1)

Update the content of the texture.

build_mipmaps(base=0, max_level=1000)

Generate mipmaps.

use(location=0)

Bind the texture.

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

bool – The repeat_x of the texture.

repeat_y

bool – The repeat_y of the texture.

repeat_z

bool – The repeat_z of the texture.

filter

TextureFilter – The filter of the texture.

swizzle

str – The swizzle of the texture.

width

int – The width of the texture.

height

int – The height of the texture.

depth

int – The depth of the texture.

size

tuple – The size of the texture.

components

int – The number of components of the texture.

floats

bool – Is the texture using floats?

Framebuffers

Context.framebuffer(color_attachments, depth_attachment=None) → Framebuffer

A Framebuffer is a collection of buffers that can be used as the destination for rendering. The buffers for Framebuffer objects reference images from either Textures or Renderbuffers.

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

A Framebuffer is a collection of buffers that can be used as the destination for rendering. The buffers for Framebuffer objects reference images from either Textures or Renderbuffers.

Create a Framebuffer using Context.framebuffer().

clear(red=0.0, green=0.0, blue=0.0, alpha=0.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 (float) – color component.
  • green (float) – color component.
  • blue (float) – color component.
  • alpha (float) – alpha component.
Keyword Arguments:
 

viewport (tuple) – The viewport.

use()

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

read(viewport=None, components=3, attachment=0, alignment=1, floats=False) → bytes

Read the content of the framebuffer.

Parameters:
  • viewport (tuple) – The viewport.
  • components (int) – The number of components to read.
Keyword Arguments:
 
  • attachment (int) – The color attachment.
  • alignment (int) – The byte alignment of the pixels.
  • floats (bool) – The precision of the pixels.
Returns:

the pixels

Return type:

bytes

read_into(buffer, viewport=None, components=3, attachment=0, alignment=1, floats=False, write_offset=0)

Read the content of the framebuffer into a buffer.

Parameters:
  • buffer (bytearray) – The buffer that will receive the pixels.
  • viewport (tuple) – The viewport.
  • components (int) – The number of components to read.
Keyword Arguments:
 
  • attachment (int) – The color attachment.
  • alignment (int) – The byte alignment of the pixels.
  • floats (bool) – The precision of the pixels.
  • write_offset (int) – The write offset.
viewport

tuple – The viewport of the framebuffer.

color_mask

tuple – The color mask of the framebuffer.

depth_mask

tuple – The depth mask of the framebuffer.

width

int – The width of the framebuffer.

height

int – The height of the framebuffer.

size

tuple – The size of the framebuffer.

samples

int – The samples of the framebuffer.

Renderbuffers

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

Renderbuffer objects are OpenGL objects that contain images. They are created and used specifically with Framebuffer objects.

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

renderbuffer

Return type:

Renderbuffer

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

Renderbuffer objects are OpenGL objects that contain images. They are created and used specifically with Framebuffer objects.

Parameters:size (tuple) – The width and height of the renderbuffer.
Keyword Arguments:
 samples (int) – 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

A ComputeShader is a Shader Stage that is used entirely for computing arbitrary information. While it can do rendering, it is generally used for tasks not directly related to drawing.

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

A Compute Shader is a Shader Stage that is used entirely for computing arbitrary information. While it can do rendering, it is generally used for tasks not directly related to drawing.

run(group_x=1, group_y=1, group_z=1)

Run the compute shader.

Parameters:
  • group_x (int) – The number of work groups to be launched in the X dimension.
  • group_y (int) – The number of work groups to be launched in the Y dimension.
  • group_z (int) – The number of work groups to be launched in the Z dimension.
source

str – The source code of the compute shader.

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

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

Hello World

Hello World

Alpha Blending

Alpha Blending

Julia Fractal

Julia Fractal

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
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;
            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);
        }
    '''),
    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
82
83
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() {
            mat2 rot = mat2(
                cos(rotation), sin(rotation),
                -sin(rotation), cos(rotation)
            );
            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())
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)

Tutorials

Contributing

ModernGL on Github

TL;DR

For those who want to learn from examples.

Install

pip install ModernGL
pip install Pillow numpy

Choose a Window

pip install PyQt5
pip install GLWindow
pip install pygame
pip install pyglet
pip install PyOpenGL

Snippets

  1. Download the vscode snippets
  2. Open vscode
  3. Press ctrl+shift+P
  4. Select Open User Snippets
  5. Select Python
  6. Paste some snippets from vscode snippets

Warning

Do not replace the python.json if you have python snippets already.

  1. Create a new python file
  2. Type mgl_new_ (not working? -> see the note below)
new_example_snippet

Note

  • Press ctrl+space to trigger suggestions.
  • Try to save an empty file with .py ending.
  • Select the language in the right left corner of the window.
  • Make sure you have pasted the snipptes correctly.
  • Press ctrl+shift+P then select Insert Snippet then type mgl_new_

All the ModernGL snippets starts with mgl_

Linting

Full linting support is provided:

ModernGL linted

Hello World with PyQt5

import struct

import ModernGL
from PyQt5 import QtOpenGL, QtWidgets


class QGLControllerWidget(QtOpenGL.QGLWidget):
        def __init__(self):
                fmt = QtOpenGL.QGLFormat()
                fmt.setVersion(3, 3)
                fmt.setProfile(QtOpenGL.QGLFormat.CoreProfile)
                fmt.setSampleBuffers(True)
                super(QGLControllerWidget, self).__init__(fmt, None)

        def initializeGL(self):
                self.ctx = ModernGL.create_context()

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

                vbo = self.ctx.buffer(struct.pack('6f', 0.0, 0.8, -0.6, -0.8, 0.6, -0.8))
                self.vao = self.ctx.simple_vertex_array(prog, vbo, ['vert'])

        def paintGL(self):
                self.ctx.viewport = (0, 0, self.width(), self.height())
                self.ctx.clear(0.9, 0.9, 0.9)
                self.vao.render()
                self.ctx.finish()


app = QtWidgets.QApplication([])
window = QGLControllerWidget()
window.show()
app.exec_()

Hello World with GLWindow

import struct

import GLWindow
import ModernGL

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

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);
                }
        '''),
])

vbo = ctx.buffer(struct.pack('6f', 0.0, 0.8, -0.6, -0.8, 0.6, -0.8))
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()

Help

Rendering

To render we will need:

  1. Context — OpenGL support
  2. Program — Shaders
  3. Buffer filled with the vertex data.
  4. VertexArray to connect the buffer and the program.

Note

The ModernGL implementation of the VertexArray stores a reference to the program, the original OpenGL implementation for Vertex Arrays does not provide this.

To see what other things are different please see: ModernGL vs OpenGL

Context

The context can be created once the window is ready.

ctx = ModernGL.create_context()

The ctx provides functionality to create the Program, Buffer and VertexArray

Program

A Program Object represents fully processed executable code, in the OpenGL Shading Language.

We will create a simple program from a vertex and fragment shader.

Vertex Shader

Takes the vertices as the input.

#version 330
in vec2 vert;
void main() {
        gl_Position = vec4(vert, 0.0, 1.0);
}

Fragment Shader

Writes the output to the screen.

#version 330
out vec4 color;
void main() {
        color = vec4(0.3, 0.5, 1.0, 1.0);
}

The entire program can be created:

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

The buffer in the example is created with the following line of code:

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

The triangle is given as:

#   x     y
   0.0,  0.8
  -0.6, -0.8
   0.6, -0.8
Window Coordinates

The window coordinates

Triangle in Window Coordinates

The triangle in window coordinates

VertexArray

We want the following to happen:

  1. Feed the vertex shader with the coordinates of the triangle.
  2. The vertex shader transforms the triangle to screen coordinates. (we gave the vertices in window coordinates so the vertex shader just passes the coordinates towards)
  3. The triangle will be rasterized.
  4. The triangle gets “painted” with the color given in the fragment shader.

In the example the VertexArray is created with the following line of code:

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

The simple_vertex_array() creates a VertexArray object. The VertexArray will ensure that the prog program object will be used when rendering. Additionally detects the format of the in vec2 vert and binds the attribute to the buffer.

Then finally the rendering is done by:

vao.render()

The expression above expands to:

vao.render(ModernGL.TRIANGLES, vertices=3)

Note

The render() method’s first parameter is the rendering mode (by default ModernGL.TRIANGLES are rendered). The numer of vertices to render is detected form the size of the buffer assigned to the vert vertex attribute. The vbo has 6f (six floats), with the total of 24 bytes, The vert vertex attribute is a vec2 with the size of 8 bytes. The number of vertices detected is 24 / 8 = 3. It can be read/changed programatically by using the VertexArray.vertices attibute.

Note

To create a complex VertexArray use the Context.vertex_array() method.

  • Multiple buffers can be used to create VertexArrays.
  • Padding, Per Vertex, Per Instance and Per Render attributes are also supported.
  • Individual vertex attributes can be bound to different buffers with a custom offset, stride and divisor specified.

Indices and tables