screen-reading_shaders.rst 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. .. _doc_screen-reading_shaders:
  2. Screen-reading shaders
  3. ======================
  4. Introduction
  5. ~~~~~~~~~~~~
  6. It is often desired to make a shader that reads from the same
  7. screen to which it's writing. 3D APIs, such as OpenGL or DirectX, make this very
  8. difficult because of internal hardware limitations. GPUs are extremely
  9. parallel, so reading and writing causes all sorts of cache and coherency
  10. problems. As a result, not even the most modern hardware supports this
  11. properly.
  12. The workaround is to make a copy of the screen, or a part of the screen,
  13. to a back-buffer and then read from it while drawing. Godot provides a
  14. few tools that make this process easy.
  15. Screen texture
  16. ~~~~~~~~~~~~~~
  17. Godot :ref:`doc_shading_language` has a special texture to access the already
  18. rendered contents of the screen. It is used by specifying a hint when declaring
  19. a ``sampler2D`` uniform: ``hint_screen_texture``. A special built-in varying
  20. ``SCREEN_UV`` can be used to obtain the UV relative to the screen for the current
  21. fragment. As a result, this canvas_item fragment shader results in an invisible
  22. object, because it only shows what lies behind:
  23. .. code-block:: glsl
  24. shader_type canvas_item;
  25. uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
  26. void fragment() {
  27. COLOR = textureLod(screen_texture, SCREEN_UV, 0.0);
  28. }
  29. ``textureLod`` is used here as we only want to read from the bottom mipmap. If
  30. you want to read from a blurred version of the texture instead, you can increase
  31. the third argument to ``textureLod`` and change the hint ``filter_nearest`` to
  32. ``filter_nearest_mipmap`` (or any other filter with mipmaps enabled). If using a
  33. filter with mipmaps, Godot will automatically calculate the blurred texture for
  34. you.
  35. .. warning::
  36. If the filter mode is not changed to a filter mode that contains ``mipmap`` in its name,
  37. ``textureLod`` with an LOD parameter greater than ``0.0`` will have the same appearance
  38. as with the ``0.0`` LOD parameter.
  39. Screen texture example
  40. ~~~~~~~~~~~~~~~~~~~~~~
  41. The screen texture can be used for many things. There is a
  42. special demo for *Screen Space Shaders*, that you can download to see
  43. and learn. One example is a simple shader to adjust brightness, contrast
  44. and saturation:
  45. .. code-block:: glsl
  46. shader_type canvas_item;
  47. uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
  48. uniform float brightness = 1.0;
  49. uniform float contrast = 1.0;
  50. uniform float saturation = 1.0;
  51. void fragment() {
  52. vec3 c = textureLod(screen_texture, SCREEN_UV, 0.0).rgb;
  53. c.rgb = mix(vec3(0.0), c.rgb, brightness);
  54. c.rgb = mix(vec3(0.5), c.rgb, contrast);
  55. c.rgb = mix(vec3(dot(vec3(1.0), c.rgb) * 0.33333), c.rgb, saturation);
  56. COLOR.rgb = c;
  57. }
  58. Behind the scenes
  59. ~~~~~~~~~~~~~~~~~
  60. While this seems magical, it's not. In 2D, when ``hint_screen_texture`` is first
  61. found in a node that is about to be drawn, Godot does a full-screen copy to a
  62. back-buffer. Subsequent nodes that use it in shaders will not have the screen
  63. copied for them, because this ends up being inefficient. In 3D, the screen is
  64. copied after the opaque geometry pass, but before the transparent geometry pass,
  65. so transparent objects will not be captured in the screen texture.
  66. As a result, in 2D, if shaders that use ``hint_screen_texture`` overlap, the
  67. second one will not use the result of the first one, resulting in unexpected
  68. visuals:
  69. .. image:: img/texscreen_demo1.png
  70. In the above image, the second sphere (top right) is using the same source for
  71. the screen texture as the first one below, so the first one "disappears", or is
  72. not visible.
  73. In 2D, this can be corrected via the :ref:`BackBufferCopy <class_BackBufferCopy>`
  74. node, which can be instantiated between both spheres. BackBufferCopy can work by
  75. either specifying a screen region or the whole screen:
  76. .. image:: img/texscreen_bbc.png
  77. With correct back-buffer copying, the two spheres blend correctly:
  78. .. image:: img/texscreen_demo2.png
  79. .. warning::
  80. In 3D, materials that use ``hint_screen_texture`` are considered transparent themselves and
  81. will not appear in the resulting screen texture of other materials.
  82. If you plan to instance a scene that uses a material with ``hint_screen_texture``,
  83. you will need to use a BackBufferCopy node.
  84. In 3D, there is less flexibility to solve this particular issue because the
  85. screen texture is only captured once. Be careful when using the screen texture
  86. in 3D as it won't capture transparent objects and may capture some opaque
  87. objects that are in front of the object using the screen texture.
  88. You can reproduce the back-buffer logic in 3D by creating a :ref:`Viewport <class_Viewport>`
  89. with a camera in the same position as your object, and then use the
  90. :ref:`Viewport's <class_Viewport>` texture instead of the screen texture.
  91. Back-buffer logic
  92. ~~~~~~~~~~~~~~~~~
  93. So, to make it clearer, here's how the backbuffer copying logic works in 2D in
  94. Godot:
  95. - If a node uses ``hint_screen_texture``, the entire screen is copied to the
  96. back buffer before drawing that node. This only happens the first
  97. time; subsequent nodes do not trigger this.
  98. - If a BackBufferCopy node was processed before the situation in the point
  99. above (even if ``hint_screen_texture`` was not used), the behavior described
  100. in the point above does not happen. In other words, automatic copying of the
  101. entire screen only happens if ``hint_screen_texture`` is used in a node for
  102. the first time and no BackBufferCopy node (not disabled) was found before in
  103. tree-order.
  104. - BackBufferCopy can copy either the entire screen or a region. If set to only
  105. a region (not the whole screen) and your shader uses pixels not in the region
  106. copied, the result of that read is undefined (most likely garbage from
  107. previous frames). In other words, it's possible to use BackBufferCopy to copy
  108. back a region of the screen and then read the screen texture from a different
  109. region. Avoid this behavior!
  110. Depth texture
  111. ~~~~~~~~~~~~~
  112. For 3D shaders, it's also possible to access the screen depth buffer. For this,
  113. the ``hint_depth_texture`` hint is used. This texture is not linear; it must be
  114. converted using the inverse projection matrix.
  115. The following code retrieves the 3D position below the pixel being drawn:
  116. .. code-block:: glsl
  117. uniform sampler2D depth_texture : hint_depth_texture, repeat_disable, filter_nearest;
  118. void fragment() {
  119. float depth = textureLod(depth_texture, SCREEN_UV, 0.0).r;
  120. vec4 upos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, depth, 1.0);
  121. vec3 pixel_position = upos.xyz / upos.w;
  122. }
  123. Normal-roughness texture
  124. ~~~~~~~~~~~~~~~~~~~~~~~~
  125. .. note::
  126. Normal-roughness texture is only supported in the Forward+ rendering method,
  127. not Mobile or Compatibility.
  128. Similarly, the normal-roughness texture can be used to read the normals and
  129. roughness of objects rendered in the depth prepass. The normal is stored in the
  130. ``.xyz`` channels (mapped to the 0-1 range) while the roughness is stored in the
  131. ``.w`` channel.
  132. .. code-block:: glsl
  133. uniform sampler2D normal_roughness_texture : hint_normal_roughness_texture, repeat_disable, filter_nearest;
  134. void fragment() {
  135. float screen_roughness = texture(normal_roughness_texture, SCREEN_UV).w;
  136. vec3 screen_normal = texture(normal_roughness_texture, SCREEN_UV).xyz;
  137. screen_normal = screen_normal * 2.0 - 1.0;
  138. Redefining screen textures
  139. ~~~~~~~~~~~~~~~~~~~~~~~~~~
  140. The screen texture hints (``hint_screen_texture``, ``hint_depth_texture``, and
  141. ``hint_normal_roughness_texture``) can be used with multiple uniforms. For
  142. example, you may want to read from the texture multiple times with a different
  143. repeat flag or filter flag.
  144. The following example shows a shader that reads the screen space normal with
  145. linear filtering, but reads the screen space roughness using nearest neighbor
  146. filtering.
  147. .. code-block:: glsl
  148. uniform sampler2D normal_roughness_texture : hint_normal_roughness_texture, repeat_disable, filter_nearest;
  149. uniform sampler2D normal_roughness_texture2 : hint_normal_roughness_texture, repeat_enable, filter_linear;
  150. void fragment() {
  151. float screen_roughness = texture(normal_roughness_texture, SCREEN_UV).w;
  152. vec3 screen_normal = texture(normal_roughness_texture2, SCREEN_UV).xyz;
  153. screen_normal = screen_normal * 2.0 - 1.0;