screen-reading_shaders.rst 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. .. _doc_screen-reading_shaders:
  2. Screen-reading shaders
  3. ======================
  4. Introduction
  5. ~~~~~~~~~~~~
  6. Very often it is desired to make a shader that reads from the same
  7. screen it's writing to. 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 sort 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 makes 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 case of 3D).
  18. It takes as parameter 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 2D 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 it's 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. SCREEN_TEXTURE example
  31. ~~~~~~~~~~~~~~~~~~~~~~
  32. SCREEN_TEXTURE can be used for a lot of things. There is a
  33. special demo for *Screen Space Shaders*, that you can download to see
  34. and learn. One example is a simple shader to adjust brightness, contrast
  35. and saturation:
  36. .. code-block:: glsl
  37. shader_type canvas_item;
  38. uniform float brightness = 1.0;
  39. uniform float contrast = 1.0;
  40. uniform float saturation = 1.0;
  41. void fragment() {
  42. vec3 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0).rgb;
  43. c.rgb = mix(vec3(0.0), c.rgb, brightness);
  44. c.rgb = mix(vec3(0.5), c.rgb, contrast);
  45. c.rgb = mix(vec3(dot(vec3(1.0), c.rgb)*0.33333), c.rgb, saturation);
  46. COLOR.rgb = c;
  47. }
  48. Behind the scenes
  49. ~~~~~~~~~~~~~~~~~
  50. While this seems magical, it's not. The SCREEN_TEXTURE built-in, when
  51. first found in a node that is about to be drawn, does a full-screen
  52. copy to a back-buffer. Subsequent nodes that use it in
  53. shaders will not have the screen copied for them, because this ends up
  54. being inefficient.
  55. As a result, if shaders that use SCREEN_TEXTURE overlap, the second one
  56. will not use the result of the first one, resulting in unexpected
  57. visuals:
  58. .. image:: img/texscreen_demo1.png
  59. In the above image, the second sphere (top right) is using the same
  60. source for SCREEN_TEXTURE as the first one below, so the first one
  61. "disappears", or is not visible.
  62. In 3D, this is unavoidable because copying happens when opaque rendering
  63. completes.
  64. In 2D this can be corrected via the :ref:`BackBufferCopy <class_BackBufferCopy>`
  65. node, which can be instantiated between both spheres. BackBufferCopy can work by
  66. either specifying a screen region or the whole screen:
  67. .. image:: img/texscreen_bbc.png
  68. With correct back-buffer copying, the two spheres blend correctly:
  69. .. image:: img/texscreen_demo2.png
  70. Back-buffer logic
  71. ~~~~~~~~~~~~~~~~~
  72. So, to make it clearer, here's how the backbuffer copying logic works in
  73. Godot:
  74. - If a node uses the SCREEN_TEXTURE, the entire screen is copied to the
  75. back buffer before drawing that node. This only happens the first
  76. time, subsequent nodes do not trigger this.
  77. - If a BackBufferCopy node was processed before the situation in the
  78. point above (even if SCREEN_TEXTURE was not used), this behavior
  79. described in the point above does not happen. In other words,
  80. automatic copying of the entire screen only happens if SCREEN_TEXTURE is
  81. used in a node for the first time and no BackBufferCopy node (not
  82. disabled) was found before in tree-order.
  83. - BackBufferCopy can copy either the entire screen or a region. If set
  84. to only a region (not the whole screen) and your shader uses pixels
  85. not in the region copied, the result of that read is undefined
  86. (most likely garbage from previous frames). In other words, it's
  87. possible to use BackBufferCopy to copy back a region of the screen
  88. and then use SCREEN_TEXTURE on a different region. Avoid this behavior!
  89. DEPTH_TEXTURE
  90. ~~~~~~~~~~~~~
  91. For 3D Shaders, it's also possible to access the screen depth buffer. For this,
  92. the DEPTH_TEXTURE built-in is used. This texture is not linear, it must be
  93. converted via the inverse projection matrix.
  94. The following code retrieves the 3D position below the pixel being drawn:
  95. .. code-block:: glsl
  96. void fragment() {
  97. float depth = textureLod(DEPTH_TEXTURE,SCREEN_UV,0.0).r;
  98. vec4 upos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV*2.0-1.0,depth*2.0-1.0,1.0);
  99. vec3 pixel_position = upos.xyz/upos.w;
  100. }