忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

かんたんXNA その29 テクスチャへの描画 RenderTarget

このページは古いです
最新版はこちら

今まではウィンドウの画面にだけポリゴンを描画していましたが、
XNAでは実はテクスチャに対して描画することも出来ます。

これによりテクスチャを動的に作り出すことができ、
例えばゲームの中のアイテムとしてパソコンを作って、その画面を動かしたり出来るでしょう。
テクスチャに対していろいろ描画して、
それをゲームの中のパソコン画面に貼り付ければいいのです。



この、描画の対象となるテクスチャを
レンダーターゲットといいます。
クラスはMicrosoft.Xna.Framework.RenderTarget2Dです。

public class RenderTarget2D : RenderTarget

このクラスのインスタンスを作成し、GraphicsDeviceにセットすると、
これまでと違って、画面ではなくこのレンダーターゲットにたいして
描画されるようになります。

コンストラクタを見てみましょう。

public RenderTarget2D (
        GraphicsDevice graphicsDevice,
        int width,
        int height,
        int numberLevels,
        SurfaceFormat format
)


graphicsDeviceはグラフィックスデバイスです。
widthheightは作成するレンダーターゲットのテクスチャのサイズを表します。
numberLevelsは・・・MSDNによるとテクスチャを前処理する時に作られるダウンサンプルされたサーフェイスの数だそうで、とりあえず0でいいようです。
formatはテクスチャのピクセルのフォーマットです。これはSurfaceFormat.Colorでいいでしょう。

このコンストラクタによって作成したレンダーターゲットを
GraphicsDeviceにセットするには、
GraphicsDevice.SetRenderTargetメソッドを使います。

public void SetRenderTarget (
        int renderTargetIndex,
        RenderTarget2D renderTarget
)


renderTargetIndexはこれからセットするレンダーターゲットのデバイス上でのインデックスです。これはグラフィックスデバイスにセットできるレンダーターゲットの数のうちなら何でもかまいません(GraphicsDeviceCapabilities.NumberSimultaneousRenderTargetsプロパティでこの数がわかります)。まぁ普通は0になるでしょうが・・・。
renderTargetはセットするレンダーターゲットです。


このメソッドを使ってセットし、ポリゴンを描画した後、やらなければならないことがあります。
まず、SetRenderTargetをもう一度呼んで、今度はrenderTargetにnullをセットします。
そしてGraphicsDevice.ResolveRenderTargetメソッドを呼んで、行った描画をRenderTarget2Dに反映させるのです。

public void ResolveRenderTarget ( int index )

indexは解決するレンダーターゲットのインデックスです。

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;


class MyGame : Game
{
    GraphicsDeviceManager graphics;

    SpriteBatch spriteBatch;
    RenderTarget2D renderTarget;
    BasicEffect effect;

    VertexPositionColor[] vertices = { 
        new VertexPositionColor(new Vector3(0, 1, 0), Color.White),
        new VertexPositionColor(new Vector3(1, 0, 0), Color.Blue),
        new VertexPositionColor(new Vector3(-1, 0, 0),Color.Red)
    };

    public MyGame()
    {
        graphics = new GraphicsDeviceManager(this);
    }

    protected override void LoadGraphicsContent(bool loadAllContent)
    {
        if (loadAllContent)
        {
            spriteBatch = new SpriteBatch(graphics.GraphicsDevice);

            renderTarget = new RenderTarget2D(
                graphics.GraphicsDevice,
                400, 200,
                0,
                SurfaceFormat.Color
                );

            effect = new BasicEffect(graphics.GraphicsDevice, null);
            effect.View = Matrix.CreateLookAt(
                new Vector3(0, 0, 5),
                new Vector3(),
                new Vector3(0, 1, 0)
                );
            effect.Projection = Matrix.CreatePerspectiveFieldOfView(
                MathHelper.ToRadians(45),
                400 / 200,
                1, 100
                );
            effect.VertexColorEnabled = true;
        }
    }

    protected override void Draw(GameTime gameTime)
    {
        graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

        renderToRenderTarget();

        spriteBatch.Begin();
        spriteBatch.Draw(
            renderTarget.GetTexture(),
            new Rectangle(0, 0, 400, 200),
            Color.White
            );
        spriteBatch.End();
    }

    private void renderToRenderTarget()
    {
        graphics.GraphicsDevice.SetRenderTarget(0, renderTarget);
        graphics.GraphicsDevice.Clear(Color.Gray);

        graphics.GraphicsDevice.VertexDeclaration = new VertexDeclaration(
            graphics.GraphicsDevice,
            VertexPositionColor.VertexElements
            );

        effect.Begin();

        foreach (EffectPass pass in effect.CurrentTechnique.Passes)
        {
            pass.Begin();

            graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(
                PrimitiveType.TriangleList,
                vertices,
                0,
                vertices.Length / 3
                );

            pass.End();
        }

        effect.End();

        graphics.GraphicsDevice.ResolveRenderTarget(0);
        graphics.GraphicsDevice.SetRenderTarget(0, null);
    }
}

renderTarget.JPG
このサンプルでは、まず400x200のテクスチャに対して三角形を描画した後、
そのテクスチャをさらにSpriteBatchで画面に描画しています。

レンダーターゲットをGraphicsDeviceにセットして、そのテクスチャをクリア、三角形をを描画します。
その後、その描画を解決、GraphicsDeviceからレンダーターゲットを取り除きます。
(これをやらないとウィンドウに何も映らなくなります。
その後の描画が全て画面ではなくレンダーターゲットに行われるのです)


これだけではビューポートを使った描画と区別がつかないかもしれません。
しかし、こちらはビューポートを使う場合よりもずっと柔軟で、用途が広いのです。

まず、ここではSpriteBatchで2Dとして描画していますが、
実際には3Dのモデルのテクスチャとして使ってもかまいません。
例えば斜めに傾いたモデルに貼り付ければ、そのモデルが実はディスプレイで、
そのテクスチャを表示しているように見えるでしょう。
これはビューポートには出来ない芸当です。

また、レンダーターゲットは光加減やぼやけ具合みたいな描画の効果を表現するのに利用されたりもします。
まずレンダーターゲットに対して何か描画して、
その後そのテクスチャを参考にして光やぼやけ具合などを計算するといった具合です。
XNA Creators Clubのサンプル、BloomPostprocessSampleもそのようなことをやっています。

このように、レンダーターゲットはかなりいろいろなことに使えるのです!

拍手[0回]

PR