custom_postprocessing.rst 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. .. _doc_custom_postprocessing:
  2. Custom post-processing
  3. ======================
  4. Introduction
  5. ------------
  6. Godot provides many post-processing effects out of the box, including Bloom,
  7. DOF, and SSAO, which are described in :ref:`doc_environment_and_post_processing`.
  8. However, advanced use cases may require custom effects. This article explains how
  9. to write your own custom effects.
  10. The easiest way to implement a custom post-processing shader is to use Godot's
  11. built-in ability to read from the screen texture. If you're not familiar with
  12. this, you should read the
  13. :ref:`Screen Reading Shaders Tutorial <doc_screen-reading_shaders>` first.
  14. Single pass post-processing
  15. ---------------------------
  16. Post-processing effects are shaders applied to a frame after Godot has rendered
  17. it. To apply a shader to a frame, create a :ref:`CanvasLayer
  18. <class_CanvasLayer>`, and give it a :ref:`ColorRect <class_ColorRect>`. Assign a
  19. new :ref:`ShaderMaterial <class_ShaderMaterial>` to the newly created
  20. ``ColorRect``, and set the ``ColorRect``'s layout to "Full Rect".
  21. Your scene tree will look something like this:
  22. .. image:: img/post_tree1.png
  23. .. note::
  24. Another more efficient method is to use a :ref:`BackBufferCopy
  25. <class_BackBufferCopy>` to copy a region of the screen to a buffer and to
  26. access it in a shader script through a ``sampler2D`` using
  27. ``hint_screen_texture``.
  28. .. note::
  29. As of the time of writing, Godot does not support rendering to multiple
  30. buffers at the same time. Your post-processing shader will not have access
  31. to normals or other render passes. You only have access to the rendered
  32. frame.
  33. For this demo, we will use this :ref:`Sprite <class_Sprite2D>` of a sheep.
  34. .. image:: img/post_example1.png
  35. Assign a new :ref:`Shader <class_Shader>` to the ``ColorRect``'s
  36. ``ShaderMaterial``. You can access the frame's texture and UV with a
  37. ``sampler2D`` using ``hint_screen_texture`` and the built in ``SCREEN_UV``
  38. uniforms.
  39. Copy the following code to your shader. The code below is a hex pixelization
  40. shader by `arlez80 <https://bitbucket.org/arlez80/hex-mosaic/src/master/>`_,
  41. .. code-block:: glsl
  42. shader_type canvas_item;
  43. uniform vec2 size = vec2(32.0, 28.0);
  44. uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
  45. void fragment() {
  46. vec2 norm_size = size * SCREEN_PIXEL_SIZE;
  47. bool half = mod(SCREEN_UV.y / 2.0, norm_size.y) / norm_size.y < 0.5;
  48. vec2 uv = SCREEN_UV + vec2(norm_size.x * 0.5 * float(half), 0.0);
  49. vec2 center_uv = floor(uv / norm_size) * norm_size;
  50. vec2 norm_uv = mod(uv, norm_size) / norm_size;
  51. center_uv += mix(vec2(0.0, 0.0),
  52. mix(mix(vec2(norm_size.x, -norm_size.y),
  53. vec2(0.0, -norm_size.y),
  54. float(norm_uv.x < 0.5)),
  55. mix(vec2(0.0, -norm_size.y),
  56. vec2(-norm_size.x, -norm_size.y),
  57. float(norm_uv.x < 0.5)),
  58. float(half)),
  59. float(norm_uv.y < 0.3333333) * float(norm_uv.y / 0.3333333 < (abs(norm_uv.x - 0.5) * 2.0)));
  60. COLOR = textureLod(screen_texture, center_uv, 0.0);
  61. }
  62. The sheep will look something like this:
  63. .. image:: img/post_example2.png
  64. Multi-pass post-processing
  65. --------------------------
  66. Some post-processing effects like blurs are resource intensive. You can make
  67. them run a lot faster if you break them down in multiple passes. In a multipass
  68. material, each pass takes the result from the previous pass as an input and
  69. processes it.
  70. To produce a multi-pass post-processing shader, you stack ``CanvasLayer`` and
  71. ``ColorRect`` nodes. In the example above, you use a ``CanvasLayer`` object to
  72. render a shader using the frame on the layer below. Apart from the node
  73. structure, the steps are the same as with the single-pass post-processing
  74. shader.
  75. Your scene tree will look something like this:
  76. .. image:: img/post_tree2.png
  77. As an example, you could write a full screen Gaussian blur effect by attaching
  78. the following pieces of code to each of the ``ColorRect`` nodes. The order in
  79. which you apply the shaders depends on the position of the ``CanvasLayer`` in
  80. the scene tree, higher means sooner. For this blur shader, the order does not
  81. matter.
  82. .. code-block:: glsl
  83. shader_type canvas_item;
  84. uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
  85. // Blurs the screen in the X-direction.
  86. void fragment() {
  87. vec3 col = texture(screen_texture, SCREEN_UV).xyz * 0.16;
  88. col += texture(screen_texture, SCREEN_UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
  89. col += texture(screen_texture, SCREEN_UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
  90. col += texture(screen_texture, SCREEN_UV + vec2(2.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
  91. col += texture(screen_texture, SCREEN_UV + vec2(2.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
  92. col += texture(screen_texture, SCREEN_UV + vec2(3.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
  93. col += texture(screen_texture, SCREEN_UV + vec2(3.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
  94. col += texture(screen_texture, SCREEN_UV + vec2(4.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
  95. col += texture(screen_texture, SCREEN_UV + vec2(4.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
  96. COLOR.xyz = col;
  97. }
  98. .. code-block:: glsl
  99. shader_type canvas_item;
  100. uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
  101. // Blurs the screen in the Y-direction.
  102. void fragment() {
  103. vec3 col = texture(screen_texture, SCREEN_UV).xyz * 0.16;
  104. col += texture(screen_texture, SCREEN_UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
  105. col += texture(screen_texture, SCREEN_UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
  106. col += texture(screen_texture, SCREEN_UV + vec2(0.0, 2.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
  107. col += texture(screen_texture, SCREEN_UV + vec2(0.0, 2.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
  108. col += texture(screen_texture, SCREEN_UV + vec2(0.0, 3.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
  109. col += texture(screen_texture, SCREEN_UV + vec2(0.0, 3.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
  110. col += texture(screen_texture, SCREEN_UV + vec2(0.0, 4.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
  111. col += texture(screen_texture, SCREEN_UV + vec2(0.0, 4.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
  112. COLOR.xyz = col;
  113. }
  114. Using the above code, you should end up with a full screen blur effect like
  115. below.
  116. .. image:: img/post_example3.png