RenderTarget を使用してテクスチャーに描画したものをテクスチャーとして使用する

ページ更新日 :
ページ作成日 :

検証環境

Windows
  • Windows 10
  • Windows 11
Visual Studio
  • Visual Studio 2022
MonoGame
  • 3.8.1

レンダーターゲットについて

通常描画を行うと画面上に対して描画内容が表示されますが、レンダーターゲットを使用すると描画内容をテクスチャーに書き込むことができます。 テクスチャーに書き込んだ内容はそのままテクスチャーとして使用することが出来ます。 例えばテレビの内容をテクスチャーに書き込んだ後、そのテクスチャーをテレビの画面に貼り付けるとによって通常とは別の映像をテレビ内で動かすことができます。 もちろん静画として使用することもでき、あらかじめテクスチャー用の画像ファイルを用意しなくても動的にテクスチャーを作ることができます。

画像の用意

レンダーターゲットを使用するにあたって画像ファイルは必要ありませんが、今回サンプルとして分かりやすくするために画像ファイルを用意しておきます。

通常のコンテンツファイルとして使用しますので MGCB に登録しておいてください。名前は Texture としておきます。

フィールド

Game1 クラスに記述していきます。既存のコードについては特に変更しませんので記載はせず追記した分だけ記載します。

/// <summary>描画するテクスチャー画像。</summary>
private Texture2D _texture;

/// <summary>描画対象となるレンダーターゲット。</summary>
private RenderTarget2D _renderTarget;

_texture フィールドは用意した画像を読み込むためのものなので今回の Tips の内容とは特に関係ありません。

RenderTarget2D クラスで定義したフィールドがレンダーターゲットとなる対象となります。 インスタンスを生成するには GraphicsDevice が必要であるためこの段階では生成することはできませんので定義のみとします。

Game1 コンストラクタ

public Game1()
{
  _graphics = new GraphicsDeviceManager(this);
  Content.RootDirectory = "Content";
  IsMouseVisible = true;

  // ゲームの解像度の設定
  _graphics.PreferredBackBufferWidth = 1280;
  _graphics.PreferredBackBufferHeight = 720;
}

ここでやることは特にありません。一応ゲームの解像度を指定していてレンダーターゲットと比較できるようにしておいてます。

LoadContent

protected override void LoadContent()
{
  _spriteBatch = new SpriteBatch(GraphicsDevice);

  // TODO: this.Content を使用してゲーム コンテンツをここに読み込みます

  // コンテンツからテクスチャーを読み込みます
  _texture = Content.Load<Texture2D>("Texture");

  _renderTarget = new RenderTarget2D(GraphicsDevice, 540, 360);
}

まずは描画に使う Texture2DContent.Load で読み込んでおきます。

GraphicsDevice があるならば RenderTarget2D をどこで生成しても構いませんが、サンプルでは LoadContent メソッド内で作成しておきます。 レンダーターゲットは実質テクスチャーなのでサイズがあります。コンストラクタでは GraphicsDevice と一緒にサイズも指定します。

Draw

レンダーターゲットを使用した場合、ここのコードがメインとなります。

protected override void Draw(GameTime gameTime)
{
  // TODO: ここに描画コードを追加します

  // 描画先対象をレンダーターゲットに設定
  GraphicsDevice.SetRenderTarget(_renderTarget);

  // テクスチャーの情報をクリアし単色で塗りつぶします
  GraphicsDevice.Clear(Color.LightYellow);

  // スプライトバッチを使用してスプライトを書き込みます
  // レンダーターゲットの外に影響ないことを確認するためにわざと領域をはみ出るように描画しています
  _spriteBatch.Begin();
  for (int i = 0; i < 4; i++)
  {
    _spriteBatch.Draw(_texture, new Vector2((i / 2) * 200 - 50, (i % 2) * 200 - 50), Color.White);
  }
  _spriteBatch.End();

  // レンダーターゲットを解除します。これ以降の描画処理はメインウインドウに対して行われます
  GraphicsDevice.SetRenderTarget(null);

  // メインウィンドウの描画情報をクリアし単色で塗りつぶします
  GraphicsDevice.Clear(Color.CornflowerBlue);

  // メインウィンドウに対してレンダーターゲットをテクスチャーとして描画します
  _spriteBatch.Begin();
  _spriteBatch.Draw(_renderTarget, new Vector2(100, 100), Color.White);
  _spriteBatch.End();

  base.Draw(gameTime);
}

描画は基本的に GraphicsDevice に対して行うため、そのまま描画処理を行ってもメインウィンドウに対して描画されてしまいます。 GraphicsDevice.SetRenderTarget メソッドに RenderTarget2D を指定することにより、描画先をテクスチャーに切り換えることができます。

切り替えた後はメインウィンドウに描画するときと同じように描画処理を行います。 GraphicsDevice.Clear で描画情報をクリアし、SpriteBatch でスプライトを描画しています。 本当にテクスチャーに対して描画しているかを確認するために、今回あえてスプライトがはみ出るように描画位置を指定しています。

レンダーターゲットへの描画が終わったら GraphicsDevice.SetRenderTarget メソッドに null を指定します。 これにより描画対象をメインウィンドウに戻すことが出来ます。

RenderTarget2D はそのままテクスチャーとして使用できるので SpriteBatch.Draw メソッドに _renderTarget を指定してスプライトとして描画するようにしています。

実行

コードを書いたら実行してみてください。図のように表示されれば OK です。

レンダーターゲットに対しての塗りつぶしとメインウィンドウに対しての塗りつぶしの色を分けているのでどこがレンダーターゲットとして作成したテクスチャーか分かると思います。 レンダーターゲットに対してスプライトがはみ出るように描画しましたが、きちんとクリッピングされメインウィンドウ側に描画されていないことを確認できると思います。

レンダーターゲットを実現するコードはこれだけです。特に難しいような所はないでしょう。 気にするところとしては描画先の切り替えの管理と RenderTarget2D の管理ぐらいだと思います。

動かしてみる

先ほどの実行結果だと単に画像を切り抜いて描画しているようにしか見えないので、 次はスプライトを動かしてみてきちんとレンダーターゲット内に描画していることを確認してみたいと思います。

Update ごとに回転を更新したいのでフィールドに回転角度を用意します。

/// <summary>テクスチャーを回転させる角度。</summary>
private float _rotate;

Update メソッドでは _rotate の数値を増やしていきます。

protected override void Update(GameTime gameTime)
{
  if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
    Exit();

  // TODO: ここに更新ロジックを追加します

  // テクスチャーの回転角度を更新します
  _rotate += 0.01f;

  base.Update(gameTime);
}

Draw メソッドではスプライトが回転するように _rotate を設定しています。 回転していることが分かればいいので他の値は適当に設定しています。

protected override void Draw(GameTime gameTime)
{
  // 中略

  // スプライトバッチを使用してスプライトを書き込みます
  // レンダーターゲットの外に影響ないことを確認するためにわざと領域をはみ出るように描画しています
  _spriteBatch.Begin();
  for (int i = 0; i < 4; i++)
  {
    _spriteBatch.Draw(_texture, new Vector2((i / 2) * 200 + 32, (i % 2) * 200 + 32),
      null, Color.White, _rotate * i, new Vector2(64, 64), 1, SpriteEffects.None, 0);
  }
  _spriteBatch.End();

  // 中略

  // メインウィンドウに対してレンダーターゲットをテクスチャーとして描画します
  _spriteBatch.Begin();
  _spriteBatch.Draw(_renderTarget, new Vector2(400, 400), null, Color.White,
    _rotate, new Vector2(_renderTarget.Width / 2, _renderTarget.Height / 2), 1, SpriteEffects.None, 0);
  _spriteBatch.End();

  base.Draw(gameTime);
}

実行すると図のように回転しながら描画されるはずです。