Uniform#

At some point you will need to make a lot of small changes to your rendering. Changing the screen aspect ratio, viewing angle, changing perspective/orthographic projection and much more. And in many situations it will be very convenient to use Uniform -s.

Uniform -s can be specified and used at any time during rendering. They allow you to replace all constants in the shader with variables and change them as needed. The Uniform is initialized in the shader as follows:

uniform float var1;

Changing the Uniform -s value in ModernGL is very easy. For example, setting the value for our variable 140.02 is done as follows:

vao.program['var1'].value = 140.02

# or (using `__setitem__` shortcut)
vao.program['var1'] = 140.02

If the variable type is not float, but vec4, simply list the values separated by commas:

vao.program['var2'] = 1, 2, 3, 4
# or
vao.program['var2'] = [1, 2, 3, 4]
# or
vao.program['var2'] = (1, 2, 3, 4)

You need to list as many values as the value type takes: float will take 1 number, vec2 will take 2 numbers, vec4 will take 4 numbers, mat4 will take 16 numbers, etc.

Let’s consider a case where we need to change the size of our triangle. Take the original triangle drawing code and make the following changes.

To change the scale (size) of the triangle, add a scale Uniform. In the vertex shader it will be multiplied by all vertices and thus allow us to control the size of all triangles.

Entire source

 1import moderngl
 2import numpy as np
 3
 4from PIL import Image
 5
 6ctx = moderngl.create_context(standalone=True)
 7
 8prog = ctx.program(
 9    vertex_shader="""
10        #version 330
11
12        in vec2 in_vert;
13        in vec3 in_color;
14
15        uniform float scale;
16
17        out vec3 v_color;
18
19        void main() {
20            v_color = in_color;
21            gl_Position = vec4(in_vert * scale, 0.0, 1.0);
22        }
23    """,
24    fragment_shader="""
25        #version 330
26
27        in vec3 v_color;
28
29        out vec3 f_color;
30
31        void main() {
32            f_color = v_color;
33        }
34    """,
35)
36
37vertices = np.asarray([
38
39    -0.75, -0.75,  1, 0, 0,
40    0.75, -0.75,  0, 1, 0,
41    0.0, 0.649,  0, 0, 1
42
43], dtype='f4')
44
45vbo = ctx.buffer(vertices.tobytes())
46vao = ctx.vertex_array(prog, vbo, "in_vert", "in_color")
47
48fbo = ctx.framebuffer(
49    color_attachments=[ctx.texture((512, 512), 3)]
50)
51fbo.use()
52fbo.clear(0.0, 0.0, 0.0, 1.0)
53
54vao.program['scale'] = 2
55
56vao.render()  # "mode" is moderngl.TRIANGLES by default
57
58Image.frombytes(
59    "RGB", fbo.size, fbo.color_attachments[0].read(),
60    "raw", "RGB", 0, -1
61).show()

We set the scale value to 2.0, which means our triangle will be enlarged by 2 times.

Enlarged triangle

Enlarged triangle#

Now let’s set the scale value to 0.5 to reduce the triangle by 2 times:

vao.program['scale'] = 0.5
Reduced triangle

Reduced triangle#

Uniforms can not only be set, but also read. This is done as follows:

scale = vao.program['scale'].value

Also Uniforms can be written or read directly, in the form of bytes:

# write
scale = 2
b_scale = numpy.asarray([scale], dtype='f4').tobytes()
vao.program['scale'].write(b_scale)

# read
b_scale = vao.program['scale'].read()
scale = numpy.frombuffer(b_scale, dtype='f4')[0]

# `numpy.frombuffer()` converts a byte string into an array,
# since we have one number, we select it from the array.

In most cases, directly using Uniform -s .read()/.write() methods can speed up the code, but constantly manually converting variables into bytes does not make sense, since ModernGL already does it in the most optimized way.