Welcome to ModernGL’s documentation!¶
Start here.
ModernGL¶
ModernGL on Github
If you have less time to read this then go to the TL;DR section.
ModernGL and OpenGL
OpenGL is a great environment for developing portable, platform independent, interactive 2D and 3D graphics applications. The API implementation in Python is cumbersome, resulting in applications with high latency. To solve this problem we have developed ModernGL, a wrapper over OpenGL that simplifies the creation of simple graphics applications like scientific simulations, small games or user interfaces. Usually, acquiring in-depth knowledge of OpenGL requires a steep learning curve. In contrast, ModernGL is easy to learn and use, moreover it is capable of rendering with the same performance and quality, with less code written.
How to install?
pip install ModernGL
How to create a window?
ModernGL encapsulates the use of the OpenGL API, a separate module must be used for creating a window. ModernGL can be integrated easily in GLWindow, PyQt5, pyglet, pygame, GLUT and many more.
How to create a context?
- Create a window
- Call
ModernGL.create_context()
ModernGL
Examples¶
01. Hello World!¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | import struct
import GLWindow
import ModernGL
# Window & Context
wnd = GLWindow.create_window()
ctx = ModernGL.create_context()
# Shaders & Program
prog = ctx.program([
ctx.vertex_shader('''
#version 330
in vec2 vert;
void main() {
gl_Position = vec4(vert, 0.0, 1.0);
}
'''),
ctx.fragment_shader('''
#version 330
out vec4 color;
void main() {
color = vec4(0.3, 0.5, 1.0, 1.0);
}
'''),
])
# Buffer
vbo = ctx.buffer(struct.pack(
'6f',
0.0, 0.8,
-0.6, -0.8,
0.6, -0.8,
))
# Put everything together
vao = ctx.simple_vertex_array(prog, vbo, ['vert'])
# Main loop
while wnd.update():
ctx.viewport = wnd.viewport
ctx.clear(0.9, 0.9, 0.9)
vao.render()
|
02. Uniforms and Attributes¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | import struct
import GLWindow
import ModernGL
# Window & Context
wnd = GLWindow.create_window()
ctx = ModernGL.create_context()
# Shaders & Program
prog = ctx.program([
ctx.vertex_shader('''
#version 330
in vec2 vert;
in vec3 vert_color;
out vec3 frag_color;
uniform vec2 scale;
uniform float rotation;
void main() {
frag_color = vert_color;
mat2 rot = mat2(
cos(rotation), sin(rotation),
-sin(rotation), cos(rotation)
);
gl_Position = vec4((rot * vert) * scale, 0.0, 1.0);
}
'''),
ctx.fragment_shader('''
#version 330
in vec3 frag_color;
out vec4 color;
void main() {
color = vec4(frag_color, 1.0);
}
'''),
])
# Uniforms
scale = prog.uniforms['scale']
rotation = prog.uniforms['rotation']
width, height = wnd.size
scale.value = (height / width * 0.75, 0.75)
# Buffer
vbo = ctx.buffer(struct.pack(
'15f',
1.0, 0.0,
1.0, 0.0, 0.0,
-0.5, 0.86,
0.0, 1.0, 0.0,
-0.5, -0.86,
0.0, 0.0, 1.0,
))
# Put everything together
vao = ctx.simple_vertex_array(prog, vbo, ['vert', 'vert_color'])
# Main loop
while wnd.update():
ctx.viewport = wnd.viewport
ctx.clear(0.9, 0.9, 0.9)
rotation.value = wnd.time
vao.render()
|
03. Blending¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | import struct
import GLWindow
import ModernGL
# Window & Context
wnd = GLWindow.create_window()
ctx = ModernGL.create_context()
# Shaders & Program
prog = ctx.program([
ctx.vertex_shader('''
#version 330
in vec2 vert;
in vec4 vert_color;
out vec4 frag_color;
uniform vec2 scale;
uniform float rotation;
void main() {
frag_color = vert_color;
mat2 rot = mat2(
cos(rotation), sin(rotation),
-sin(rotation), cos(rotation)
);
gl_Position = vec4((rot * vert) * scale, 0.0, 1.0);
}
'''),
ctx.fragment_shader('''
#version 330
in vec4 frag_color;
out vec4 color;
void main() {
color = vec4(frag_color);
}
'''),
])
# Uniforms
scale = prog.uniforms['scale']
rotation = prog.uniforms['rotation']
width, height = wnd.size
scale.value = (height / width * 0.75, 0.75)
# Buffer
vbo = ctx.buffer(struct.pack(
'18f',
1.0, 0.0,
1.0, 0.0, 0.0, 0.5,
-0.5, 0.86,
0.0, 1.0, 0.0, 0.5,
-0.5, -0.86,
0.0, 0.0, 1.0, 0.5,
))
# Put everything together
vao = ctx.simple_vertex_array(prog, vbo, ['vert', 'vert_color'])
# Main loop
while wnd.update():
ctx.viewport = wnd.viewport
ctx.clear(0.9, 0.9, 0.9)
ctx.enable(ModernGL.BLEND)
rotation.value = wnd.time
vao.render(instances=10)
|
04. Texture¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | import os
import struct
import GLWindow
import ModernGL
from PIL import Image
# Window & Context
wnd = GLWindow.create_window()
ctx = ModernGL.create_context()
# Shaders & Program
prog = ctx.program([
ctx.vertex_shader('''
#version 330
in vec2 vert;
out vec2 tex_coord;
uniform vec2 scale;
uniform float rotation;
void main() {
float r = rotation * (0.5 + gl_InstanceID * 0.05);
mat2 rot = mat2(cos(r), sin(r), -sin(r), cos(r));
gl_Position = vec4((rot * vert) * scale, 0.0, 1.0);
tex_coord = vert;
}
'''),
ctx.fragment_shader('''
#version 330
uniform sampler2D texture;
in vec2 tex_coord;
out vec4 color;
void main() {
color = vec4(texture2D(texture, tex_coord).rgb, 1.0);
}
'''),
])
# Uniforms
scale = prog.uniforms['scale']
rotation = prog.uniforms['rotation']
width, height = wnd.size
scale.value = (height / width * 0.75, 0.75)
# Buffer
vbo = ctx.buffer(struct.pack(
'6f',
1.0, 0.0,
-0.5, 0.86,
-0.5, -0.86,
))
# Put everything together
vao = ctx.simple_vertex_array(prog, vbo, ['vert'])
# Texture
img = Image.open(os.path.join(os.path.dirname(__file__), '..', 'data', 'noise.jpg'))
texture = ctx.texture(img.size, 3, img.tobytes())
texture.use()
# Main loop
while wnd.update():
ctx.viewport = wnd.viewport
ctx.clear(0.9, 0.9, 0.9)
rotation.value = wnd.time
vao.render()
|
05. Perspective¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | import struct
import GLWindow
import ModernGL
# Window & Context
wnd = GLWindow.create_window()
ctx = ModernGL.create_context()
vert = ctx.vertex_shader('''
#version 330
in vec3 vert;
uniform float znear;
uniform float zfar;
uniform float fovy;
uniform float ratio;
uniform vec3 center;
uniform vec3 eye;
uniform vec3 up;
mat4 perspective() {
float zmul = (-2.0 * znear * zfar) / (zfar - znear);
float ymul = 1.0 / tan(fovy * 3.14159265 / 360);
float xmul = ymul / ratio;
return mat4(
xmul, 0.0, 0.0, 0.0,
0.0, ymul, 0.0, 0.0,
0.0, 0.0, -1.0, -1.0,
0.0, 0.0, zmul, 0.0
);
}
mat4 lookat() {
vec3 forward = normalize(center - eye);
vec3 side = normalize(cross(forward, up));
vec3 upward = cross(side, forward);
return mat4(
side.x, upward.x, -forward.x, 0,
side.y, upward.y, -forward.y, 0,
side.z, upward.z, -forward.z, 0,
-dot(eye, side), -dot(eye, upward), dot(eye, forward), 1
);
}
void main() {
gl_Position = perspective() * lookat() * vec4(vert, 1.0);
}
''')
frag = ctx.fragment_shader('''
#version 330
out vec4 color;
void main() {
color = vec4(0.04, 0.04, 0.04, 1.0);
}
''')
width, height = wnd.size
prog = ctx.program([vert, frag])
prog.uniforms['znear'].value = 0.1
prog.uniforms['zfar'].value = 1000.0
prog.uniforms['ratio'].value = width / height
prog.uniforms['fovy'].value = 60
prog.uniforms['eye'].value = (3, 3, 3)
prog.uniforms['center'].value = (0, 0, 0)
prog.uniforms['up'].value = (0, 0, 1)
grid = bytes()
for i in range(0, 65):
grid += struct.pack('6f', i - 32, -32.0, 0.0, i - 32, 32.0, 0.0)
grid += struct.pack('6f', -32.0, i - 32, 0.0, 32.0, i - 32, 0.0)
vbo = ctx.buffer(grid)
vao = ctx.simple_vertex_array(prog, vbo, ['vert'])
while wnd.update():
ctx.viewport = wnd.viewport
ctx.clear(0.9, 0.9, 0.9)
vao.render(ModernGL.LINES, 65 * 4)
|
Julia Fractal¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | import struct
import GLWindow
import ModernGL
# Window & Context
wnd = GLWindow.create_window()
ctx = ModernGL.create_context()
vert = ctx.vertex_shader('''
#version 330
in vec2 vert;
out vec2 tex;
void main() {
gl_Position = vec4(vert, 0.0, 1.0);
tex = vert / 2.0 + vec2(0.5, 0.5);
}
''')
frag = ctx.fragment_shader('''
#version 330
in vec2 tex;
out vec4 color;
uniform vec2 center;
uniform int iter;
void main() {
vec2 z = vec2(5.0 * (tex.x - 0.5), 3.0 * (tex.y - 0.5));
vec2 c = center;
int i;
for(i = 0; i < iter; i++) {
vec2 v = vec2(
(z.x * z.x - z.y * z.y) + c.x,
(z.y * z.x + z.x * z.y) + c.y
);
if (dot(v, v) > 4.0) break;
z = v;
}
float cm = fract((i == iter ? 0.0 : float(i)) * 10 / iter);
color = vec4(
fract(cm + 0.0 / 3.0),
fract(cm + 1.0 / 3.0),
fract(cm + 2.0 / 3.0),
1.0
);
}
''')
prog = ctx.program([vert, frag])
vbo = ctx.buffer(struct.pack('8f', -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0))
vao = ctx.simple_vertex_array(prog, vbo, ['vert'])
prog.uniforms['iter'].value = 100
x, y = (0.49, 0.32)
wnd.grab_mouse(True)
while wnd.update():
ctx.viewport = wnd.viewport
ctx.clear(0.9, 0.9, 0.9)
mx, my = wnd.mouse_delta
x -= mx / 100
y -= my / 100
prog.uniforms['center'].value = (y, x)
vao.render(ModernGL.TRIANGLE_STRIP)
|
Particle System¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | import math
import random
import struct
import GLWindow
import ModernGL
# Window & Context
wnd = GLWindow.create_window()
ctx = ModernGL.create_context()
tvert = ctx.vertex_shader('''
#version 330
uniform vec2 acc;
in vec2 in_pos;
in vec2 in_prev;
out vec2 out_pos;
out vec2 out_prev;
void main() {
out_pos = in_pos * 2.0 - in_prev + acc;
out_prev = in_pos;
}
''')
vert = ctx.vertex_shader('''
#version 330
in vec2 vert;
void main() {
gl_Position = vec4(vert, 0.0, 1.0);
}
''')
frag = ctx.fragment_shader('''
#version 330
out vec4 color;
void main() {
color = vec4(0.30, 0.50, 1.00, 1.0);
}
''')
prog = ctx.program([vert, frag])
transform = ctx.program(tvert, ['out_pos', 'out_prev'])
def particle():
a = random.uniform(0.0, math.pi * 2.0)
r = random.uniform(0.0, 0.001)
return struct.pack('2f2f', 0.0, 0.0, math.cos(a) * r - 0.003, math.sin(a) * r - 0.008)
vbo1 = ctx.buffer(b''.join(particle() for i in range(1024)))
vbo2 = ctx.buffer(reserve=vbo1.size)
vao1 = ctx.simple_vertex_array(transform, vbo1, ['in_pos', 'in_prev'])
vao2 = ctx.simple_vertex_array(transform, vbo2, ['in_pos', 'in_prev'])
render_vao = ctx.vertex_array(prog, [
(vbo1, '2f8x', ['vert']),
])
transform.uniforms['acc'].value = (0, -0.0001)
idx = 0
ctx.point_size = 5.0
while wnd.update():
ctx.viewport = wnd.viewport
ctx.clear(0.9, 0.9, 0.9)
for i in range(8):
vbo1.write(particle(), offset=idx * struct.calcsize('2f2f'))
idx = (idx + 1) % 1024
render_vao.render(ModernGL.POINTS, 1024)
vao1.transform(vbo2, ModernGL.POINTS, 1024)
ctx.copy_buffer(vbo1, vbo2)
|