| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 | .. _doc_custom_postprocessing:Custom post-processing======================Introduction------------Godot provides many post-processing effects out of the box, including Bloom,DOF, and SSAO, which are described in :ref:`doc_environment_and_post_processing`.However, advanced use cases may require custom effects. This article explains howto write your own custom effects.The easiest way to implement a custom post-processing shader is to use Godot'sbuilt-in ability to read from the screen texture. If you're not familiar withthis, you should read the:ref:`Screen Reading Shaders Tutorial <doc_screen-reading_shaders>` first.Single pass post-processing---------------------------Post-processing effects are shaders applied to a frame after Godot has renderedit. To apply a shader to a frame, create a :ref:`CanvasLayer<class_CanvasLayer>`, and give it a :ref:`ColorRect <class_ColorRect>`. Assign anew :ref:`ShaderMaterial <class_ShaderMaterial>` to the newly created``ColorRect``, and set the ``ColorRect``'s anchor preset to Full Rect:.. figure:: img/custom_postprocessing_anchors_preset_full_rect.webp   :align: center   :alt: Setting the anchor preset to Full Rect on the ColorRect node   Setting the anchor preset to Full Rect on the ColorRect nodeYour scene tree will look something like this:.. image:: img/post_tree1.png.. note::   Another more efficient method is to use a :ref:`BackBufferCopy   <class_BackBufferCopy>` to copy a region of the screen to a buffer and to   access it in a shader script through a ``sampler2D`` using   ``hint_screen_texture``... note::    As of the time of writing, Godot does not support rendering to multiple    buffers at the same time. Your post-processing shader will not have access    to other render passes and buffers not exposed by Godot (such as depth or    normal/roughness). You only have access to the rendered frame and buffers    exposed by Godot as samplers.For this demo, we will use this :ref:`Sprite <class_Sprite2D>` of a sheep... image:: img/post_example1.pngAssign a new :ref:`Shader <class_Shader>` to the ``ColorRect``'s``ShaderMaterial``. You can access the frame's texture and UV with a``sampler2D`` using ``hint_screen_texture`` and the built-in ``SCREEN_UV``uniforms.Copy the following code to your shader. The code below is a hex pixelizationshader by `arlez80 <https://bitbucket.org/arlez80/hex-mosaic/src/master/>`_,.. code-block:: glsl    shader_type canvas_item;    uniform vec2 size = vec2(32.0, 28.0);    // If you intend to read from mipmaps with `textureLod()` LOD values greater than `0.0`,    // use `filter_nearest_mipmap` instead. This shader doesn't require it.    uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;    void fragment() {            vec2 norm_size = size * SCREEN_PIXEL_SIZE;            bool less_than_half = mod(SCREEN_UV.y / 2.0, norm_size.y) / norm_size.y < 0.5;            vec2 uv = SCREEN_UV + vec2(norm_size.x * 0.5 * float(less_than_half), 0.0);            vec2 center_uv = floor(uv / norm_size) * norm_size;            vec2 norm_uv = mod(uv, norm_size) / norm_size;            center_uv += mix(vec2(0.0, 0.0),                             mix(mix(vec2(norm_size.x, -norm_size.y),                                     vec2(0.0, -norm_size.y),                                     float(norm_uv.x < 0.5)),                                 mix(vec2(0.0, -norm_size.y),                                     vec2(-norm_size.x, -norm_size.y),                                     float(norm_uv.x < 0.5)),                                 float(less_than_half)),                             float(norm_uv.y < 0.3333333) * float(norm_uv.y / 0.3333333 < (abs(norm_uv.x - 0.5) * 2.0)));            COLOR = textureLod(screen_texture, center_uv, 0.0);    }The sheep will look something like this:.. image:: img/post_example2.pngMulti-pass post-processing--------------------------Some post-processing effects like blurs are resource intensive. You can makethem run a lot faster if you break them down in multiple passes. In a multipassmaterial, each pass takes the result from the previous pass as an input andprocesses it.To produce a multi-pass post-processing shader, you stack ``CanvasLayer`` and``ColorRect`` nodes. In the example above, you use a ``CanvasLayer`` object torender a shader using the frame on the layer below. Apart from the nodestructure, the steps are the same as with the single-pass post-processingshader.Your scene tree will look something like this:.. image:: img/post_tree2.pngAs an example, you could write a full screen Gaussian blur effect by attachingthe following pieces of code to each of the ``ColorRect`` nodes. The order inwhich you apply the shaders depends on the position of the ``CanvasLayer`` inthe scene tree, higher means sooner. For this blur shader, the order does notmatter... code-block:: glsl    shader_type canvas_item;    uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;    // Blurs the screen in the X-direction.    void fragment() {        vec3 col = texture(screen_texture, SCREEN_UV).xyz * 0.16;        col += texture(screen_texture, SCREEN_UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;        col += texture(screen_texture, SCREEN_UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;        col += texture(screen_texture, SCREEN_UV + vec2(2.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;        col += texture(screen_texture, SCREEN_UV + vec2(2.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;        col += texture(screen_texture, SCREEN_UV + vec2(3.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;        col += texture(screen_texture, SCREEN_UV + vec2(3.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;        col += texture(screen_texture, SCREEN_UV + vec2(4.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;        col += texture(screen_texture, SCREEN_UV + vec2(4.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;        COLOR.xyz = col;    }.. code-block:: glsl    shader_type canvas_item;    uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;    // Blurs the screen in the Y-direction.    void fragment() {        vec3 col = texture(screen_texture, SCREEN_UV).xyz * 0.16;        col += texture(screen_texture, SCREEN_UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz * 0.15;        col += texture(screen_texture, SCREEN_UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz * 0.15;        col += texture(screen_texture, SCREEN_UV + vec2(0.0, 2.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.12;        col += texture(screen_texture, SCREEN_UV + vec2(0.0, 2.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.12;        col += texture(screen_texture, SCREEN_UV + vec2(0.0, 3.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.09;        col += texture(screen_texture, SCREEN_UV + vec2(0.0, 3.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.09;        col += texture(screen_texture, SCREEN_UV + vec2(0.0, 4.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.05;        col += texture(screen_texture, SCREEN_UV + vec2(0.0, 4.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.05;        COLOR.xyz = col;    }Using the above code, you should end up with a full screen blur effect likebelow... image:: img/post_example3.png
 |