Exibindo caixas usando buffers de índice

Página atualizada :
Data de criação de página :

resumo

Eu uso muitos polígonos para criar uma caixa. Ao fazer isso, um buffer de índice é usado para reduzir a quantidade de dados nos dados de vértice.

インデックスバッファを使用したボックスの表示

Ambiente operacional

Pré-requisitos

Versões do XNA suportadas
  • 4.0
Plataformas suportadas
  • Windows (XP SP2 ou posterior, Vista, 7)
  • Xbox 360
  • Windows Phone 7
Versão do sombreador de vértice necessária para Windows 2.0
Versão do sombreador de pixel necessária para Windows 2.0

Ambiente operacional

plataforma
  • janelas 7
  • Xbox 360
  • Emulador do Windows Phone 7

substância

Sobre a caixa

A caixa consiste em seis faces, uma das quais consiste em dois polígonos triangulares. Isso significa que o número total de polígonos triangulares é "2×6 = 12". Além disso, como o polígono triangular tem três vértices, o total de vértices é "12×3 = 36". Portanto, ao criar apenas com "VertexBuffer", é possível exibi-lo como uma caixa se você decidir as informações de posição de forma que 36 dados tenham a forma de uma caixa e escrevê-los. (24 são necessários para TriangleStrip)

Mas imagine uma caixa. Os cantos da caixa são de 8 peças. Oito devem ser suficientes para informações de localização. À medida que o número de dados de vértices aumenta, ele pressiona a memória. Para reduzir isso de alguma forma, usamos "IndexBuffer".

Você só precisa de 8 informações de posição, mas sempre precisa de 36 vértices para um polígono. Portanto, a finalidade de usar "IndexBuffer" é compartilhar dados de 8 vértices.

四角形ポリゴン

campo

/// <summary>
/// インデックスバッファ
/// </summary>
private IndexBuffer indexBuffer = null;

/// <summary>
/// インデックスバッファの各頂点番号配列
/// </summary>
private static readonly Int16[] vertexIndices = new Int16[] {
    2, 0, 1, // 1枚目のポリゴン
    1, 3, 2, // 2枚目のポリゴン
    4, 0, 2, // 3枚目のポリゴン
    2, 6, 4, // 4枚目のポリゴン
    5, 1, 0, // 5枚目のポリゴン
    0, 4, 5, // 6枚目のポリゴン
    7, 3, 1, // 7枚目のポリゴン
    1, 5, 7, // 8枚目のポリゴン
    6, 2, 3, // 9枚目のポリゴン
    3, 7, 6, // 10枚目のポリゴン
    4, 6, 7, // 11枚目のポリゴン
    7, 5, 4  // 12枚目のポリゴン
};

O campo é declarado um "IndexBuffer", mas uma "matriz de número de vértice" é pré-criada sob ele. Essa matriz reserva uma matriz para 36 vértices, mas o significado de cada número é quantos dados de vértice dos oito dados de vértice cada polígono do triângulo usa. Se você olhar de perto, verá que os dados internos são gravados com um índice entre "0 ~ 7". É fácil ver nos comentários.

A propósito, o tipo do array é "Int16[]", mas também pode ser "short[]" (2 bytes). Em alguns casos, uma matriz é criada com "int" (4 bytes), mas isso é usado quando o número de vértices excede "65535". Se o número de vértices nunca exceder esse número, crie uma matriz de dados de 2 bytes para reduzir o consumo de memória.

criação

// 頂点の数
int vertexCount = 8;

// 頂点バッファ作成
this.vertexBuffer = new VertexBuffer(this.GraphicsDevice,
    typeof(VertexPositionColor), vertexCount, BufferUsage.None);

// 頂点データを作成する
VertexPositionColor[] vertives = new VertexPositionColor[vertexCount];

vertives[0] = new VertexPositionColor(new Vector3(-2.0f, 2.0f, -2.0f), Color.Yellow);
vertives[1] = new VertexPositionColor(new Vector3(2.0f, 2.0f, -2.0f), Color.Gray);
vertives[2] = new VertexPositionColor(new Vector3(-2.0f, 2.0f, 2.0f), Color.Purple);
vertives[3] = new VertexPositionColor(new Vector3(2.0f, 2.0f, 2.0f), Color.Red);
vertives[4] = new VertexPositionColor(new Vector3(-2.0f, -2.0f, -2.0f), Color.SkyBlue);
vertives[5] = new VertexPositionColor(new Vector3(2.0f, -2.0f, -2.0f), Color.Orange);
vertives[6] = new VertexPositionColor(new Vector3(-2.0f, -2.0f, 2.0f), Color.Green);
vertives[7] = new VertexPositionColor(new Vector3(2.0f, -2.0f, 2.0f), Color.Blue);

// 頂点データを頂点バッファに書き込む
this.vertexBuffer.SetData(vertives);

Criando buffers de vértice. Originalmente, é necessário criar 36 vértices, mas usando um buffer de índice, você só precisa criar 8 vértices.

// インデックスバッファを作成
this.indexBuffer = new IndexBuffer(this.GraphicsDevice,
    IndexElementSize.SixteenBits, 3 * 12, BufferUsage.None);

// 頂点インデックスを書き込む
this.indexBuffer.SetData(vertexIndices);

Criando buffers de índice. O segundo argumento especifica o número de bits do índice de vértice a ser gravado. Como um índice tem 2 bytes, especifique "IndexElementSize.SixteenBits".

O terceiro argumento é o número de índices. Nesse caso, desenharemos 12 polígonos, então especifique 36, que é o número de vértices × polígonos dos polígonos triangulares. Claro, não há problema se você especificar o número de elementos na matriz de índice como está, mas desta vez os números são intencionalmente separados para maior clareza.

Como já criamos uma matriz de índices de vértices com campos, vamos escrevê-los com o método "IndexBuffer.SetData".

IndexBuffer construtor

Cria uma instância da classe IndexBuffer que gerencia o índice que faz referência aos dados de vértice.

Placa gráficapara dispositivos Dispositivo gráfico Especifica o GraphicsDevice a ser associado ao buffer de índice.
indexElementSize IndexElementSize O tamanho de um único índice de vértice. Especifique "SixteenBits" para 2 bytes, "ThirtyTwoBits" para 4 bytes e especifique BufferUsage.None.
indexCount int Especifica o número de índices.
uso Uso do Buffer Uso do buffer de índice. Especifique BufferUsage.None, a menos que seja o contrário.

IndexBuffer.SetData método

Copie a matriz de índices de vértice para o buffer de índice.

T Tipo de valor Tipo de matriz de índice de vértice
dados T Matriz de índice de vértice a ser copiada

desenho

// インデックスバッファをセット
this.GraphicsDevice.Indices = this.indexBuffer;

Se você quiser usar um buffer de índice, defina o buffer de índice no dispositivo antes de desenhar o polígono.

// インデックスを使用してポリゴンを描画する
this.GraphicsDevice.DrawIndexedPrimitives(
    PrimitiveType.TriangleList,
    0,
    0,
    8,
    0,
    12
);

Se você estiver usando buffers de índice e vértice, use o método "GraphicsDevice.DrawIndexedPrimitives" para desenhar polígonos.

O quarto argumento é o número de vértices criados. No exemplo, "8" é especificado porque 8 dados de vértice são compartilhados.

O sexto argumento especifica o número de primitivas. É "12" porque desenha 12 polígonos triangulares.

Para outros parâmetros numéricos, 0 é bom.

GraphicsDevice.DrawIndexedPrimitives método

Desenha um primitivo com base no índice de vértice especificado e no buffer de vértice.

tipo primitivo Tipo primitivo Especifica o primitivo a ser desenhado.
baseVertex int O deslocamento a ser adicionado a cada índice de vértice no buffer de índice. Por exemplo, quando o primeiro índice de vértice aponta para dados de vértice 2, se "1" for especificado neste argumento, o primeiro índice de vértice apontará para dados de vértice 3.
minVertexIndex int O índice mínimo de vértices do vértice usado na chamada. Por exemplo, um minVertexIndex de 1 aumenta o índice dos dados de vértice em 1 (ele não aumenta o número de buffers, portanto, o último elemento dos dados de vértice não pode ser especificado). Se o índice de vértices apontar para os dados do segundo vértice, ele apontará para os primeiros dados de vértice.
numVertices int O número de dados de vértice usados.
Índice inicial int O deslocamento inicial do índice de vértice. Por exemplo, se você especificar TriangleList como primitiveType, especifique "3, 6, 9,..." para ignorar os polígonos que começam a desenhar. Se você especificar um valor diferente do número dividido por 3, o modelo será recolhido. (Porque todos os índices estão desativados)
primitiveCount int O número de primitivos a serem desenhados. O valor máximo que pode ser especificado é "Número de índices de vértice÷ Número de vértices de primitivos - startIndex"

Todos os códigos

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
#if WINDOWS_PHONE
using Microsoft.Xna.Framework.Input.Touch;
#endif

namespace IndexBufferBox
{
    /// <summary>
    /// ゲームメインクラス
    /// </summary>
    public class GameMain : Microsoft.Xna.Framework.Game
    {
        /// <summary>
        /// グラフィックデバイス管理クラス
        /// </summary>
        private GraphicsDeviceManager graphics = null;

        /// <summary>
        /// スプライトのバッチ化クラス
        /// </summary>
        private SpriteBatch spriteBatch = null;

        /// <summary>
        /// 基本エフェクト
        /// </summary>
        private BasicEffect basicEffect = null;

        /// <summary>
        /// 頂点バッファ
        /// </summary>
        private VertexBuffer vertexBuffer = null;

        /// <summary>
        /// インデックスバッファ
        /// </summary>
        private IndexBuffer indexBuffer = null;

        /// <summary>
        /// インデックスバッファの各頂点番号配列
        /// </summary>
        private static readonly Int16[] vertexIndices = new Int16[] {
            2, 0, 1, // 1枚目のポリゴン
            1, 3, 2, // 2枚目のポリゴン
            4, 0, 2, // 3枚目のポリゴン
            2, 6, 4, // 4枚目のポリゴン
            5, 1, 0, // 5枚目のポリゴン
            0, 4, 5, // 6枚目のポリゴン
            7, 3, 1, // 7枚目のポリゴン
            1, 5, 7, // 8枚目のポリゴン
            6, 2, 3, // 9枚目のポリゴン
            3, 7, 6, // 10枚目のポリゴン
            4, 6, 7, // 11枚目のポリゴン
            7, 5, 4  // 12枚目のポリゴン
        };


        /// <summary>
        /// GameMain コンストラクタ
        /// </summary>
        public GameMain()
        {
            // グラフィックデバイス管理クラスの作成
            this.graphics = new GraphicsDeviceManager(this);

            // ゲームコンテンツのルートディレクトリを設定
            this.Content.RootDirectory = "Content";

#if WINDOWS_PHONE
            // Windows Phone のデフォルトのフレームレートは 30 FPS
            this.TargetElapsedTime = TimeSpan.FromTicks(333333);

            // バックバッファサイズの設定
            this.graphics.PreferredBackBufferWidth = 480;
            this.graphics.PreferredBackBufferHeight = 800;

            // フルスクリーン表示
            this.graphics.IsFullScreen = true;
#endif
        }

        /// <summary>
        /// ゲームが始まる前の初期化処理を行うメソッド
        /// グラフィック以外のデータの読み込み、コンポーネントの初期化を行う
        /// </summary>
        protected override void Initialize()
        {
            // TODO: ここに初期化ロジックを書いてください

            // コンポーネントの初期化などを行います
            base.Initialize();
        }

        /// <summary>
        /// ゲームが始まるときに一回だけ呼ばれ
        /// すべてのゲームコンテンツを読み込みます
        /// </summary>
        protected override void LoadContent()
        {
            // テクスチャーを描画するためのスプライトバッチクラスを作成します
            this.spriteBatch = new SpriteBatch(this.GraphicsDevice);

            // エフェクトを作成
            this.basicEffect = new BasicEffect(this.GraphicsDevice);

            // エフェクトで頂点カラーを有効にする
            this.basicEffect.VertexColorEnabled = true;

            // ビューマトリックスをあらかじめ設定 ((10, 10, 10) から原点を見る)
            this.basicEffect.View = Matrix.CreateLookAt(
                    new Vector3(10.0f, 10.0f, 10.0f),
                    Vector3.Zero,
                    Vector3.Up
                );

            // プロジェクションマトリックスをあらかじめ設定
            this.basicEffect.Projection = Matrix.CreatePerspectiveFieldOfView(
                    MathHelper.ToRadians(45.0f),
                    (float)this.GraphicsDevice.Viewport.Width /
                        (float)this.GraphicsDevice.Viewport.Height,
                    1.0f,
                    100.0f
                );

            // 頂点の数
            int vertexCount = 8;

            // 頂点バッファ作成
            this.vertexBuffer = new VertexBuffer(this.GraphicsDevice,
                typeof(VertexPositionColor), vertexCount, BufferUsage.None);

            // 頂点データを作成する
            VertexPositionColor[] vertives = new VertexPositionColor[vertexCount];

            vertives[0] = new VertexPositionColor(new Vector3(-2.0f, 2.0f, -2.0f), Color.Yellow);
            vertives[1] = new VertexPositionColor(new Vector3(2.0f, 2.0f, -2.0f), Color.Gray);
            vertives[2] = new VertexPositionColor(new Vector3(-2.0f, 2.0f, 2.0f), Color.Purple);
            vertives[3] = new VertexPositionColor(new Vector3(2.0f, 2.0f, 2.0f), Color.Red);
            vertives[4] = new VertexPositionColor(new Vector3(-2.0f, -2.0f, -2.0f), Color.SkyBlue);
            vertives[5] = new VertexPositionColor(new Vector3(2.0f, -2.0f, -2.0f), Color.Orange);
            vertives[6] = new VertexPositionColor(new Vector3(-2.0f, -2.0f, 2.0f), Color.Green);
            vertives[7] = new VertexPositionColor(new Vector3(2.0f, -2.0f, 2.0f), Color.Blue);

            // 頂点データを頂点バッファに書き込む
            this.vertexBuffer.SetData(vertives);

            // インデックスバッファを作成
            this.indexBuffer = new IndexBuffer(this.GraphicsDevice,
                IndexElementSize.SixteenBits, 3 * 12, BufferUsage.None);

            // 頂点インデックスを書き込む
            this.indexBuffer.SetData(vertexIndices);
        }

        /// <summary>
        /// ゲームが終了するときに一回だけ呼ばれ
        /// すべてのゲームコンテンツをアンロードします
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: ContentManager で管理されていないコンテンツを
            //       ここでアンロードしてください
        }

        /// <summary>
        /// 描画以外のデータ更新等の処理を行うメソッド
        /// 主に入力処理、衝突判定などの物理計算、オーディオの再生など
        /// </summary>
        /// <param name="gameTime">このメソッドが呼ばれたときのゲーム時間</param>
        protected override void Update(GameTime gameTime)
        {
            // Xbox 360 コントローラ、Windows Phone の BACK ボタンを押したときに
            // ゲームを終了させます
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            {
                this.Exit();
            }

            // TODO: ここに更新処理を記述してください

            // 登録された GameComponent を更新する
            base.Update(gameTime);
        }

        /// <summary>
        /// 描画処理を行うメソッド
        /// </summary>
        /// <param name="gameTime">このメソッドが呼ばれたときのゲーム時間</param>
        protected override void Draw(GameTime gameTime)
        {
            // 画面を指定した色でクリアします
            this.GraphicsDevice.Clear(Color.CornflowerBlue);

            // 描画に使用する頂点バッファをセット
            this.GraphicsDevice.SetVertexBuffer(this.vertexBuffer);

            // インデックスバッファをセット
            this.GraphicsDevice.Indices = this.indexBuffer;

            // パスの数だけ繰り替えし描画 (といっても BasicEffect は通常1回)
            foreach (EffectPass pass in this.basicEffect.CurrentTechnique.Passes)
            {
                // パスの開始
                pass.Apply();
                
                // インデックスを使用してポリゴンを描画する
                this.GraphicsDevice.DrawIndexedPrimitives(
                    PrimitiveType.TriangleList,
                    0,
                    0,
                    8,
                    0,
                    12
                );
            }

            // 登録された DrawableGameComponent を描画する
            base.Draw(gameTime);
        }
    }
}