Rendering to a Pillow image using ModernGL
pip install -r requirements.txt
python main.py
pip install -r requirements_dev.txt
Headless-rendering-with-python on master on 🐳 v19.03.5 (localhost) via py3.7.2_ubuntu-headless-ModernGL via 🐍 py3.7.2_ubuntu-headless-ModernGL
➜ PYTHONPATH=. pytest -vvv -s --durations=0
=================================================================== test session starts ===================================================================
platform linux -- Python 3.7.2, pytest-5.3.4, py-1.8.1, pluggy-0.13.1 -- /home/latty/.pyenv/versions/3.7.2/envs/py3.7.2_ubuntu-headless-ModernGL/bin/python3.7
cachedir: .pytest_cache
rootdir: /c/Users/latty/Prog/__COMPUTER_GRAPHICS__/ubuntu-headless-ModernGL/Headless-rendering-with-python
plugins: pyfakefs-3.7.1, xvfb-1.2.0
collected 2 items
tests/test_main.py::test_screen PASSED
tests/test_main.py::test_main libGL error: failed to create drawable
libGL error: failed to create drawable
PASSED
================================================================= slowest test durations ==================================================================
0.75s call tests/test_main.py::test_main
0.08s setup tests/test_main.py::test_main
0.00s teardown tests/test_main.py::test_main
0.00s setup tests/test_main.py::test_screen
0.00s teardown tests/test_main.py::test_screen
0.00s call tests/test_main.py::test_screen
==================================================================== 2 passed in 1.27s ====================================================================
A workflow pipeline is set for a standard python application (running flake8 and pytest):
- Workflow results can be seen here: Headless-rendering-with-python/actions
- Pipeline configuration can be seen/modified here: .github/workflows/pythonapp.yml
name: Python application
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.8
uses: actions/setup-python@v1
with:
python-version: 3.8
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements_dev.txt
- name: Lint with flake8
run: |
pip install flake8
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
pip install pytest
PYTHONPATH=. pytest
#version 330
uniform mat4 Mvp;
in vec3 in_vert;
in vec3 in_norm;
in vec3 in_text;
out vec3 v_vert;
out vec3 v_norm;
out vec3 v_text;
void main() {
v_vert = in_vert;
v_norm = in_norm;
v_text = in_text;
gl_Position = Mvp * vec4(v_vert, 1.0);
}
#version 330
uniform sampler2D Texture;
uniform vec4 Color;
uniform vec3 Light;
in vec3 v_vert;
in vec3 v_norm;
in vec3 v_text;
out vec4 f_color;
void main() {
float lum = dot(normalize(v_norm), normalize(v_vert - Light));
lum = acos(lum) / 3.14159265;
lum = clamp(lum, 0.0, 1.0);
vec3 color = texture(Texture, v_text.xy).rgb;
color = color * (1.0 - Color.a) + Color.rgb * Color.a;
f_color = vec4(color * lum, 1.0);
}
import ModernGL
from ModernGL.ext import obj
from PIL import Image
from pyrr import Matrix44
# Data files
vertex_data = obj.load('data/sitting.obj')
texture_image = Image.open('data/wood.jpg')
vertex_shader_source = open('data/shader.vert').read()
fragment_shader_source = open('data/shader.frag').read()
# Context creation
ctx = ModernGL.create_standalone_context()
# Shaders
vert = ctx.vertex_shader(vertex_shader_source)
frag = ctx.fragment_shader(fragment_shader_source)
prog = ctx.program([vert, frag])
# Matrices and Uniforms
perspective = Matrix44.perspective_projection(45.0, 1.0, 0.1, 1000.0)
lookat = Matrix44.look_at(
(-85, -180, 140),
(0.0, 0.0, 65.0),
(0.0, 0.0, 1.0),
)
mvp = perspective * lookat
prog.uniforms['Light'].value = (-140.0, -300.0, 350.0)
prog.uniforms['Color'].value = (1.0, 1.0, 1.0, 0.25)
prog.uniforms['Mvp'].write(mvp.astype('float32').tobytes())
# Texture
texture = ctx.texture(texture_image.size, 3, texture_image.tobytes())
texture.build_mipmaps()
# Vertex Buffer and Vertex Array
vbo = ctx.buffer(vertex_data)
vao = ctx.simple_vertex_array(prog, vbo, ['in_vert', 'in_text', 'in_norm'])
# Framebuffers
fbo1 = ctx.framebuffer(ctx.renderbuffer((512, 512), samples=4))
fbo2 = ctx.framebuffer(ctx.renderbuffer((512, 512)))
# Rendering
fbo1.use()
ctx.enable(ModernGL.DEPTH_TEST)
ctx.clear(0.9, 0.9, 0.9)
texture.use()
vao.render()
# Downsampling and loading the image using Pillow
ctx.copy_framebuffer(fbo2, fbo1)
data = fbo2.read(components=3, alignment=1)
img = Image.frombytes('RGB', fbo2.size, data).transpose(Image.FLIP_TOP_BOTTOM)
img.show()
- The model and texture was sanitized programatically.
- The texture was resized.
You can download the original artwork here.
The model and the texture was created by triduza. Thank you!