screen-reading_shaders.rst 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  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 built-in texture
  16. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  17. Godot :ref:`doc_shading_language` has a special texture, ``SCREEN_TEXTURE`` (and ``DEPTH_TEXTURE`` for depth, in the case of 3D).
  18. It takes as argument the UV of the screen and returns a vec3 RGB with the color. A
  19. special built-in varying: SCREEN_UV can be used to obtain the UV for
  20. the current fragment. As a result, this simple canvas_item fragment shader:
  21. .. code-block:: glsl
  22. void fragment() {
  23. COLOR = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0);
  24. }
  25. results in an invisible object, because it just shows what lies behind.
  26. The reason why textureLod must be used is because, when Godot copies back
  27. a chunk of the screen, it also does an efficient separatable gaussian blur to its mipmaps.
  28. This allows for not only reading from the screen, but reading from it with different amounts
  29. of blur at no cost.
  30. .. note::
  31. Mipmaps are not generated in GLES2 due to poor performance and compatibility with older
  32. devices.
  33. SCREEN_TEXTURE example
  34. ~~~~~~~~~~~~~~~~~~~~~~
  35. ``SCREEN_TEXTURE`` can be used for many things. There is a
  36. special demo for *Screen Space Shaders*, that you can download to see
  37. and learn. One example is a simple shader to adjust brightness, contrast
  38. and saturation:
  39. .. code-block:: glsl
  40. shader_type canvas_item;
  41. uniform float brightness = 1.0;
  42. uniform float contrast = 1.0;
  43. uniform float saturation = 1.0;
  44. void fragment() {
  45. vec3 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0).rgb;
  46. c.rgb = mix(vec3(0.0), c.rgb, brightness);
  47. c.rgb = mix(vec3(0.5), c.rgb, contrast);
  48. c.rgb = mix(vec3(dot(vec3(1.0), c.rgb) * 0.33333), c.rgb, saturation);
  49. COLOR.rgb = c;
  50. }
  51. Behind the scenes
  52. ~~~~~~~~~~~~~~~~~
  53. While this seems magical, it's not. In 2D, the ``SCREEN_TEXTURE`` built-in, when
  54. first found in a node that is about to be drawn, does a full-screen
  55. copy to a back-buffer. Subsequent nodes that use it in
  56. shaders will not have the screen copied for them, because this ends up
  57. being inefficient. In 3D, the screen is copied after the opaque geometry pass,
  58. but before the transparent geometry pass, so transparent objects will not be
  59. captured in the ``SCREEN_TEXTURE``.
  60. As a result, in 2D, if shaders that use ``SCREEN_TEXTURE`` overlap, the second one
  61. will not use the result of the first one, resulting in unexpected
  62. visuals:
  63. .. image:: img/texscreen_demo1.png
  64. In the above image, the second sphere (top right) is using the same
  65. source for ``SCREEN_TEXTURE`` as the first one below, so the first one
  66. "disappears", or is not visible.
  67. In 2D, this can be corrected via the :ref:`BackBufferCopy <class_BackBufferCopy>`
  68. node, which can be instantiated between both spheres. BackBufferCopy can work by
  69. either specifying a screen region or the whole screen:
  70. .. image:: img/texscreen_bbc.png
  71. With correct back-buffer copying, the two spheres blend correctly:
  72. .. image:: img/texscreen_demo2.png
  73. .. warning:
  74. Materials that use ``SCREEN_TEXTURE`` are considered transparent themselves and
  75. will not appear in the resulting ``SCREEN_TEXTURE`` of other materials.
  76. If you plan to instance a scene that uses a material with ``SCREEN_TEXTURE``,
  77. you will need to use a BackBufferCopy node.
  78. In 3D, there is less flexibility to solve this particular issue because the
  79. ``SCREEN_TEXTURE`` is only captured once. Be careful when using
  80. ``SCREEN_TEXTURE`` in 3D as it won't capture transparent objects and may capture
  81. some opaque objects that are in front of the object.
  82. You can reproduce the back-buffer logic in 3D by creating a :ref:`Viewport <class_Viewport>`
  83. with a camera in the same position as your object, and then use the
  84. :ref:`Viewport's <class_Viewport>` texture instead of ``SCREEN_TEXTURE``.
  85. Back-buffer logic
  86. ~~~~~~~~~~~~~~~~~
  87. So, to make it clearer, here's how the backbuffer copying logic works in
  88. Godot:
  89. - If a node uses the ``SCREEN_TEXTURE``, the entire screen is copied to the
  90. back buffer before drawing that node. This only happens the first
  91. time; subsequent nodes do not trigger this.
  92. - If a BackBufferCopy node was processed before the situation in the
  93. point above (even if ``SCREEN_TEXTURE`` was not used), the behavior
  94. described in the point above does not happen. In other words,
  95. automatic copying of the entire screen only happens if ``SCREEN_TEXTURE`` is
  96. used in a node for the first time and no BackBufferCopy node (not
  97. disabled) was found before in tree-order.
  98. - BackBufferCopy can copy either the entire screen or a region. If set
  99. to only a region (not the whole screen) and your shader uses pixels
  100. not in the region copied, the result of that read is undefined
  101. (most likely garbage from previous frames). In other words, it's
  102. possible to use BackBufferCopy to copy back a region of the screen
  103. and then use ``SCREEN_TEXTURE`` on a different region. Avoid this behavior!
  104. DEPTH_TEXTURE
  105. ~~~~~~~~~~~~~
  106. For 3D shaders, it's also possible to access the screen depth buffer. For this,
  107. the ``DEPTH_TEXTURE`` built-in is used. This texture is not linear; it must be
  108. converted via the inverse projection matrix.
  109. The following code retrieves the 3D position below the pixel being drawn:
  110. .. code-block:: glsl
  111. void fragment() {
  112. float depth = textureLod(DEPTH_TEXTURE, SCREEN_UV, 0.0).r;
  113. vec4 upos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
  114. vec3 pixel_position = upos.xyz / upos.w;
  115. }