ModernGL

ModernGL is a high performance rendering module for Python.

Install

From PyPI (pip)

ModernGL is available on PyPI for Windows, OS X and Linux as pre-built wheels. No complication is needed unless you are setting up a development environment.

$ pip install moderngl

Verify that the package is working:

$ python -m moderngl
moderngl 5.6.0
--------------
vendor: NVIDIA Corporation
renderer: GeForce RTX 2080 SUPER/PCIe/SSE2
version: 3.3.0 NVIDIA 441.87
python: 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)]
platform: win32
code: 330

Note

If you experience issues it’s probably related to context creation. More configuration might be needed to run moderngl in some cases. This is especially true on linux running without X. See the context section.

Development environment

Ideally you want to fork the repository first.

# .. or clone for your fork
git clone https://github.com/moderngl/moderngl.git
cd moderngl

Building on various platforms:

  • On Windows you need visual c++ build tools installed: https://visualstudio.microsoft.com/visual-cpp-build-tools/
  • On OS X you need X Code installed + command line tools (xcode-select --install)
  • Building on linux should pretty much work out of the box
  • To compile moderngl: python setup.py build_ext --inplace

Package and dev dependencies:

  • Install requirements.txt, tests/requirements.txt and docs/requirements.txt
  • Install the package in editable mode: pip install -e .

The Guide

An introduction to OpenGL

The simplified story

OpenGL (Open Graphics Library) has a long history reaching all the way back to 1992 when it was created by Silicon Graphics. It was partly based in their proprietary IRIS GL (Integrated Raster Imaging System Graphics Library) library.

Today OpenGL is managed by the Khronos Group, an open industry consortium of over 150 leading hardware and software companies creating advanced, royalty-free, acceleration standards for 3D graphics, Augmented and Virtual Reality, vision and machine learning

The purpose of OpenGL is to provide a standard way to interact with the graphics processing unit to achieve hardware accelerated rendering across several platforms. How this is done under the hood is up to the vendors (AMD, Nvidia, Intel, ARM .. etc) as long as the the specifications are followed.

OpenGL have gone though many versions and it can be confusing when looking up resources. Today we separate “Old OpenGL” and “Modern OpenGL”. From 2008 to 2010 version 3.x of OpenGL evolved until version 3.3 and 4.1 was released simultaneously

In 2010 version 3.3, 4.0 and 4.1 was released to modernize the api (simplified explanation) creating something that would be able to utilize Direct3D 11-class hardware. OpenGL 3.3 is the first “Modern OpenGL” version (simplified explanation). Everything from this version is forward compatible all the way to the latest 4.x version. An optional deprecation mechanism was introduced to disable outdated features. Running OpenGL in core mode would remove all old features while running in compatibility mode would still allow mixing the old and new api.

Note

OpenGL 2.x, 3.0, 3.1 and 3.2 can of course access some modern OpenGL features directly, but for simplicity we are are focused on version 3.3 as it created the final standard we are using today. Older OpenGL was also a pretty wild world with countless vendor specific extensions. Modern OpenGL cleaned this up quite a bit.

In OpenGL we often talk about the Fixed Pipeline and the Programmable Pipeline.

OpenGL code using the Fixed Pipeline (Old OpenGL) would use functions like glVertex, glColor, glMaterial glMatrixMode, glLoadIdentity, glBegin, glEnd, glVertexPointer, glColorPointer, glPushMatrix and glPopMatrix. The api had strong opinions and limitations on what you could do hiding what really went on under the hood.

OpenGL code using the Programmable Pipeline (Modern OpenGL) would use functions like glCreateProgram, UseProgram. glCreateShader, VertexAttrib*, glBindBuffer*, glUniform*. This API mainly works with buffers of data and smaller programs called “shaders” running on the GPU to process this data using the OpenGL Shading Language (GLSL). This gives enormous flexibility but requires that we understand the OpenGL pipeline (actually not that complicated).

Beyond OpenGL

OpenGL has a lot of “baggage” after 25 years and hardware have drastically changed since its inception. Plans for “OpenGL 5” was started as the Next Generation OpenGL Initiative (glNext). This Turned into the Vulkan API and was a grounds-up redesign to unify OpenGL and OpenGL ES into one common API that will not be backwards compatible with existing OpenGL versions.

This doesn’t mean OpenGL is not worth learning today. In fact learning 3.3+ shaders and understanding the rendering pipeline will greatly help you understand Vulkan. In some cases you can pretty much copy paste the shaders over to Vulkan.

Where do ModernGL fit into all this?

The ModernGL library exposes the Programmable Pipeline using OpenGL 3.3 core or higher. However, we don’t expose OpenGL functions directly. Instead we expose features though various objects like Buffer and Program in a much more “pythonic” way. It’s in other words a higher level wrapper making OpenGL much easier to reason with. We try to hide most of the complicated details to make the user more productive. There are a lot of pitfalls with OpenGL and we remove most of them.

Learning ModernGL is more about learning shaders and the OpenGL pipeline.

Creating a Context

Before we can do anything with ModernGL we need a Context. The Context object makes us able to create OpenGL resources. ModernGL can only create headless contexts (no window), but it can also detect and use contexts from a large range of window libraries. The moderngl-window library is a good start or reference for rendering to a window.

Most of the example code here assumes a ctx variable exists with a headless context:

# standalone=True makes a headless context
ctx = moderngl.create_context(standalone=True)

Detecting an active context created by a window library is simply:

ctx = moderngl.create_context()

More details about context creation can be found in the Context Creation section.

ModernGL Types

Before throwing you into doing shaders we’ll go through some of the most important types/objects in ModernGL.

  • Buffer is an OpenGL buffer we can for example write vertex data into. This data will reside in graphics memory.
  • Program is a shader program. We can feed it GLSL source code as strings to set up our shader program
  • VertexArray is a light object responsible for communication between Buffer and Program so it can understand how to access the provided buffers and do the rendering call. These objects are currently immutable but are cheap to make.
  • Texture, TextureArray, Texture3D and TextureCube represents the different texture types. Texture is a 2d texture and is most commonly used.
  • Framebuffer is an offscreen render target. It supports different attachments types such as a Texture and a depth texture/buffer.

All of the objects above can only be created from a Context object:

The ModernGL types cannot be extended as in; you cannot subclass them. Extending them must be done through substitution and not inheritance. This is related to performance. Most objects have an extra property that can contain any python object.

Shader Introduction

Shaders are small programs running on the GPU (Graphics Processing Unit). We are using a fairly simple language called GLSL (OpenGL Shading Language). This is a C-style language, so it covers most of the features you would expect with such a language. Control structures (for-loops, if-else statements, etc) exist in GLSL, including the switch statement.

Note

The name “shader” comes from the fact that these small GPU programs was originally created for shading (lighting) 3D scenes. This started as per-vertex lighting when the early shaders could only process vertices and evolved into per-pixel lighting when the fragment shader was introduced. They are used in many other areas today, but the name have stuck around.

Examples of types are:

bool value = true;
int value = 1;
uint value = 1;
float value = 0.0;
double value = 0.0;

Each type above also has a 2, 3 and 4 component version:

// float (default) type
vec2 value = vec2(0.0, 1.0);
vec3 value = vec3(0.0, 1.0, 2.0);
vec4 value = vec4(0.0);

// signed and unsigned integer vectors
ivec3 value = ivec3(0);
uvec3 value = ivec3(0);
// etc ..

More about GLSL data types can be found in the Khronos wiki.

The available functions are for example: radians, degrees sin, `cos, tan, asin, acos, atan, pow exp, log, exp2, log2, sqrt, inversesqrt, abs, sign, floor, ceil, fract, mod, min, max, clamp, mix, step, smoothstep, length, distance, dot, cross, normalize, faceforward, reflect, refract, any, all etc.

All functions can be found in the OpenGL Reference Page (exclude functions starting with gl). Most of the functions exist in several overloaded versions supporting different data types.

The basic setup for a shader is the following:

#version 330

void main() {
}

The #version statement is mandatory and should at least be 330 (GLSL version 3.3 matching OpenGL version 3.3). The version statement should always be the first line in the source code. Higher version number is only needed if more fancy features are needed. By the time you need those you probably know what you are doing.

What we also need to realize when working with shaders is that they are executed in parallel across all the cores on your GPU. This can be everything from tens, hundreds, thousands or more cores. Even integrated GPUs today are very competent.

For those who have not worked with shaders before it can be mind-boggling to see the work they can get done in a matter of microseconds. All shader executions / rendering calls are also asynchronous running in the background while your python code is doing other things (but certain operations can cause a “sync” stalling until the shader program is done)

Vertex Shader (transforms)

Let’s get our hands dirty right away and jump into it by showing the simplest forms of shaders in OpenGL. These are called transforms or transform feedback. Instead of drawing to the screen we simply capture the output of a shader into a Buffer.

The example below shows shader program with only a vertex shader. It has no input data, but we can still force it to run N times. The gl_VertexID (int) variable is a built-in value in vertex shaders containing an integer representing the vertex number being processed.

Input variables in vertex shaders are called attributes (we have no inputs in this example) while output values are called varyings.

import struct
import moderngl

ctx = moderngl.create_context(standalone=True)

program = ctx.program(
    vertex_shader="""
    #version 330

    // Output values for the shader. They end up in the buffer.
    out float value;
    out float product;

    void main() {
        // Implicit type conversion from int to float will happen here
        value = gl_VertexID;
        product = gl_VertexID * gl_VertexID;
    }
    """,
    # What out varyings to capture in our buffer!
    varyings=["value", "product"],
)

NUM_VERTICES = 10

# We always need a vertex array in order to execute a shader program.
# Our shader doesn't have any buffer inputs, so we give it an empty array.
vao = ctx.vertex_array(program, [])

# Create a buffer allocating room for 20 32 bit floats
buffer = ctx.buffer(reserve=NUM_VERTICES * 8)

# Start a transform with buffer as the destination.
# We force the vertex shader to run 10 times
vao.transform(buffer, vertices=NUM_VERTICES)

# Unpack the 20 float values from the buffer (copy from graphics memory to system memory).
# Reading from the buffer will cause a sync (the python program stalls until the shader is done)
data = struct.unpack("20f", buffer.read())
for i in range(0, 20, 2):
    print("value = {}, product = {}".format(*data[i:i+2]))

Output the program is:

value = 0.0, product = 0.0
value = 1.0, product = 1.0
value = 2.0, product = 4.0
value = 3.0, product = 9.0
value = 4.0, product = 16.0
value = 5.0, product = 25.0
value = 6.0, product = 36.0
value = 7.0, product = 49.0
value = 8.0, product = 64.0
value = 9.0, product = 81.0

The GPU is at the very least slightly offended by the meager amount work we assigned it, but this at least shows the basic concept of transforms. We would in most situations also not read the results back into system memory because it’s slow, but sometimes it is needed.

This shader program could for example be modified to generate some geometry or data for any other purpose you might imagine useful. Using modulus (mod) on gl_VertexID can get you pretty far.

Rendering

 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
import moderngl
import numpy as np

from PIL import Image

ctx = moderngl.create_standalone_context()

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

        in vec2 in_vert;
        in vec3 in_color;

        out vec3 v_color;

        void main() {
            v_color = in_color;
            gl_Position = vec4(in_vert, 0.0, 1.0);
        }
    ''',
    fragment_shader='''
        #version 330

        in vec3 v_color;

        out vec3 f_color;

        void main() {
            f_color = v_color;
        }
    ''',
)

x = np.linspace(-1.0, 1.0, 50)
y = np.random.rand(50) - 0.5
r = np.ones(50)
g = np.zeros(50)
b = np.zeros(50)

vertices = np.dstack([x, y, r, g, b])

vbo = ctx.buffer(vertices.astype('f4').tobytes())
vao = ctx.simple_vertex_array(prog, vbo, 'in_vert', 'in_color')

fbo = ctx.simple_framebuffer((512, 512))
fbo.use()
fbo.clear(0.0, 0.0, 0.0, 1.0)
vao.render(moderngl.LINE_STRIP)

Image.frombytes('RGB', fbo.size, fbo.read(), 'raw', 'RGB', 0, -1).show()

Program

ModernGL is different from standard plotting libraries. You can define your own shader program to render stuff. This could complicate things, but also provides freedom on how you render your data.

Here is a sample program that passes the input vertex coordinates as is to screen coordinates.

Screen coordinates are in the [-1, 1], [-1, 1] range for x and y axes. The (-1, -1) point is the lower left corner of the screen.

Screen Coordinates

The screen coordinates

The program will also process a color information.

Entire source

 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
import moderngl

ctx = moderngl.create_standalone_context()

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

        in vec2 in_vert;
        in vec3 in_color;

        out vec3 v_color;

        void main() {
            v_color = in_color;
            gl_Position = vec4(in_vert, 0.0, 1.0);
        }
    ''',
    fragment_shader='''
        #version 330

        in vec3 v_color;

        out vec3 f_color;

        void main() {
            f_color = v_color;
        }
    ''',
)

Vertex Shader

in vec2 in_vert;
in vec3 in_color;

out vec3 v_color;

void main() {
    v_color = in_color;
    gl_Position = vec4(in_vert, 0.0, 1.0);
}

Fragment Shader

in vec3 v_color;

out vec3 f_color;

void main() {
    f_color = v_color;
}

Proceed to the next step.

VertexArray

 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
import moderngl
import numpy as np

ctx = moderngl.create_standalone_context()

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

        in vec2 in_vert;
        in vec3 in_color;

        out vec3 v_color;

        void main() {
            v_color = in_color;
            gl_Position = vec4(in_vert, 0.0, 1.0);
        }
    ''',
    fragment_shader='''
        #version 330

        in vec3 v_color;

        out vec3 f_color;

        void main() {
            f_color = v_color;
        }
    ''',
)

x = np.linspace(-1.0, 1.0, 50)
y = np.random.rand(50) - 0.5
r = np.ones(50)
g = np.zeros(50)
b = np.zeros(50)

vertices = np.dstack([x, y, r, g, b])

vbo = ctx.buffer(vertices.astype('f4').tobytes())
vao = ctx.simple_vertex_array(prog, vbo, 'in_vert', 'in_color')

Proceed to the next step.

Advanced

The Lifecycle of a ModernGL Object

Note

Future version of ModernGL might support different GC models. It’s an area currently being explored.

Releasing Objects

Objects in moderngl don’t automatically release the OpenGL resources it allocated. Each type has a release() method that needs to be called to properly clean up everything:

# Create a texture
texture = ctx.texture((10, 10), 4)

# Properly release the opengl resources
texture.release()

# Ensure we don't keep the object around
texture = None

This comes as a surprise for most people, but there are a number of reasons moderngl have chosen this approach. Unless you are doing headless rendering we don’t even “own” the context itself. It’s the window library creating the context for us and we simply detect it. We don’t really know exactly when this context is destroyed. There are also other more complicated situations such as contexts with shared resources.

You can create your own __del__ methods in wrappers if needed, but keep in mind that moderngl types cannot be extended. They only have an extra attribute that can contain anything.

Detecting Released Objects

If you for some reason need to detect if a resource was released it can be done by checking the type of the internal moderngl object (.mglo property):

>> import moderngl
>> ctx = moderngl.create_standalone_context()
>> buffer = ctx.buffer(reserve=1024)
>> type(buffer.mglo)
<class 'mgl.Buffer'>
>> buffer.release()
>> type(buffer.mglo)
<class 'mgl.InvalidObject'>
>> type(buffer.mglo) == moderngl.mgl.InvalidObject
True

Context Creation

Note

From moderngl 5.6 context creation is handled by the glcontext package. This makes expanding context support easier for users lowering the bar for contributions. It also means context creation is no longer limited by a moderngl releases.

Note

This page might not list all supported backends as the glcontext project keeps evolving. If using anything outside of the default contexts provided per OS, please check the listed backends in the glcontext project.

Introduction

A context is an object giving moderngl access to opengl instructions (greatly simplified). How a context is created depends on your operating system and what kind of platform you want to target.

In the vast majority of cases you’ll be using the default context backend supported by your operating system. This backend will be automatically selected unless a specific backend parameter is used.

Default backend per OS

  • Windows: wgl / opengl32.dll
  • Linux: x11/glx/libGL
  • OS X: CGL

These default backends support two modes:

  • Detecting an exiting active context possibly created by a window library such as glfw, sdl2, pyglet etc.
  • Creating a headless context (No visible window)

Detecting an existing active context created by a window library:

import moderngl
# Create the window with an OpenGL context (Most window libraries support this)
ctx = moderngl.create_context()
# If successful we can now render to the window
print("Default framebuffer is:", ctx.screen)

A great reference using various window libraries can be found here: https://github.com/moderngl/moderngl-window/tree/master/moderngl_window/context

Creating a headless context:

import moderngl
# Create the context
ctx = moderngl.create_context(standalone=True)
# Create a framebuffer we can render to
fbo = ctx.simple_framebuffer((100, 100), 4)
fbo.use()

Require a minimum OpenGL version

ModernGL only support 3.3+ contexts. By default version 3.3 is passed in as the minimum required version of the context returned by the backend.

To require a specific version:

moderngl.create_context(require=430)

This will require OpenGL 4.3. If a lower context version is returned the context creation will fail.

This attribute can be accessed in Context.version_code and will be updated to contain the actual version code of the context (If higher than required).

Specifying context backend

A backend can be passed in for more advanced usage.

For example: Making a headless EGL context on linux:

ctx = moderngl.create_context(standalone=True, backend='egl')

Note

Each backend supports additional keyword arguments for more advanced configuration. This can for example be the exact name of the library to load. More information in the glcontext docs.

Context Sharing

Warning

Object sharing is an experimental feature

Some context support the share parameters enabling object sharing between contexts. This is not needed if you are attaching to existing context with share mode enabled. For example if you create two windows with glfw enabling object sharing.

ModernGL objects (such as moderngl.Buffer, moderngl.Texture, ..) has a ctx property containing the context they were created in. Still ModernGL do not check what context is currently active when accessing these objects. This means the object can be used in both contexts when sharing is enabled.

This should in theory work fine with object sharing enabled:

data1 = numpy.array([1, 2, 3, 4], dtype='u1')
data2 = numpy.array([4, 3, 2, 1], dtype='u1')

ctx1 = moderngl.create_context(standalone=True)
ctx2 = moderngl.create_context(standalone=True, share=True)

with ctx1 as ctx:
    b1 = ctx.buffer(data1)

with ctx2 as ctx:
    b2 = ctx.buffer(data2)

print(b1.glo)  # Displays: 1
print(b2.glo)  # Displays: 2

with ctx1:
    print(b1.read())
    print(b2.read())

with ctx2:
    print(b1.read())
    print(b2.read())

Still, there are some limitations to object sharing. Especially objects that reference other objects (framebuffer, vertex array object, etc.)

More information for a deeper dive:

Context Info

Various information such as limits and driver information can be found in the info property. It can often be useful to know the vendor and render for the context:

>>> import moderngl
>>> ctx = moderngl.create_context(standalone=True, gl_version=(4.6))
>>> ctx.info["GL_VENDOR"]
'NVIDIA Corporation'
>>> ctx.info["GL_RENDERER"]
'GeForce RTX 2080 SUPER/PCIe/SSE2'
>>> ctx.info["GL_VERSION"]
'3.3.0 NVIDIA 456.71'

Note that it reports version 3.3 here because ModernGL by default requests a version 3.3 context (minimum requirement).

Texture Format

Description

The format of a texture can be described by the dtype parameter during texture creation. For example the moderngl.Context.texture(). The default dtype is f1. Each component is an unsigned byte (0-255) that is normalized when read in a shader into a value from 0.0 to 1.0.

The formats are based on the string formats used in numpy.

Some quick example of texture creation:

# RGBA (4 component) f1 texture
texture = ctx.texture((100, 100), 4)  # dtype f1 is default

# R (1 component) f4 texture (32 bit float)
texture = ctx.texture((100, 100), 1, dype="f4")

# RG (2 component) u2 texture (16 bit unsigned integer)
texture = ctx.texture((100, 100), 2, dtype="u2")

Texture contents can be passed in using the data parameter during creation or by using the write() method. The object passed in data can be bytes or any object supporting the buffer protocol.

Float Textures

f1 textures are just unsigned bytes (8 bits per component) (GL_UNSIGNED_BYTE)

dtype Components Base Format Internal Format
f1 1 GL_RED GL_R8
f1 2 GL_RG GL_RG8
f1 3 GL_RGB GL_RGB8
f1 4 GL_RGBA GL_RGBA8

f2 textures stores 16 bit float values (GL_HALF_FLOAT`).

dtype Components Base Format Internal Format
f2 1 GL_RED GL_R16F
f2 2 GL_RG GL_RG16F
f2 3 GL_RGB GL_RGB16F
f2 4 GL_RGBA GL_RGBA16F

f4 textures store 32 bit float values. (GL_FLOAT) Note that some drivers do not like 3 components because of alignment.

dtype Components Base Format Internal Format
f4 1 GL_RED GL_R32F
f4 2 GL_RG GL_RG32F
f4 3 GL_RGB GL_RGB32F
f4 4 GL_RGBA GL_RGBA32F

Integer Textures

Integer textures come in a signed and unsigned version. The advantage with integer textures is that shader can read the raw integer values from them using for example usampler2D (unsigned) or isampler2D (signed).

Unsigned

u1 textures store unsigned byte values (GL_UNSIGNED_BYTE).

dtype Components Base Format Internal Format
u1 1 GL_RED_INTEGER GL_R8UI
u1 2 GL_RG_INTEGER GL_RG8UI
u1 3 GL_RGB_INTEGER GL_RGB8UI
u1 4 GL_RGBA_INTEGER GL_RGBA8UI

u2 textures store 16 bit unsigned integers (GL_UNSIGNED_SHORT).

dtype Components Base Format Internal Format
u2 1 GL_RED_INTEGER GL_R16UI
u2 2 GL_RG_INTEGER GL_RG16UI
u2 3 GL_RGB_INTEGER GL_RGB16UI
u2 4 GL_RGBA_INTEGER GL_RGBA16UI

u4 textures store 32 bit unsigned integers (GL_UNSIGNED_INT)

dtype Components Base Format Internal Format
u4 1 GL_RED_INTEGER GL_R32UI
u4 2 GL_RG_INTEGER GL_RG32UI
u4 3 GL_RGB_INTEGER GL_RGB32UI
u4 4 GL_RGBA_INTEGER GL_RGBA32UI
Signed

u1 textures store signed byte values (GL_BYTE).

dtype Components Base Format Internal Format
i1 1 GL_RED_INTEGER GL_R8I
i1 2 GL_RG_INTEGER GL_RG8I
i1 3 GL_RGB_INTEGER GL_RGB8I
i1 4 GL_RGBA_INTEGER GL_RGBA8I

u2 textures store 16 bit integers (GL_SHORT).

dtype Components Base Format Internal Format
i2 1 GL_RED_INTEGER GL_R16I
i2 2 GL_RG_INTEGER GL_RG16I
i2 3 GL_RGB_INTEGER GL_RGB16I
i2 4 GL_RGBA_INTEGER GL_RGBA16I

u4 textures store 32 bit integers (GL_INT)

dtype Components Base Format Internal Format
i4 1 GL_RED_INTEGER GL_R32I
i4 2 GL_RG_INTEGER GL_RG32I
i4 3 GL_RGB_INTEGER GL_RGB32I
i4 4 GL_RGBA_INTEGER GL_RGBA32I

Overriding internalformat

Context.texture() supports overriding the internalformat of the texture. This is only necessary when needing a different internalformats from the tables above. This can for example be GL_SRGB8 = 0x8C41 or some compressed format. You may also need to look up in Context.extensions to ensure the context supports internalformat you are using. We do not provide the enum values for these alternative internalformats. They can be looked up in the registry : https://raw.githubusercontent.com/KhronosGroup/OpenGL-Registry/master/xml/gl.xml

Example:

texture = ctx.texture(image.size, 3, data=srbg_data, internal_format=GL_SRGB8)

Buffer Format

Description

A buffer format is a short string describing the layout of data in a vertex buffer object (VBO).

A VBO often contains a homogeneous array of C-like structures. The buffer format describes what each element of the array looks like. For example, a buffer containing an array of high-precision 2D vertex positions might have the format "2f8" - each element of the array consists of two floats, each float being 8 bytes wide, ie. a double.

Buffer formats are used in the Context.vertex_array() constructor, as the 2nd component of the content arg. See the Example of simple usage below.

Syntax

A buffer format looks like:

[count]type[size] [[count]type[size]...] [/usage]

Where:

  • count is an optional integer. If omitted, it defaults to 1.

  • type is a single character indicating the data type:

    • f float
    • i int
    • u unsigned int
    • x padding
  • size is an optional number of bytes used to store the type. If omitted, it defaults to 4 for numeric types, or to 1 for padding bytes.

    A format may contain multiple, space-separated [count]type[size] triples (See the Example of single interleaved array), followed by:

  • /usage is optional. It should be preceded by a space, and then consists of a slash followed by a single character, indicating how successive values in the buffer should be passed to the shader:

    • /v per vertex. Successive values from the buffer are passed to each vertex. This is the default behavior if usage is omitted.
    • /i per instance. Successive values from the buffer are passed to each instance.
    • /r per render. the first buffer value is passed to every vertex of every instance. ie. behaves like a uniform.

    When passing multiple VBOs to a VAO, the first one must be of usage /v, as shown in the Example of multiple arrays with differing /usage.

Valid combinations of type and size are:

  size
type 1 2 4 8
f Unsigned byte (normalized) Half float Float Double
i Byte Short Int -
u Unsigned byte Unsigned short Unsigned int -
x 1 byte 2 bytes 4 bytes 8 bytes

The entry f1 has two unusual properties:

  1. Its type is f (for float), but it defines a buffer containing unsigned bytes. For this size of floats only, the values are normalized, ie. unsigned bytes from 0 to 255 in the buffer are converted to float values from 0.0 to 1.0 by the time they reach the vertex shader. This is intended for passing in colors as unsigned bytes.
  2. Three unsigned bytes, with a format of 3f1, may be assigned to a vec3 attribute, as one would expect. But, from ModernGL v6.0, they can alternatively be passed to a vec4 attribute. This is intended for passing a buffer of 3-byte RGB values into an attribute which also contains an alpha channel.

There are no size 8 variants for types i and u.

This buffer format syntax is specific to ModernGL. As seen in the usage examples below, the formats sometimes look similar to the format strings passed to struct.pack, but that is a different syntax (documented here.)

Buffer formats can represent a wide range of vertex attribute formats. For rare cases of specialized attribute formats that are not expressible using buffer formats, there is a VertexArray.bind() method, to manually configure the underlying OpenGL binding calls. This is not generally recommended.

Examples

Example buffer formats

"2f" has a count of 2 and a type of f (float). Hence it describes two floats, passed to a vertex shader’s vec2 attribute. The size of the floats is unspecified, so defaults to 4 bytes. The usage of the buffer is unspecified, so defaults to /v (vertex), meaning each successive pair of floats in the array are passed to successive vertices during the render call.

"3i2/i" means three i (integers). The size of each integer is 2 bytes, ie. they are shorts, passed to an ivec3 attribute. The trailing /i means that consecutive values in the buffer are passed to successive instances during an instanced render call. So the same value is passed to every vertex within a particular instance.

Buffers contining interleaved values are represented by multiple space separated count-type-size triples. Hence:

"2f 3u x /v" means:

  • 2f: two floats, passed to a vec2 attribute, followed by
  • 3u: three unsigned bytes, passed to a uvec3, then
  • x: a single byte of padding, for alignment.

The /v indicates successive elements in the buffer are passed to successive vertices during the render. This is the default, so the /v could be omitted.

Example of simple usage

Consider a VBO containing 2D vertex positions, forming a single triangle:

# a 2D triangle (ie. three (x, y) vertices)
verts = [
     0.0, 0.9,
    -0.5, 0.0,
     0.5, 0.0,
]

# pack all six values into a binary array of C-like floats
verts_buffer = struct.pack("6f", *verts)

# put the array into a VBO
vbo = ctx.buffer(verts_buffer)

# use the VBO in a VAO
vao = ctx.vertex_array(
    shader_program,
    [
        (vbo, "2f", "in_vert"), # <---- the "2f" is the buffer format
    ]
    index_buffer_object
)

The line (vbo, "2f", "in_vert"), known as the VAO content, indicates that vbo contains an array of values, each of which consists of two floats. These values are passed to an in_vert attribute, declared in the vertex shader as:

in vec2 in_vert;

The "2f" format omits a size component, so the floats default to 4-bytes each. The format also omits the trailing /usage component, which defaults to /v, so successive (x, y) rows from the buffer are passed to successive vertices during the render call.

Example of single interleaved array

A buffer array might contain elements consisting of multiple interleaved values.

For example, consider a buffer array, each element of which contains a 2D vertex position as floats, an RGB color as unsigned ints, and a single byte of padding for alignment:

position color padding
x y r g b -
float float unsigned byte unsigned byte unsigned byte byte

Such a buffer, however you choose to contruct it, would then be passed into a VAO using:

vao = ctx.vertex_array(
    shader_program,
    [
        (vbo, "2f 3f1 x", "in_vert", "in_color")
    ]
    index_buffer_object
)

The format starts with 2f, for the two position floats, which will be passed to the shader’s in_vert attribute, declared as:

in vec2 in_vert;

Next, after a space, is 3f1, for the three color unsigned bytes, which get normalized to floats by f1. These floats will be passed to the shader’s in_color attribute:

in vec3 in_color;

Finally, the format ends with x, a single byte of padding, which needs no shader attribute name.

Example of multiple arrays with differing /usage

To illustrate the trailing /usage portion, consider rendering a dozen cubes with instanced rendering. We will use:

  • vbo_verts_normals contains vertices (3 floats) and normals (3 floats) for the vertices within a single cube.
  • vbo_offset_orientation contains offsets (3 floats) and orientations (9 float matrices) that are used to position and orient each cube.
  • vbo_colors contains colors (3 floats). In this example, there is only one color in the buffer, that will be used for every vertex of every cube.

Our shader will take all the above values as attributes.

We bind the above VBOs in a single VAO, to prepare for an instanced rendering call:

vao = ctx.vertex_array(
    shader_program,
    [
        (vbo_verts_normals,      "3f 3f /v", "in_vert", "in_norm"),
        (vbo_offset_orientation, "3f 9f /i", "in_offset", "in_orientation"),
        (vbo_colors,             "3f /r",    "in_color"),
    ]
    index_buffer_object
)

So, the vertices and normals, using /v, are passed to each vertex within an instance. This fulfills the rule tha the first VBO in a VAO must have usage /v. These are passed to vertex attributes as:

in vec3 in_vert;
in vec3 in_norm;

The offsets and orientations pass the same value to each vertex within an instance, but then pass the next value in the buffer to the vertices of the next instance. Passed as:

in vec3 in_offset;
in mat3 in_orientation;

The single color is passed to every vertex of every instance. If we had stored the color with /v or /i, then we would have had to store duplicate identical color values in vbo_colors - one per instance or one per vertex. To render all our cubes in a single color, this is needless duplication. Using /r, only one color is require the buffer, and it is passed to every vertex of every instance for the whole render call:

in vec3 in_color;

An alternative approach would be to pass in the color as a uniform, since it is constant. But doing it as an attribute is more flexible. It allows us to reuse the same shader program, bound to a different buffer, to pass in color data which varies per instance, or per vertex.

Techniques

Headless on Ubuntu 18 Server

Dependencies

Headless rendering can be achieved with EGL or X11. We’ll cover both cases.

Starting with fresh ubuntu 18 server install we need to install required packages:

sudo apt-install python3-pip mesa-utils libegl1-mesa xvfb

This should install mesa an diagnostic tools if needed later.

  • mesa-utils installs libgl1-mesa and tools like glxinfo`
  • libegl1-mesa is optional if using EGL instead of X11

Creating a context

The libraries we are going to interact with has the following locations:

/usr/lib/x86_64-linux-gnu/libGL.so.1
/usr/lib/x86_64-linux-gnu/libX11.so.6
/usr/lib/x86_64-linux-gnu/libEGL.so.1

Double check what library versions you actually have installed and make modifications to what versions we refer to below. moderngl will attempt to load libGL.so, libX11.so and libEGL.so by default. Optionally you can create symlinks or use python to locate the desired lib files. For simplicity we will be using the exact library names.

Before we can create a context we to run a virtual display:

export DISPLAY=:99.0
Xvfb :99 -screen 0 640x480x24 &

Now we can create a context with x11 or egl:

# X11
import moderngl
ctx = moderngl.create_context(
    standalone=True,
    libgl='libGL.so.1',
    libx11='libX11.so.6',
)

# EGL
import moderngl
ctx = moderngl.create_context(
    standalone=True,
    backend='egl',
    libgl='libGL.so.1',
    libegl='libEGL.so.1',
)

Running an example

Checking that everything works can be done with a basic triangle example.

Install dependencies:

pip3 install moderngl numpy pyrr pillow

The following example renders a triangle and writes it to a png file so we can verify the contents.

_images/output.png
import moderngl
import numpy as np
from PIL import Image
from pyrr import Matrix44

# -------------------
# CREATE CONTEXT HERE
# -------------------

prog = ctx.program(vertex_shader="""
    #version 330
    uniform mat4 model;
    in vec2 in_vert;
    in vec3 in_color;
    out vec3 color;
    void main() {
        gl_Position = model * vec4(in_vert, 0.0, 1.0);
        color = in_color;
    }
    """,
    fragment_shader="""
    #version 330
    in vec3 color;
    out vec4 fragColor;
    void main() {
        fragColor = vec4(color, 1.0);
    }
""")

vertices = np.array([
    -0.6, -0.6,
    1.0, 0.0, 0.0,
    0.6, -0.6,
    0.0, 1.0, 0.0,
    0.0, 0.6,
    0.0, 0.0, 1.0,
], dtype='f4')

vbo = ctx.buffer(vertices)
vao = ctx.simple_vertex_array(prog, vbo, 'in_vert', 'in_color')
fbo = ctx.framebuffer(color_attachments=[ctx.texture((512, 512), 4)])

fbo.use()
ctx.clear()
prog['model'].write(Matrix44.from_eulers((0.0, 0.1, 0.0), dtype='f4'))
vao.render(moderngl.TRIANGLES)

data = fbo.read(components=3)
image = Image.frombytes('RGB', fbo.size, data)
image = image.transpose(Image.FLIP_TOP_BOTTOM)
image.save('output.png')

Reference

moderngl

Attributes

Attributes available in the root moderngl module. Some may be listed in their original sub-module, but they are imported during initialization.

Context Flags

Also available in the Context instance including mode details.

moderngl.context.NOTHING = 0

Represents no states. Can be used with Context.enable_only() to disable all states.

moderngl.context.BLEND = 1

Enable/disable blending

moderngl.context.DEPTH_TEST = 2

Enable/disable depth testing

moderngl.context.CULL_FACE = 4

Enable/disable face culling

moderngl.context.RASTERIZER_DISCARD = 8

Enable/disable rasterization

moderngl.context.PROGRAM_POINT_SIZE = 16

When enabled we can write to gl_PointSize in the vertex shader to specify the point size. When disabled Context.point_size is used.

Primitive Modes

Also available in the Context instance including mode details.

moderngl.vertex_array.POINTS = 0

Each vertex represents a point

moderngl.vertex_array.LINES = 1

Vertices 0 and 1 are considered a line. Vertices 2 and 3 are considered a line. And so on. If the user specifies a non-even number of vertices, then the extra vertex is ignored.

moderngl.vertex_array.LINE_LOOP = 2

As line strips, except that the first and last vertices are also used as a line. Thus, you get n lines for n input vertices. If the user only specifies 1 vertex, the drawing command is ignored. The line between the first and last vertices happens after all of the previous lines in the sequence.

moderngl.vertex_array.LINE_STRIP = 3

The adjacent vertices are considered lines. Thus, if you pass n vertices, you will get n-1 lines. If the user only specifies 1 vertex, the drawing command is ignored.

moderngl.vertex_array.TRIANGLES = 4

Vertices 0, 1, and 2 form a triangle. Vertices 3, 4, and 5 form a triangle. And so on.

moderngl.vertex_array.TRIANGLE_STRIP = 5

Every group of 3 adjacent vertices forms a triangle. The face direction of the strip is determined by the winding of the first triangle. Each successive triangle will have its effective face order reversed, so the system compensates for that by testing it in the opposite way. A vertex stream of n length will generate n-2 triangles.

moderngl.vertex_array.TRIANGLE_FAN = 6

The first vertex is always held fixed. From there on, every group of 2 adjacent vertices form a triangle with the first. So with a vertex stream, you get a list of triangles like so: (0, 1, 2) (0, 2, 3), (0, 3, 4), etc. A vertex stream of n length will generate n-2 triangles.

moderngl.vertex_array.LINES_ADJACENCY = 10

These are special primitives that are expected to be used specifically with geomtry shaders. These primitives give the geometry shader more vertices to work with for each input primitive. Data needs to be duplicated in buffers.

moderngl.vertex_array.LINE_STRIP_ADJACENCY = 11

These are special primitives that are expected to be used specifically with geomtry shaders. These primitives give the geometry shader more vertices to work with for each input primitive. Data needs to be duplicated in buffers.

moderngl.vertex_array.TRIANGLES_ADJACENCY = 12

These are special primitives that are expected to be used specifically with geomtry shaders. These primitives give the geometry shader more vertices to work with for each input primitive. Data needs to be duplicated in buffers.

moderngl.vertex_array.TRIANGLE_STRIP_ADJACENCY = 13

These are special primitives that are expected to be used specifically with geomtry shaders. These primitives give the geometry shader more vertices to work with for each input primitive. Data needs to be duplicated in buffers.

moderngl.vertex_array.PATCHES = 14

primitive type can only be used when Tessellation is active. It is a primitive with a user-defined number of vertices, which is then tessellated based on the control and evaluation shaders into regular points, lines, or triangles, depending on the TES’s settings.

Texture Filters

Also available in the Context instance including mode details.

moderngl.texture.NEAREST = 9728

Returns the value of the texture element that is nearest (in Manhattan distance) to the specified texture coordinates.

moderngl.texture.LINEAR = 9729

Returns the weighted average of the four texture elements that are closest to the specified texture coordinates. These can include items wrapped or repeated from other parts of a texture, depending on the values of texture repeat mode, and on the exact mapping.

moderngl.texture.NEAREST_MIPMAP_NEAREST = 9984

Chooses the mipmap that most closely matches the size of the pixel being textured and uses the NEAREST` criterion (the texture element closest to the specified texture coordinates) to produce a texture value.

moderngl.texture.LINEAR_MIPMAP_NEAREST = 9985

Chooses the mipmap that most closely matches the size of the pixel being textured and uses the LINEAR criterion (a weighted average of the four texture elements that are closest to the specified texture coordinates) to produce a texture value.

moderngl.texture.NEAREST_MIPMAP_LINEAR = 9986

Chooses the two mipmaps that most closely match the size of the pixel being textured and uses the NEAREST criterion (the texture element closest to the specified texture coordinates ) to produce a texture value from each mipmap. The final texture value is a weighted average of those two values.

moderngl.texture.LINEAR_MIPMAP_LINEAR = 9987

Chooses the two mipmaps that most closely match the size of the pixel being textured and uses the LINEAR criterion (a weighted average of the texture elements that are closest to the specified texture coordinates) to produce a texture value from each mipmap. The final texture value is a weighted average of those two values.

Blend Functions

Also available in the Context instance including mode details.

moderngl.context.ZERO = 0

(0,0,0,0)

moderngl.context.ONE = 1

(1,1,1,1)

moderngl.context.SRC_COLOR = 768

(Rs0/kR,Gs0/kG,Bs0/kB,As0/kA)

moderngl.context.ONE_MINUS_SRC_COLOR = 769

(1,1,1,1) − (Rs0/kR,Gs0/kG,Bs0/kB,As0/kA)

moderngl.context.SRC_ALPHA = 770

(As0/kA,As0/kA,As0/kA,As0/kA)

moderngl.context.ONE_MINUS_SRC_ALPHA = 771

(1,1,1,1) − (As0/kA,As0/kA,As0/kA,As0/kA)

moderngl.context.DST_ALPHA = 772

(Ad/kA,Ad/kA,Ad/kA,Ad/kA)

moderngl.context.ONE_MINUS_DST_ALPHA = 773

(1,1,1,1) − (Ad/kA,Ad/kA,Ad/kA,Ad/kA)

moderngl.context.DST_COLOR = 774

(Rd/kR,Gd/kG,Bd/kB,Ad/kA)

moderngl.context.ONE_MINUS_DST_COLOR = 775

(1,1,1,1) − (Rd/kR,Gd/kG,Bd/kB,Ad/kA)

Shortcuts
moderngl.context.DEFAULT_BLENDING = (770, 771)

Shotcut for the default blending SRC_ALPHA, ONE_MINUS_SRC_ALPHA

moderngl.context.ADDITIVE_BLENDING = (1, 1)

Shotcut for additive blending ONE, ONE

moderngl.context.PREMULTIPLIED_ALPHA = (770, 1)

Shotcut for blend mode when using premultiplied alpha SRC_ALPHA, ONE

Blend Equations

Also available in the Context instance including mode details.

moderngl.context.FUNC_ADD = 32774

source + destination

moderngl.context.FUNC_SUBTRACT = 32778

source - destination

moderngl.context.FUNC_REVERSE_SUBTRACT = 32779

destination - source

moderngl.context.MIN = 32775

Minimum of source and destination

moderngl.context.MAX = 32776

Maximum of source and destination

Provoking Vertex

Also available in the Context instance including mode details.

moderngl.context.FIRST_VERTEX_CONVENTION = 36429

Specifies the first vertex should be used as the source of data for flat shaded varyings. Used with Context.provoking_vertex.

moderngl.context.LAST_VERTEX_CONVENTION = 36430

Specifies the last vertex should be used as the source of data for flat shaded varyings. Used with Context.provoking_vertex.

Functions

Also see Context.

moderngl.create_context(require=None, standalone=False, share=False, **settings) → moderngl.context.Context

Create a ModernGL context by loading OpenGL functions from an existing OpenGL context. An OpenGL context must exists.

Example:

# Accept the current context version
ctx = moderngl.create_context()

# Require at least OpenGL 4.3
ctx = moderngl.create_context(require=430)

# Create a headless context requiring OpenGL 4.3
ctx = moderngl.create_context(require=430, standalone=True)
Keyword Arguments:
 
  • require (int) – OpenGL version code (default: 330)
  • standalone (bool) – Headless flag
  • **settings – Other backend specific settings
Returns:

Context object

moderngl.create_standalone_context(require=None, share=False, **settings) → moderngl.context.Context

Create a standalone/headless ModernGL context. The preferred way of making a context is through moderngl.create_context().

Example:

# Create a context with highest possible supported version
ctx = moderngl.create_context()

# Require at least OpenGL 4.3
ctx = moderngl.create_context(require=430)
Keyword Arguments:
 require (int) – OpenGL version code.
Returns:Context object
moderngl.detect_format(program, attributes, mode='mgl') → str

Detect format for vertex attributes. The format returned does not contain padding.

Parameters:
  • program (Program) – The program.
  • attributes (list) – A list of attribute names.
Returns:

str

Context

class moderngl.Context

Class exposing OpenGL features. ModernGL objects can be created from this class.

Create

moderngl.create_context(require=None) → Context

Create a ModernGL context by loading OpenGL functions from an existing OpenGL context. An OpenGL context must exists.

Example:

# Accept the current context version
ctx = moderngl.create_context()

# Require at least OpenGL 4.3
ctx = moderngl.create_context(require=430)

# Create a headless context requiring OpenGL 4.3
ctx = moderngl.create_context(require=430, standalone=True)
Keyword Arguments:
 
  • require (int) – OpenGL version code (default: 330)
  • standalone (bool) – Headless flag
  • **settings – Other backend specific settings
Returns:

Context object

moderngl.create_standalone_context(require=None) → Context

Create a standalone/headless ModernGL context. The preferred way of making a context is through moderngl.create_context().

Example:

# Create a context with highest possible supported version
ctx = moderngl.create_context()

# Require at least OpenGL 4.3
ctx = moderngl.create_context(require=430)
Keyword Arguments:
 require (int) – OpenGL version code.
Returns:Context object

ModernGL Objects

Context.program(vertex_shader, fragment_shader=None, geometry_shader=None, tess_control_shader=None, tess_evaluation_shader=None, varyings=()) → Program

Create a Program object.

Only linked programs will be returned.

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 object

Context.simple_vertex_array(program, buffer, *attributes, index_buffer=None, index_element_size=4) → VertexArray

Create a VertexArray object.

Warning

This method is deprecated and may be removed in the future. Use Context.vertex_array() instead. It also supports the argument format this method describes.

Parameters:
  • program (Program) – The program used when rendering.
  • buffer (Buffer) – The buffer.
  • attributes (list) – A list of attribute names.
Keyword Arguments:
 
  • index_element_size (int) – byte size of each index element, 1, 2 or 4.
  • index_buffer (Buffer) – An index buffer.
Returns:

VertexArray object

Context.vertex_array(*args, **kwargs) → VertexArray

Create a VertexArray object.

This method also supports arguments for Context.simple_vertex_array().

Parameters:
  • program (Program) – The program used when rendering.
  • content (list) – A list of (buffer, format, attributes). See Buffer Format.
  • index_buffer (Buffer) – An index buffer.
Keyword Arguments:
 
  • index_element_size (int) – byte size of each index element, 1, 2 or 4.
  • skip_errors (bool) – Ignore skip_errors varyings.
Returns:

VertexArray object

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

Create a Buffer object.

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 object

Context.texture(size, components, data=None, samples=0, alignment=1, dtype='f1', internal_format=None) → Texture

Create a Texture object.

Warning

Do not play with internal_format unless you know exactly you are doing. This is an override to support sRGB and compressed textures if needed.

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.
  • dtype (str) – Data type.
  • internal_format (int) – Override the internalformat of the texture (IF needed)
Returns:

Texture object

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

Create a Texture object.

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:

Texture object

Context.texture3d(size, components, data=None, alignment=1, dtype='f1') → Texture3D

Create a Texture3D object.

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.
  • dtype (str) – Data type.
Returns:

Texture3D object

Context.texture_array(size, components, data=None, alignment=1, dtype='f1') → TextureArray

Create a TextureArray object.

Parameters:
  • size (tuple) – The (width, height, layers) of the texture.
  • components (int) – The number of components 1, 2, 3 or 4.
  • data (bytes) – Content of the texture. The size must be (width, height * layers) so each layer is stacked vertically.
Keyword Arguments:
 
  • alignment (int) – The byte alignment 1, 2, 4 or 8.
  • dtype (str) – Data type.
Returns:

Texture3D object

Context.texture_cube(size, components, data=None, alignment=1, dtype='f1') → TextureCube

Create a TextureCube object.

Parameters:
  • size (tuple) – The width, height of the texture. Each side of the cube will have this size.
  • components (int) – The number of components 1, 2, 3 or 4.
  • data (bytes) – Content of the texture. The data should be have the following ordering: positive_x, negative_x, positive_y, negative_y, positive_z + negative_z
Keyword Arguments:
 
  • alignment (int) – The byte alignment 1, 2, 4 or 8.
  • dtype (str) – Data type.
Returns:

TextureCube object

Context.simple_framebuffer(size, components=4, samples=0, dtype='f1') → Framebuffer

Creates a Framebuffer with a single color attachment and depth buffer using moderngl.Renderbuffer attachments.

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.
  • dtype (str) – Data type.
Returns:

Framebuffer object

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

Framebuffer object

Context.renderbuffer(size, components=4, samples=0, dtype='f1') → 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.
  • dtype (str) – Data type.
Returns:

Renderbuffer object

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:Renderbuffer object
Context.scope(framebuffer=None, enable_only=None, textures=(), uniform_buffers=(), storage_buffers=(), samplers=(), enable=None) → Scope

Create a Scope object.

Parameters:
  • framebuffer (Framebuffer) – The framebuffer to use when entering.
  • enable_only (int) – The enable_only flags to set when entering.
Keyword Arguments:
 
  • textures (list) – List of (texture, binding) tuples.
  • uniform_buffers (list) – List of (buffer, binding) tuples.
  • storage_buffers (list) – List of (buffer, binding) tuples.
  • samplers (list) – List of sampler bindings
  • enable (int) – Flags to enable for this vao such as depth testing and blending
Context.query(samples=False, any_samples=False, time=False, primitives=False) → Query

Create a Query object.

Keyword Arguments:
 
  • samples (bool) – Query GL_SAMPLES_PASSED or not.
  • any_samples (bool) – Query GL_ANY_SAMPLES_PASSED or not.
  • time (bool) – Query GL_TIME_ELAPSED or not.
  • primitives (bool) – Query GL_PRIMITIVES_GENERATED or not.
Context.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:ComputeShader object
Context.sampler(repeat_x=True, repeat_y=True, repeat_z=True, filter=None, anisotropy=1.0, compare_func='?', border_color=None, min_lod=-1000.0, max_lod=1000.0, texture=None) → Sampler

Create a Sampler object.

Keyword Arguments:
 
  • repeat_x (bool) – Repeat texture on x
  • repeat_y (bool) – Repeat texture on y
  • repeat_z (bool) – Repeat texture on z
  • filter (tuple) – The min and max filter
  • anisotropy (float) – Number of samples for anisotropic filtering. Any value greater than 1.0 counts as a use of anisotropic filtering
  • compare_func – Compare function for depth textures
  • border_color (tuple) – The (r, g, b, a) color for the texture border. When this value is set the repeat_ values are overridden setting the texture wrap to return the border color when outside [0, 1] range.
  • min_lod (float) – Minimum level-of-detail parameter (Default -1000.0). This floating-point value limits the selection of highest resolution mipmap (lowest mipmap level)
  • max_lod (float) – Minimum level-of-detail parameter (Default 1000.0). This floating-point value limits the selection of the lowest resolution mipmap (highest mipmap level)
  • texture (Texture) – The texture for this sampler
Context.clear_samplers(start=0, end=-1)

Unbinds samplers from texture units. Sampler bindings do clear automatically between every frame, but lingering samplers can still be a source of weird bugs during the frame rendering. This methods provides a fairly brute force and efficient way to ensure texture units are clear.

Keyword Arguments:
 
  • start (int) – The texture unit index to start the clearing samplers
  • stop (int) – The texture unit index to stop clearing samplers

Example:

# Clear texture unit 0, 1, 2, 3, 4
ctx.clear_samplers(start=0, end=5)

# Clear texture unit 4, 5, 6, 7
ctx.clear_samplers(start=4, end=8)
Context.release()

Release the ModernGL context.

If the context is not standalone the standard backends in glcontext will not do anything because the context was not created by moderngl.

Standalone contexts can normally be released.

Methods

Context.clear(red=0.0, green=0.0, blue=0.0, alpha=0.0, depth=1.0, viewport=None, color=None)

Clear the bound framebuffer.

If a viewport passed in, a scissor test will be used to clear the given viewport. This viewport take prescense over the framebuffers scissor. Clearing can still be done with scissor if no viewport is passed in.

This method also respects the color_mask and depth_mask. It can for example be used to only clear the depth or color buffer or specific components in the color buffer.

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.
  • depth (float) – depth value.
Keyword Arguments:
 

viewport (tuple) – The viewport.

Context.enable_only(flags)

Clears all existing flags applying new ones.

Note that the enum values defined in moderngl are not the same as the ones in opengl. These are defined as bit flags so we can logical or them together.

Available flags:

  • moderngl.NOTHING
  • moderngl.BLEND
  • moderngl.DEPTH_TEST
  • moderngl.CULL_FACE
  • moderngl.RASTERIZER_DISCARD
  • moderngl.PROGRAM_POINT_SIZE

Examples:

# Disable all flags
ctx.enable_only(moderngl.NOTHING)

# Ensure only depth testing and face culling is enabled
ctx.enable_only(moderngl.DEPTH_TEST | moderngl.CULL_FACE)
Parameters:flags (EnableFlag) – The flags to enable
Context.enable(flags)

Enable flags.

Note that the enum values defined in moderngl are not the same as the ones in opengl. These are defined as bit flags so we can logical or them together.

For valid flags, please see enable_only().

Examples:

# Enable a single flag
ctx.enable(moderngl.DEPTH_TEST)

# Enable multiple flags
ctx.enable(moderngl.DEPTH_TEST | moderngl.CULL_FACE | moderngl.BLEND)
Parameters:flag (int) – The flags to enable.
Context.disable(flags)

Disable flags.

For valid flags, please see enable_only().

Examples:

# Only disable depth testing
ctx.disable(moderngl.DEPTH_TEST)

# Disable depth testing and face culling
ctx.disable(moderngl.DEPTH_TEST | moderngl.CULL_FACE)
Parameters:flag (int) – The flags to disable.
Context.finish()

Wait for all drawing commands to finish.

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

Copy buffer content.

Parameters:
  • dst (Buffer) – The destination buffer.
  • src (Buffer) – The source buffer.
  • size (int) – The number of bytes to copy.
Keyword Arguments:
 
  • read_offset (int) – The read offset.
  • write_offset (int) – The write offset.
Context.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:
Context.detect_framebuffer(glo=None) → Framebuffer

Detect framebuffer. This is already done when creating a context, but if the underlying window library for some changes the default framebuffer during the lifetime of the application this might be necessary.

Parameters:glo (int) – Framebuffer object.
Returns:Framebuffer object
Context.__enter__()

Enters the context.

This should ideally be used with the with statement:

with other_context as ctx:
    # Do something in this context

When exiting the context the previously bound context is activated again.

Warning

Context switching can be risky unless you know what you are doing. Use with care.

Context.__exit__(exc_type, exc_val, exc_tb)

Exit the context.

See Context.__enter__()

Attributes

Context.line_width

Warning

A line width other than 1.0 is not guaranteed to work across different OpenGL implementations. For wide lines you should be using geometry shaders.

float: Set the default line width.

Context.point_size

Set/get the default point size.

Type:float
Context.depth_func

Set the default depth func. The depth function is set using a string.

Example:

ctx.depth_func = '<='  # GL_LEQUAL
ctx.depth_func = '<'   # GL_LESS
ctx.depth_func = '>='  # GL_GEQUAL
ctx.depth_func = '>'   # GL_GREATER
ctx.depth_func = '=='  # GL_EQUAL
ctx.depth_func = '!='  # GL_NOTEQUAL
ctx.depth_func = '0'   # GL_NEVER
ctx.depth_func = '1'   # GL_ALWAYS
Type:int
Context.blend_func

Set the blend func (write only) Blend func can be set for rgb and alpha separately if needed.

Supported blend functions are:

moderngl.ZERO
moderngl.ONE
moderngl.SRC_COLOR
moderngl.ONE_MINUS_SRC_COLOR
moderngl.DST_COLOR
moderngl.ONE_MINUS_DST_COLOR
moderngl.SRC_ALPHA
moderngl.ONE_MINUS_SRC_ALPHA
moderngl.DST_ALPHA
moderngl.ONE_MINUS_DST_ALPHA

Example:

# For both rgb and alpha
ctx.blend_func = moderngl.SRC_ALPHA, moderngl.ONE_MINUS_SRC_ALPHA

# Separate for rgb and alpha
ctx.blend_func = (
    moderngl.SRC_ALPHA, moderngl.ONE_MINUS_SRC_ALPHA,
    moderngl.ONE, moderngl.ONE
)
Type:tuple
Context.blend_equation

Set the blend equation (write only).

Blend equations specify how source and destination colors are combined in blending operations. By default FUNC_ADD is used.

Blend equation can be set for rgb and alpha separately if needed.

Supported functions are:

moderngl.FUNC_ADD               # source + destination
moderngl.FUNC_SUBTRACT          # source - destination
moderngl.FUNC_REVERSE_SUBTRACT  # destination - source
moderngl.MIN                    # Minimum of source and destination
moderngl.MAX                    # Maximum of source and destination

Example:

# For both rgb and alpha channel
ctx.blend_func = moderngl.FUNC_ADD

# Separate for rgb and alpha channel
ctx.blend_func = moderngl.FUNC_ADD, moderngl.MAX
Type:tuple
Context.viewport

Get or set the viewport of the active framebuffer.

Example:

>>> ctx.viewport
(0, 0, 1280, 720)
>>> ctx.viewport = (0, 0, 640, 360)
>>> ctx.viewport
(0, 0, 640, 360)

If no framebuffer is bound (0, 0, 0, 0) will be returned.

Type:tuple
Context.scissor

Get or set the scissor box for the active framebuffer

When scissor testing is enabled fragments outside the defined scissor box will be discarded. This applies to rendered geometry or Context.clear().

Setting is value enables scissor testing in the framebuffer. Setting the scissor to None disables scissor testing and reverts the scissor box to match the framebuffer size.

Example:

# Enable scissor testing
>>> ctx.scissor = 100, 100, 200, 100
# Disable scissor testing
>>> ctx.scissor = None

If no framebuffer is bound (0, 0, 0, 0) will be returned.

Type:tuple
Context.version_code

The OpenGL version code. Reports 410 for OpenGL 4.1

Type:int
Context.screen

A Framebuffer instance representing the screen usually set when creating a context with create_context() attaching to an existing context. This is the special system framebuffer represented by framebuffer id=0.

When creating a standalone context this property is not set.

Type:Framebuffer
Context.fbo

The active framebuffer. Set every time Framebuffer.use() is called.

Type:Framebuffer
Context.front_face

The front_face. Acceptable values are 'ccw' (default) or 'cw'.

Face culling must be enabled for this to have any effect: ctx.enable(moderngl.CULL_FACE).

Example:

# Triangles winded counter-clockwise considered front facing
ctx.front_face = 'ccw'
# Triangles winded clockwise considered front facing
ctx.front_face = 'cw'
Type:str
Context.cull_face

The face side to cull. Acceptable values are 'back' (default) 'front' or 'front_and_back'.

This is similar to Context.front_face()

Face culling must be enabled for this to have any effect: ctx.enable(moderngl.CULL_FACE).

Example:

#
ctx.cull_face = 'front'
#
ctx.cull_face = 'back'
#
ctx.cull_face = 'front_and_back'
Type:str
Context.wireframe

Wireframe settings for debugging.

Type:bool
Context.max_samples

The maximum supported number of samples for multisampling

Type:int
Context.max_integer_samples

The max integer samples.

Type:int
Context.max_texture_units

The max texture units.

Type:int
Context.default_texture_unit

The default texture unit.

Type:int
Context.max_anisotropy

The maximum value supported for anisotropic filtering.

Type:float
Context.multisample

Enable/disable multisample mode (GL_MULTISAMPLE). This property is write only.

Example:

# Enable
ctx.multisample = True
# Disable
ctx.multisample = False
Type:bool
Context.patch_vertices

The number of vertices that will be used to make up a single patch primitive.

Type:int
Context.provoking_vertex

Specifies the vertex to be used as the source of data for flat shaded varyings.

Flatshading a vertex shader varying output (ie. flat out vec3 pos) means to assign all vetices of the primitive the same value for that output. The vertex from which these values is derived is known as the provoking vertex.

It can be configured to be the first or the last vertex.

This property is write only.

Example:

# Use first vertex
ctx.provoking_vertex = moderngl.FIRST_VERTEX_CONVENTION

# Use last vertex
ctx.provoking_vertex = moderngl.LAST_VERTEX_CONVENTION
Type:int
Context.error

The result of glGetError() but human readable. This values is provided for debug purposes only and is likely to reduce performace when used in a draw loop.

Type:str
Context.extensions

The extensions supported by the context

All extensions names have a GL_ prefix, so if the spec refers to ARB_compute_shader we need to look for GL_ARB_compute_shader:

# If compute shaders are supported ...
>> "GL_ARB_compute_shader" in ctx.extensions
True

Example data:

{
    'GL_ARB_multi_bind',
    'GL_ARB_shader_objects',
    'GL_ARB_half_float_vertex',
    'GL_ARB_map_buffer_alignment',
    'GL_ARB_arrays_of_arrays',
    'GL_ARB_pipeline_statistics_query',
    'GL_ARB_provoking_vertex',
    'GL_ARB_gpu_shader5',
    'GL_ARB_uniform_buffer_object',
    'GL_EXT_blend_equation_separate',
    'GL_ARB_tessellation_shader',
    'GL_ARB_multi_draw_indirect',
    'GL_ARB_multisample',
    .. etc ..
}
Type:Set[str]
Context.info

OpenGL Limits and information about the context

Example:

# The maximum width and height of a texture
>> ctx.info["GL_MAX_TEXTURE_SIZE"]
16384

# Vendor and renderer
>> ctx.info["GL_VENDOR"]
NVIDIA Corporation
>> ctx.info["GL_RENDERER"]
NVIDIA GeForce GT 650M OpenGL Engine

Example data:

{
    'GL_VENDOR': 'NVIDIA Corporation',
    'GL_RENDERER': 'NVIDIA GeForce GT 650M OpenGL Engine',
    'GL_VERSION': '4.1 NVIDIA-10.32.0 355.11.10.10.40.102',
    'GL_POINT_SIZE_RANGE': (1.0, 2047.0),
    'GL_SMOOTH_LINE_WIDTH_RANGE': (0.5, 1.0),
    'GL_ALIASED_LINE_WIDTH_RANGE': (1.0, 1.0),
    'GL_POINT_FADE_THRESHOLD_SIZE': 1.0,
    'GL_POINT_SIZE_GRANULARITY': 0.125,
    'GL_SMOOTH_LINE_WIDTH_GRANULARITY': 0.125,
    'GL_MIN_PROGRAM_TEXEL_OFFSET': -8.0,
    'GL_MAX_PROGRAM_TEXEL_OFFSET': 7.0,
    'GL_MINOR_VERSION': 1,
    'GL_MAJOR_VERSION': 4,
    'GL_SAMPLE_BUFFERS': 0,
    'GL_SUBPIXEL_BITS': 8,
    'GL_CONTEXT_PROFILE_MASK': 1,
    'GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT': 256,
    'GL_DOUBLEBUFFER': False,
    'GL_STEREO': False,
    'GL_MAX_VIEWPORT_DIMS': (16384, 16384),
    'GL_MAX_3D_TEXTURE_SIZE': 2048,
    'GL_MAX_ARRAY_TEXTURE_LAYERS': 2048,
    'GL_MAX_CLIP_DISTANCES': 8,
    'GL_MAX_COLOR_ATTACHMENTS': 8,
    'GL_MAX_COLOR_TEXTURE_SAMPLES': 8,
    'GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS': 233472,
    'GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS': 231424,
    'GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS': 80,
    'GL_MAX_COMBINED_UNIFORM_BLOCKS': 70,
    'GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS': 233472,
    'GL_MAX_CUBE_MAP_TEXTURE_SIZE': 16384,
    'GL_MAX_DEPTH_TEXTURE_SAMPLES': 8,
    'GL_MAX_DRAW_BUFFERS': 8,
    'GL_MAX_DUAL_SOURCE_DRAW_BUFFERS': 1,
    'GL_MAX_ELEMENTS_INDICES': 150000,
    'GL_MAX_ELEMENTS_VERTICES': 1048575,
    'GL_MAX_FRAGMENT_INPUT_COMPONENTS': 128,
    'GL_MAX_FRAGMENT_UNIFORM_COMPONENTS': 4096,
    'GL_MAX_FRAGMENT_UNIFORM_VECTORS': 1024,
    'GL_MAX_FRAGMENT_UNIFORM_BLOCKS': 14,
    'GL_MAX_GEOMETRY_INPUT_COMPONENTS': 128,
    'GL_MAX_GEOMETRY_OUTPUT_COMPONENTS': 128,
    'GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS': 16,
    'GL_MAX_GEOMETRY_UNIFORM_BLOCKS': 14,
    'GL_MAX_GEOMETRY_UNIFORM_COMPONENTS': 2048,
    'GL_MAX_INTEGER_SAMPLES': 1,
    'GL_MAX_SAMPLES': 8,
    'GL_MAX_RECTANGLE_TEXTURE_SIZE': 16384,
    'GL_MAX_RENDERBUFFER_SIZE': 16384,
    'GL_MAX_SAMPLE_MASK_WORDS': 1,
    'GL_MAX_SERVER_WAIT_TIMEOUT': -1,
    'GL_MAX_TEXTURE_BUFFER_SIZE': 134217728,
    'GL_MAX_TEXTURE_IMAGE_UNITS': 16,
    'GL_MAX_TEXTURE_LOD_BIAS': 15,
    'GL_MAX_TEXTURE_SIZE': 16384,
    'GL_MAX_UNIFORM_BUFFER_BINDINGS': 70,
    'GL_MAX_UNIFORM_BLOCK_SIZE': 65536,
    'GL_MAX_VARYING_COMPONENTS': 0,
    'GL_MAX_VARYING_VECTORS': 31,
    'GL_MAX_VARYING_FLOATS': 0,
    'GL_MAX_VERTEX_ATTRIBS': 16,
    'GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS': 16,
    'GL_MAX_VERTEX_UNIFORM_COMPONENTS': 4096,
    'GL_MAX_VERTEX_UNIFORM_VECTORS': 1024,
    'GL_MAX_VERTEX_OUTPUT_COMPONENTS': 128,
    'GL_MAX_VERTEX_UNIFORM_BLOCKS': 14,
    'GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET': 0,
    'GL_MAX_VERTEX_ATTRIB_BINDINGS': 0,
    'GL_VIEWPORT_BOUNDS_RANGE': (-32768, 32768),
    'GL_VIEWPORT_SUBPIXEL_BITS': 0,
    'GL_MAX_VIEWPORTS': 16
}
Type:dict
Context.mglo

Internal representation for debug purposes only.

Context.extra

Any - Attribute for storing user defined objects

Context Flags

Context flags are used to enable or disable states in the context. These are not the same enum values as in opengl, but are rather bit flags so we can or them together setting multiple states in a simple way.

These values are available in the Context object and in the moderngl module when you don’t have access to the context.

import moderngl

# From moderngl
ctx.enable_only(moderngl.DEPTH_TEST | moderngl.CULL_FACE)

# From context
ctx.enable_only(ctx.DEPTH_TEST | ctx.CULL_FACE)
Context.NOTHING = 0

Represents no states. Can be used with Context.enable_only() to disable all states.

Context.BLEND = 1

Enable/disable blending

Context.DEPTH_TEST = 2

Enable/disable depth testing

Context.CULL_FACE = 4

Enable/disable face culling

Context.RASTERIZER_DISCARD = 8

Enable/disable rasterization

Context.PROGRAM_POINT_SIZE = 16

When enabled we can write to gl_PointSize in the vertex shader to specify the point size. When disabled Context.point_size is used.

Primitive Modes

Context.POINTS = 0

Each vertex represents a point

Context.LINES = 1

Vertices 0 and 1 are considered a line. Vertices 2 and 3 are considered a line. And so on. If the user specifies a non-even number of vertices, then the extra vertex is ignored.

Context.LINE_LOOP = 2

As line strips, except that the first and last vertices are also used as a line. Thus, you get n lines for n input vertices. If the user only specifies 1 vertex, the drawing command is ignored. The line between the first and last vertices happens after all of the previous lines in the sequence.

Context.LINE_STRIP = 3

The adjacent vertices are considered lines. Thus, if you pass n vertices, you will get n-1 lines. If the user only specifies 1 vertex, the drawing command is ignored.

Context.TRIANGLES = 4

Vertices 0, 1, and 2 form a triangle. Vertices 3, 4, and 5 form a triangle. And so on.

Context.TRIANGLE_STRIP = 5

Every group of 3 adjacent vertices forms a triangle. The face direction of the strip is determined by the winding of the first triangle. Each successive triangle will have its effective face order reversed, so the system compensates for that by testing it in the opposite way. A vertex stream of n length will generate n-2 triangles.

Context.TRIANGLE_FAN = 6

The first vertex is always held fixed. From there on, every group of 2 adjacent vertices form a triangle with the first. So with a vertex stream, you get a list of triangles like so: (0, 1, 2) (0, 2, 3), (0, 3, 4), etc. A vertex stream of n length will generate n-2 triangles.

Context.LINES_ADJACENCY = 10

These are special primitives that are expected to be used specifically with geomtry shaders. These primitives give the geometry shader more vertices to work with for each input primitive. Data needs to be duplicated in buffers.

Context.LINE_STRIP_ADJACENCY = 11

These are special primitives that are expected to be used specifically with geomtry shaders. These primitives give the geometry shader more vertices to work with for each input primitive. Data needs to be duplicated in buffers.

Context.TRIANGLES_ADJACENCY = 12

These are special primitives that are expected to be used specifically with geomtry shaders. These primitives give the geometry shader more vertices to work with for each input primitive. Data needs to be duplicated in buffers.

Context.TRIANGLE_STRIP_ADJACENCY = 13

These are special primitives that are expected to be used specifically with geomtry shaders. These primitives give the geometry shader more vertices to work with for each input primitive. Data needs to be duplicated in buffers.

Context.PATCHES = 14

primitive type can only be used when Tessellation is active. It is a primitive with a user-defined number of vertices, which is then tessellated based on the control and evaluation shaders into regular points, lines, or triangles, depending on the TES’s settings.

Texture Filters

Also available in the Context instance including mode details.

Context.NEAREST = 9728

Returns the value of the texture element that is nearest (in Manhattan distance) to the specified texture coordinates.

Context.LINEAR = 9729

Returns the weighted average of the four texture elements that are closest to the specified texture coordinates. These can include items wrapped or repeated from other parts of a texture, depending on the values of texture repeat mode, and on the exact mapping.

Context.NEAREST_MIPMAP_NEAREST = 9984

Chooses the mipmap that most closely matches the size of the pixel being textured and uses the NEAREST criterion (the texture element closest to the specified texture coordinates) to produce a texture value.

Context.LINEAR_MIPMAP_NEAREST = 9985

Chooses the mipmap that most closely matches the size of the pixel being textured and uses the LINEAR criterion (a weighted average of the four texture elements that are closest to the specified texture coordinates) to produce a texture value.

Context.NEAREST_MIPMAP_LINEAR = 9986

Chooses the two mipmaps that most closely match the size of the pixel being textured and uses the NEAREST criterion (the texture element closest to the specified texture coordinates ) to produce a texture value from each mipmap. The final texture value is a weighted average of those two values.

Context.LINEAR_MIPMAP_LINEAR = 9987

Chooses the two mipmaps that most closely match the size of the pixel being textured and uses the LINEAR criterion (a weighted average of the texture elements that are closest to the specified texture coordinates) to produce a texture value from each mipmap. The final texture value is a weighted average of those two values.

Blend Functions

Blend functions are used with Context.blend_func to control blending operations.

# Default value
ctx.blend_func = ctx.SRC_ALPHA, ctx.ONE_MINUS_SRC_ALPHA
Context.ZERO = 0

(0,0,0,0)

Context.ONE = 1

(1,1,1,1)

Context.SRC_COLOR = 768

(Rs0/kR,Gs0/kG,Bs0/kB,As0/kA)

Context.ONE_MINUS_SRC_COLOR = 769

(1,1,1,1) − (Rs0/kR,Gs0/kG,Bs0/kB,As0/kA)

Context.SRC_ALPHA = 770

(As0/kA,As0/kA,As0/kA,As0/kA)

Context.ONE_MINUS_SRC_ALPHA = 771

(1,1,1,1) − (As0/kA,As0/kA,As0/kA,As0/kA)

Context.DST_ALPHA = 772

(Ad/kA,Ad/kA,Ad/kA,Ad/kA)

Context.ONE_MINUS_DST_ALPHA = 773

(1,1,1,1) − (Ad/kA,Ad/kA,Ad/kA,Ad/kA)

Context.DST_COLOR = 774

(Rd/kR,Gd/kG,Bd/kB,Ad/kA)

Context.ONE_MINUS_DST_COLOR = 775

(1,1,1,1) − (Rd/kR,Gd/kG,Bd/kB,Ad/kA)

Blend Function Shortcuts

Context.DEFAULT_BLENDING = (770, 771)

Shotcut for the default blending SRC_ALPHA, ONE_MINUS_SRC_ALPHA

Context.ADDITIVE_BLENDING = (1, 1)

Shotcut for additive blending ONE, ONE

Context.PREMULTIPLIED_ALPHA = (770, 1)

Shotcut for blend mode when using premultiplied alpha SRC_ALPHA, ONE

Blend Equations

Used with Context.blend_equation.

Context.FUNC_ADD = 32774

source + destination

Context.FUNC_SUBTRACT = 32778

source - destination

Context.FUNC_REVERSE_SUBTRACT = 32779

destination - source

Context.MIN = 32775

Minimum of source and destination

Context.MAX = 32776

Maximum of source and destination

Other Enums

Context.FIRST_VERTEX_CONVENTION = 36429

Specifies the first vertex should be used as the source of data for flat shaded varyings. Used with Context.provoking_vertex.

Context.LAST_VERTEX_CONVENTION = 36430

Specifies the last vertex should be used as the source of data for flat shaded varyings. Used with Context.provoking_vertex.

Examples

import moderngl
# create a window
ctx = moderngl.create_context()
print(ctx.version_code)
import moderngl
ctx = moderngl.create_standalone_context()
print(ctx.version_code)

context_manager.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import moderngl


class ContextManager:
    ctx = None

    @staticmethod
    def get_default_context(allow_fallback_standalone_context=True) -> moderngl.Context:
        '''
            Default context
        '''

        if ContextManager.ctx is None:
            try:
                ContextManager.ctx = moderngl.create_context()
            except:
                if allow_fallback_standalone_context:
                    ContextManager.ctx = moderngl.create_standalone_context()
                else:
                    raise

        return ContextManager.ctx

example.py

1
2
3
4
from context_manager import ContextManager

ctx = ContextManager.get_default_context()
print(ctx.version_code)

Buffer

class moderngl.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().

Create

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

Create a Buffer object.

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 object

Methods

Buffer.assign(index)

Helper method for assigning a buffer.

Returns:(self, index) tuple
Buffer.bind(*attribs, layout=None)

Helper method for binding a buffer.

Returns:(self, layout, *attribs) tuple
Buffer.write(data, offset=0)

Write the content.

Parameters:data (bytes) – The data.
Keyword Arguments:
 offset (int) – The offset.
Buffer.write_chunks(data, start, step, count)

Split data to count equal parts.

Write the chunks using offsets calculated from start, step and stop.

Parameters:
  • data (bytes) – The data.
  • start (int) – First offset.
  • step (int) – Offset increment.
  • count (int) – The number of offsets.
Buffer.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:bytes
Buffer.read_into(buffer, size=-1, offset=0, write_offset=0)

Read the content into a buffer.

Parameters:
  • buffer (bytearray) – 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.
Buffer.read_chunks(chunk_size, start, step, count) → bytes

Read the content.

Read and concatenate the chunks of size chunk_size using offsets calculated from start, step and stop.

Parameters:
  • chunk_size (int) – The chunk size.
  • start (int) – First offset.
  • step (int) – Offset increment.
  • count (int) – The number of offsets.
Returns:

bytes

Buffer.read_chunks_into(buffer, chunk_size, start, step, count, write_offset=0)

Read the content.

Read and concatenate the chunks of size chunk_size using offsets calculated from start, step and stop.

Parameters:
  • buffer (bytearray) – The buffer that will receive the content.
  • chunk_size (int) – The chunk size.
  • start (int) – First offset.
  • step (int) – Offset increment.
  • count (int) – The number of offsets.
Keyword Arguments:
 

write_offset (int) – The write offset.

Buffer.clear(size=-1, offset=0, chunk=None)

Clear the content.

Parameters:

size (int) – The size. Value -1 means all.

Keyword Arguments:
 
  • offset (int) – The offset.
  • chunk (bytes) – The chunk to use repeatedly.
Buffer.bind_to_uniform_block(binding=0, offset=0, size=-1)

Bind the buffer to a uniform block.

Parameters:

binding (int) – The uniform block binding.

Keyword Arguments:
 
  • offset (int) – The offset.
  • size (int) – The size. Value -1 means all.
Buffer.bind_to_storage_buffer(binding=0, offset=0, size=-1)

Bind the buffer to a shader storage buffer.

Parameters:

binding (int) – The shader storage binding.

Keyword Arguments:
 
  • offset (int) – The offset.
  • size (int) – The size. Value -1 means all.
Buffer.orphan(size=-1)

Orphan the buffer with the option to specify a new size.

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.

Keyword Arguments:
 size (int) – The new byte size if the buffer. If not supplied the buffer size will be unchanged.

Example

# For simplicity the VertexArray creation is omitted

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

# Fill the buffer

>>> vbo.write(some_temporary_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_temporary_data)
>>> vao.render(...)

# We can also resize the buffer. In this case we double the size

>> vbo.orphan(vbo.size * 2)
Buffer.release()

Release the ModernGL object.

Attributes

Buffer.size

The size of the buffer in bytes.

Type:int
Buffer.dynamic

Is the buffer created with the dynamic flag?

Type:bool
Buffer.glo

The internal OpenGL object. This values is provided for debug purposes only.

Type:int
Buffer.mglo

Internal representation for debug purposes only.

Buffer.extra

Any - Attribute for storing user defined objects

Buffer.ctx

The context this object belongs to

VertexArray

class moderngl.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.

Note

Compared to OpenGL, VertexArray objects have some additional responsibilities:

Create

Context.simple_vertex_array(program, buffer, *attributes, index_buffer=None, index_element_size=4) → VertexArray

Create a VertexArray object.

Warning

This method is deprecated and may be removed in the future. Use Context.vertex_array() instead. It also supports the argument format this method describes.

Parameters:
  • program (Program) – The program used when rendering.
  • buffer (Buffer) – The buffer.
  • attributes (list) – A list of attribute names.
Keyword Arguments:
 
  • index_element_size (int) – byte size of each index element, 1, 2 or 4.
  • index_buffer (Buffer) – An index buffer.
Returns:

VertexArray object

Context.vertex_array(*args, **kwargs) → VertexArray

Create a VertexArray object.

This method also supports arguments for Context.simple_vertex_array().

Parameters:
  • program (Program) – The program used when rendering.
  • content (list) – A list of (buffer, format, attributes). See Buffer Format.
  • index_buffer (Buffer) – An index buffer.
Keyword Arguments:
 
  • index_element_size (int) – byte size of each index element, 1, 2 or 4.
  • skip_errors (bool) – Ignore skip_errors varyings.
Returns:

VertexArray object

Methods

VertexArray.render(mode=None, vertices=-1, first=0, instances=-1)

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

Parameters:
  • mode (int) – 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.
VertexArray.render_indirect(buffer, mode=None, count=-1, first=0)

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

The draw commands are 5 integers: (count, instanceCount, firstIndex, baseVertex, baseInstance).

Parameters:
  • buffer (Buffer) – Indirect drawing commands.
  • mode (int) – By default TRIANGLES will be used.
  • count (int) – The number of draws.
Keyword Arguments:
 

first (int) – The index of the first indirect draw command.

VertexArray.transform(buffer, mode=None, vertices=-1, first=0, instances=-1, buffer_offset=0)

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 (int) – By default POINTS 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.
  • buffer_offset (int) – Byte offset for the output buffer
VertexArray.bind(attribute, cls, buffer, fmt, offset=0, stride=0, divisor=0, normalize=False)

Bind individual attributes to buffers.

Parameters:
  • location (int) – The attribute location.
  • cls (str) – The attribute class. Valid values are f, i or d.
  • buffer (Buffer) – The buffer.
  • format (str) – The buffer format.
Keyword Arguments:
 
  • offset (int) – The offset.
  • stride (int) – The stride.
  • divisor (int) – The divisor.
  • normalize (bool) – The normalize parameter, if applicable.
VertexArray.release()

Release the ModernGL object.

Attributes

VertexArray.program

The program assigned to the VertexArray. The program used when rendering or transforming primitives.

Type:Program
VertexArray.index_buffer

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

Type:Buffer
VertexArray.index_element_size

The byte size of each element in the index buffer

Type:int
VertexArray.scope

The moderngl.Scope.

VertexArray.vertices

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.

Type:int
VertexArray.instances

Get or set the number of instances to render

Type:int
VertexArray.subroutines

The subroutines assigned to the VertexArray. The subroutines used when rendering or transforming primitives.

Type:tuple
VertexArray.glo

The internal OpenGL object. This values is provided for debug purposes only.

Type:int
VertexArray.mglo

Internal representation for debug purposes only.

VertexArray.extra

Any - Attribute for storing user defined objects

VertexArray.ctx

The context this object belongs to

Program

class moderngl.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.

Uniform buffers can be bound using Buffer.bind_to_uniform_block() or can be set individually. For more complex binding yielding higher performance consider using moderngl.Scope.

Create

Context.program(vertex_shader, fragment_shader=None, geometry_shader=None, tess_control_shader=None, tess_evaluation_shader=None, varyings=()) → Program

Create a Program object.

Only linked programs will be returned.

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 object

Methods

Program.get(key, default) → Union[Uniform, UniformBlock, Subroutine, Attribute, Varying]

Returns a Uniform, UniformBlock, Subroutine, Attribute or Varying.

Parameters:default – This is the value to be returned in case key does not exist.
Returns:Uniform, UniformBlock, Subroutine, Attribute or Varying
Program.__getitem__(key) → Union[Uniform, UniformBlock, Subroutine, Attribute, Varying]

Get a member such as uniforms, uniform blocks, subroutines, attributes and varyings by name.

# Get a uniform
uniform = program['color']

# Uniform values can be set on the returned object
# or the `__setitem__` shortcut can be used.
program['color'].value = 1.0, 1.0, 1.0, 1.0

# Still when writing byte data we need to use the `write()` method
program['color'].write(buffer)
Program.__setitem__(key, value)

Set a value of uniform or uniform block

# Set a vec4 uniform
uniform['color'] = 1.0, 1.0, 1.0, 1.0

# Optionally we can store references to a member and set the value directly
uniform = program['color']
uniform.value = 1.0, 0.0, 0.0, 0.0

uniform = program['cameraMatrix']
uniform.write(camera_matrix)
Program.__iter__() → Generator[str, NoneType, NoneType]

Yields the internal members names as strings. This includes all members such as uniforms, attributes etc.

Example:

# Print member information
for name in program:
    member = program[name]
    print(name, type(member), member)

Output:

vert <class 'moderngl.program_members.attribute.Attribute'> <Attribute: 0>
vert_color <class 'moderngl.program_members.attribute.Attribute'> <Attribute: 1>
gl_InstanceID <class 'moderngl.program_members.attribute.Attribute'> <Attribute: -1>
rotation <class 'moderngl.program_members.uniform.Uniform'> <Uniform: 0>
scale <class 'moderngl.program_members.uniform.Uniform'> <Uniform: 1>

We can filter on member type if needed:

for name in prog:
    member = prog[name]
    if isinstance(member, moderngl.Uniform):
        print("Uniform", name, member)

or a less verbose version using dict comprehensions:

uniforms = {name: self.prog[name] for name in self.prog
            if isinstance(self.prog[name], moderngl.Uniform)}
print(uniforms)

Output:

{'rotation': <Uniform: 0>, 'scale': <Uniform: 1>}
Program.__eq__(other) → bool

Compares two programs opengl names (mglo).

Returns:If the programs have the same opengl name
Return type:bool

Example:

# True if the internal opengl name is the same
program_1 == program_2
Program.release()

Release the ModernGL object.

Attributes

Program.geometry_input

The geometry input primitive.

The GeometryShader’s input primitive if the GeometryShader exists. The geometry input primitive will be used for validation. (from layout(input_primitive) in;)

This can only be POINTS, LINES, LINES_ADJACENCY, TRIANGLES, TRIANGLE_ADJACENCY.

Type:int
Program.geometry_output

The geometry output primitive.

The GeometryShader’s output primitive if the GeometryShader exists. This can only be POINTS, LINE_STRIP and TRIANGLE_STRIP (from layout(output_primitive​, max_vertices = vert_count) out;)

Type:int
Program.geometry_vertices

The maximum number of vertices that

the geometry shader will output. (from layout(output_primitive​, max_vertices = vert_count) out;)

Type:int
Program.subroutines

The subroutine uniforms.

Type:tuple
Program.glo

The internal OpenGL object. This values is provided for debug purposes only.

Type:int
Program.mglo

Internal representation for debug purposes only.

Program.extra

Any - Attribute for storing user defined objects

Program.ctx

The context this object belongs to

Examples

A simple program designed for rendering

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
my_render_program = ctx.program(
    vertex_shader='''
        #version 330

        in vec2 vert;

        void main() {
            gl_Position = vec4(vert, 0.0, 1.0);
        }
    ''',
    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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
my_transform_program = ctx.program(
    vertex_shader='''
        #version 330

        in vec4 vert;
        out float vert_length;

        void main() {
            vert_length = length(vert);
        }
    ''',
    varyings=['vert_length']
)

Program Members

Uniform
class moderngl.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.__getitem__() or Program.__iter__()

Methods
Uniform.read() → bytes

Read the value of the uniform.

Uniform.write(data)

Write the value of the uniform.

Attributes
Uniform.location

The location of the uniform. The location holds the value returned by the glGetUniformLocation. To set the value of the uniform use the value instead.

Type:int
Uniform.dimension

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
Type:int
Uniform.array_length

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

Type:int
Uniform.name

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

Type:str
Uniform.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.

Uniform.extra

Any - Attribute for storing user defined objects

Uniform.mglo

Internal representation for debug purposes only.

UniformBlock
class moderngl.UniformBlock
UniformBlock.binding

The binding of the uniform block.

Type:int
UniformBlock.value

The value of the uniform block.

Type:int
UniformBlock.name

The name of the uniform block.

Type:str
UniformBlock.index

The index of the uniform block.

Type:int
UniformBlock.size

The size of the uniform block.

Type:int
UniformBlock.extra

Any - Attribute for storing user defined objects

UniformBlock.mglo

Internal representation for debug purposes only.

Subroutine
class moderngl.Subroutine

This class represents a program subroutine.

Subroutine.index

The index of the subroutine.

Type:int
Subroutine.name

The name of the subroutine.

Type:str
Subroutine.extra

Any - Attribute for storing user defined objects

Attribute
class moderngl.Attribute

This class represents a program attribute.

Attribute.location

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

Type:int
Attribute.array_length

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

Type:int
Attribute.dimension

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
Type:int
Attribute.shape

The shape is a single character, representing the scalar type of the attribute.

shape GLSL types
'i' int
ivec2 ivec3 ivec4
'I' uint
uvec2 uvec3 uvec4
'f' float
vec2 vec3 vec4
mat2 mat3 mat4
mat2x3 mat2x4 mat3x4 mat4x2 mat4x2 mat4x3
'd' double
dvec2 dvec3 dvec4
dmat2 dmat3 dmat4
dmat2x3 dmat2x4 dmat3x4 dmat4x2 dmat4x2 dmat4x3
Type:str
Attribute.name

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

Type:str
Attribute.extra

Any - Attribute for storing user defined objects

Varying
class moderngl.Varying

This class represents a program varying.

Varying.name

The name of the varying.

Type:str
Varying.number

The number of the varying.

Type:int
Varying.extra

Any - Attribute for storing user defined objects

Sampler

class moderngl.Sampler

A Sampler Object is an OpenGL Object that stores the sampling parameters for a Texture access inside of a shader. When a sampler object is bound to a texture image unit, the internal sampling parameters for a texture bound to the same image unit are all ignored. Instead, the sampling parameters are taken from this sampler object.

Unlike textures, a samplers state can also be changed freely be at any time without the sampler object being bound/in use.

Samplers are bound to a texture unit and not a texture itself. Be careful with leaving samplers bound to texture units as it can cause texture incompleteness issues (the texture bind is ignored).

Sampler bindings do clear automatically between every frame so a texture unit need at least one bind/use per frame.

Create

Context.sampler(repeat_x=True, repeat_y=True, repeat_z=True, filter=None, anisotropy=1.0, compare_func='?', border_color=None, min_lod=-1000.0, max_lod=1000.0, texture=None) → Sampler

Create a Sampler object.

Keyword Arguments:
 
  • repeat_x (bool) – Repeat texture on x
  • repeat_y (bool) – Repeat texture on y
  • repeat_z (bool) – Repeat texture on z
  • filter (tuple) – The min and max filter
  • anisotropy (float) – Number of samples for anisotropic filtering. Any value greater than 1.0 counts as a use of anisotropic filtering
  • compare_func – Compare function for depth textures
  • border_color (tuple) – The (r, g, b, a) color for the texture border. When this value is set the repeat_ values are overridden setting the texture wrap to return the border color when outside [0, 1] range.
  • min_lod (float) – Minimum level-of-detail parameter (Default -1000.0). This floating-point value limits the selection of highest resolution mipmap (lowest mipmap level)
  • max_lod (float) – Minimum level-of-detail parameter (Default 1000.0). This floating-point value limits the selection of the lowest resolution mipmap (highest mipmap level)
  • texture (Texture) – The texture for this sampler

Methods

Sampler.use(location=0)

Bind the sampler to a texture unit

Parameters:location (int) – The texture unit
Sampler.clear(location=0)

Clear the sampler binding on a texture unit

Parameters:location (int) – The texture unit
Sampler.assign(index)

Helper method for assigning samplers to scopes.

Example:

s1 = ctx.sampler(...)
s2 = ctx.sampler(...)
ctx.scope(samplers=(s1.assign(0), s1.assign(1)), ...)
Returns:(self, index) tuple
Sampler.release()

Release/destroy the ModernGL object.

Attributes

Sampler.texture
Sampler.repeat_x

The x repeat flag for the sampler (Default True)

Example:

# Enable texture repeat (GL_REPEAT)
sampler.repeat_x = True

# Disable texture repeat (GL_CLAMP_TO_EDGE)
sampler.repeat_x = False
Type:bool
Sampler.repeat_y

The y repeat flag for the sampler (Default True)

Example:

# Enable texture repeat (GL_REPEAT)
sampler.repeat_y = True

# Disable texture repeat (GL_CLAMP_TO_EDGE)
sampler.repeat_y = False
Type:bool
Sampler.repeat_z

The z repeat flag for the sampler (Default True)

Example:

# Enable texture repeat (GL_REPEAT)
sampler.repeat_z = True

# Disable texture repeat (GL_CLAMP_TO_EDGE)
sampler.repeat_z = False
Type:bool
Sampler.filter

The minification and magnification filter for the sampler. (Default (moderngl.LINEAR. moderngl.LINEAR))

Example:

sampler.filter == (moderngl.NEAREST, moderngl.NEAREST)
sampler.filter == (moderngl.LINEAR_MIPMAP_LINEAR, moderngl.LINEAR)
sampler.filter == (moderngl.NEAREST_MIPMAP_LINEAR, moderngl.NEAREST)
sampler.filter == (moderngl.LINEAR_MIPMAP_NEAREST, moderngl.NEAREST)
Type:tuple
Sampler.compare_func

The compare function for a depth textures (Default '?')

By default samplers don’t have depth comparison mode enabled. This means that depth texture values can be read as a sampler2D using texture() in a GLSL shader by default.

When setting this property to a valid compare mode, GL_TEXTURE_COMPARE_MODE is set to GL_COMPARE_REF_TO_TEXTURE so that texture lookup functions in GLSL will return a depth comparison result instead of the actual depth value.

Accepted compare functions:

.compare_func = ''    # Disale depth comparison completely
sampler.compare_func = '<='  # GL_LEQUAL
sampler.compare_func = '<'   # GL_LESS
sampler.compare_func = '>='  # GL_GEQUAL
sampler.compare_func = '>'   # GL_GREATER
sampler.compare_func = '=='  # GL_EQUAL
sampler.compare_func = '!='  # GL_NOTEQUAL
sampler.compare_func = '0'   # GL_NEVER
sampler.compare_func = '1'   # GL_ALWAYS
Type:tuple
Sampler.anisotropy

Number of samples for anisotropic filtering (Default 1.0). The value will be clamped in range 1.0 and ctx.max_anisotropy.

Any value greater than 1.0 counts as a use of anisotropic filtering:

# Disable anisotropic filtering
sampler.anisotropy = 1.0

# Enable anisotropic filtering suggesting 16 samples as a maximum
sampler.anisotropy = 16.0
Type:float
Sampler.border_color

When setting this value the repeat_ values are overridden setting the texture wrap to return the border color when outside [0, 1] range.

Example:

# Red border color
sampler.border_color = (1.0, 0.0, 0.0, 0.0)
Sampler.min_lod

Minimum level-of-detail parameter (Default -1000.0). This floating-point value limits the selection of highest resolution mipmap (lowest mipmap level)

Type:float
Sampler.max_lod

Minimum level-of-detail parameter (Default 1000.0). This floating-point value limits the selection of the lowest resolution mipmap (highest mipmap level)

Type:float
Sampler.extra

Any - Attribute for storing user defined objects

Sampler.mglo

Internal representation for debug purposes only.

Sampler.ctx

The context this object belongs to

Texture

class moderngl.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.

Create

Context.texture(size, components, data=None, samples=0, alignment=1, dtype='f1', internal_format=None) → Texture

Create a Texture object.

Warning

Do not play with internal_format unless you know exactly you are doing. This is an override to support sRGB and compressed textures if needed.

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.
  • dtype (str) – Data type.
  • internal_format (int) – Override the internalformat of the texture (IF needed)
Returns:

Texture object

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

Create a Texture object.

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:

Texture object

Methods

Texture.read(level=0, alignment=1) → bytes

Read the pixel data as bytes into system memory.

Keyword Arguments:
 
  • level (int) – The mipmap level.
  • alignment (int) – The byte alignment of the pixels.
Returns:

bytes

Texture.read_into(buffer, level=0, alignment=1, write_offset=0)

Read the content of the texture into a bytearray or Buffer. The advantage of reading into a Buffer is that pixel data does not need to travel all the way to system memory:

# Reading pixel data into a bytearray
data = bytearray(4)
texture = ctx.texture((2, 2), 1)
texture.read_into(data)

# Reading pixel data into a buffer
data = ctx.buffer(reserve=4)
texture = ctx.texture((2, 2), 1)
texture.read_into(data)
Parameters:

buffer (Union[bytearray, Buffer]) – The buffer that will receive the pixels.

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

Update the content of the texture from byte data or a moderngl Buffer:

# Write data from a moderngl Buffer
data = ctx.buffer(reserve=4)
texture = ctx.texture((2, 2), 1)
texture.write(data)

# Write data from bytes
data = b'ÿÿÿÿ'
texture = ctx.texture((2, 2), 1)
texture.write(data)
Parameters:
  • data (Union[bytes, Buffer]) – The pixel data.
  • viewport (tuple) – The viewport.
Keyword Arguments:
 
  • level (int) – The mipmap level.
  • alignment (int) – The byte alignment of the pixels.
Texture.build_mipmaps(base=0, max_level=1000)

Generate mipmaps.

This also changes the texture filter to LINEAR_MIPMAP_LINEAR, LINEAR

Keyword Arguments:
 
  • base (int) – The base level
  • max_level (int) – The maximum levels to generate
Texture.bind_to_image(unit: int, read: bool = True, write: bool = True, level: int = 0, format: int = 0)

Bind a texture to an image unit (OpenGL 4.2 required)

This is used to bind textures to image units for shaders. The idea with image load/store is that the user can bind one of the images in a Texture to a number of image binding points (which are separate from texture image units). Shaders can read information from these images and write information to them, in ways that they cannot with textures.

It’s important to specify the right access type for the image. This can be set with the read and write arguments. Allowed combinations are:

  • Read-only: read=True and write=False
  • Write-only: read=False and write=True
  • Read-write: read=True and write=True

format specifies the format that is to be used when performing formatted stores into the image from shaders. format must be compatible with the texture’s internal format. By default the format of the texture is passed in. The format parameter is only needed when overriding this behavior.

More information:

Parameters:
  • unit (int) – Specifies the index of the image unit to which to bind the texture
  • texture (moderngl.Texture) – The texture to bind
Keyword Arguments:
 
  • read (bool) – Allows the shader to read the image (default: True)
  • write (bool) – Allows the shader to write to the image (default: True)
  • level (int) – Level of the texture to bind (default: 0).
  • format (int) – (optional) The OpenGL enum value representing the format (defaults to the texture’s format)
Texture.use(location=0)

Bind the texture to a texture unit.

The location is the texture unit we want to bind the texture. This should correspond with the value of the sampler2D uniform in the shader because samplers read from the texture unit we assign to them:

# Define what texture unit our two sampler2D uniforms should represent
program['texture_a'] = 0
program['texture_b'] = 1
# Bind textures to the texture units
first_texture.use(location=0)
second_texture.use(location=1)
Parameters:location (int) – The texture location/unit.
Texture.release()

Release the ModernGL object.

Attributes

Texture.repeat_x

The x repeat flag for the texture (Default True)

Example:

# Enable texture repeat (GL_REPEAT)
texture.repeat_x = True

# Disable texture repeat (GL_CLAMP_TO_EDGE)
texture.repeat_x = False
Type:bool
Texture.repeat_y

The y repeat flag for the texture (Default True)

Example:

# Enable texture repeat (GL_REPEAT)
texture.repeat_y = True

# Disable texture repeat (GL_CLAMP_TO_EDGE)
texture.repeat_y = False
Type:bool
Texture.filter

The minification and magnification filter for the texture. (Default (moderngl.LINEAR. moderngl.LINEAR))

Example:

texture.filter == (moderngl.NEAREST, moderngl.NEAREST)
texture.filter == (moderngl.LINEAR_MIPMAP_LINEAR, moderngl.LINEAR)
texture.filter == (moderngl.NEAREST_MIPMAP_LINEAR, moderngl.NEAREST)
texture.filter == (moderngl.LINEAR_MIPMAP_NEAREST, moderngl.NEAREST)
Type:tuple
Texture.swizzle

The swizzle mask of the texture (Default 'RGBA').

The swizzle mask change/reorder the vec4 value returned by the texture() function in a GLSL shaders. This is represented by a 4 character string were each character can be:

'R' GL_RED
'G' GL_GREEN
'B' GL_BLUE
'A' GL_ALPHA
'0' GL_ZERO
'1' GL_ONE

Example:

# Alpha channel will always return 1.0
texture.swizzle = 'RGB1'

# Only return the red component. The rest is masked to 0.0
texture.swizzle = 'R000'

# Reverse the components
texture.swizzle = 'ABGR'
Type:str
Texture.compare_func

The compare function of the depth texture (Default '<=')

By default depth textures have GL_TEXTURE_COMPARE_MODE set to GL_COMPARE_REF_TO_TEXTURE, meaning any texture lookup will return a depth comparison value.

If you need to read the actual depth value in shaders, setting compare_func to a blank string will set GL_TEXTURE_COMPARE_MODE to GL_NONE making you able to read the depth texture as a sampler2D:

uniform sampler2D depth;
out vec4 fragColor;
in vec2 uv;

void main() {
    float raw_depth_nonlinear = texture(depth, uv);
    fragColor = vec4(raw_depth_nonlinear);
}

Accepted compare functions:

texture.compare_func = ''    # Disale depth comparison completely
texture.compare_func = '<='  # GL_LEQUAL
texture.compare_func = '<'   # GL_LESS
texture.compare_func = '>='  # GL_GEQUAL
texture.compare_func = '>'   # GL_GREATER
texture.compare_func = '=='  # GL_EQUAL
texture.compare_func = '!='  # GL_NOTEQUAL
texture.compare_func = '0'   # GL_NEVER
texture.compare_func = '1'   # GL_ALWAYS
Type:tuple
Texture.anisotropy

Number of samples for anisotropic filtering (Default 1.0). The value will be clamped in range 1.0 and ctx.max_anisotropy.

Any value greater than 1.0 counts as a use of anisotropic filtering:

# Disable anisotropic filtering
texture.anisotropy = 1.0

# Enable anisotropic filtering suggesting 16 samples as a maximum
texture.anisotropy = 16.0
Type:float
Texture.width

The width of the texture.

Type:int
Texture.height

The height of the texture.

Type:int
Texture.size

The size of the texture.

Type:tuple
Texture.dtype

Data type.

Type:str
Texture.components

The number of components of the texture.

Type:int
Texture.samples

The number of samples set for the texture used in multisampling.

Type:int
Texture.depth

Is the texture a depth texture?

Type:bool
Texture.glo

The internal OpenGL object. This values is provided for debug purposes only.

Type:int
Texture.mglo

Internal representation for debug purposes only.

Texture.extra

Any - Attribute for storing user defined objects

Texture.ctx

The context this object belongs to

TextureArray

class moderngl.TextureArray

An Array Texture is a Texture where each mipmap level contains an array of images of the same size. Array textures may have Mipmaps, but each mipmap in the texture has the same number of levels.

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

Create

Context.texture_array(size, components, data=None, alignment=1, dtype='f1') → TextureArray

Create a TextureArray object.

Parameters:
  • size (tuple) – The (width, height, layers) of the texture.
  • components (int) – The number of components 1, 2, 3 or 4.
  • data (bytes) – Content of the texture. The size must be (width, height * layers) so each layer is stacked vertically.
Keyword Arguments:
 
  • alignment (int) – The byte alignment 1, 2, 4 or 8.
  • dtype (str) – Data type.
Returns:

Texture3D object

Methods

TextureArray.read(alignment=1) → bytes

Read the pixel data as bytes into system memory.

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

Read the content of the texture array into a bytearray or Buffer. The advantage of reading into a Buffer is that pixel data does not need to travel all the way to system memory:

# Reading pixel data into a bytearray
data = bytearray(8)
texture = ctx.texture((2, 2, 2), 1)
texture.read_into(data)

# Reading pixel data into a buffer
data = ctx.buffer(reserve=8)
texture = ctx.texture((2, 2, 2), 1)
texture.read_into(data)
Parameters:

buffer (Union[bytearray, Buffer]) – The buffer that will receive the pixels.

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

Update the content of the texture array from byte data or a moderngl Buffer.

The viewport can be used for finer control of where the data should be written in the array. The valid versions are:

# Writing multiple layers from the begining of the texture
texture.write(data, viewport=(width, hight, num_layers))

# Writing sub-sections of the array
texture.write(data, viewport=(x, y, layer, width, height, num_layers))

Like with other texture types we can also use bytes or Buffer as a source:

# Using a moderngl buffer
data = ctx.buffer(reserve=8)
texture = ctx.texture_array((2, 2, 2), 1)
texture.write(data)

# Using byte data from system memory
data = b"ÿÿÿÿÿÿÿÿ"
texture = ctx.texture_array((2, 2, 2), 1)
texture.write(data)
Parameters:
  • data (bytes) – The pixel data.
  • viewport (tuple) – The viewport.
Keyword Arguments:
 

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

TextureArray.build_mipmaps(base=0, max_level=1000)

Generate mipmaps.

This also changes the texture filter to LINEAR_MIPMAP_LINEAR, LINEAR (Will be removed in 6.x)

Keyword Arguments:
 
  • base (int) – The base level
  • max_level (int) – The maximum levels to generate
TextureArray.use(location=0)

Bind the texture to a texture unit.

The location is the texture unit we want to bind the texture. This should correspond with the value of the sampler2DArray uniform in the shader because samplers read from the texture unit we assign to them:

# Define what texture unit our two sampler2DArray uniforms should represent
program['texture_a'] = 0
program['texture_b'] = 1
# Bind textures to the texture units
first_texture.use(location=0)
second_texture.use(location=1)
Parameters:location (int) – The texture location/unit.
TextureArray.release()

Release the ModernGL object.

Attributes

TextureArray.repeat_x

The x repeat flag for the texture (Default True)

Example:

# Enable texture repeat (GL_REPEAT)
texture.repeat_x = True

# Disable texture repeat (GL_CLAMP_TO_EDGE)
texture.repeat_x = False
Type:bool
TextureArray.repeat_y

The y repeat flag for the texture (Default True)

Example:

# Enable texture repeat (GL_REPEAT)
texture.repeat_y = True

# Disable texture repeat (GL_CLAMP_TO_EDGE)
texture.repeat_y = False
Type:bool
TextureArray.filter

The minification and magnification filter for the texture. (Default (moderngl.LINEAR. moderngl.LINEAR))

Example:

texture.filter == (moderngl.NEAREST, moderngl.NEAREST)
texture.filter == (moderngl.LINEAR_MIPMAP_LINEAR, moderngl.LINEAR)
texture.filter == (moderngl.NEAREST_MIPMAP_LINEAR, moderngl.NEAREST)
texture.filter == (moderngl.LINEAR_MIPMAP_NEAREST, moderngl.NEAREST)
Type:tuple
TextureArray.swizzle

The swizzle mask of the texture (Default 'RGBA').

The swizzle mask change/reorder the vec4 value returned by the texture() function in a GLSL shaders. This is represented by a 4 character string were each character can be:

'R' GL_RED
'G' GL_GREEN
'B' GL_BLUE
'A' GL_ALPHA
'0' GL_ZERO
'1' GL_ONE

Example:

# Alpha channel will always return 1.0
texture.swizzle = 'RGB1'

# Only return the red component. The rest is masked to 0.0
texture.swizzle = 'R000'

# Reverse the components
texture.swizzle = 'ABGR'
Type:str
TextureArray.anisotropy

Number of samples for anisotropic filtering (Default 1.0). The value will be clamped in range 1.0 and ctx.max_anisotropy.

Any value greater than 1.0 counts as a use of anisotropic filtering:

# Disable anisotropic filtering
texture.anisotropy = 1.0

# Enable anisotropic filtering suggesting 16 samples as a maximum
texture.anisotropy = 16.0
Type:float
TextureArray.width

The width of the texture array.

Type:int
TextureArray.height

The height of the texture array.

Type:int
TextureArray.layers

The number of layers of the texture array.

Type:int
TextureArray.size

The size of the texture array.

Type:tuple
TextureArray.dtype

Data type.

Type:str
TextureArray.components

The number of components of the texture array.

Type:int
TextureArray.glo

The internal OpenGL object. This values is provided for debug purposes only.

Type:int
TextureArray.mglo

Internal representation for debug purposes only.

TextureArray.extra

Any - Attribute for storing user defined objects

TextureArray.ctx

The context this object belongs to

Texture3D

class moderngl.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.

Create

Context.texture3d(size, components, data=None, alignment=1, dtype='f1') → Texture3D

Create a Texture3D object.

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.
  • dtype (str) – Data type.
Returns:

Texture3D object

Methods

Texture3D.read(alignment=1) → bytes

Read the pixel data as bytes into system memory.

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

Read the content of the texture into a bytearray or Buffer. The advantage of reading into a Buffer is that pixel data does not need to travel all the way to system memory:

# Reading pixel data into a bytearray
data = bytearray(8)
texture = ctx.texture3d((2, 2, 2), 1)
texture.read_into(data)

# Reading pixel data into a buffer
data = ctx.buffer(reserve=8)
texture = ctx.texture3d((2, 2), 1)
texture.read_into(data)
Parameters:

buffer (Union[bytearray, Buffer]) – The buffer that will receive the pixels.

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

Update the content of the texture from byte data or a moderngl Buffer:

# Write data from a moderngl Buffer
data = ctx.buffer(reserve=8)
texture = ctx.texture3d((2, 2, 2), 1)
texture.write(data)

# Write data from bytes
data = b'ÿÿÿÿÿÿÿÿ'
texture = ctx.texture3d((2, 2), 1)
texture.write(data)
Parameters:
  • data (bytes) – The pixel data.
  • viewport (tuple) – The viewport.
Keyword Arguments:
 

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

Texture3D.build_mipmaps(base=0, max_level=1000)

Generate mipmaps.

This also changes the texture filter to LINEAR_MIPMAP_LINEAR, LINEAR (Will be removed in 6.x)

Keyword Arguments:
 
  • base (int) – The base level
  • max_level (int) – The maximum levels to generate
Texture3D.use(location=0)

Bind the texture to a texture unit.

The location is the texture unit we want to bind the texture. This should correspond with the value of the sampler3D uniform in the shader because samplers read from the texture unit we assign to them:

# Define what texture unit our two sampler3D uniforms should represent
program['texture_a'] = 0
program['texture_b'] = 1
# Bind textures to the texture units
first_texture.use(location=0)
second_texture.use(location=1)
Parameters:location (int) – The texture location/unit.
Texture3D.release()

Release the ModernGL object.

Attributes

Texture3D.repeat_x

The x repeat flag for the texture (Default True)

Example:

# Enable texture repeat (GL_REPEAT)
texture.repeat_x = True

# Disable texture repeat (GL_CLAMP_TO_EDGE)
texture.repeat_x = False
Type:bool
Texture3D.repeat_y

The y repeat flag for the texture (Default True)

Example:

# Enable texture repeat (GL_REPEAT)
texture.repeat_y = True

# Disable texture repeat (GL_CLAMP_TO_EDGE)
texture.repeat_y = False
Type:bool
Texture3D.repeat_z

The z repeat flag for the texture (Default True)

Example:

# Enable texture repeat (GL_REPEAT)
texture.repeat_z = True

# Disable texture repeat (GL_CLAMP_TO_EDGE)
texture.repeat_z = False
Type:bool
Texture3D.filter

The filter of the texture.

Type:tuple
Texture3D.swizzle

The swizzle mask of the texture (Default 'RGBA').

The swizzle mask change/reorder the vec4 value returned by the texture() function in a GLSL shaders. This is represented by a 4 character string were each character can be:

'R' GL_RED
'G' GL_GREEN
'B' GL_BLUE
'A' GL_ALPHA
'0' GL_ZERO
'1' GL_ONE

Example:

# Alpha channel will always return 1.0
texture.swizzle = 'RGB1'

# Only return the red component. The rest is masked to 0.0
texture.swizzle = 'R000'

# Reverse the components
texture.swizzle = 'ABGR'
Type:str
Texture3D.width

The width of the texture.

Type:int
Texture3D.height

The height of the texture.

Type:int
Texture3D.depth

The depth of the texture.

Type:int
Texture3D.size

The size of the texture.

Type:tuple
Texture3D.dtype

Data type.

Type:str
Texture3D.components

The number of components of the texture.

Type:int
Texture3D.glo

The internal OpenGL object. This values is provided for debug purposes only.

Type:int
Texture3D.mglo

Internal representation for debug purposes only.

Texture3D.extra

Any - Attribute for storing user defined objects

Texture3D.ctx

The context this object belongs to

TextureCube

class moderngl.TextureCube

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.

Note

ModernGL enables GL_TEXTURE_CUBE_MAP_SEAMLESS globally to ensure filtering will be done across the cube faces.

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

Create

Context.texture_cube(size, components, data=None, alignment=1, dtype='f1') → TextureCube

Create a TextureCube object.

Parameters:
  • size (tuple) – The width, height of the texture. Each side of the cube will have this size.
  • components (int) – The number of components 1, 2, 3 or 4.
  • data (bytes) – Content of the texture. The data should be have the following ordering: positive_x, negative_x, positive_y, negative_y, positive_z + negative_z
Keyword Arguments:
 
  • alignment (int) – The byte alignment 1, 2, 4 or 8.
  • dtype (str) – Data type.
Returns:

TextureCube object

Methods

TextureCube.read(face, alignment=1) → bytes

Read a face from the cubemap as bytes into system memory.

Parameters:face (int) – The face to read.
Keyword Arguments:
 alignment (int) – The byte alignment of the pixels.
TextureCube.read_into(buffer, face, alignment=1, write_offset=0)

Read a face from the cubemap texture.

Read a face of the cubemap into a bytearray or Buffer. The advantage of reading into a Buffer is that pixel data does not need to travel all the way to system memory:

# Reading pixel data into a bytearray
data = bytearray(4)
texture = ctx.texture_cube((2, 2), 1)
texture.read_into(data, 0)

# Reading pixel data into a buffer
data = ctx.buffer(reserve=4)
texture = ctx.texture_cube((2, 2), 1)
texture.read_into(data, 0)
Parameters:
  • buffer (bytearray) – The buffer that will receive the pixels.
  • face (int) – The face to read.
Keyword Arguments:
 
  • alignment (int) – The byte alignment of the pixels.
  • write_offset (int) – The write offset.
TextureCube.write(face, data, viewport=None, alignment=1)

Update the content of the texture.

Update the content of a face in the cubemap from byte data or a moderngl Buffer:

# Write data from a moderngl Buffer
data = ctx.buffer(reserve=4)
texture = ctx.texture_cube((2, 2), 1)
texture.write(0, data)

# Write data from bytes
data = b'ÿÿÿÿ'
texture = ctx.texture_cube((2, 2), 1)
texture.write(0, data)
Parameters:
  • face (int) – The face to update.
  • data (bytes) – The pixel data.
  • viewport (tuple) – The viewport.
Keyword Arguments:
 

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

TextureCube.use(location=0)

Bind the texture to a texture unit.

The location is the texture unit we want to bind the texture. This should correspond with the value of the samplerCube uniform in the shader because samplers read from the texture unit we assign to them:

# Define what texture unit our two samplerCube uniforms should represent
program['texture_a'] = 0
program['texture_b'] = 1
# Bind textures to the texture units
first_texture.use(location=0)
second_texture.use(location=1)
Parameters:location (int) – The texture location/unit.
TextureCube.release()

Release the ModernGL object.

Attributes

TextureCube.size

The size of the texture.

Type:tuple
TextureCube.dtype

Data type.

Type:str
TextureCube.components

The number of components of the texture.

Type:int
TextureCube.filter

The minification and magnification filter for the texture. (Default (moderngl.LINEAR. moderngl.LINEAR))

Example:

texture.filter == (moderngl.NEAREST, moderngl.NEAREST)
texture.filter == (moderngl.LINEAR_MIPMAP_LINEAR, moderngl.LINEAR)
texture.filter == (moderngl.NEAREST_MIPMAP_LINEAR, moderngl.NEAREST)
texture.filter == (moderngl.LINEAR_MIPMAP_NEAREST, moderngl.NEAREST)
Type:tuple
TextureCube.swizzle

The swizzle mask of the texture (Default 'RGBA').

The swizzle mask change/reorder the vec4 value returned by the texture() function in a GLSL shaders. This is represented by a 4 character string were each character can be:

'R' GL_RED
'G' GL_GREEN
'B' GL_BLUE
'A' GL_ALPHA
'0' GL_ZERO
'1' GL_ONE

Example:

# Alpha channel will always return 1.0
texture.swizzle = 'RGB1'

# Only return the red component. The rest is masked to 0.0
texture.swizzle = 'R000'

# Reverse the components
texture.swizzle = 'ABGR'
Type:str
TextureCube.anisotropy

Number of samples for anisotropic filtering (Default 1.0). The value will be clamped in range 1.0 and ctx.max_anisotropy.

Any value greater than 1.0 counts as a use of anisotropic filtering:

# Disable anisotropic filtering
texture.anisotropy = 1.0

# Enable anisotropic filtering suggesting 16 samples as a maximum
texture.anisotropy = 16.0
Type:float
TextureCube.glo

The internal OpenGL object. This values is provided for debug purposes only.

Type:int
TextureCube.mglo

Internal representation for debug purposes only.

TextureCube.extra

Any - Attribute for storing user defined objects

TextureCube.ctx

The context this object belongs to

Framebuffer

class moderngl.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().

Create

Context.simple_framebuffer(size, components=4, samples=0, dtype='f1') → Framebuffer

Creates a Framebuffer with a single color attachment and depth buffer using moderngl.Renderbuffer attachments.

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.
  • dtype (str) – Data type.
Returns:

Framebuffer object

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

Framebuffer object

Methods

Framebuffer.clear(red=0.0, green=0.0, blue=0.0, alpha=0.0, depth=1.0, viewport=None, color=None)

Clear the framebuffer.

If a viewport passed in, a scissor test will be used to clear the given viewport. This viewport take prescense over the framebuffers scissor. Clearing can still be done with scissor if no viewport is passed in.

This method also respects the color_mask and depth_mask. It can for example be used to only clear the depth or color buffer or specific components in the color buffer.

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.
  • depth (float) – depth value.
Keyword Arguments:
 
  • viewport (tuple) – The viewport.
  • color (tuple) – Optional tuple replacing the red, green, blue and alpha arguments
Framebuffer.read(viewport=None, components=3, attachment=0, alignment=1, dtype='f1') → 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.
  • dtype (str) – Data type.
Returns:

bytes

Framebuffer.read_into(buffer, viewport=None, components=3, attachment=0, alignment=1, dtype='f1', 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.
  • dtype (str) – Data type.
  • write_offset (int) – The write offset.
Framebuffer.use()

Bind the framebuffer. Sets the target for rendering commands.

Framebuffer.release()

Release the ModernGL object.

Attributes

Framebuffer.viewport

Get or set the viewport of the framebuffer.

Type:tuple
Framebuffer.scissor

Get or set the scissor box of the framebuffer.

When scissor testing is enabled fragments outside the defined scissor box will be discarded. This applies to rendered geometry or Framebuffer.clear().

Setting is value enables scissor testing in the framebuffer. Setting the scissor to None disables scissor testing and reverts the scissor box to match the framebuffer size.

Example:

# Enable scissor testing
>>> ctx.scissor = 100, 100, 200, 100
# Disable scissor testing
>>> ctx.scissor = None
Type:tuple
Framebuffer.color_mask

The color mask of the framebuffer.

Color masking controls what components in color attachments will be affected by fragment write operations. This includes rendering geometry and clearing the framebuffer.

Default value: (True, True, True, True).

Examples:

# Block writing to all color components (rgba) in color attachments
fbo.color_mask = False, False, False, False

# Re-enable writing to color attachments
fbo.color_mask = True, True, True, True

# Block fragment writes to alpha channel
fbo.color_mask = True, True, True, False
Type:tuple
Framebuffer.depth_mask

The depth mask of the framebuffer.

Depth mask enables or disables write operations to the depth buffer. This also applies when clearing the framebuffer. If depth testing is enabled fragments will still be culled, but the depth buffer will not be updated with new values. This is a very useful tool in many rendering techniques.

Default value: True

Type:bool
Framebuffer.width

The width of the framebuffer.

Framebuffers created by a window will only report its initial size. It’s better get size information from the window itself.

Type:int
Framebuffer.height

The height of the framebuffer.

Framebuffers created by a window will only report its initial size. It’s better get size information from the window itself.

Type:int
Framebuffer.size

The size of the framebuffer.

Framebuffers created by a window will only report its initial size. It’s better get size information from the window itself.

Type:tuple
Framebuffer.samples

The samples of the framebuffer.

Type:int
Framebuffer.bits

The bits of the framebuffer.

Type:dict
Framebuffer.color_attachments

The color attachments of the framebuffer.

Type:tuple
Framebuffer.depth_attachment

The depth attachment of the framebuffer.

Type:Texture or Renderbuffer
Framebuffer.glo

The internal OpenGL object. This values is provided for debug purposes only.

Type:int
Framebuffer.mglo

Internal representation for debug purposes only.

Framebuffer.extra

Any - Attribute for storing user defined objects

Framebuffer.ctx

The context this object belongs to

Renderbuffer

class moderngl.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.

Create

Context.renderbuffer(size, components=4, samples=0, dtype='f1') → 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.
  • dtype (str) – Data type.
Returns:

Renderbuffer object

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:Renderbuffer object

Methods

Renderbuffer.release()

Release the ModernGL object.

Attributes

Renderbuffer.width

The width of the renderbuffer.

Type:int
Renderbuffer.height

The height of the renderbuffer.

Type:int
Renderbuffer.size

The size of the renderbuffer.

Type:tuple
Renderbuffer.samples

The samples of the renderbuffer.

Type:int
Renderbuffer.components

The components of the renderbuffer.

Type:int
Renderbuffer.depth

Is the renderbuffer a depth renderbuffer?

Type:bool
Renderbuffer.dtype

Data type.

Type:str
Renderbuffer.glo

The internal OpenGL object. This values is provided for debug purposes only.

Type:int
Renderbuffer.mglo

Internal representation for debug purposes only.

Renderbuffer.extra

Any - Attribute for storing user defined objects

Renderbuffer.ctx

The context this object belongs to

Scope

class moderngl.Scope

This class represents a Scope object.

Responsibilities on enter:

  • Set the enable flags.
  • Bind the framebuffer.
  • Assigning textures to texture locations.
  • Assigning buffers to uniform buffers.
  • Assigning buffers to shader storage buffers.

Responsibilities on exit:

  • Restore the enable flags.
  • Restore the framebuffer.

Create

Context.scope(framebuffer=None, enable_only=None, textures=(), uniform_buffers=(), storage_buffers=(), samplers=(), enable=None) → Scope

Create a Scope object.

Parameters:
  • framebuffer (Framebuffer) – The framebuffer to use when entering.
  • enable_only (int) – The enable_only flags to set when entering.
Keyword Arguments:
 
  • textures (list) – List of (texture, binding) tuples.
  • uniform_buffers (list) – List of (buffer, binding) tuples.
  • storage_buffers (list) – List of (buffer, binding) tuples.
  • samplers (list) – List of sampler bindings
  • enable (int) – Flags to enable for this vao such as depth testing and blending

Attributes

Scope.extra

Any - Attribute for storing user defined objects

Scope.mglo

Internal representation for debug purposes only.

Scope.ctx

The context this object belongs to

Examples

Simple scope example

scope1 = ctx.scope(fbo1, moderngl.BLEND)
scope2 = ctx.scope(fbo2, moderngl.DEPTH_TEST | moderngl.CULL_FACE)

with scope1:
    # do some rendering

with scope2:
    # do some rendering

Scope for querying

query = ctx.query(samples=True)
scope = ctx.scope(ctx.screen, moderngl.DEPTH_TEST | moderngl.RASTERIZER_DISCARD)

with scope, query:
    # do some rendering

print(query.samples)

Understanding what scope objects do

scope = ctx.scope(
    framebuffer=framebuffer1,
    enable_only=moderngl.BLEND,
    textures=[
        (texture1, 4),
        (texture2, 3),
    ],
    uniform_buffers=[
        (buffer1, 6),
        (buffer2, 5),
    ],
    storage_buffers=[
        (buffer3, 8),
    ],
)

# Let's assume we have some state before entering the scope
some_random_framebuffer.use()
some_random_texture.use(3)
some_random_buffer.bind_to_uniform_block(5)
some_random_buffer.bind_to_storage_buffer(8)
ctx.enable_only(moderngl.DEPTH_TEST)

with scope:
    # on __enter__
    #     framebuffer1.use()
    #     ctx.enable_only(moderngl.BLEND)
    #     texture1.use(4)
    #     texture2.use(3)
    #     buffer1.bind_to_uniform_block(6)
    #     buffer2.bind_to_uniform_block(5)
    #     buffer3.bind_to_storage_buffer(8)

    # do some rendering

    # on __exit__
    #     some_random_framebuffer.use()
    #     ctx.enable_only(moderngl.DEPTH_TEST)

# Originally we had the following, let's see what was changed
some_random_framebuffer.use()                 # This was restored hurray!
some_random_texture.use(3)                    # Have to restore it manually.
some_random_buffer.bind_to_uniform_block(5)   # Have to restore it manually.
some_random_buffer.bind_to_storage_buffer(8)  # Have to restore it manually.
ctx.enable_only(moderngl.DEPTH_TEST)          # This was restored too.

# Scope objects only do as much as necessary.
# Restoring the framebuffer and enable flags are lowcost operations and
# without them you could get a hard time debugging the application.

Query

class moderngl.Query

This class represents a Query object.

Create

Context.query(samples=False, any_samples=False, time=False, primitives=False) → Query

Create a Query object.

Keyword Arguments:
 
  • samples (bool) – Query GL_SAMPLES_PASSED or not.
  • any_samples (bool) – Query GL_ANY_SAMPLES_PASSED or not.
  • time (bool) – Query GL_TIME_ELAPSED or not.
  • primitives (bool) – Query GL_PRIMITIVES_GENERATED or not.

Attributes

Query.samples

The number of samples passed.

Type:int
Query.primitives

The number of primitives generated.

Type:int
Query.elapsed

The time elapsed in nanoseconds.

Type:int
Query.crender

Can be used in a with statement.

Type:ConditionalRender
Query.extra

Any - Attribute for storing user defined objects

Query.mglo

Internal representation for debug purposes only.

Query.ctx

The context this object belongs to

Examples

Simple query example

 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
import moderngl
import numpy as np

ctx = moderngl.create_standalone_context()
prog = ctx.program(
    vertex_shader='''
        #version 330

        in vec2 in_vert;

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

        out vec4 color;

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

vertices = np.array([
    0.0, 0.0,
    1.0, 0.0,
    0.0, 1.0,
], dtype='f4')

vbo = ctx.buffer(vertices.tobytes())
vao = ctx.simple_vertex_array(prog, vbo, 'in_vert')

fbo = ctx.simple_framebuffer((64, 64))
fbo.use()

query = ctx.query(samples=True, time=True)

with query:
    vao.render()

print('It took %d nanoseconds' % query.elapsed)
print('to render %d samples' % query.samples)

Output

It took 13529 nanoseconds
to render 496 samples

ConditionalRender

class moderngl.ConditionalRender

This class represents a ConditionalRender object.

ConditionalRender objects can only be accessed from Query objects.

Attributes

ConditionalRender.mglo

Internal representation for debug purposes only.

Examples

Simple conditional rendering example

query = ctx.query(any_samples=True)

with query:
    vao1.render()

with query.crender:
    print('This will always get printed')
    vao2.render()  # But this will be rendered only if vao1 has passing samples.

ComputeShader

class moderngl.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.

Create

Context.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:ComputeShader object

Methods

ComputeShader.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.
ComputeShader.get(key, default) → Union[Uniform, UniformBlock, Subroutine, Attribute, Varying]

Returns a Uniform, UniformBlock, Subroutine, Attribute or Varying.

Parameters:default – This is the value to be returned in case key does not exist.
Returns:Uniform, UniformBlock, Subroutine, Attribute or Varying
ComputeShader.release()

Release the ModernGL object.

ComputeShader.__eq__(other)

Compares to compute shaders ensuring the internal opengl name/id is the same

ComputeShader.__getitem__(key) → Union[Uniform, UniformBlock, Subroutine, Attribute, Varying]

Get a member such as uniforms, uniform blocks, subroutines, attributes and varyings by name.

# Get a uniform
uniform = program['color']

# Uniform values can be set on the returned object
# or the `__setitem__` shortcut can be used.
program['color'].value = 1.0, 1.0, 1.0, 1.0

# Still when writing byte data we need to use the `write()` method
program['color'].write(buffer)
ComputeShader.__setitem__(key, value)

Set a value of uniform or uniform block

# Set a vec4 uniform
uniform['color'] = 1.0, 1.0, 1.0, 1.0

# Optionally we can store references to a member and set the value directly
uniform = program['color']
uniform.value = 1.0, 0.0, 0.0, 0.0

uniform = program['cameraMatrix']
uniform.write(camera_matrix)
ComputeShader.__iter__() → Generator[str, NoneType, NoneType]

Yields the internal members names as strings. This includes all members such as uniforms, attributes etc.

Attributes

ComputeShader.glo

The internal OpenGL object. This values is provided for debug purposes only.

Type:int
ComputeShader.mglo

Internal representation for debug purposes only.

ComputeShader.extra

Any - Attribute for storing user defined objects

ComputeShader.ctx

The context this object belongs to

Indices and tables