From 4965cacaef876308594b96fef0515a7ab74bbb98 Mon Sep 17 00:00:00 2001 From: Nathan Lovato Date: Fri, 5 Oct 2018 17:24:20 +0900 Subject: [PATCH] Proofed the custom_postprocessing tutorial --- tutorials/viewports/custom_postprocessing.rst | 161 +++++++++--------- 1 file changed, 77 insertions(+), 84 deletions(-) diff --git a/tutorials/viewports/custom_postprocessing.rst b/tutorials/viewports/custom_postprocessing.rst index 753067695..e098b0c84 100644 --- a/tutorials/viewports/custom_postprocessing.rst +++ b/tutorials/viewports/custom_postprocessing.rst @@ -1,81 +1,76 @@ .. _doc_custom_postprocessing: -Custom post-processing +Custom post-processing ====================== Introduction ------------ -Godot provides many post-processing effects out of the box including, Bloom, DOF, and SSAO. -Sometimes you will want to write your own post-processing effect. This can be done easily -in Godot by rendering your scene into a :ref:`Viewport ` and then rendering the -:ref:`Viewport's ` :ref:`texture ` to a full screen quad. +Godot provides many post-processing effects out of the box including, Bloom, DOF, and SSAO. Sometimes you +want to write your own custom effect. Here's how you can do so. -.. note:: At the time of writing Godot does not support rendering to multiple buffers at the same - time so the post-processing shader will not have access to normals, or anything else. - You only have access to the fullscreen color texture. +Post-processing effects are shaders applied to a frame after Godot rendered it. You first want to render +your scene into a :ref:`Viewport `, then render the ``Viewport`` +inside a :ref:`ViewportTexture ` and show it on the screen. -Screen reading shaders ----------------------- +The easiest way to implement a custom post-processing shader is to use Godot's built-in ability to read from +the screen texture. If you're not familiar with this, you should read the :ref:`Screen Reading Shaders +Tutorial ` first. -Before starting, a brief note on screen reading shaders is in order. +.. note:: -The easiest way to do a custom post-processing shader is to use Godot's built-in ability to read -from the screen texture. In order to take advantage of this you simply render an object over the -entire scene (a sprite in 2D or a quad in 3D). And use ``texture(SCREEN_TEXTURE, SCREEN_UV)`` -in the shader. Multi-pass post-processing shaders can even be used with a -:ref:`BackBufferCopy ` node. For more information on how do to this see the -:ref:`Screen Reading Shaders Tutorial `. + At the time of writing Godot does not support rendering to multiple buffers at the same time. Your + post-processing shader will not have access to normals or other render passes. You only have + access to the rendered frame. -Single pass ------------ +Single pass post-processing +--------------------------- -The primary benefit to using a :ref:`Viewport ` is that you have full control over -the rendering of the scene (including how frequently to update it) and you can take advantage of -using the :ref:`ViewportContainer ` to render 3D objects within a 2D root scene. +You will need a ``Viewport`` to render your scene to, and a scene to render your +``Viewport`` on the screen. You can use a :ref:`ViewportContainer +` to display your ``Viewport`` on the entire screen or inside +another :ref:`Control ` node. -First we need a :ref:`Viewport ` to render our scene to. Then we need a scene that can -render our :ref:`Viewport `. This can either be a 2D or 3D scene. The benefit -of a 2D scene is it is easy to set up a fullscreen :ref:`ViewportContainer ` -to display our :ref:`Viewport `. +.. note:: -For this demo let's use a :ref:`Node2D ` with a -:ref:`ViewportContainer ` and finally a :ref:`Viewport `. + Rendering using a ``Viewport`` gives you control over the + how the scene render, including the framerate, and you can use the + ``ViewportContainer`` to render 3D objects in a 2D scene. -Your hierarchy should look like this: +For this demo we will use a :ref:`Node2D ` with a ``ViewportContainer`` and finally a +``Viewport``. Your Node tab should look like this: .. image:: img/post_hierarchy1.png -Inside the :ref:`Viewport ` you can have whatever you want. This will contain -your main scene. For this tutorial we will use a field of random boxes. +Inside the ``Viewport`` you can have whatever you want. This will contain +your main scene. For this tutorial we will use a field of random boxes: .. image:: img/post_boxes.png -In order to take advantage of the :ref:`Viewport `, attach a material to the -:ref:`ViewportContainer `. +Add a new a :ref:`ShaderMaterial ` to the ``ViewportContainer``, and assign a new +shader resource to it. You can access your rendered ``Viewport`` with the built-in ``TEXTURE`` uniform. -The benefit of using the :ref:`ViewportContainer ` is that the -:ref:`Viewport ` can be accessed easily in the shader with the built-in -``TEXTURE`` uniform. You can choose not to use a :ref:`ViewportContainer `, -but if you do so you will need to create your own uniform in the shader and pass the -:ref:`Viewport ` texture in manually. To do so, add a uniform to the shader you use. +.. note:: -:: + You can choose not to use a ``ViewportContainer``, but if you do so you will + need to create your own uniform in the shader and pass the ``Viewport`` texture in + manually, like so: - //In Godot Shader - uniform sampler2D ViewportTexture; + :: -And you can pass the texture into the shader from GDScript like so: + // Inside the Shader + uniform sampler2D ViewportTexture; -:: + And you can pass the texture into the shader from GDScript like so: - #In Gdscript - func _ready(): - $Sprite.material.set_shader_param("ViewportTexture", $Viewport.get_texture()) + :: -Now, assuming you are using a :ref:`ViewportContainer `, add a -:ref:`ShaderMaterial ` to the :ref:`ViewportContainer ` -and add the following code. + # In GDScript + func _ready(): + $Sprite.material.set_shader_param("ViewportTexture", $Viewport.get_texture()) + +Copy the following code to your shader. The above code is a single pass edge detection filter, a +`Sobel filter `_. :: @@ -94,52 +89,50 @@ and add the following code. COLOR.xyz = col; } -The above code is for a single pass edge detection filter in this case we are using a -`Sobel filter `_. It reads pixels from a screen in a -9x9 grid around the current pixel and adds them together. What makes it interesting is that -it assigns weights to each pixel; +1 for each of the eight around the center and -8 for the -center pixel. The choice of weights is called a "kernel". You can use different kernels to -achieve all kinds of different effects. +.. note:: -.. image:: img/post_outline.png + The Sobel filter reads pixels in a 9x9 grid around the current pixel and adds them together, using weight. + What makes it interesting is that it assigns weights to each pixel; +1 for each of the eight around the + center and -8 for the center pixel. The choice of weights is called a "kernel". You can use different + kernels to create edge detection filters, outlines, and all sorts of effects. -Multi-pass ----------- + .. image:: img/post_outline.png -Multi-pass post-processing is useful for effects like blur which can be made significantly -faster if done in two passes or for other effects that require the output of the previous -stage of post-processing. It is done using the same method as single-pass post-processing -with the added complication that you need to be aware of the order in which the -:ref:`Viewports ` are rendered. +Multi-pass post-processing +-------------------------- -In order to make a multi-pass post-processing shader you stack :ref:`Viewports `. -In the example above you rendered the contents of one :ref:`Viewport ` into -the root :ref:`Viewport ` with a :ref:`ViewportContainer `. -You can do the same thing for a multi-pass shader. Just render the contents of one -:ref:`Viewport ` into another and then render the contents of that -:ref:`Viewport ` into the root :ref:`Viewport `. +Some post-processing effects like blur are resource intensive. If you break them down in multiple passes +however, you can make them run a lot faster. In a multipass material, each pass takes the result from the +previous pass as an input and processes it. -Your scene hierarchy should look something like this +To make a multi-pass post-processing shader, you stack ``Viewport`` nodes. In the example above you +rendered the content of one ``Viewport`` object into the root ``Viewport``, through a ``ViewportContainer`` +node. You can do the same thing for a multi-pass shader by rendering the content of one ``Viewport`` into +another and then rendering the last ``Viewport`` into the root ``Viewport``. + +Your scene hierarchy will look something like this: .. image:: img/post_hierarchy2.png -Godot will render the child :ref:`Viewport ` first, so if the order matters for -your shaders make sure that you assign the material you want used first to the lowest -:ref:`ViewportContainer ` in the tree. +Godot will render the bottom ``Viewport`` node first. So if the order of the passes matters for your +shaders, make sure that you assign the shader you want to apply first to the lowest ``ViewportContainer`` in +the tree. -.. note:: You can also render your Viewports seperately without nesting them like this. You just - need to use two Viewports and render them in the proper order. +.. note:: -Everything else is the same as with the single-pass post-processing shader. + You can also render your Viewports seperately without nesting them like this. You just + need to use two Viewports and to render them one after the other. -As an example, you could write a full screen Gaussian blur effect by attaching the following -pieces of code to each of the :ref:`ViewportContainers `. For this -example, order does not matter. +Besides the node structure, the steps are the the same as with the single-pass post-processing shader. + +As an example, you could write a full screen Gaussian blur effect by attaching the following pieces of code +to each of the :ref:`ViewportContainers `. The order in which you apply the shaders +does not matter: :: - + shader_type canvas_item; - + //Blurs the screen in the X-direction void fragment() { vec3 col = texture(TEXTURE, SCREEN_UV).xyz * 0.16; @@ -155,9 +148,9 @@ example, order does not matter. } :: - + shader_type canvas_item; - + //Blurs the screen in the Y-direction void fragment() { vec3 col = texture(TEXTURE, SCREEN_UV).xyz * 0.16; @@ -176,4 +169,4 @@ Using the above code you should end up with a full screen blur effect like below .. image:: img/post_blur.png -For more information on how :ref:`Viewports ` work see the :ref:`Viewports Tutorial `. +For more information on how ``Viewport`` nodes work see the :ref:`Viewports Tutorial `.