custom_postprocessing.rst 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  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, DOF, and SSAO. Sometimes you
  7. want to write your own custom effect. Here's how you can do so.
  8. Post-processing effects are shaders applied to a frame after Godot rendered it. You first want to render
  9. your scene into a :ref:`Viewport <class_Viewport>`, then render the ``Viewport``
  10. inside a :ref:`ViewportTexture <class_ViewportTexture>` and show it on the screen.
  11. The easiest way to implement a custom post-processing shader is to use Godot's built-in ability to read from
  12. the screen texture. If you're not familiar with this, you should read the :ref:`Screen Reading Shaders
  13. Tutorial <doc_screen-reading_shaders>` first.
  14. .. note::
  15. As of the time of writing, Godot does not support rendering to multiple buffers at the same time. Your
  16. post-processing shader will not have access to normals or other render passes. You only have
  17. access to the rendered frame.
  18. Single pass post-processing
  19. ---------------------------
  20. You will need a ``Viewport`` to render your scene to, and a scene to render your
  21. ``Viewport`` on the screen. You can use a :ref:`ViewportContainer
  22. <class_ViewportContainer>` to display your ``Viewport`` on the entire screen or inside
  23. another :ref:`Control <class_Control>` node.
  24. .. note::
  25. Rendering using a ``Viewport`` gives you control over
  26. how the scene render, including the framerate, and you can use the
  27. ``ViewportContainer`` to render 3D objects in a 2D scene.
  28. For this demo, we will use a :ref:`Node2D <class_Node2D>` with a ``ViewportContainer`` and finally a
  29. ``Viewport``. Your **Scene** tab should look like this:
  30. .. image:: img/post_hierarchy1.png
  31. Inside the ``Viewport``, you can have whatever you want. This will contain
  32. your main scene. For this tutorial, we will use a field of random boxes:
  33. .. image:: img/post_boxes.png
  34. Add a new :ref:`ShaderMaterial <class_ShaderMaterial>` to the ``ViewportContainer``, and assign a new
  35. shader resource to it. You can access your rendered ``Viewport`` with the built-in ``TEXTURE`` uniform.
  36. .. note::
  37. You can choose not to use a ``ViewportContainer``, but if you do so, you will
  38. need to create your own uniform in the shader and pass the ``Viewport`` texture in
  39. manually, like so:
  40. .. code-block:: glsl
  41. // Inside the Shader.
  42. uniform sampler2D ViewportTexture;
  43. And you can pass the texture into the shader from GDScript like so:
  44. ::
  45. # In GDScript.
  46. func _ready():
  47. $Sprite.material.set_shader_param("ViewportTexture", $Viewport.get_texture())
  48. Copy the following code to your shader. The above code is a single pass edge detection filter, a
  49. `Sobel filter <https://en.wikipedia.org/wiki/Sobel_operator>`_.
  50. .. code-block:: glsl
  51. shader_type canvas_item;
  52. void fragment() {
  53. vec3 col = -8.0 * texture(TEXTURE, UV).xyz;
  54. col += texture(TEXTURE, UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz;
  55. col += texture(TEXTURE, UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz;
  56. col += texture(TEXTURE, UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz;
  57. col += texture(TEXTURE, UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz;
  58. col += texture(TEXTURE, UV + SCREEN_PIXEL_SIZE.xy).xyz;
  59. col += texture(TEXTURE, UV - SCREEN_PIXEL_SIZE.xy).xyz;
  60. col += texture(TEXTURE, UV + vec2(-SCREEN_PIXEL_SIZE.x, SCREEN_PIXEL_SIZE.y)).xyz;
  61. col += texture(TEXTURE, UV + vec2(SCREEN_PIXEL_SIZE.x, -SCREEN_PIXEL_SIZE.y)).xyz;
  62. COLOR.xyz = col;
  63. }
  64. .. note::
  65. The Sobel filter reads pixels in a 9x9 grid around the current pixel and adds them together, using weight.
  66. What makes it interesting is that it assigns weights to each pixel; +1 for each of the eight around the
  67. center and -8 for the center pixel. The choice of weights is called a "kernel". You can use different
  68. kernels to create edge detection filters, outlines, and all sorts of effects.
  69. .. image:: img/post_outline.png
  70. Multi-pass post-processing
  71. --------------------------
  72. Some post-processing effects like blur are resource intensive. If you break them down in multiple passes
  73. however, you can make them run a lot faster. In a multipass material, each pass takes the result from the
  74. previous pass as an input and processes it.
  75. To make a multi-pass post-processing shader, you stack ``Viewport`` nodes. In the example above, you
  76. rendered the content of one ``Viewport`` object into the root ``Viewport``, through a ``ViewportContainer``
  77. node. You can do the same thing for a multi-pass shader by rendering the content of one ``Viewport`` into
  78. another and then rendering the last ``Viewport`` into the root ``Viewport``.
  79. Your scene hierarchy will look something like this:
  80. .. image:: img/post_hierarchy2.png
  81. Godot will render the bottom ``Viewport`` node first. So if the order of the passes matters for your
  82. shaders, make sure that you assign the shader you want to apply first to the lowest ``ViewportContainer`` in
  83. the tree.
  84. .. note::
  85. You can also render your Viewports separately without nesting them like this. You just
  86. need to use two Viewports and to render them one after the other.
  87. Apart from the node structure, the steps are the same as with the single-pass post-processing shader.
  88. As an example, you could write a full screen Gaussian blur effect by attaching the following pieces of code
  89. to each of the :ref:`ViewportContainers <class_ViewportContainer>`. The order in which you apply the shaders
  90. does not matter:
  91. .. code-block:: glsl
  92. shader_type canvas_item;
  93. // Blurs the screen in the X-direction.
  94. void fragment() {
  95. vec3 col = texture(TEXTURE, UV).xyz * 0.16;
  96. col += texture(TEXTURE, UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
  97. col += texture(TEXTURE, UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
  98. col += texture(TEXTURE, UV + vec2(2.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
  99. col += texture(TEXTURE, UV + vec2(2.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
  100. col += texture(TEXTURE, UV + vec2(3.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
  101. col += texture(TEXTURE, UV + vec2(3.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
  102. col += texture(TEXTURE, UV + vec2(4.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
  103. col += texture(TEXTURE, UV + vec2(4.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
  104. COLOR.xyz = col;
  105. }
  106. .. code-block:: glsl
  107. shader_type canvas_item;
  108. // Blurs the screen in the Y-direction.
  109. void fragment() {
  110. vec3 col = texture(TEXTURE, UV).xyz * 0.16;
  111. col += texture(TEXTURE, UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
  112. col += texture(TEXTURE, UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
  113. col += texture(TEXTURE, UV + vec2(0.0, 2.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
  114. col += texture(TEXTURE, UV + vec2(0.0, 2.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
  115. col += texture(TEXTURE, UV + vec2(0.0, 3.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
  116. col += texture(TEXTURE, UV + vec2(0.0, 3.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
  117. col += texture(TEXTURE, UV + vec2(0.0, 4.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
  118. col += texture(TEXTURE, UV + vec2(0.0, 4.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
  119. COLOR.xyz = col;
  120. }
  121. Using the above code, you should end up with a full screen blur effect like below.
  122. .. image:: img/post_blur.png
  123. For more information on how ``Viewport`` nodes work, see the :ref:`Viewports Tutorial <doc_viewports>`.