WisdomSoft - for your serial experiences.

頂点ストリーム

空間座標や色、テクスチャ座標など、頂点を構成する個々の要素を異なる頂点バッファに分解し、複数の頂点バッファに含まれるデータを同時に使用して描画することができます。このとき、頂点バッファに含まれる頂点データを入力する個々のラインを頂点ストリームと呼びます。

異なる頂点バッファを描画時に組み合わせる

グラフィックデバイスに頂点バッファを設定して DrawPrimitives() メソッド、または DrawIndexedPrimitives() メソッドからプリミティブを描画する場合、複数の頂点バッファを結合して利用することができます。複数の頂点バッファから入力される頂点の流れを頂点ストリームと呼びます。

マルチストリームなどと呼ばれるこの手法を使うことで、座標や色といった頂点要素を個別に設定できるようになります。これによって、頂点の座標はそのままに、色だけを変更するといったことが可能になります。複数の頂点ストリームを用いる場合も、適切なストリーム番号を設定した頂点要素を作成し、これに基づく頂点宣言を設定しなければなりません。

複数の頂点バッファを頂点ストリームごとに設定するには SetVertexBuffer() メソッドではなく SetVertexBuffers() メソッドを使ってください。

GraphicsDevice クラス SetVertexBuffers() メソッド
public void SetVertexBuffers (
         params VertexBufferBinding[] vertexBuffers
)

vertexBuffers パラメータに、頂点ストリームごとに分割された頂点バッファの配列を指定します。

例えば、空間座標と色を複数の頂点ストリームに分解して描画する場合、空間座標の配列を保存する頂点バッファと、色の配列を保存する頂点バッファを用意して SetVertexBuffers() メソッドに渡します。このてき、空間座標と色は個別の独立したカスタム頂点を用意するものとします。

positions = new CustomVertexPosition[] {
    new CustomVertexPosition(0, 0.5F, 0),
    new CustomVertexPosition(0.5F, -0.5F, 0),
    new CustomVertexPosition(-0.5F, -0.5F, 0),
};
colors = new CustomVertexColor[]
{
    new CustomVertexColor(0xFF0000FF),
    new CustomVertexColor(0x0000FFFF),
    new CustomVertexColor(0x00FF00FF),
};

上記の CustomVertexPosition 型と CustomVertexColor 型は適切な頂点宣言を持つ頂点型だと仮定します。CustomVertexPosition 型は空間座標のベクトル、CustomVertexColor 型は頂点の色を持ちます。これらの配列をそれぞれ頂点バッファに設定し、デバイスに設定します。

VertexBuffer vertexPositionBuffer = new VertexBuffer(
    GraphicsDevice, typeof(CustomVertexPosition), positions.Length, BufferUsage.None);
vertexPositionBuffer.SetData(positions);

VertexBuffer vertexColorBuffer = new VertexBuffer(
    GraphicsDevice, typeof(CustomVertexColor), colors.Length, BufferUsage.None);
vertexColorBuffer.SetData(colors);

GraphicsDevice.SetVertexBuffers(vertexPositionBuffer, vertexColorBuffer);

この状態でプリミティブを描画すると、複数の頂点ストリームから個別に頂点が入力され、対応するインデックスで結合します。最初の頂点は positions[0] の空間座標に colors[0] の色が用いられるでしょう。

 コード1
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

public struct CustomVertexPosition : IVertexType
{
    public float X, Y, Z;

    public static readonly VertexDeclaration  VertexDeclaration;
    static CustomVertexPosition()
    {
        VertexElement[] vertexElements = new VertexElement[] {
            new VertexElement(
                0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0
            ),
        };

        VertexDeclaration = new VertexDeclaration(vertexElements);
    }

    public CustomVertexPosition(float x, float y, float z)
    {
        X = x;
        Y = y;
        Z = z;
    }

    VertexDeclaration IVertexType.VertexDeclaration
    {
        get { return CustomVertexPosition.VertexDeclaration; }
    }
}

public struct CustomVertexColor : IVertexType
{
    public byte R, G, B, A;

    public static readonly VertexDeclaration  VertexDeclaration;
    static CustomVertexColor()
    {
        VertexElement[] vertexElements = new VertexElement[] {
            new VertexElement(
               0, VertexElementFormat.Color, VertexElementUsage.Color, 0
            ),
        };

        VertexDeclaration = new VertexDeclaration(vertexElements);
    }

    public CustomVertexColor(uint color)
    {
        R = (byte)(color >> 24);
        G = (byte)(color >> 16);
        B = (byte)(color >> 8);
        A = (byte)color;
    }

    VertexDeclaration IVertexType.VertexDeclaration
    {
        get { return CustomVertexColor.VertexDeclaration; }
    }
}

public class TestGame : Game
{
    public static void Main(string[] args)
    {
        using (Game game = new TestGame()) game.Run();
    }

    private GraphicsDeviceManager graphicsDeviceManager;
    private BasicEffect effect;
    private CustomVertexPosition[] positions;
    private CustomVertexColor[] colors;

    public TestGame()
    {
        graphicsDeviceManager = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
    }

    protected override void Initialize()
    {
        positions = new CustomVertexPosition[] {
	        new CustomVertexPosition(0, 0.5F, 0),
	        new CustomVertexPosition(0.5F, -0.5F, 0),
	        new CustomVertexPosition(-0.5F, -0.5F, 0),
        };
        colors = new CustomVertexColor[]
        {
	        new CustomVertexColor(0xFF0000FF),
	        new CustomVertexColor(0x0000FFFF),
	        new CustomVertexColor(0x00FF00FF),
        };

        base.Initialize();
    }

    protected override void LoadContent()
    {
        effect = new BasicEffect(GraphicsDevice);
        effect.VertexColorEnabled = true;

        VertexBuffer vertexPositionBuffer = new VertexBuffer(
            GraphicsDevice, typeof(CustomVertexPosition), positions.Length, BufferUsage.None);
        vertexPositionBuffer.SetData(positions);

        VertexBuffer vertexColorBuffer = new VertexBuffer(
            GraphicsDevice, typeof(CustomVertexColor), colors.Length, BufferUsage.None);
        vertexColorBuffer.SetData(colors);

        GraphicsDevice.SetVertexBuffers(vertexPositionBuffer, vertexColorBuffer);

        base.LoadContent();
    }

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

        foreach (EffectPass pass in effect.CurrentTechnique.Passes)
        {
            pass.Apply();
            GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
        }

        base.Draw(gameTime);
    }
}
実行結果
コード1 実行結果

コード1は空間座標を格納する CustomVertexPosition 構造体と、頂点の色を格納する CustomVertexColor 構造体をユーザー定義のカスタム頂点として用意し、それぞれを異なる頂点バッファ vertexPositionBuffer と vertexColorBuffer に設定しています。これらの頂点バッファをグラフィックスデバイスに設定し描画すると、それぞれの頂点ストリームから正しく頂点が入力されます。