カスタムポストプロセッシング

はじめに

Godotは、ブルーム、DOF、SSAOを含む多くのポストプロセッシング効果をすぐに利用できます。でも時々、独自のカスタム効果を作成したい場合もあります。そのための方法は次のとおりです。

ポストプロセッシング効果は、Godotがフレームをレンダリングした後に適用されるシェーダーです。最初にシーンを Viewport にレンダリングし、次に ViewportTexture 内で Viewport をレンダリングして、画面に表示します。

カスタムのポストプロセッシングシェーダーを実装する最も簡単な方法は、Godotの組み込み機能を使用して画面テクスチャから読み取ることです。これに慣れていない場合は、最初に Screen Reading Shaders Tutorial を読む必要があります。

注釈

執筆時点では、Godotは複数のバッファーへの同時レンダリングをサポートしていません。ポストプロセッシングシェーダーは、法線または他のレンダーパスにアクセスできません。レンダリングされたフレームにのみアクセスできます。

シングルパスポストプロセッシング

シーンをレンダリングするための Viewport と、画面上で Viewport をレンダリングするためのシーンが必要になります。ViewportContainer を使用して、画面全体または別の Control ノード内に Viewport を表示できます。

注釈

Viewport を使用してレンダリングすると、フレームレートを含むシーンのレンダリング方法を制御できます。また、ViewportContainer を使用して2Dシーンに3Dオブジェクトをレンダリングできます。

このデモでは、Node2DViewportContainer とともに使用し、最後に Viewport を使用します。 [シーン]タブは次のようになります:

../../_images/post_hierarchy1.png

``Viewport``の中には、望みのものを保持できます。これにはメインシーンが含まれます。このチュートリアルでは、ランダムボックスのフィールドを使用します:

../../_images/post_boxes.png

新しい ShaderMaterialViewportContainer に追加し、新しいシェーダーリソースを割り当てます。レンダリングされた Viewport には、組み込みの TEXTURE ユニフォーム(uniform)を使用してアクセスできます。

注釈

ViewportContainer を使用しないこともできますが、使用する場合は、シェーダーで独自のユニフォーム(uniform)を作成し、次のように Viewport テクスチャを手動で渡す必要があります:

// Inside the Shader
uniform sampler2D ViewportTexture;

そして、次のように、GDScriptからシェーダーにテクスチャを渡すことができます:

# In GDScript
func _ready():
  $Sprite.material.set_shader_param("ViewportTexture", $Viewport.get_texture())

次のコードをシェーダーにコピーします。このコードは、シングルパスエッジ検出フィルター、Sobel filter です。

shader_type canvas_item;

void fragment() {
    vec3 col = -8.0 * texture(TEXTURE, SCREEN_UV).xyz;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz;
    col += texture(TEXTURE, SCREEN_UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz;
    col += texture(TEXTURE, SCREEN_UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz;
    col += texture(TEXTURE, SCREEN_UV + SCREEN_PIXEL_SIZE.xy).xyz;
    col += texture(TEXTURE, SCREEN_UV - SCREEN_PIXEL_SIZE.xy).xyz;
    col += texture(TEXTURE, SCREEN_UV + vec2(-SCREEN_PIXEL_SIZE.x, SCREEN_PIXEL_SIZE.y)).xyz;
    col += texture(TEXTURE, SCREEN_UV + vec2(SCREEN_PIXEL_SIZE.x, -SCREEN_PIXEL_SIZE.y)).xyz;
    COLOR.xyz = col;
}

注釈

Sobelフィルターは、現在のピクセルの周囲の9x9グリッドのピクセルを読み取り、重みを使用してそれらを加算します。興味深いのは、各ピクセルに重みを割り当てることです。中心の周りの8個のそれぞれに+1、中心のピクセルに-8。重みの選択は「カーネル」と呼ばれます。異なるカーネルを使用して、エッジ検出フィルター、アウトライン、およびあらゆる種類の効果を作成できます。

../../_images/post_outline.png

マルチパスポストプロセッシング

ぼかしなどの後処理の効果の中には、リソースを大量に消費するものがあります。ただし、複数のパスでそれらを分解する場合、それらをはるかに高速に実行できます。マルチパスマテリアルでは、各パスは前のパスの結果を入力として処理します。

マルチパスポストプロセッシングシェーダーを作成するには、Viewport ノードをスタックします。上記の例では、ViewportContainer ノードを介して、1つの Viewport オブジェクトのコンテンツをルート Viewport にレンダリングしました。マルチパスシェーダーに対して同じことを行うには、ある Viewport のコンテンツを別の Viewport にレンダリングし、最後の Viewport をルート Viewport にレンダリングします。

シーンの階層は次のようになります:

../../_images/post_hierarchy2.png

Godotは下の Viewport ノードを最初にレンダリングします。したがって、シェーダーにとってパスの順序が重要な場合は、最初に適用するシェーダーをツリー内の最も低い ViewportContainer に割り当てるようにしてください。

注釈

このようにネストせずに、ビューポートを個別にレンダリングすることもできます。 2つのビューポートを使用し、それらを次々にレンダリングするだけです。

ノード構造を除き、手順はシングルパスポストプロセッシングシェーダーと同じです。

例として、次のコードを各 ViewportContainers にアタッチすることにより、フルスクリーンのガウスぼかし効果を作成できます。シェーダーを適用する順序は重要ではありません:

shader_type canvas_item;

//Blurs the screen in the X-direction.
void fragment() {
    vec3 col = texture(TEXTURE, SCREEN_UV).xyz * 0.16;
    col += texture(TEXTURE, SCREEN_UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
    col += texture(TEXTURE, SCREEN_UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
    col += texture(TEXTURE, SCREEN_UV + vec2(2.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
    col += texture(TEXTURE, SCREEN_UV + vec2(2.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
    col += texture(TEXTURE, SCREEN_UV + vec2(3.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
    col += texture(TEXTURE, SCREEN_UV + vec2(3.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
    col += texture(TEXTURE, SCREEN_UV + vec2(4.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
    col += texture(TEXTURE, SCREEN_UV + vec2(4.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
    COLOR.xyz = col;
}
shader_type canvas_item;

//Blurs the screen in the Y-direction.
void fragment() {
    vec3 col = texture(TEXTURE, SCREEN_UV).xyz * 0.16;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 2.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 2.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 3.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 3.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 4.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
    col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 4.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
    COLOR.xyz = col;
}

上記のコードを使用すると、次のような全画面ぼかし効果が得られます。

../../_images/post_blur.png

Viewport ノードの機能の詳細については、Viewports Tutorial を参照してください。