custom_postprocessing.rst 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  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 other render passes and buffers not exposed by Godot (such as depth or
  32. normal/roughness). You only have access to the rendered frame and buffers
  33. exposed by Godot as samplers.
  34. For this demo, we will use this :ref:`Sprite <class_Sprite2D>` of a sheep.
  35. .. image:: img/post_example1.png
  36. Assign a new :ref:`Shader <class_Shader>` to the ``ColorRect``'s
  37. ``ShaderMaterial``. You can access the frame's texture and UV with a
  38. ``sampler2D`` using ``hint_screen_texture`` and the built-in ``SCREEN_UV``
  39. uniforms.
  40. Copy the following code to your shader. The code below is a hex pixelization
  41. shader by `arlez80 <https://bitbucket.org/arlez80/hex-mosaic/src/master/>`_,
  42. .. code-block:: glsl
  43. shader_type canvas_item;
  44. uniform vec2 size = vec2(32.0, 28.0);
  45. // If you intend to read from mipmaps with `textureLod()` LOD values greater than `0.0`,
  46. // use `filter_nearest_mipmap` instead. This shader doesn't require it.
  47. uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
  48. void fragment() {
  49. vec2 norm_size = size * SCREEN_PIXEL_SIZE;
  50. bool half = mod(SCREEN_UV.y / 2.0, norm_size.y) / norm_size.y < 0.5;
  51. vec2 uv = SCREEN_UV + vec2(norm_size.x * 0.5 * float(half), 0.0);
  52. vec2 center_uv = floor(uv / norm_size) * norm_size;
  53. vec2 norm_uv = mod(uv, norm_size) / norm_size;
  54. center_uv += mix(vec2(0.0, 0.0),
  55. mix(mix(vec2(norm_size.x, -norm_size.y),
  56. vec2(0.0, -norm_size.y),
  57. float(norm_uv.x < 0.5)),
  58. mix(vec2(0.0, -norm_size.y),
  59. vec2(-norm_size.x, -norm_size.y),
  60. float(norm_uv.x < 0.5)),
  61. float(half)),
  62. float(norm_uv.y < 0.3333333) * float(norm_uv.y / 0.3333333 < (abs(norm_uv.x - 0.5) * 2.0)));
  63. COLOR = textureLod(screen_texture, center_uv, 0.0);
  64. }
  65. The sheep will look something like this:
  66. .. image:: img/post_example2.png
  67. Multi-pass post-processing
  68. --------------------------
  69. Some post-processing effects like blurs are resource intensive. You can make
  70. them run a lot faster if you break them down in multiple passes. In a multipass
  71. material, each pass takes the result from the previous pass as an input and
  72. processes it.
  73. To produce a multi-pass post-processing shader, you stack ``CanvasLayer`` and
  74. ``ColorRect`` nodes. In the example above, you use a ``CanvasLayer`` object to
  75. render a shader using the frame on the layer below. Apart from the node
  76. structure, the steps are the same as with the single-pass post-processing
  77. shader.
  78. Your scene tree will look something like this:
  79. .. image:: img/post_tree2.png
  80. As an example, you could write a full screen Gaussian blur effect by attaching
  81. the following pieces of code to each of the ``ColorRect`` nodes. The order in
  82. which you apply the shaders depends on the position of the ``CanvasLayer`` in
  83. the scene tree, higher means sooner. For this blur shader, the order does not
  84. matter.
  85. .. code-block:: glsl
  86. shader_type canvas_item;
  87. uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
  88. // Blurs the screen in the X-direction.
  89. void fragment() {
  90. vec3 col = texture(screen_texture, SCREEN_UV).xyz * 0.16;
  91. col += texture(screen_texture, SCREEN_UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
  92. col += texture(screen_texture, SCREEN_UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
  93. col += texture(screen_texture, SCREEN_UV + vec2(2.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
  94. col += texture(screen_texture, SCREEN_UV + vec2(2.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
  95. col += texture(screen_texture, SCREEN_UV + vec2(3.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
  96. col += texture(screen_texture, SCREEN_UV + vec2(3.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
  97. col += texture(screen_texture, SCREEN_UV + vec2(4.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
  98. col += texture(screen_texture, SCREEN_UV + vec2(4.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
  99. COLOR.xyz = col;
  100. }
  101. .. code-block:: glsl
  102. shader_type canvas_item;
  103. uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
  104. // Blurs the screen in the Y-direction.
  105. void fragment() {
  106. vec3 col = texture(screen_texture, SCREEN_UV).xyz * 0.16;
  107. col += texture(screen_texture, SCREEN_UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
  108. col += texture(screen_texture, SCREEN_UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
  109. col += texture(screen_texture, SCREEN_UV + vec2(0.0, 2.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
  110. col += texture(screen_texture, SCREEN_UV + vec2(0.0, 2.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
  111. col += texture(screen_texture, SCREEN_UV + vec2(0.0, 3.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
  112. col += texture(screen_texture, SCREEN_UV + vec2(0.0, 3.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
  113. col += texture(screen_texture, SCREEN_UV + vec2(0.0, 4.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
  114. col += texture(screen_texture, SCREEN_UV + vec2(0.0, 4.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
  115. COLOR.xyz = col;
  116. }
  117. Using the above code, you should end up with a full screen blur effect like
  118. below.
  119. .. image:: img/post_example3.png