From 8bf41b6ef1fcfb6df2fd8ebb47342bd9b1b2947c Mon Sep 17 00:00:00 2001 From: clayjohn Date: Thu, 5 Jan 2023 13:35:13 -0800 Subject: [PATCH] Document how to use screen space textures using uniform hints --- tutorials/shaders/custom_postprocessing.rst | 51 +++--- tutorials/shaders/screen-reading_shaders.rst | 159 +++++++++++------- .../shader_reference/canvas_item_shader.rst | 7 +- .../shader_reference/shading_language.rst | 6 + .../shader_reference/spatial_shader.rst | 6 +- tutorials/shaders/shaders_style_guide.rst | 4 +- 6 files changed, 145 insertions(+), 88 deletions(-) diff --git a/tutorials/shaders/custom_postprocessing.rst b/tutorials/shaders/custom_postprocessing.rst index 97e61af3f..fe907a51c 100644 --- a/tutorials/shaders/custom_postprocessing.rst +++ b/tutorials/shaders/custom_postprocessing.rst @@ -32,7 +32,8 @@ Your scene tree will look something like this: Another more efficient method is to use a :ref:`BackBufferCopy ` to copy a region of the screen to a buffer and to - access it in a shader script through ``texture(SCREEN_TEXTURE, ...)``. + access it in a shader script through a ``sampler2D`` using + ``hint_screen_texture``. .. note:: @@ -46,8 +47,9 @@ For this demo, we will use this :ref:`Sprite ` of a sheep. .. image:: img/post_example1.png Assign a new :ref:`Shader ` to the ``ColorRect``'s -``ShaderMaterial``. You can access the frame's texture and UV with the built in -``SCREEN_TEXTURE`` and ``SCREEN_UV`` uniforms. +``ShaderMaterial``. You can access the frame's texture and UV with a +``sampler2D`` using ``hint_screen_texture`` and the built in ``SCREEN_UV`` +uniforms. Copy the following code to your shader. The code below is a hex pixelization shader by `arlez80 `_, @@ -57,6 +59,7 @@ shader by `arlez80 `_, shader_type canvas_item; uniform vec2 size = vec2(32.0, 28.0); + uniform sampler2D screen_texture : hint_screen_texture, repeat_disabled, filter_nearest; void fragment() { vec2 norm_size = size * SCREEN_PIXEL_SIZE; @@ -74,7 +77,7 @@ shader by `arlez80 `_, float(half)), float(norm_uv.y < 0.3333333) * float(norm_uv.y / 0.3333333 < (abs(norm_uv.x - 0.5) * 2.0))); - COLOR = textureLod(SCREEN_TEXTURE, center_uv, 0.0); + COLOR = textureLod(screen_texture, center_uv, 0.0); } The sheep will look something like this: @@ -109,17 +112,19 @@ matter. shader_type canvas_item; + uniform sampler2D screen_texture : hint_screen_texture, repeat_disabled, filter_nearest; + // Blurs the screen in the X-direction. void fragment() { - vec3 col = texture(SCREEN_TEXTURE, SCREEN_UV).xyz * 0.16; - col += texture(SCREEN_TEXTURE, SCREEN_UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15; - col += texture(SCREEN_TEXTURE, SCREEN_UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15; - col += texture(SCREEN_TEXTURE, SCREEN_UV + vec2(2.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12; - col += texture(SCREEN_TEXTURE, SCREEN_UV + vec2(2.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12; - col += texture(SCREEN_TEXTURE, SCREEN_UV + vec2(3.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09; - col += texture(SCREEN_TEXTURE, SCREEN_UV + vec2(3.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09; - col += texture(SCREEN_TEXTURE, SCREEN_UV + vec2(4.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05; - col += texture(SCREEN_TEXTURE, SCREEN_UV + vec2(4.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05; + vec3 col = texture(screen_texture, SCREEN_UV).xyz * 0.16; + col += texture(screen_texture, SCREEN_UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15; + col += texture(screen_texture, SCREEN_UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15; + col += texture(screen_texture, SCREEN_UV + vec2(2.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12; + col += texture(screen_texture, SCREEN_UV + vec2(2.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12; + col += texture(screen_texture, SCREEN_UV + vec2(3.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09; + col += texture(screen_texture, SCREEN_UV + vec2(3.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09; + col += texture(screen_texture, SCREEN_UV + vec2(4.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05; + col += texture(screen_texture, SCREEN_UV + vec2(4.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05; COLOR.xyz = col; } @@ -127,17 +132,19 @@ matter. shader_type canvas_item; + uniform sampler2D screen_texture : hint_screen_texture, repeat_disabled, filter_nearest; + // Blurs the screen in the Y-direction. void fragment() { - vec3 col = texture(SCREEN_TEXTURE, SCREEN_UV).xyz * 0.16; - col += texture(SCREEN_TEXTURE, SCREEN_UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz * 0.15; - col += texture(SCREEN_TEXTURE, SCREEN_UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz * 0.15; - col += texture(SCREEN_TEXTURE, SCREEN_UV + vec2(0.0, 2.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.12; - col += texture(SCREEN_TEXTURE, SCREEN_UV + vec2(0.0, 2.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.12; - col += texture(SCREEN_TEXTURE, SCREEN_UV + vec2(0.0, 3.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.09; - col += texture(SCREEN_TEXTURE, SCREEN_UV + vec2(0.0, 3.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.09; - col += texture(SCREEN_TEXTURE, SCREEN_UV + vec2(0.0, 4.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.05; - col += texture(SCREEN_TEXTURE, SCREEN_UV + vec2(0.0, 4.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.05; + vec3 col = texture(screen_texture, SCREEN_UV).xyz * 0.16; + col += texture(screen_texture, SCREEN_UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz * 0.15; + col += texture(screen_texture, SCREEN_UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz * 0.15; + col += texture(screen_texture, SCREEN_UV + vec2(0.0, 2.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.12; + col += texture(screen_texture, SCREEN_UV + vec2(0.0, 2.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.12; + col += texture(screen_texture, SCREEN_UV + vec2(0.0, 3.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.09; + col += texture(screen_texture, SCREEN_UV + vec2(0.0, 3.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.09; + col += texture(screen_texture, SCREEN_UV + vec2(0.0, 4.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.05; + col += texture(screen_texture, SCREEN_UV + vec2(0.0, 4.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.05; COLOR.xyz = col; } diff --git a/tutorials/shaders/screen-reading_shaders.rst b/tutorials/shaders/screen-reading_shaders.rst index 75e4a0c93..8c27ce7e1 100644 --- a/tutorials/shaders/screen-reading_shaders.rst +++ b/tutorials/shaders/screen-reading_shaders.rst @@ -17,36 +17,37 @@ The workaround is to make a copy of the screen, or a part of the screen, to a back-buffer and then read from it while drawing. Godot provides a few tools that make this process easy. -SCREEN_TEXTURE built-in texture -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Screen texture +~~~~~~~~~~~~~~ -Godot :ref:`doc_shading_language` has a special texture, ``SCREEN_TEXTURE`` (and ``DEPTH_TEXTURE`` for depth, in the case of 3D). -It takes as argument the UV of the screen and returns a vec3 RGB with the color. A -special built-in varying: SCREEN_UV can be used to obtain the UV for -the current fragment. As a result, this canvas_item fragment shader results in an invisible object, -because it only shows what lies behind: +Godot :ref:`doc_shading_language` has a special texture to access the already +rendered contents of the screen. It is used by specifying a hint when declaring +a ``sampler2D`` uniform: ``hint_screen_texture``. A special built-in varying +``SCREEN_UV`` can be used to obtain the UV relative to the screen for the current +fragment. As a result, this canvas_item fragment shader results in an invisible +object, because it only shows what lies behind: .. code-block:: glsl + shader_type canvas_item; + + uniform sampler2D screen_texture : hint_screen_texture, repeat_disabled, filter_nearest; + void fragment() { - COLOR = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0); + COLOR = textureLod(screen_texture, SCREEN_UV, 0.0); } -The reason why textureLod must be used is because, when Godot copies back -a chunk of the screen, it also does an efficient separable gaussian blur to its mipmaps. +``textureLod`` is used here as we only want to read from the bottom mipmap. If +you want to read from a blurred version of the texture instead, you can increase +the third argument to ``textureLod`` and change the hint ``filter_nearest`` to +``filter_nearest_mipmap`` (or any other filter with mipmaps enabled). If using a +filter with mipmaps, Godot will automatically calculate the blurred texture for +you. -This allows for not only reading from the screen, but reading from it with different amounts -of blur at no cost. - -.. note:: - - Mipmaps are not generated in GLES2 due to poor performance and compatibility with older - devices. - -SCREEN_TEXTURE example +Screen texture example ~~~~~~~~~~~~~~~~~~~~~~ -``SCREEN_TEXTURE`` can be used for many things. There is a +The screen texture can be used for many things. There is a special demo for *Screen Space Shaders*, that you can download to see and learn. One example is a simple shader to adjust brightness, contrast and saturation: @@ -55,12 +56,14 @@ and saturation: shader_type canvas_item; + uniform sampler2D screen_texture : hint_screen_texture, repeat_disabled, filter_nearest; + uniform float brightness = 1.0; uniform float contrast = 1.0; uniform float saturation = 1.0; void fragment() { - vec3 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0).rgb; + vec3 c = textureLod(screen_texture, SCREEN_UV, 0.0).rgb; c.rgb = mix(vec3(0.0), c.rgb, brightness); c.rgb = mix(vec3(0.5), c.rgb, contrast); @@ -72,23 +75,22 @@ and saturation: Behind the scenes ~~~~~~~~~~~~~~~~~ -While this seems magical, it's not. In 2D, the ``SCREEN_TEXTURE`` built-in, when -first found in a node that is about to be drawn, does a full-screen -copy to a back-buffer. Subsequent nodes that use it in -shaders will not have the screen copied for them, because this ends up -being inefficient. In 3D, the screen is copied after the opaque geometry pass, -but before the transparent geometry pass, so transparent objects will not be -captured in the ``SCREEN_TEXTURE``. +While this seems magical, it's not. In 2D, when ``hint_screen_texture`` is first +found in a node that is about to be drawn, Godot does a full-screen copy to a +back-buffer. Subsequent nodes that use it in shaders will not have the screen +copied for them, because this ends up being inefficient. In 3D, the screen is +copied after the opaque geometry pass, but before the transparent geometry pass, +so transparent objects will not be captured in the screen texture. -As a result, in 2D, if shaders that use ``SCREEN_TEXTURE`` overlap, the second one -will not use the result of the first one, resulting in unexpected +As a result, in 2D, if shaders that use ``hint_screen_texture`` overlap, the +second one will not use the result of the first one, resulting in unexpected visuals: .. image:: img/texscreen_demo1.png -In the above image, the second sphere (top right) is using the same -source for ``SCREEN_TEXTURE`` as the first one below, so the first one -"disappears", or is not visible. +In the above image, the second sphere (top right) is using the same source for +the screen texture as the first one below, so the first one "disappears", or is +not visible. In 2D, this can be corrected via the :ref:`BackBufferCopy ` node, which can be instantiated between both spheres. BackBufferCopy can work by @@ -102,56 +104,97 @@ With correct back-buffer copying, the two spheres blend correctly: .. warning: - Materials that use ``SCREEN_TEXTURE`` are considered transparent themselves and - will not appear in the resulting ``SCREEN_TEXTURE`` of other materials. - If you plan to instance a scene that uses a material with ``SCREEN_TEXTURE``, + In 3D, materials that use ``hint_screen_texture`` are considered transparent themselves and + will not appear in the resulting screen texture of other materials. + If you plan to instance a scene that uses a material with ``hint_screen_texture``, you will need to use a BackBufferCopy node. In 3D, there is less flexibility to solve this particular issue because the -``SCREEN_TEXTURE`` is only captured once. Be careful when using -``SCREEN_TEXTURE`` in 3D as it won't capture transparent objects and may capture -some opaque objects that are in front of the object. +screen texture is only captured once. Be careful when using the screen texture +in 3D as it won't capture transparent objects and may capture some opaque +objects that are in front of the object using the screen texture. You can reproduce the back-buffer logic in 3D by creating a :ref:`Viewport ` with a camera in the same position as your object, and then use the -:ref:`Viewport's ` texture instead of ``SCREEN_TEXTURE``. +:ref:`Viewport's ` texture instead of the screen texture. Back-buffer logic ~~~~~~~~~~~~~~~~~ -So, to make it clearer, here's how the backbuffer copying logic works in +So, to make it clearer, here's how the backbuffer copying logic works in 2D in Godot: -- If a node uses the ``SCREEN_TEXTURE``, the entire screen is copied to the +- If a node uses ``hint_screen_texture``, the entire screen is copied to the back buffer before drawing that node. This only happens the first time; subsequent nodes do not trigger this. -- If a BackBufferCopy node was processed before the situation in the - point above (even if ``SCREEN_TEXTURE`` was not used), the behavior - described in the point above does not happen. In other words, - automatic copying of the entire screen only happens if ``SCREEN_TEXTURE`` is - used in a node for the first time and no BackBufferCopy node (not - disabled) was found before in tree-order. -- BackBufferCopy can copy either the entire screen or a region. If set - to only a region (not the whole screen) and your shader uses pixels - not in the region copied, the result of that read is undefined - (most likely garbage from previous frames). In other words, it's - possible to use BackBufferCopy to copy back a region of the screen - and then use ``SCREEN_TEXTURE`` on a different region. Avoid this behavior! +- If a BackBufferCopy node was processed before the situation in the point + above (even if ``hint_screen_texture`` was not used), the behavior described + in the point above does not happen. In other words, automatic copying of the + entire screen only happens if ``hint_screen_texture`` is used in a node for + the first time and no BackBufferCopy node (not disabled) was found before in + tree-order. +- BackBufferCopy can copy either the entire screen or a region. If set to only + a region (not the whole screen) and your shader uses pixels not in the region + copied, the result of that read is undefined (most likely garbage from + previous frames). In other words, it's possible to use BackBufferCopy to copy + back a region of the screen and then read the screen texture from a different + region. Avoid this behavior! -DEPTH_TEXTURE +Depth texture ~~~~~~~~~~~~~ For 3D shaders, it's also possible to access the screen depth buffer. For this, -the ``DEPTH_TEXTURE`` built-in is used. This texture is not linear; it must be -converted via the inverse projection matrix. +the ``hint_depth_texture`` hint is used. This texture is not linear; it must be +converted using the inverse projection matrix. The following code retrieves the 3D position below the pixel being drawn: .. code-block:: glsl + uniform sampler2D depth_texture : hint_depth_texture, repeat_disabled, filter_nearest; + void fragment() { - float depth = textureLod(DEPTH_TEXTURE, SCREEN_UV, 0.0).r; + float depth = textureLod(depth_texture, SCREEN_UV, 0.0).r; vec4 upos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, depth, 1.0); vec3 pixel_position = upos.xyz / upos.w; } + +Normal-roughness texture +~~~~~~~~~~~~~~~~~~~~~~~~ + +Similarly, the normal-roughness texture can be used to read the normals and +roughness of objects rendered in the depth prepass. The normal is stored in the +``.xyz`` channels (mapped to the 0-1 range) while the roughness is stored in the +``.w`` channel. + +.. code-block:: glsl + + uniform sampler2D normal_roughness_texture : hint_normal_roughness_texture, repeat_disabled, filter_nearest; + + void fragment() { + float screen_roughness = texture(normal_roughness_texture, SCREEN_UV).w; + vec3 screen_normal = texture(normal_roughness_texture, SCREEN_UV).xyz; + screen_normal = screen_normal * 2.0 - 1.0; + +Redefining screen textures +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The screen texture hints (``hint_screen_texture``, ``hint_depth_texture``, and +``hint_normal_roughness_texture``) can be used with multiple uniforms. For +example, you may want to read from the texture multiple times with a different +repeat flag or filter flag. + +The following example shows a shader that reads the screen space normal with +linear filtering, but reads the screen space roughness using nearest neighbor +filtering. + +.. code-block:: glsl + + uniform sampler2D normal_roughness_texture : hint_normal_roughness_texture, repeat_disabled, filter_nearest; + uniform sampler2D normal_roughness_texture2 : hint_normal_roughness_texture, repeat_enabled, filter_linear; + + void fragment() { + float screen_roughness = texture(normal_roughness_texture, SCREEN_UV).w; + vec3 screen_normal = texture(normal_roughness_texture2, SCREEN_UV).xyz; + screen_normal = screen_normal * 2.0 - 1.0; diff --git a/tutorials/shaders/shader_reference/canvas_item_shader.rst b/tutorials/shaders/shader_reference/canvas_item_shader.rst index 9ac4644d6..175b84847 100644 --- a/tutorials/shaders/shader_reference/canvas_item_shader.rst +++ b/tutorials/shaders/shader_reference/canvas_item_shader.rst @@ -172,9 +172,10 @@ it to the ``NORMALMAP`` property. Godot will handle converting it for use in 2D +---------------------------------------------+---------------------------------------------------------------+ | in vec2 **UV** | UV from vertex function. | +---------------------------------------------+---------------------------------------------------------------+ -| in vec2 **SCREEN_UV** | Screen UV for use with **SCREEN_TEXTURE**. | +| in vec2 **SCREEN_UV** | Screen UV coordinate for current pixel. | +---------------------------------------------+---------------------------------------------------------------+ -| sampler2D **SCREEN_TEXTURE** | Screen texture, mipmaps contain gaussian blurred versions. | +| sampler2D **SCREEN_TEXTURE** | Removed in Godot 4. Use a ``sampler2D`` with | +| | ``hint_screen_texture`` instead. | +---------------------------------------------+---------------------------------------------------------------+ | inout vec3 **NORMAL** | Normal read from **NORMAL_TEXTURE**. Writable. | +---------------------------------------------+---------------------------------------------------------------+ @@ -236,7 +237,7 @@ Below is an example of a light shader that takes a CanvasItem's normal map into | | For a Sprite2D with a texture of size 64x32px, | | | **TEXTURE_PIXEL_SIZE** = :code:`vec2(1/64, 1/32)` | +----------------------------------+------------------------------------------------------------------------------+ -| in vec2 **SCREEN_UV** | **SCREEN_TEXTURE** Coordinate (for using with screen texture). | +| in vec2 **SCREEN_UV** | Screen UV coordinate for current pixel. | +----------------------------------+------------------------------------------------------------------------------+ | in vec2 **POINT_COORD** | UV for Point Sprite. | +----------------------------------+------------------------------------------------------------------------------+ diff --git a/tutorials/shaders/shader_reference/shading_language.rst b/tutorials/shaders/shader_reference/shading_language.rst index 6502cff83..9071fb9a0 100644 --- a/tutorials/shaders/shader_reference/shading_language.rst +++ b/tutorials/shaders/shader_reference/shading_language.rst @@ -783,6 +783,12 @@ Full list of hints below: +----------------------+--------------------------------------------------+-----------------------------------------------------------------------------+ | **sampler2D** | repeat[_enable, _disable] | Enabled texture repeating. | +----------------------+--------------------------------------------------+-----------------------------------------------------------------------------+ +| **sampler2D** | hint_screen_texture | Texture is the screen texture. | ++----------------------+--------------------------------------------------+-----------------------------------------------------------------------------+ +| **sampler2D** | hint_depth_texture | Texture is the depth texture. | ++----------------------+--------------------------------------------------+-----------------------------------------------------------------------------+ +| **sampler2D** | hint_normal_roughness_texture | Texture is the normal roughness texture. | ++----------------------+--------------------------------------------------+-----------------------------------------------------------------------------+ GDScript uses different variable types than GLSL does, so when passing variables from GDScript to shaders, Godot converts the type automatically. Below is a diff --git a/tutorials/shaders/shader_reference/spatial_shader.rst b/tutorials/shaders/shader_reference/spatial_shader.rst index 538f29195..a2d249954 100644 --- a/tutorials/shaders/shader_reference/spatial_shader.rst +++ b/tutorials/shaders/shader_reference/spatial_shader.rst @@ -289,13 +289,11 @@ these properties, and if you don't write to them, Godot will optimize away the c +----------------------------------------+--------------------------------------------------------------------------------------------------+ | in int **VIEW_RIGHT** | | +----------------------------------------+--------------------------------------------------------------------------------------------------+ -| sampler2D **SCREEN_TEXTURE** | Built-in Texture for reading from the screen. Mipmaps contain increasingly blurred copies. | +| sampler2D **SCREEN_TEXTURE** | Removed in Godot 4. Use a ``sampler2D`` with ``hint_screen_texture`` instead. | +----------------------------------------+--------------------------------------------------------------------------------------------------+ | in vec2 **SCREEN_UV** | Screen UV coordinate for current pixel. | +----------------------------------------+--------------------------------------------------------------------------------------------------+ -| sampler2D **NORMAL_ROUGHNESS_TEXTURE** | | -+----------------------------------------+--------------------------------------------------------------------------------------------------+ -| sampler2D **DEPTH_TEXTURE** | Built-in Texture for reading depth from the screen. Must convert to linear using INV_PROJECTION. | +| sampler2D **DEPTH_TEXTURE** | Removed in Godot 4. Use a ``sampler2D`` with ``hint_depth_texture`` instead. | +----------------------------------------+--------------------------------------------------------------------------------------------------+ | out float **DEPTH** | Custom depth value (0..1). If ``DEPTH`` is being written to in any shader branch, then you are | | | responsible for setting the ``DEPTH`` for **all** other branches. Otherwise, the graphics API | diff --git a/tutorials/shaders/shaders_style_guide.rst b/tutorials/shaders/shaders_style_guide.rst index 26a8a7436..e36073082 100644 --- a/tutorials/shaders/shaders_style_guide.rst +++ b/tutorials/shaders/shaders_style_guide.rst @@ -36,8 +36,10 @@ Here is a complete shader example based on these guidelines: uniform float contrast = 1.5; uniform float saturation = 1.8; + uniform sampler2D screen_texture : hint_screen_texture, repeat_disabled, filter_nearest; + void fragment() { - vec3 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0).rgb; + vec3 c = textureLod(screen_texture, SCREEN_UV, 0.0).rgb; c.rgb = mix(vec3(0.0), c.rgb, brightness); c.rgb = mix(vec3(0.5), c.rgb, contrast);