Displaying triangular polygons

Page update date :
Page creation date :

summary

It displays triangular polygons in 3D space.

3角形ポリゴンの表示

Operating environment

Prerequisites

Supported XNA Versions
  • 4.0
Supported Platforms
  • Windows (XP SP2 or later, Vista, 7)
  • Xbox 360
  • Windows Phone 7
Windows Required Vertex Shader Version 2.0
Windows Required Pixel Shader Version 2.0

Operating environment

platform
  • Windows 7
  • Xbox 360
  • Windows Phone 7 Emulator

substance

What is a polygon?

A polygon is a face that is generated by multiple vertex definitions. In general, a polygon is a triangular face consisting of three vertices. And the trigon is the smallest unit of the polygon. Some modeling software may display polygons as quadrilateral or polygonal, but these will eventually be decomposed into triangular polygons.

The model displayed in games is formed by combining multiple of these triangular polygons.

最小単位のポリゴン

Polygons are formed by vertices. Vertices can have data such as position and color. This sample is also made from "position" and "color" data.

ポリゴンと頂点

The color of the face in this example is neatly interpolated by the color and distance set for each vertex, but you can freely change it with your own shader program (more on shader programs another time).

Vertex Data Definitions

In order to display a polygon, "vertex data" is required, and the programmer must decide what elements to include in that vertex. When drawing a polygon, you have to tell the device, which is the drawing engine, what vertex data to draw the polygon with. To do this, create a "VertexDeclaration" class and set the "Vertex Data Definition".

However, starting with XNA Game Studio 4.0, this setup has been simplified, and you don't need to prepare a VertexDeclaration class for this tip. (This is because the definition information is already embedded in the vertex data provided by the framework.)

Vertex data

I wrote that vertex data is required to draw polygons, but first we have to decide what kind of data we want to have. In this case, we will use "position" and "color" data. Once you've decided what data you want to have, you need to create a structure to hold that data. You have some freedom to decide what vertex data is, but the commonly used vertex data is already defined in the XNA Framework, so the sample uses it.

Vertex data with "position" and "color" is defined as a "VertexPositionColor" structure. Since multiple vertices are needed to form a polygon, we declare it as an array.

/// <summary>
/// 頂点データリスト
/// </summary>
private VertexPositionColor[] vertices = null;

effect

In XNA, when you draw a polygon, you have to write a separate shader program to decide how to draw it. To do this, create a separate effect file, write a program, load it as an effect class, and run the shader program.

However, if you don't need to draw simple triangular polygons like this one, or complex drawing effects, it can be a very cumbersome task.

For this reason, XNA defines extended effects that allow you to set the required items as properties so that you don't have to write a shader program for basic drawing. That's the "BasicEffect" class. Since the purpose of this article is to draw polygons, we will use "BasicEffect" that does not take much effort to draw.

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

I'll talk more about effects and BasicEffects at another time.

By the way, Windows Phone 7 doesn't allow you to use your own effects, only those built into frameworks like BasicEffect.

Create a vertex data definition

Up until the XNA Framework 3.1, you had to explicitly create them programmatically, but starting with 4.0, the framework's built-in vertex information is already included in the vertex information as "IVertexType.VertexDeclaration", so we will use it.

Creating Effects

Create a BasicEffect class. Set the BasicEffect.VertexColorEnabled property to true to keep the vertex colors.

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

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

BasicEffect constructor

Create an instance of the effect class "BasicEffect" that performs vertex color, texture, and lighting using Shader Model 2.0.

device GraphicsDevice Specifies the GraphicsDevice for creating the effect

View Matrix and Projection Matrix

Set the BasicEffect to a view matrix and a projection matrix. For a conceptual explanation of each, see the links below.

// ビューマトリックスをあらかじめ設定 ((0, 0, 15) から原点を見る)
this.basicEffect.View = Matrix.CreateLookAt(
        new Vector3(0.0f, 0.0f, 15.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
    );

To generate a view matrix, use the "Matrix.CreateLookAt" method.

The first argument specifies the position of the camera, the second argument specifies the camera's point of interest, and the third argument specifies the upward direction of the camera.

In this case, it is set to look at the origin from the position (0, 0, 15).

Matrix.CreateLookAt method

Create a view matrix.

cameraPosition Vector3 Camera Position
cameraTarget Vector3 Camera Point of Interest
cameraUpVector Vector3 Upward direction of the camera

To generate a projection matrix, use the "Matrix.CreatePerspectiveFieldOfView" method.

The first argument is the viewing angle in radians. In the sample, the degree unit is converted to radian using the "MathHelper.ToRadians" method. For more information about Radian and Degree, see Radian and Degree.

The second argument specifies the aspect ratio (aspect ratio). Typically, you specify a value for View Width ÷ Height. In the sample, it is calculated from the width and height set for the viewport of the device.

The third argument specifies the forward clipping position, and the fourth argument specifies the backward clipping position.

Matrix.CreatePerspectiveFieldOfView method

Creates a perspective projection matrix based on the settings of the view field.

fieldOfView float Viewing angle. Specified in radian units.
aspectRatio float Aspect ratio (aspect ratio). Normally, you specify a value for "View Width ÷ Height"
nearPlaneDistance float Forward clip position. Objects in front of this position are not drawn.
farPlaneDistance float Rear clip position. Objects beyond this position are not drawn.

Creating Vertex Data

Create three vertex data. First, we will create an array and create each vertex.

// 頂点データを作成する
this.vertices = new VertexPositionColor[3];

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

To create vertex data, specify the "vertex position" and "vertex color" in the constructor of "VertexPositionColor".

VertexPositionColor constructor

Create an instance of the structure "VertexPositionColor" with the position and color vertex data.

position Vector3 Vertex Position
color Color Vertex color

Set the position of the vertices to the range that can be seen from the camera. Also, set the vertex arrangement to be "clockwise (clockwise)". If you set it to "counterclockwise", the polygon will not be visible. For an explanation of this, see Specifying the Faces of a Polygon to Draw.

Drawing polygons

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

    // ポリゴンを描画する
    this.GraphicsDevice.DrawUserPrimitives(
        PrimitiveType.TriangleList,
        this.vertices,
        0,
        1
    );
}

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

There are two types of effects, called "techniques" and "paths," through which you run the actual drawing program. Since there can be multiple paths in a single effect, I try to call them repeatedly in foreach. However, in BasicEffect, there is one technique and one path, so you can specify the index of the path directly, but the above description will be cleaner because you can substitute it for other effects.

Before starting the actual drawing, call the "EffectPass.Apply" method to start the pass. By calling this method, the parameters of the effect to be used this time are applied to the graphics device.

Once you have started the path, draw the polygon with the "GraphicsDevice.DrawUserPrimitives" method.

The first argument specifies the type of primitive to be drawn. In this case, we will draw a triangular polygon, so specify "PrimitiveType.TriangleList".

The second argument specifies the created vertex data.

The third argument specifies the vertex to draw from. Normally, this is 0.

The fourth argument specifies the number of primitives to draw. In this case, there is only one triangular polygon, so specify 1. Note that it is not the number of vertices.

GraphicsDevice.DrawUserPrimitives method

Draws primitives based on user-supplied vertex data.

T No limit Vertex Data Structures
primitiveType PrimitiveType The type of primitive to draw
vertexData T[] An array of vertex data to draw
vertexOffset int Specify the number of vertex data to be used for drawing
primitiveCount int The number of primitives to draw.

That's it for the drawing program. If you actually run it and a triangle is displayed, you're done.

All Codes

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

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

        /// <summary>
        /// 頂点データリスト
        /// </summary>
        private VertexPositionColor[] vertices = null;

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


        /// <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;

            // ビューマトリックスをあらかじめ設定 ((0, 0, 15) から原点を見る)
            this.basicEffect.View = Matrix.CreateLookAt(
                    new Vector3(0.0f, 0.0f, 15.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
                );

            // 頂点データを作成する
            this.vertices = new VertexPositionColor[3];

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

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

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

                // ポリゴンを描画する
                this.GraphicsDevice.DrawUserPrimitives(
                    PrimitiveType.TriangleList,
                    this.vertices,
                    0,
                    1
                );
            }

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