Skip to content

moderngl

moderngl supports OpenGL >= 3.3.

install

pip install moderngl # only headless
pip install moderngl-window # for window creation

troubles

version 5.6 just fails: (maybe caused by the new glcontext)

(torch) tang@CIS5:~/projects/nerf_pl$ python  -m moderngl
Traceback (most recent call last):
  File "/home/tang/anaconda3/envs/torch/lib/python3.6/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/home/tang/anaconda3/envs/torch/lib/python3.6/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/home/tang/anaconda3/envs/torch/lib/python3.6/site-packages/moderngl/__main__.py", line 50, in <module>
    main()
  File "/home/tang/anaconda3/envs/torch/lib/python3.6/site-packages/moderngl/__main__.py", line 33, in main
    ctx = moderngl.create_standalone_context()
  File "/home/tang/anaconda3/envs/torch/lib/python3.6/site-packages/moderngl/context.py", line 1664, in create_standalone_context
    ctx.mglo, ctx.version_code = mgl.create_context(glversion=require, mode=mode, **settings)
SystemError: <built-in function create_context> returned NULL without setting an error

however, version5.5 is OK.

example

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]))

Context

the GL Context, all the operations are called in this context.

# create
ctx = moderngl.create_context()

# clear the bound framebuffer
ctx.clear(red=0, green=0, blue=0, alpha=0, depth=1, ...)

# viewport
ctx.viewport = (0, 0, 640, 360)

# opengl version
ctx.version_code

Primitive Modes

ctx.POINTS
ctx.LINES
ctx.LINE_LOOP
Context.LINE_STRIP
Context.TRIANGLES
...

program

shader programs.

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

    out float value;
    out float product;

    void main() {
        value = gl_VertexID;
        product = gl_VertexID * gl_VertexID;
    }
    """,
    varyings=["value", "product"],
)

buffer (vbo)

OpenGL objects that stores an array in GPU allocated by the context.

can be used to store vertex, pixel, or framebuffer data.

vbo = ctx.buffer(data=None, reverse=0, dynamic=False)

vbo.write(data, offset=0)
vbo.clear(size=-1, offset=0) # -1 means all
vbo.release()

vbo.size
vbo.ctx

vertex_array (vao)

describes how buffer is read by shader.

# Simple version with a single buffer
vao = ctx.vertex_array(program, buffer, "in_position", "in_normal")

vao.render(mode=None, vertices=-1) # None --> TRIANGLES
vao.release()

vao.mode
vao.program
vao.extra

Texture

texture contains one or more images of the same format, can be the source access from a shader, or a render target.

# create
ctx.texture(size, components, data=None, samples=0, alignment=1, dtype='f1')

# 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)

# 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)

# Write to a sub-section of the texture using viewport
texture = ctx.texture((100, 100), 4)
# Fill the lower left 50x50 pixels with new data
texture.write(data, viewport=(0, 0, 50, 50))