From 7368c6bfd4d69b1be66e58079fa457efa9b3b600 Mon Sep 17 00:00:00 2001 From: Szabolcs Dombi Date: Thu, 4 Jul 2024 16:46:54 +0300 Subject: [PATCH] update docs --- README.md | 8 +- docs/index.rst | 326 +++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 278 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 886a6164..6350878e 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,9 @@ ZenGL is a low level graphics library. Works on all platforms including the brow ## Description - **Context** is the root object to access OpenGL -- **Image** is an OpenGL Textures or OpenGL Renderbuffer +- **Image** is an OpenGL Texture or Renderbuffer - **Buffer** is an OpenGL Buffer -- **Pipeline** is an OpenGL Program + OpenGL Vertex Array + OpenGL Framebuffer + _complete state for rendering_ +- **Pipeline** is an OpenGL Program + Vertex Array + Framebuffer + _complete state for rendering_ ```py ctx = zengl.context() @@ -259,9 +259,9 @@ On the other hand, ModernGL supports a wide variety of OpenGL versions and exten - ZenGL is a drop-in replacement for pure OpenGL code - Using ZenGL requires some OpenGL knowledge -- ZenGL Images are OpenGL [Texture Objects](https://www.khronos.org/opengl/wiki/Texture) or OpenGL [Renderbuffer Objects](https://www.khronos.org/opengl/wiki/Renderbuffer_Object) +- ZenGL Images are OpenGL [Texture Objects](https://www.khronos.org/opengl/wiki/Texture) or [Renderbuffer Objects](https://www.khronos.org/opengl/wiki/Renderbuffer_Object) - ZenGL Buffers are OpenGL [Buffer Objects](https://www.khronos.org/opengl/wiki/Buffer_Object) -- ZenGL Pipelines contain an OpenGL [Vertex Array Object](https://www.khronos.org/opengl/wiki/Vertex_Specification#Vertex_Array_Object), an OpenGL [Program Object](https://www.khronos.org/opengl/wiki/GLSL_Object#Program_objects), and an OpenGL [Framebuffer Object](https://www.khronos.org/opengl/wiki/Framebuffer) +- ZenGL Pipelines contain an OpenGL [Vertex Array Object](https://www.khronos.org/opengl/wiki/Vertex_Specification#Vertex_Array_Object), a [Program Object](https://www.khronos.org/opengl/wiki/GLSL_Object#Program_objects), and a [Framebuffer Object](https://www.khronos.org/opengl/wiki/Framebuffer) - ZenGL Pielines may also contain OpenGL [Sampler Objects](https://www.khronos.org/opengl/wiki/Sampler_Object) - Creating ZenGL Pipelines does not necessarily compile the shader from source - The ZenGL Shader Cache exists independently from the Pipeline objects diff --git a/docs/index.rst b/docs/index.rst index 0bfff112..534e1de9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,7 +1,7 @@ ZenGL ----- -ZenGL is a minimalist Python module providing exactly **one** way to render scenes with OpenGL. +ZenGL is a low level graphics library. Works on all platforms including the browser. .. code:: @@ -11,16 +11,266 @@ ZenGL is a minimalist Python module providing exactly **one** way to render scen - `zengl on Github `_ - `zengl on PyPI `_ -**ZenGL is ...** +Description +=========== -- **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 caught 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* +- **Context** is the root object to access OpenGL +- **Image** is an OpenGL Texture or Renderbuffer +- **Buffer** is an OpenGL Buffer +- **Pipeline** is an OpenGL Program + Vertex Array + Framebuffer + *complete state for rendering* + +.. code:: + + ctx = zengl.context() + texture = ctx.image(size, 'rgba8unorm', pixels) + renderbuffer = ctx.image(size, 'rgba8unorm', samples=4) + vertex_buffer = ctx.buffer(vertices) + pipeline = ctx.pipeline(...) + +The complete OpenGL state is encapsulated by the **Pipeline**. + +Rendering with multiple pipelines guarantees proper state with minimal changes and api calls. + +.. code:: + + background.render() + scene.render() + particles.render() + bloom.render() + +**Pipelines** render to framebuffers, **Images** can be blit to the screen. + +.. code:: + + # init time + pipeline = ctx.pipeline( + framebuffer=[image, depth], + ) + +.. code:: + + # per frame + image.clear() + depth.clear() + pipeline.render() + image.blit() + +Programs are simple, easy, and cached. Unique shader sources are only compiled once. + +.. code:: + + pipeline = ctx.pipeline( + vertex_shader=''' + #version 330 core + + void main() { + gl_Position = ... + } + ''', + fragment_shader=''' + #version 330 core + + out vec4 frag_color; + + void main() { + frag_color = ... + } + ''', + ) + +Vertex Arrays are simple. + +.. code:: + + # simple + pipeline = ctx.pipeline( + vertex_buffers=zengl.bind(vertex_buffer, '3f 3f 2f', 0, 1, 2), + vertex_count=vertex_buffer.size // zengl.calcsize('3f 3f 2f'), + ) + +.. code:: + + # indexed + pipeline = ctx.pipeline( + vertex_buffers=zengl.bind(vertex_buffer, '3f 3f 2f', 0, 1, 2), + index_buffer=index_buffer, + vertex_count=index_buffer.size // 4, + ) + +.. code:: + + # instanced + pipeline = ctx.pipeline( + vertex_buffers=[ + *zengl.bind(vertex_buffer, '3f 3f 2f', 0, 1, 2), + *zengl.bind(instance_buffer, '3f 4f /i', 3, 4), + ], + vertex_count=vertex_buffer.size // zengl.calcsize('3f 3f 2f'), + instance_count=1000, + ) + +Uniform Buffer, Texture, and Sampler binding is easy. + +.. code:: + + # uniform buffers + pipeline = ctx.pipeline( + layout=[ + { + 'name': 'Common', + 'binding': 0, + }, + ], + resources=[ + { + 'type': 'uniform_buffer', + 'binding': 0, + 'buffer': uniform_buffer, + }, + ], + ) + +.. code:: + + # textures + pipeline = ctx.pipeline( + layout=[ + { + 'name': 'Texture', + 'binding': 0, + }, + ], + resources=[ + { + 'type': 'sampler', + 'binding': 0, + 'image': texture, + 'wrap_x': 'clamp_to_edge', + 'wrap_y': 'clamp_to_edge', + 'min_filter': 'nearest', + 'mag_filter': 'nearest', + }, + ], + ) + +Postprocessing and Compute can be implemented as rendering a fullscreen quad. + +.. code:: + + pipeline = ctx.pipeline( + vertex_shader=''' + #version 330 core + + vec2 vertices[3] = vec2[]( + vec2(-1.0, -1.0), + vec2(3.0, -1.0), + vec2(-1.0, 3.0) + ); + + void main() { + gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0); + } + ''', + fragment_shader=''' + #version 330 core + + out vec4 frag_color; + + void main() { + frag_color = ... + } + ''', + topology='triangles', + vertex_count=3, + ) + +.. code:: + + particle_system = ctx.pipeline( + fragment_shader=''' + #version 330 core + + uniform sampler2D Position; + uniform sampler2D Velocity; + uniform vec3 Acceleration; + + layout (location = 0) out vec3 OutputPosition; + layout (location = 1) out vec3 OutputVelocity; + + void main() { + OutputPosition = Position + Velocity; + OutputVelocity = Velocity + Acceleration; + } + ''', + ) + +ZenGL intentionally does not support: + +- Transform Feedback +- Geometry Shaders +- Tesselation +- Compute Shaders +- 3D Textures +- Storage Buffers + +Most of the above can be implemented in a more hardware friendly way using the existing ZenGL API. +Interoperability with other modules is also possible. Using such may reduce the application's portablity. +It is even possible to use direct OpenGL calls together with ZenGL, however this is likely not necessary. + +It is common to render directly to the screen with OpenGL. +With ZenGL, the right way is to render to a framebuffer and blit the final image to the screen. +This allows fine-grained control of the framebuffer format, guaranteed multisampling settings, correct depth/stencil precison. +It is also possible to render directly to the screen, however this feature is designed to be used for the postprocessing step. + +This design allows ZenGL to support: + +- Rendering without a window +- Rendering to multiple windows +- Rendering to HDR monitors +- Refreshing the screen without re-rendering the scene +- Apply post-processing without changing how the scene is rendered +- Making reusable shaders and components +- Taking screenshots or exporting a video + +The `default framebuffer `_ in OpenGL is highly dependent on how the Window is created. +It is often necessary to configure the Window to provide the proper depth precision, stencil buffer, multisampling and double buffering. +Often the "best pixel format" lacks all of these features on purpose. ZenGL aims to allow choosing these pixel formats and ensures the user specifies the rendering requirements. +It is even possible to render low-resolution images and upscale them for high-resolution monitors. +Tearing can be easily prevented by decoupling the scene rendering from the screen updates. + +ZenGL was designed for Prototyping + +It is tempting to start a project with Vulkan, however even getting a simple scene rendered requires tremendous work and advanced tooling to compile shaders ahead of time. ZenGL provides self-contained Pipelines which can be easily ported to Vulkan. +ZenGL code is verbose and easy to read. + +ZenGL support multiple design patters + +Many libraries enfore certain design patterns. +ZenGL avoids this by providing cached pipeline creation, pipeline templating and lean resourece and framebuffer definition. +It is supported to create pipelines on the fly or template them for certain use-cases. + +ZenGL emerged from an experimental version of `ModernGL `_. +To keep ModernGL backward compatible, ZenGL was re-designed from the ground-up to support a strict subset of OpenGL. +On the other hand, ModernGL supports a wide variety of OpenGL versions and extensions. + +Disambiguation +============== + +- ZenGL is a drop-in replacement for pure OpenGL code +- Using ZenGL requires some OpenGL knowledge +- ZenGL Images are OpenGL `Texture Objects `_ or `Renderbuffer Objects `_ +- ZenGL Buffers are OpenGL `Buffer Objects `_ +- ZenGL Pipelines contain an OpenGL `Vertex Array Object `_, a `Program Object `_, and a `Framebuffer Object `_ +- ZenGL Pielines may also contain OpenGL `Sampler Objects `_ +- Creating ZenGL Pipelines does not necessarily compile the shader from source +- The ZenGL Shader Cache exists independently from the Pipeline objects +- A Framebuffer is always represented by a Python list of ZenGL Images +- There is no `Pipeline.clear()` method, individual images must be cleared independently +- GLSL Uniform Blocks and sampler2D objects are bound in the Pipeline layout +- Textures and Uniform Buffers are bound in the Pipeline resources + +Documentation +============= .. py:class:: Context @@ -39,36 +289,8 @@ ZenGL is a minimalist Python module providing exactly **one** way to render scen | Represents an entire rendering pipeline including the global state, shader program, framebuffer, vertex state, uniform buffer bindings, samplers, and sampler bindings. -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 was not possible to include in ModernGL. - Context -------- +======= .. py:method:: zengl.context() -> Context @@ -132,7 +354,7 @@ This method is automatically called by :py:meth:`zengl.context`. | A boolean to wait for a ``glFenceSync``. Buffer ------- +====== | Buffers hold vertex, index, and uniform data used by rendering. | Buffers have a fixed size allocated upfront in the device memory. @@ -215,7 +437,7 @@ Buffer An int, representing the size of the buffer in bytes. Image ------ +===== | Images hold texture data or render outputs. | Images with texture support are implemented with OpenGL textures. @@ -357,7 +579,7 @@ Generate mipmaps for the image. | For depth and stencil images this value is False. Pipeline --------- +======== .. py:method:: Context.pipeline(vertex_shader, fragment_shader, layout, resources, uniforms, depth, stencil, blend, framebuffer, vertex_buffers, index_buffer, short_index, cull_face, topology, vertex_count, instance_count, first_vertex, viewport, uniform_data, viewport_data, render_data, includes, template) -> Pipeline @@ -487,7 +709,7 @@ Pipeline | Execute the rendering pipeline. Shader Code ------------ +=========== - **do** use ``#version 330 core`` or ``#version 300 es`` as the first line in the shader. - **do** use ``layout (std140)`` for uniform buffers. @@ -509,7 +731,7 @@ Shader Code - **do** arrange pipelines in such an order to minimize framebuffer then program changes. Shader Includes ---------------- +=============== | Shader includes were designed to solve a single problem of sharing code among shaders without having to field format the shader code. | Includes are simple string replacements from :py:attr:`Context.includes` @@ -539,7 +761,7 @@ Shader Includes ) Include Patterns ----------------- +================ **common uniform buffer** @@ -570,7 +792,7 @@ Include Patterns y = np.exp(-x * x / (s * s / 4)) y /= y.sum() v = ', '.join(f'{t:.8f}' for t in y) - return f'const int N = {s * 2 + 1};\nfloat coeff[N] = float[]({v});' + return f'const int N = {s * 2 + 1};\nfloat coeff[N] = float[ <{v});' ctx.includes['kernel'] = kernel(19) @@ -587,14 +809,14 @@ Include Patterns ''' Rendering to Texture --------------------- +==================== Rendering to texture is supported. However, multisampled images must be downsampled before being used as textures. In that case, an intermediate render target must be samples > 1 and texture = False. Then this image can be downsampled with :py:meth:`Image.blit` to another image with samples = 1 and texture = True. Cleanup -------- +======= Clean only if necessary. It is ok not to clean up before the program ends. @@ -610,7 +832,7 @@ it calls glDeleteShader for all the previously created vertex and fragment shade When the string ``all`` is passed to this method, it releases all the resources allocated from this context. Interoperability ----------------- +================ | Some window implementations expose a framebuffer object for drawing. | Detecting this framebuffer is an error-prone and non-reliable solution. @@ -631,7 +853,7 @@ Returns a dictionary with all of the OpenGL objects. | You may want to change this attribute when using PyQt. Utils ------ +===== .. py:attribute:: Context.info @@ -674,7 +896,7 @@ Utils .. _Image Formats: Image Formats -------------- +============= ==================== ===================== ================== ================= ZenGL format internal format format type @@ -718,7 +940,7 @@ depth32float GL_DEPTH_COMPONENT32F GL_DEPTH_COMPONENT GL_FLOAT .. _Vertex Formats: Vertex Formats --------------- +============== ========== ============= ================== ==== ========== ZenGL bind ZenGL format type size normalized