カウンタ

XNAXNA

ページ更新日 2009/ 1/ 4
広告

ポリゴンの描画する面を指定する

概要
 ポリゴンの描画する面に関しての説明を行っています。サンプルでは左右にカメラを回転させることができます。また A, B, X でカリングモードを変更することができます。
ポリゴンの描画する面を指定する
動作確認バージョン
対応 XNA バージョン
  • 2.0
  • 3.0
対応プラットフォーム
  • Windows (XP SP2 以降, Vista)
  • Xbox 360
必要な頂点シェーダバージョン
1.1
必要なピクセルシェーダバージョン
1.1
サンプルの操作方法
内容

 カメラをまわしてみるとわかりますが、ポリゴンの反対側を見るとポリゴンが描画されません。

ポリゴンの表 ポリゴンの裏
左がポリゴンの表側で、右が裏側

 これはカリングという処理を行っているためで、ポリゴンの裏側を描画しないようになっています。例えば下のような閉じた箱をイメージしてみると分かりますが、箱の内側は通常見えないので見えない部分をわざわざ描画する必要がありません。多くのモデルはこのような閉じた形状であることが多く、ポリゴンの裏側を描画しないで描画コストを減らそうというのがカリングです。

ボックスの内側はもともと見えない
手前の3面だけが見えて奥の3面は見えない

 面の表裏は「頂点の位置」と「頂点の順番」で決まります。一般的には頂点が視点から見て右回り(時計周り)の順番で配置されている面が表になります。

頂点の配置が右回りの面が表

 カリングは描画コストを減らすのに有効なのですが、場合によっては裏面だけを描画させたかったり、薄いオブジェクトを描画するために1枚のポリゴンで両面を描画させたい場合などがあるかと思います。

 プログラムではこれらの設定を「GraphicsDevice.RenderState.CullMode」プロパティで切り替えることができます。「CullMode」列挙には下の3つの値が用意されており、用途に合わせて切り替えられます。

CullMode 列挙
カリングの方法を示します
CullClockwiseFace 右回り(時計周り)の面をカリングします。面の裏側を描画
CullCounterClockwiseFace 左回り(反時計周り)の面をカリングします。面の表側を描画
None カリングを行わず、両面を描画します。
MSDN ライブラリの Web ページへリンク
///// カリングの設定 /////
if (keyState.IsKeyDown(Keys.A) || padState.Buttons.A == ButtonState.Pressed)
{
    // 反時計回りをカリング
    this.GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
}
else if (keyState.IsKeyDown(Keys.B) || padState.Buttons.B == ButtonState.Pressed)
{
    // 時計回りをカリング
    this.GraphicsDevice.RenderState.CullMode = CullMode.CullClockwiseFace;
}
else if (keyState.IsKeyDown(Keys.X) || padState.Buttons.X == ButtonState.Pressed)
{
    // カリングなし
    this.GraphicsDevice.RenderState.CullMode = CullMode.None;
}

 下が、カリングによって描画される結果になります。左が面の表で、右が面の裏です。

  • CullMode.CullCounterClockwiseFace

CullMode.CullCounterClockwiseFace(面の表) CullMode.CullCounterClockwiseFace(面の裏)

  • CullMode.CullClockwiseFace

CullMode.CullClockwiseFace(面の表) CullMode.CullClockwiseFace(面の裏)

  • CullMode.None

CullMode.None(面の表) CullMode.None(面の裏)

サンプル実行ファイル (Windows のみ)
ファイル サイズ 対応XNAバージョン プラットフォーム 作成日
xna_tips_faceculling_3_0_exe.zip 9.5 KB 3.0 Windows (XP SP2 以降, Vista) 2009/01/04
xna_tips_faceculling_2_0_exe.zip 9.8 KB 2.0 Windows (XP SP2, Vista) 2008/01/01
xna_tips_faceculling_1_0_ref_exe.zip 10.0 KB 1.0 Refresh Windows (XP SP2, Vista) 2007/08/25
プロジェクト ダウンロード
ファイル サイズ 対応XNAバージョン プラットフォーム 作成日
xna_tips_faceculling_3_0_project.zip 22.2 KB 3.0 Windows (XP SP2 以降, Vista), Xbox 360 2009/01/04
xna_tips_faceculling_2_0_project.zip 20.6 KB 2.0 Windows (XP SP2, Vista), Xbox360 2008/01/01
xna_tips_faceculling_1_0_ref_project.zip 21.1 KB 1.0 Refresh Windows (XP SP2, Vista), Xbox360 2007/08/25
全コード
using System;
using System.Collections.Generic;
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.Net;
using Microsoft.Xna.Framework.Storage;

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

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

        /// <summary>
        /// 頂点データ定義
        /// </summary>
        private VertexDeclaration vertexDeclaration = null;

        /// <summary>
        /// ポリゴン用頂点データリスト
        /// </summary>
        private VertexPositionColor[] triangleVertives = null;

        /// <summary>
        /// 面の表側を示すラインの頂点データリスト
        /// </summary>
        private VertexPositionColor[] lineVertices = null;

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

        /// <summary>
        /// スプライトでテキストを描画するためのフォント
        /// </summary>
        private SpriteFont font = null;

        /// <summary>
        /// カメラの回転位置
        /// </summary>
        private float cameraRotate = 0.0f;


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

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

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

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

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

            // 頂点定義データを作成
            this.vertexDeclaration = new VertexDeclaration(
                this.GraphicsDevice, VertexPositionColor.VertexElements);

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

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

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

            // ポリゴンの頂点データを作成する
            this.triangleVertives = new VertexPositionColor[3];

            this.triangleVertives[0] = new VertexPositionColor(new Vector3(0.0f, 3.0f, 0.0f),
                                                               Color.Red);
            this.triangleVertives[1] = new VertexPositionColor(new Vector3(3.0f, -2.0f, 0.0f),
                                                               Color.Blue);
            this.triangleVertives[2] = new VertexPositionColor(new Vector3(-3.0f, -2.0f, 0.0f),
                                                               Color.Green);

            // 面の表側を指すようにラインを作成
            this.lineVertices = new VertexPositionColor[2];

            this.lineVertices[0] = new VertexPositionColor(new Vector3(0.0f, -1.0f, 0.0f),
                                                           Color.Blue);
            this.lineVertices[1] = new VertexPositionColor(new Vector3(0.0f, -1.0f, 10.0f),
                                                           Color.Blue);

            // フォントをコンテンツパイプラインから読み込む
            this.font = this.Content.Load<SpriteFont>("Font");
        }

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

        /// <summary>
        /// 描画以外のデータ更新等の処理を行うメソッド
        /// 主に入力処理、衝突判定などの物理計算、オーディオの再生など
        /// </summary>
        /// <param name="gameTime">このメソッドが呼ばれたときのゲーム時間</param>
        protected override void Update(GameTime gameTime)
        {
            // キーボードの情報取得
            KeyboardState keyState = Keyboard.GetState();

            // ゲームパッドの情報取得
            GamePadState padState = GamePad.GetState(PlayerIndex.One);

            // Xbox360 コントローラの BACK ボタンを押したときにゲームを終了させます
            if (padState.Buttons.Back == ButtonState.Pressed)
            {
                this.Exit();
            }

            ///// カリングの設定 /////
            if (keyState.IsKeyDown(Keys.A) || padState.Buttons.A == ButtonState.Pressed)
            {
                // 反時計回りをカリング
                this.GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
            }
            else if (keyState.IsKeyDown(Keys.B) || padState.Buttons.B == ButtonState.Pressed)
            {
                // 時計回りをカリング
                this.GraphicsDevice.RenderState.CullMode = CullMode.CullClockwiseFace;
            }
            else if (keyState.IsKeyDown(Keys.X) || padState.Buttons.X == ButtonState.Pressed)
            {
                // カリングなし
                this.GraphicsDevice.RenderState.CullMode = CullMode.None;
            }

            ///// カメラの位置回転 /////
            if (keyState.IsKeyDown(Keys.Left))
            {
                this.cameraRotate -= MathHelper.Pi / 16.0f;
            }
            else if (keyState.IsKeyDown(Keys.Right))
            {
                this.cameraRotate += MathHelper.Pi / 16.0f;
            }
            this.cameraRotate += padState.ThumbSticks.Left.X * MathHelper.Pi / 16.0f;

            // ビューマトリックスを設定
            this.basicEffect.View = Matrix.CreateLookAt(
                    Vector3.Transform(new Vector3(0.0f, 0.0f, 10.0f),
                        Matrix.CreateRotationY(this.cameraRotate)),
                    Vector3.Zero,
                    Vector3.Up
                );

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

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

            // 描画する頂点データの定義を設定
            this.GraphicsDevice.VertexDeclaration = this.vertexDeclaration;

            // エフェクトの使用を開始します
            this.basicEffect.Begin();

            // パスの数だけ繰り替えし描画 (といっても BasicEffect は通常1回)
            foreach (EffectPass pass in this.basicEffect.CurrentTechnique.Passes)
            {
                // パスの開始
                pass.Begin();

                // 三角形を描画する
                this.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(
                    PrimitiveType.TriangleList,
                    this.triangleVertives,
                    0,
                    1
                );

                // 面の表側を示すラインを描画
                this.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(
                    PrimitiveType.LineList,
                    this.lineVertices,
                    0,
                    1
                );

                // パスの終了
                pass.End();
            }

            // エフェクトの使用を終了する
            this.basicEffect.End();

            // スプライトの描画準備
            this.spriteBatch.Begin(
                SpriteBlendMode.AlphaBlend,
                SpriteSortMode.BackToFront,
                SaveStateMode.SaveState);

            // カリングモードを表示
            this.spriteBatch.DrawString(this.font,
                "A : CullCounterClockwiseFace",
                new Vector2(50, 50),
                (this.GraphicsDevice.RenderState.CullMode == CullMode.CullCounterClockwiseFace ?
                    Color.Yellow : Color.White));

            this.spriteBatch.DrawString(this.font,
                "B : CullClockwiseFace",
                new Vector2(50, 70),
                (this.GraphicsDevice.RenderState.CullMode == CullMode.CullClockwiseFace ?
                    Color.Yellow : Color.White));

            this.spriteBatch.DrawString(this.font,
                "X : None",
                new Vector2(50, 90),
                (this.GraphicsDevice.RenderState.CullMode == CullMode.None ?
                    Color.Yellow : Color.White));

            // スプライトの一括描画
            this.spriteBatch.End();

            // 登録された DrawableGameComponent を描画する
            base.Draw(gameTime);
        }
    }
}
更新履歴
更新日時 更新内容
2009/01/04 XNA Game Studio 3.0 のプロジェクト追加
2008/05/18 文章・プログラムの校正
2008/01/01 XNA Game Studio 2.0 用に修正
2007/08/25 ページ作成
ページトップ▲
Copyright (C) since 2005 Yuichi Onodera (オノデラユウイチ), All rights reserved.