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