⚡ZenGL is a minimalist Python module providing exactly one way to render scenes with OpenGL.

Overview

ZenGL

ZenGL is a minimalist Python module providing exactly one way to render scenes with OpenGL.

pip install zengl

ZenGL is ...

  • high-performance
  • simple - buffers, images, pipelines and there you go
  • easy-to-learn - it is simply OpenGL with no magic added
  • verbose - most common mistakes are catched and reported in a clear and understandable way
  • robust - there is no global state or external trouble-maker affecting the render
  • backward-compatible - it requires OpenGL 3.3 - it is just enough
  • cached - most OpenGL objects are reused between renders
  • zen - there is one way to do it

Concept

ZenGL provides a simple way to render from Python. We aim to support headless rendering first, rendering to a window is done by blitting the final image to the screen. By doing this we have full control of what we render. The window does not have to be multisample, and it requires no depth buffer at all.

Offscreen rendering works out of the box on all platforms if the right loader is provided. Loaders implement a load method to resolve a subset of OpenGL 3.3 core. The return value of the load method is an int, a void pointer to the function implementation. Virtualized, traced, and debug environments can be provided by custom loaders. The current implementation uses the glcontext from moderngl to load the OpenGL methods.

ZenGL's main focus is on readability and maintainability. Pipelines in ZenGL are almost entirely immutable and they cannot affect each other except when one draws on top of the other's result that is expected. No global state is affecting the render, if something breaks there is one place to debug.

ZenGL does not use anything beyond OpenGL 3.3 core, not even if the more convenient methods are available. Implementation is kept simple. Usually, this is not a bottleneck.

ZenGL does not implement transform feedback, storage buffers or storage images, tesselation, geometry shader, and maybe many more. We have a strong reason not to include them in the feature list. They add to the complexity and are against ZenGL's main philosophy. ZenGL was built on top experience gathered on real-life projects that could never make good use of any of that.

ZenGL is using the same vertex and image format naming as WebGPU and keeping the vertex array definition from ModernGL. ZenGL is not the next version of ModernGL. ZenGL is a simplification of a subset of ModernGL with some extras that were not possible to include in ModernGL.

Examples

grass.py

grass

envmap.py

envmap

instanced_crates.py

instanced_crates

julia_fractal.py

julia_fractal

blending.py

blending

render_to_texture.py

render_to_texture

pybullet_box_pile.py

pybullet_box_pile

pygmsh_shape.py

pygmsh_shape

texture_array.py

texture_array

monkey.py

monkey

reflection.py

reflection

polygon_offset.py

polygon_offset

blur.py

blur

hello_triangle.py

hello_triangle

hello_triangle_srgb.py

hello_triangle_srgb

viewports.py

viewports

points.py

points

wireframe_terrain.py

wireframe_terrain

crate.py

crate

sdf_example.py

sdf_example

sdf_tree.py

sdf_tree

mipmaps.py

mipmaps

conways_game_of_life.py

conways_game_of_life

Headless

import zengl
from PIL import Image

ctx = zengl.context(zengl.loader(headless=True))

size = (1280, 720)
image = ctx.image(size, 'rgba8unorm', samples=1)

triangle = ctx.pipeline(
    vertex_shader='''
        #version 330

        out vec3 v_color;

        vec2 positions[3] = vec2[](
            vec2(0.0, 0.8),
            vec2(-0.6, -0.8),
            vec2(0.6, -0.8)
        );

        vec3 colors[3] = vec3[](
            vec3(1.0, 0.0, 0.0),
            vec3(0.0, 1.0, 0.0),
            vec3(0.0, 0.0, 1.0)
        );

        void main() {
            gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0);
            v_color = colors[gl_VertexID];
        }
    ''',
    fragment_shader='''
        #version 330

        in vec3 v_color;

        layout (location = 0) out vec4 out_color;

        void main() {
            out_color = vec4(v_color, 1.0);
        }
    ''',
    framebuffer=[image],
    topology='triangles',
    vertex_count=3,
)

image.clear_value = (1.0, 1.0, 1.0, 1.0)
image.clear()
triangle.render()

Image.frombuffer('RGBA', size, image.read(), 'raw', 'RGBA', 0, -1).save('hello.png')
Comments
  • Error loading example

    Error loading example

    I ran into this issue while trying out examples. Technically it seems to be a pyglet issue, but since I ran into it while trying out your library you may still want to be aware.

    $ python3 examples/hello_triangle.py
    2021-11-12 19:53:40.357 Python[11913:760548] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to /var/folders/96/t_1vtxwn7z5b5g22grwztfq80000gn/T/com.apple.python3.savedState
    Traceback (most recent call last):
      File "examples/hello_triangle.py", line 5, in <module>
        window = Window(1280, 720)
      File "/Users/ades/repos/zengl/examples/window.py", line 17, in __init__
        super().__init__(width=width, height=height, config=config)
      File "/Users/ades/Library/Python/3.8/lib/python/site-packages/pyglet/window/__init__.py", line 658, in __init__
        self._create()
      File "/Users/ades/Library/Python/3.8/lib/python/site-packages/pyglet/window/cocoa/__init__.py", line 197, in _create
        self.context.attach(self.canvas)
      File "/Users/ades/Library/Python/3.8/lib/python/site-packages/pyglet/gl/cocoa.py", line 299, in attach
        self._nscontext.setView_(canvas.nsview)
    AttributeError: 'NoneType' object has no attribute 'setView_'
    

    I'm using:

    • macOS Big Sur 11.1:
    • Python 3.8.2
    • pyglet==1.5.21
    • numpy==1.21.4

    I tested another app that uses pyglet and that loaded up just fine.

    opened by anderslindho 9
  • Dependencies for the examples

    Dependencies for the examples

    I noticed that numpy was a requirement to use this library.

    I suppose that pyglet also could be added to this list, but since it technically only is used for the examples that part was less clear to me. I could add it to this MR as an optional feature if you'd like (through use of extras_requires).

    opened by anderslindho 6
  • Examples only cover lower-left corner of Pyglet window

    Examples only cover lower-left corner of Pyglet window

    Broke this out of #18.

    The examples only cover the lower-left corner of the Pyglet window:

    Screenshot 2022-06-02 at 21 15 04 Screenshot 2022-06-02 at 21 18 38

    Other output, such as the mp4 generated by ffmpeg_stream.py are just fine (1280x720).


    Python 3.9.12 on macOS 10.15.7 on AMD Radeon Pro 5500M 4 GB graphics (macbook pro 2019), libraries:

    ffmpeg-python==0.2.0
    glcontext==2.3.6
    moderngl==5.6.4
    Pillow==9.1.1
    pyglet==1.5.26
    
    opened by akx 4
  • Leave GL_FRAMEBUFFER_SRGB disabled by default

    Leave GL_FRAMEBUFFER_SRGB disabled by default

    Despite it is recommended to keep this enabled, it breaks integrations like imgui rendering to the default framebuffer. Also it affects the glBlitFramebuffers in an odd way copying between non srgb and srgb images. With the zengl examples it is more common to disable it for the blit than to actually use it while enabled. Some drivers are not supporting GL_FRAMEBUFFER_SRGB according to the specs.

    ZenGL should enable GL_FRAMEBUFFER_SRGB when needed and disable it afterwards. This change should not affect existing users.

    opened by szabolcsdombi 2
  • Examples fail with

    Examples fail with "ValueError: Unbound vertex attribute "gl_VertexID" at location 0"

    Running on a Macbook Pro 2019, Python 3.9, macOS 10.15.7, the examples (tried a few) fail with

    $ python julia_fractal.py
    2022-06-02 21:03:34.519 Python[79599:798132] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to (null)
    Traceback (most recent call last):
      File "/Users/akx/build/zengl/examples/julia_fractal.py", line 14, in <module>
        scene = ctx.pipeline(
      File "/Users/akx/build/zengl/_zengl.py", line 382, in validate
        raise ValueError(f'Unbound vertex attribute "{name}" at location {location}')
    ValueError: Unbound vertex attribute "gl_VertexID" at location 0
    

    (please ignore the f string, I'm running #17 right now)

    Is gl_VertexID supposed to be somehow implicitly bound by... something..?

    Commenting out the check makes things work, but it sounds like there's something weird here :)

    opened by akx 2
  • just getting started, using streaming_video.py example

    just getting started, using streaming_video.py example

    Realizing what I'm looking for may not be in zengl; when I run the streaming_video.py example I see my webcam0 fine, but it is vertically flipped.

    To vertically flip the video frame, would that be done at the imageio, the zengl, or some other image module level I need to bring into that minimal example? I am thinking the return value from zengl.rgba(next(it), 'rgb') would saved to an intermediate variable, potentially as input to some image object that allows basic graphical operations, such as the vertical flip I want?

    opened by bsenftner 2
  • Better support for non double buffering windows

    Better support for non double buffering windows

    Double buffering is not necessary with zengl. Rendering is done entirely offscreen and the final image may be blitted to the screen. This technique does not require double buffering to hide in-progress rendering artifacts. When double buffering is off, it seems the rendering queue is not flushed automatically at the end of a frame. (the end of the frame is not clearly defined in this case) glFlush must be called

    TODO: implement Context.flush() TODO: maybe Image.blit(..., flush=True) TODO: keep only one of the above

    opened by szabolcsdombi 2
  • Constant uniform support

    Constant uniform support

    Support binding uniform values at create time this might be useful for flags, render modes, ...

    For example:

    blur pipeline with uniform to set vertical / horizontal blur render pipeline with a uniform flag/mode to switch between rendering for reflection, rendering shadow maps render shadow pipeline to switch between light sources rendering to cubemap and define the face

    These values must be bound at render time, but at least they can be encoded in pipeline create time. Allowing to change these values at runtime would require more components for not much extra value (for that uniform buffers seem to be a better fit)

    opened by szabolcsdombi 1
  • Convert string formatting to f-strings

    Convert string formatting to f-strings

    Since this project is Python 3.6+, string formatting could just as well use the faster and more convenient f-strings.

    This was a mechanical conversion with pyupgrade and flynt, followed up by some manual fixups.

    opened by akx 1
  • Pipeline vertex_count, first_vertex and instance_count cannot be read or written after creation

    Pipeline vertex_count, first_vertex and instance_count cannot be read or written after creation

    In zengl.cpp (currently at line 2124)

    PyMemberDef Pipeline_members[] = {
        {"vertex_count", T_OBJECT_EX, offsetof(Pipeline, vertex_count), 0, NULL},
        {"instance_count", T_OBJECT_EX, offsetof(Pipeline, instance_count), 0, NULL},
        {"first_vertex", T_OBJECT_EX, offsetof(Pipeline, first_vertex), 0, NULL},
        {},
    };
    

    should use T_INT instead

    PyMemberDef Pipeline_members[] = {
        {"vertex_count", T_INT, offsetof(Pipeline, vertex_count), 0, NULL},
        {"instance_count", T_INT, offsetof(Pipeline, instance_count), 0, NULL},
        {"first_vertex", T_INT, offsetof(Pipeline, first_vertex), 0, NULL},
        {},
    };
    
    opened by mrossetti 1
  • Check for multisample support

    Check for multisample support

    It is very common to have samples=4 supported. It even works with software renderers. However, the supported number of samples should be collected and checked against the image parameters.

    opened by szabolcsdombi 1
Releases(1.10.2)
Owner
Szabolcs Dombi
Creator of ModernGL
Szabolcs Dombi
Leshycam - Generate Inscryption styled portrait sprites from any image

Leshy's Camera Generate Inscryption styled portrait sprites from any image. Setu

3 Sep 27, 2022
Create QR Code for link using Python

Quick Response QR is short and named for a quick read from a cell phone. Used to view information from transitory media and put it on your cell phone.

Coding Taggers 1 Jan 09, 2022
Fast batch image resizer and rotator for JPEG and PNG images.

imgp is a command line image resizer and rotator for JPEG and PNG images.

Terminator X 921 Dec 25, 2022
Steganography Image/Data Injector.

Byte Steganography Image/Data Injector. For artists or people to inject their own print/data into their images. TODO Add more file formats to support.

Ori 4 Nov 16, 2022
【萝莉图片算法】高损图像压缩算法!?

【萝莉图片算法】高损图像压缩算法!? 我又发明出新算法了! 这次我发明的是新型高损图像压缩算法——萝莉图片算法!为什么是萝莉图片,这是因为它是使动用法,让图片变小所以是萝莉图片,大家一定要学好语文哦! 压缩效果 太神奇了!压缩率竟然高达99.97%! 与常见压缩算法对比 在图片最终大小为1KB的情况

黄巍 49 Oct 17, 2022
A python script for extracting/removing exif data from images by @AbirHasan2005

Image-Exif A Python script for extracting exif metadata from images. How to use? Using this script you can extract exif data from image and save in .c

Abir Hasan 13 Dec 16, 2022
A quick and dirty QT Statusbar implementation for grabbing GIFs from Tenor, since there is no offical or unofficial one I found. This was intended for use under Linux, however it was also functional enough on MacOS.

Statusbar-TenorGIF App for Linux A quick and dirty QT Statusbar implementation for grabbing GIFs from Tenor, since there is no offical one and I didnt

Luigi DaVinci 1 Nov 01, 2021
python app to turn a photograph into a cartoon

Draw This. Draw This is a polaroid camera that draws cartoons. You point, and shoot - and out pops a cartoon; the camera's best interpretation of what

Dan Macnish 2k Dec 19, 2022
Tool to create a Phunk image with a custom background

Create Phunk image Tool to create a Phunk image with a custom background Installation Clone the repo git clone https://github.com/albanow/etherscan_sa

Albano Pena Torres 6 Mar 31, 2022
Utilities for SteamVR on Linux

This project contains scripts to improve the functionally of SteamVR on Linux:

86 Dec 29, 2022
PyLibTiff - a wrapper to the libtiff library to Python using ctypes

PyLibTiff is a package that provides: a wrapper to the libtiff library to Python using ctypes. a pure Python module for reading and writing TIFF and L

Pearu Peterson 105 Dec 21, 2022
A simple image-level annotation tool supporting multi-channel images for napari.

napari-labelimg4classification A simple image-level annotation tool supporting multi-channel images for napari. This napari plugin was generated with

4 May 16, 2022
Pixel Brush Processing Unit

Pixel Brush Processing Unit The Pixel Brush Processing Unit (PBPU for short) is a simple 4-Bit CPU I designed in Logisim while I was still in school a

Pixel Brush 2 Nov 03, 2022
Image2scan - a python program that can be applied on an image in order to get a scan of it back

image2scan Purpose image2scan is a python program that can be applied on an image in order to get a scan of it back. For this purpose, it searches for

Kushal Shingote 2 Feb 13, 2022
SGTL - Spectral Graph Theory Library

SGTL - Spectral Graph Theory Library SGTL is a python library of spectral graph theory methods. The library is still very new and so there are many fe

Peter Macgregor 6 Oct 01, 2022
Python QR Code image generator

Pure python QR Code generator Generate QR codes. For a standard install (which will include pillow for generating images), run: pip install qrcode[pil

Lincoln Loop 3.5k Dec 31, 2022
A 3D structural engineering finite element library for Python.

An easy to use elastic 3D structural engineering finite element analysis library for Python.

Craig 220 Dec 27, 2022
Python-fu-cartoonify - GIMP plug-in to turn a photo into a cartoon.

python-fu-cartoonify GIMP plug-in to turn a photo into a cartoon. Preview Installation Copy python-fu-cartoonify.py into the plug-in folder listed und

Pascal Reitermann 6 Aug 05, 2022
Converting Images Into Minecraft Houses

Converting Images Into Minecraft Houses In this particular project, we turned a 2D Image into Minecraft pixel art and then scaled it in 3D such that i

Mathias Oliver Valdbjørn Jørgensen 1 Feb 02, 2022
Convert any binary data to a PNG image file and vice versa.

What is PngBin? The name PngBin comes from an image format file extension PNG (Portable Network Graphics) and the word Binary. An image produced by Pn

Nathan Young 87 Dec 22, 2022