인덱스 버퍼를 사용하여 상자 표시

페이지 업데이트 :
페이지 생성 날짜 :

요약

상자를 만들기 위해 많은 다각형을 사용합니다. 이렇게 하면 인덱스 버퍼를 사용하여 꼭짓점 데이터의 데이터 양을 줄입니다.

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

운영 환경

필수 구성 요소

지원되는 XNA 버전
  • 4.0
지원되는 플랫폼
  • Windows(XP SP2 이상, Vista, 7)
  • 엑스박스 360
  • 윈도우 폰 7
Windows 필수 버텍스 셰이더 버전 2.0
Windows 필수 픽셀 셰이더 버전 2.0

운영 환경

플랫폼
  • 윈도우 7
  • 엑스박스 360
  • Windows Phone 7 에뮬레이터

물질

상자에 관하여

상자는 6개의 면으로 구성되며, 그 중 하나는 2개의 삼각형 다각형으로 구성됩니다. 즉, 삼각형 다각형의 총 수는 "2×6 = 12"입니다. 또한 삼각형 다각형에는 3개의 꼭지점이 있으므로 정점의 합계는 "12×3 = 36"입니다. 따라서 "VertexBuffer"만으로 작성하는 경우 36 개의 데이터가 상자 모양이되도록 위치 정보를 결정하고 기록하면 상자로 표시 할 수 있습니다. (TriangleStrip의 경우 24개가 필요함)

하지만 상자를 상상해 보십시오. 상자의 모서리는 8개입니다. 8개는 위치 정보에 충분해야 합니다. 꼭짓점 데이터의 수가 증가하면 메모리에 부담이 가해집니다. 이것을 어떻게 든 줄이기 위해 "IndexBuffer"를 사용합니다.

다각형에는 8개의 위치 정보만 필요하지만 항상 36개의 꼭짓점이 필요합니다. 따라서 "IndexBuffer"를 사용하는 목적은 8개의 정점 데이터를 공유하는 것입니다.

四角形ポリゴン

/// <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枚目のポリゴン
};

필드는 "IndexBuffer"로 선언되지만 그 아래에 "정점 번호 배열"이 미리 만들어집니다. 이 배열은 36개의 꼭짓점에 대한 배열을 예약하지만 각 숫자의 의미는 각 삼각형 다각형이 사용하는 8개의 꼭짓점 데이터 중 꼭짓점 데이터 수입니다. 자세히 보면 내부의 데이터가 "0 ~ 7" 사이의 인덱스로 쓰여져 있음을 알 수 있습니다. 댓글에서 쉽게 볼 수 있습니다.

그건 그렇고, 배열의 유형은 "Int16 []"이지만 "short []"(2 바이트)일 수도 있습니다. 경우에 따라 "int"(4 바이트)로 배열이 생성되지만 정점 수가 "65535"를 초과 할 때 사용됩니다. 꼭짓점 수가 이 수를 초과하지 않는 경우 2바이트 데이터로 구성된 배열을 만들어 메모리 소비를 줄입니다.

창작

// 頂点の数
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);

정점 버퍼 만들기. 원래는 36개의 꼭짓점을 만들 필요가 있습니다만, 인덱스 버퍼를 사용하면 8개의 꼭지점만 만들면 됩니다.

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

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

인덱스 버퍼 만들기. 두 번째 인수는 쓸 꼭짓점 인덱스의 비트 수를 지정합니다. 하나의 인덱스는 2바이트이므로 "IndexElementSize.SixteenBits"를 지정합니다.

세 번째 인수는 인덱스의 수입니다. 이 경우 12개의 다각형을 그릴 것이므로 삼각형 다각형의 꼭짓점 × 다각형의 수인 36을 지정합니다. 물론 index 배열의 요소 수를 그대로 지정하면 문제가 없지만 이번에는 명확성을 위해 의도적으로 숫자를 분리했습니다.

필드가 있는 꼭짓점 인덱스 배열을 이미 만들었으므로 "IndexBuffer.SetData" 메서드로 작성합니다.

IndexBuffer 생성자

꼭짓점 데이터를 참조하는 인덱스를 관리하는 IndexBuffer 클래스의 인스턴스를 만듭니다.

그래픽장치장치 그래픽장치 인덱스 버퍼와 연결할 GraphicsDevice를 지정합니다.
indexElementSize (영문) IndexElementSize (영문) 단일 꼭짓점 인덱스의 크기입니다. 2바이트의 경우 "SixteenBits", 4바이트의 경우 "ThirtyTwoBits"를 지정하고 BufferUsage.None을 지정합니다.
인덱스 수 정수 인덱스의 수를 지정합니다.
사용법 버퍼 사용량 인덱스 버퍼 사용량입니다. 그렇지 않은 경우를 제외하고는 BufferUsage.None을 지정하십시오.

IndexBuffer.SetData 메서드

꼭짓점 인덱스의 배열을 인덱스 버퍼에 복사합니다.

T ValueType 꼭짓점 인덱스 배열 유형
데이터 T 복사할 꼭짓점 인덱스 배열

그림

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

인덱스 버퍼를 사용하려면 다각형을 그리기 전에 장치에서 인덱스 버퍼를 설정합니다.

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

인덱스 및 꼭짓점 버퍼를 사용하는 경우 "GraphicsDevice.DrawIndexedPrimitives" 메서드를 사용하여 다각형을 그립니다.

네 번째 인수는 생성된 정점의 수입니다. 샘플에서는 8개의 꼭짓점 데이터가 공유되기 때문에 "8"이 지정됩니다.

여섯 번째 인수는 기본 요소의 수를 지정합니다. 12개의 삼각형 다각형을 그리기 때문에 "12"입니다.

다른 숫자 매개 변수의 경우 0을 사용해도 됩니다.

GraphicsDevice.DrawIndexedPrimitives 메서드

지정된 꼭짓점 인덱스와 꼭짓점 버퍼를 기반으로 기본 형식을 그립니다.

프리미티브 타입 프리미티브 타입 그릴 원형을 지정합니다.
baseVertex (기본정점) 정수 인덱스 버퍼의 각 꼭짓점 인덱스에 추가할 오프셋입니다. 예를 들어 첫 번째 꼭짓점 인덱스가 꼭짓점 데이터 2를 가리킬 때 이 인수에 "1"을 지정하면 첫 번째 꼭짓점 인덱스는 꼭짓점 데이터 3을 가리킵니다.
minVertexIndex (영문) 정수 호출에 사용된 꼭짓점의 최소 꼭짓점 인덱스입니다. 예를 들어 minVertexIndex가 1이면 꼭짓점 데이터의 인덱스가 1씩 증가합니다(버퍼 수는 증가하지 않으므로 꼭짓점 데이터의 마지막 요소를 지정할 수 없음). 꼭짓점 인덱스가 두 번째 꼭짓점 데이터를 가리키면 첫 번째 꼭짓점 데이터를 가리킵니다.
numVertices (숫자정점) 정수 사용된 꼭짓점 데이터의 수입니다.
시작 인덱스 정수 꼭짓점 인덱스의 시작 오프셋입니다. 예를 들어, TriangleList를 primitiveType으로 지정하는 경우 "3, 6, 9,..."를 지정하여 그리기를 시작하는 다각형을 건너뜁니다. 3으로 나눈 숫자 이외의 값을 지정하면 모델이 축소됩니다. (모든 인덱스가 꺼져 있기 때문에)
프리미티브 카운트 정수 그릴 기본 형식의 수입니다. 지정할 수 있는 최대값은 "정점 인덱스 수÷ 프리미티브의 정점 수 - startIndex"입니다.

모든 코드

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);
        }
    }
}