【Godot】Viewportから画面をキャプチャする方法

# 概要
この記事では、Godot Engineの Viewportを使用して画面をキャプチャする方法について書きます。

# 画像の登録
以下の画像をプロジェクトに追加します。名前は "mountains.png" としておきます。
![mountains.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/d320ff9a-d185-47d3-a228-81e17bc5e418.png)
ちなみにこの画像は、Godot Engineの開発者の故郷であるアルゼンチンの史跡「ティルカラのプカラ（要塞）」だそうです。（公式のサンプルに含まれていた画像です）

## 画像の配置
Node2Dシーン(Mainにリネーム)を作成して、"mountains.png" をSpriteとして配置します。
![Godot_Engine_-_TestCapture_-_Main_tscn____.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/f0b1d464-efe1-87a4-8caa-660420862ce6.png)
次のキャプチャした画像を表示するために `TextureRect` を作成します。
![Godot_Engine_-_TestCapture_-_Main_tscn____.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/533d1982-d7f6-ebd6-ef18-68163c61c6b9.png)
`Texture` を描画できるノードであればなんであっても構いません。
`TextureRect` は画面中央に配置しました。
![Godot_Engine_-_TestCapture_-_Main_tscn____.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/8480ce23-2fcd-1677-aa0f-0fda0af0306e.png)

それと`TextureRect`のインスペクタから `Rect > Scale` の(x, y)をそれぞれ 0.5 にしておきます。
![Banners_and_Alerts.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/02b5d0bc-d493-7387-d55c-6b8ed38adeab.png)
これはキャプチャの結果がわかりやすいように、半分のサイズに変形させるためです。

シーンの構成はこのようになりました。
![Godot_Engine_-_TestCapture_-_Main_tscn____.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/dea1f0ee-a5ab-8e9d-395b-92fa8de294b4.png)

## キャプチャするスクリプトの実装
Mainノード(ルートノード)に以下のスクリプトをアタッチします。

```gd:Main.gd
extends Node2D

# キャプチャした画像を保存する
onready var captured_image:TextureRect = $TextureRect

func _process(delta: float) -> void:
	if Input.is_action_just_pressed("ui_accept"):
		# "Space"キーを押したら画面のキャプチャを開始する
		
		# Wait until the frame has finished before getting the texture.
		# ①描画完了を待つようにする
		yield(VisualServer, "frame_post_draw")

		# Retrieve the captured image.
		# ②ビューポートの画像イメージを取得する
		var img = get_viewport().get_texture().get_data()

		# Flip it on the y-axis (because it's flipped).
		# ③画像の上下を逆にする
		img.flip_y()

		# Create a texture for it.
		# ④テクスチャを生成
		var tex = ImageTexture.new()
		# ⑤画像データを設定
		tex.create_from_image(img)

		# Set the texture to the captured image node.
		# ⑥キャプチャ画像を保持しておく
		captured_image.set_texture(tex)
```

以下、簡単な説明です。

1. Viewport に描画する情報は、そのフレームの最後で更新されます。そのため `VisualServer` の "frame_post_draw" を待つようにしています
2. そのフレームでの描画が終了したら、ビューポートから画像イメージを取得します
3. ビューポートの描画は上下逆（スクリーン座標系では下方向がプラスですが、3D座標系では上方向がプラスになるため）なので、画像の情報を `Image.flip_y()` で上下逆にします
4. 新しくテクスチャを生成します
5. 生成したテクスチャに画像データをコピーします
6. 生成したテクスチャを `TextureRect` にコピーします

実行すると、Spaceキーを押すたびに、キャプチャした画像が中央に表示されます。

![shot.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43772/cbdecbb3-1b9f-5a22-5039-fa45b9f4e314.gif)

こんな感じで Viewport が使えるようになるとポストエフェクトも簡単にできそう……！

* [カスタムポストプロセッシング](https://docs.godotengine.org/ja/stable/tutorials/viewports/custom_postprocessing.html)


# 参考

* [Godot Docs » ビューポート » ビューポート](https://docs.godotengine.org/ja/stable/tutorials/viewports/viewports.html)
* [公式のビューポートのデモ](https://github.com/godotengine/godot-demo-projects/tree/master/viewport)
    * ここにある「viewport > screen_capture」を参考にしました
* [Godot Docs » Godot API » Viewport](https://docs.godotengine.org/ja/stable/classes/class_viewport.html#class-viewport-constant-clear-mode-never)