Hide navigation sidebar
Hide table of contents sidebar
Back to top
Toggle table of contents sidebar

# 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 that you have these libraries installed. ModernGL through the glcontext library will use `ctype.find_library` to locate the latest installed version.

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,
        # These are OPTIONAL if you want to load a specific version
        libgl='libGL.so.1',
        libx11='libX11.so.6',
    )

    # EGL
    import moderngl
    ctx = moderngl.create_context(
        standalone=True,
        backend='egl',
        # These are OPTIONAL if you want to load a specific version
        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.

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