Headless Rendering
Because the GPU-server has no screen, you need to create a virtual one. This can be done with headless rendering.
See moderngl headless rendering for the basic overview.
Setup
Create virtual X-server
Create a new virtual screen using Xvfb
by defining a virtual display number, a screen and the desired resolution.
# Use the '&' to run the process in the background
Xvfb :"_displayNr_" -screen "_screenNr_" "_resolution_" &
Example:
Xvfb :99 -screen 0 360x360x24 &
Define environment variable
Define it before running each script:
DISPLAY=:"_displayNr_" python "_example.py_"
Or define it globally for the whole session:
export DISPLAY=:"_displayNr_"
Kill the virtual X-server process
When the x-server is started in the background, you need to find it again when you want to stop it.
List all currently running processes containing Xvfb
ps -ef | grep Xvfb
Find the process number and kill the process.
kill "_processNr_"
Usage
# Define a new context, define as 'standalone' optionally with the 'egl' backend
ctx = moderngl.create_context(standalone=True, backend='egl')
# Create a framebuffer
fbo = ctx.simple_framebuffer((512, 512), components=4)
fbo.use()
vertices = np.array([
-1.0, -1.0, 1.0, 0.0, 0.0,
1.0, -1.0, 0.0, 1.0, 0.0,
0.0, 1.0, 0.0, 0.0, 1.0],
dtype='f4',
)
prog = ctx.program(vertex_shader="""
#version 330
in vec2 in_vert;
in vec3 in_color;
out vec3 color;
void main() {
gl_Position = vec4(in_vert, 0.0, 1.0);
color = in_color;
}
""",
fragment_shader="""
#version 330
out vec4 fragColor;
in vec3 color;
void main() {
fragColor = vec4(color, 1.0);
}
""",
)
vao = ctx.simple_vertex_array(prog, ctx.buffer(vertices), 'in_vert', 'in_color')
vao.render(mode=moderngl.TRIANGLES)
image = Image.frombytes('RGBA', (512, 512), fbo.read(components=4))
image = image.transpose(Image.FLIP_TOP_BOTTOM)
image.save('triangle.png', format='png')