忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

[PR]

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。


かんたんXNA4.0 その5 三角形の表示

ここまでは2Dでしたが、すこし3Dに近づきたいと思います
(といってもこの回はまだ2Dと変わりありませんが)

XNAの3Dでは(というかXNAに限ったことではないのですが)
物体を三角形の集合で表します。
ポリゴンですね。

たとえば、四角いドアを描こうと思えば、
2つの三角形を使います。
(それだけだと厚さのない薄っぺらいドアですが、
厚みを出そうと思えばその分6つの四角を描けばいいだけです)

丸いボールを描こうと思えば、ものすごく小さい三角形を
たくさん組み合わせて表します。

三角形の描画はCGの基本です。
というわけでまずは三角形を1つだけ表示してみましょう。
 

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

class MyGame : Game
{
    GraphicsDeviceManager graphics;
    BasicEffect effect;
    VertexPositionColor[] vertices = new[]
    { 
        new VertexPositionColor(new Vector3(1, 1, 0), Color.Blue),
        new VertexPositionColor(new Vector3(0, 0, 0), Color.White),
        new VertexPositionColor(new Vector3(-1, 1, 0), Color.Red),
    };

    public MyGame()
    {
        graphics = new GraphicsDeviceManager(this);
    }

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

    protected override void UnloadContent()
    {
        effect.Dispose();
    }

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

        foreach (var pass in effect.CurrentTechnique.Passes)
        {
            pass.Apply();

            GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(
                PrimitiveType.TriangleList,
                vertices,
                0,//vertexOffset
                1 //primitiveCount
                );
        }
    }
}


xna4.0SimplestTriColorTriangle.jpg


このサンプルは三角形を1つだけ表示しています。
その座標は、
(1, 1)
(0, 0)
(-1, 0)
です。
(このとおり、座標はウィンドウの左上が(-1, 1)、右上が(1, 1)、
左下が(-1, -1)、右下が(1, -1)となっています。)


このサンプルでは色の付いた三角形ですが、
テクスチャの写った三角形を作ることも可能です。
(むしろ、実際にゲームを作るとなればそっちの方がメインになるでしょう。)
こういったことは頂点のデータで決めます。

ここで用いた頂点はVertexPositionColor構造体で、座標と色の2つのデータを持っています。
別の構造体を使えば、別の性質を持った三角形を描けるというわけです。
(例えば、VertexPositionTexture構造体を使えばテクスチャの写った三角形を作ることが出来ます。)

public struct VertexPositionColor

コンストラクタ:

public VertexPositionColor ( Vector3 position, Color color )

フィールド:

public Color Color
public Vector3 Position



さて、ここで
何で3次元の座標データを使ったのにやってることは2Dと変わらないんだ
と思うかもしれません。
その答えは、3次元のデータを扱えるだけの十分な情報を設定していないからです。
今回は詳しい説明はしませんが、3次元データを表示するには少なくとも視点が必要です。
その視点(とその他いろいろ)のデータを使って、
3次元データを2次元のディスプレイに表示できるデータに変換し、
ウィンドウに映し出す必要があるのです。
変換の仕方を設定しなければ立体的に表示できないのです。

ここで移っている3色の3角形は、データの変換の仕方が設定されていないため、
2次元のディスプレイに既に表示できるデータであると解釈されて、
そのまま表示されてしまったのです。

BasicEffect

ソースコードの詳しい部分を見ていきましょう。
目に付くのは、BasicEffectクラスです。
日本語に訳すと「基本的な効果」・・・で
何を言っているのかさっぱりわかりませんが、
これはつまり、データがどのように描画されるかを制御するクラスです。
これはかなり重要なクラスで、3Dで描画するときには常にこれを使うと考えていいでしょう。
(実際には自分でBasicEffectの代わりになるEffectを作ってもいいのですが、
これはとても大変で、下手をするとDirectX9の普通の使い方よりもはるかに難しくなります
どのくらい難しいかと言うと、HLSLという描画の方法を細かく指定するための言語を覚えて、
それでいちいち「どのピクセルがどんな色になるのか」といったことを指定しなければなりません。
これはサンプルには荷が重過ぎます。)


public class BasicEffect : Effect

例えば、次のようなものを設定します:
先ほどの3Dデータから2Dデータへの変換の仕方、
表示するときにライトは使うのか、使うとしたらどのようなライトを使うのか・・・
表示するときに霧の効果(奥に行けば行くほどぼやける)を使うのか、使うとしたらどんな霧を使うのか・・・
表示するポリゴンはテクスチャを使うのか、使うとしたらどんなテクスチャを使うのか・・・
・・・といったものを設定します。

おそらく「どのように描画されるか」をEffect(効果)と表現したのでしょう。
でも究極的にはプログラムはどんなものだって効果と言えるわけですから、
無意味なネーミングであるような気もします。

話を戻すと、このBasicEffectのインスタンスを作った後
(ちなみにコンストラクタの2番目の引数は
リソースを共有するグループを表すEffectPoolのインスタンスを指定しますが
、ここでは別に共有とか何とかをするわけではないのでnullです)

さっそくBasicEffect.VertexColorEnabledプロパティで
「表示するときに色を考慮に入れるか」を設定しています。

public bool  VertexColorEnabled { get; set; }

なお、これを設定しないと、次のように色が考慮されなくなってしまいます:
BasicEffectにはいろいろな描画するモードがあって、
そのモードをオンにしてやらないと上手く表示されないと言うわけです。
xnaSimplestTriangleWithoutColor.JPG(注をわざわざ入れる必要はないとは思いますが、全部真っ白です)




Drawメソッドの中を見てみましょう。

まず、Effect.CurrentTechniqueプロパティで現在の描画モードを取得し、
EffectTechnique.Passesプロパティで全てのエフェクトパスを取得しています。

public EffectTechnique CurrentTechnique { get; set; }

public EffectPassCollection Passes { get; }

エフェクトパス(Microsoft.Xna.Framework.Graphics.EffectPass)
というのはちょうどお絵かきソフトで言うレイヤーのようなもので、
いろんな描画の効果を別々に表したものです(たぶん)。
例えば、
描画したいものの大体の色を表すエフェクトパス、
物のハイライトを表すエフェクトパス、
周りの環境からの光の反射による色を表すエフェクトパス、
影を表すエフェクトパス、
その他いろいろでしょう(たぶん)。
Effectを自分で作るときにはこれはそこそこ重要になるかもしれません。
もっともこのプログラムでは、拍子抜けなことに、エフェクトパスは1つだけですが。
ですから、effect.CurrentTechnique.Passes[0]...などとしてもいいのです。
(ペイントでお絵かきをすることを連想させます)

そしてそれぞれのエフェクトパスについて、
EffectPass.Apply()メソッドを描画の前に呼んでいます
(お絵かきソフトでそれぞれのレイヤーを表示しているようなものです)

public void Apply()

この後に呼ばれているのが
今回最も重要なGraphicsDevice.DrawUserPrimitivesジェネリクスメソッドです。

今回はこのメソッドで3角形を実際に描画しています。

public void DrawUserPrimitives<T> (
         PrimitiveType primitiveType,
         T[] vertexData,
         int vertexOffset,
         int primitiveCount
) where T : ValueType


primitiveTypeは頂点データの役割を表します。
どういうことかというと、実はこのメソッドが描画できるのは三角形だけではなくて
点でも線でもいいのです。
ここにはどの図形を描画するかを指定するのです。

public enum PrimitiveType

メンバ
LineList lineList.JPG
LineStrip  lineStrips.JPG
TriangleList  triangleList.JPG
TriangleStrip  triangleStrips.JPG


vertexDataは頂点データです。
これは三角形の3つの頂点だったり、
線の始点と終点の集合だったりします。

vertexOffsetはオフセット、つまり引数の頂点データの使い始める場所です。
これはまず普通0でしょう。

primitiveCountは描画する三角形の数です。
vertexData.Lengthを3で割った値を入れるといいでしょう。
といっても実際は三角形以外も描画できるわけですから、
直線の数だったり(vertexData.Length / 2)
することもあります。



※なお、注意しておきたいことですが
この類のメソッド
(DrawUserPrimitives, DrawPrimitives, DrawUserIndexedPrimitives, DrawIndexedPrimitives)
はDrawメソッドの中で何回呼んでもかまいません!
私が3Dを勉強し始めたときには、
この類のメソッド(特にDrawPrimitivesとDrawIndexedPrimitives!)を一回しか呼んではいけないと勘違いしていて、
ばかげたことに、物を1つしか表示できない時期が続きました。

 

拍手[3回]

PR

かんたんXNA4.0 その4 画像ファイルを表示

ここでは画像ファイルをウィンドウに表示します。

外部のファイルをあつかうには、ContentManagerを使います。
これは、画像ファイルや3Dモデル、フォントなどをゲームにロードするときに使います。
なお、このクラスを使うときには、XNA Game Studioの助けがないと地獄を見ます
というのも、それらのファイル一つ一つに対応するxnbファイルを作らなければならないからです。
(しかもこいつはバイナリファイルです)

例えば、image.jpgというファイルを直接使うことはできません。
"image.xnb"というファイルを"image.jpg"から作って、それを使わなければならないのです。

XNA Game Studioはそれを自動で行ってくれます。
このことを全く気にする必要はありません。
ただ"image.jpg"ファイルをソリューションエクスプローラーのコンテントにペーストするだけでいいのです。

以下がXNA Game Studioでビルドしたコードです。
(Mainメソッドは別のクラスに移動しました)
 

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

class MyGame : Game
{
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    Texture2D texture;

    public MyGame()
    {
        graphics = new GraphicsDeviceManager(this);
    }

    protected override void LoadContent()
    {
        spriteBatch = new SpriteBatch(GraphicsDevice);
        texture = Content.Load<Texture2D>("Content/Penguins");
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);
        spriteBatch.Begin();
        spriteBatch.Draw(texture, new Rectangle(0, 0, 200, 150), Color.White);
        spriteBatch.End();
    }
}


xna4.0SimplestTexturePenguins.jpg

このプログラムは、
"Penguins.jpg"(から作られた"Penguins.xnb")
をゲームにロードして表示しています。

Loadジェネリックメソッドを使って"Penguins.jpg"をロードしているのです。
(正確にはそれから作られたxnbファイルですが)

 

拍手[0回]


かんたんXNA4.0 その3 2D画像の表示

前回はウィンドウの背景を塗りつぶしました。
しかしこれだけでは華がありません。
もう少しましにしましょう。

今回は1×1の画像(Texture2D)を作り、それを表示することにします。
黒い四角を表示するのです。

まず、2Dの描画を行うには、2D描画を全てカプセル化しているSpriteBatchクラスを使います。
このクラスはTexture2Dを表示するSpriteBatch.Drawメソッドや、ここでは扱いませんが、文字列を表示するDrawStringメソッドを持っています。

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

class MyGame : Game
{
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    Texture2D texture;

    public MyGame()
    {
        graphics = new GraphicsDeviceManager(this);
    }

    protected override void LoadContent()
    {
        spriteBatch = new SpriteBatch(GraphicsDevice);
        texture = new Texture2D(GraphicsDevice, 1, 1);
        texture.SetData<Color>(new Color[] { Color.Black });
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);
        spriteBatch.Begin();
        spriteBatch.Draw(texture, new Rectangle(0, 0, 100, 100), Color.White);
        spriteBatch.End();
    }

    static void Main()
    {
        using (MyGame game = new MyGame())
        {
            game.Run();
        }
    }
}

xna4.0SimplestTexture.jpg 
※左上に、黒い1x1のTexture2Dを、 100x100に拡大して描画しています。

前回と比べて新しく出てきたのが、LoadContentメソッドです。
これは、グラフィックスを表すデータ(例えば今回のようにTexture2Dだったり、あるいは3Dのモデルだったりです)をロードすべきときに自動的に呼ばれます。
このメソッドの中で、SpriteBatchのコンストラクタを呼んでいます。
引数にGraphicsDeviceがあるのは、SpriteBatchがGraphicsDeviceを カプセル化しているからでしょう。(多分)

その後Texture2Dの1×1インスタンスをを作成しています。
その後、その1×1のマスを黒で埋めています。
1×1の黒いTexture2Dができあがったというわけです。
もちろん、サイズを変えてもうちょっとカラフルにすることもできます。

        texture = new Texture2D(GraphicsDevice, 3, 1);
        texture.SetData<Color>(new Color[] { Color.Navy, Color.White, Color.Red });

xna4.0SimplestTextureTriColor.jpg

MyGame.Drawメソッドに目を転じましょう。
背景をCornflowerBlueで塗りつぶした後、 左上に100×100に拡大したTexture2Dを描画しています。
そのとき、頭と後ろにSpriteBatch.Beginメソッドと SpriteBatch.Endメソッドを呼んでやる必要があります。
呼ばなければ例外が発生し強制終了です。
これは、2D画像を表示するときだけではなくて、文字を表示するときも同じです。
ちなみに、もちろん、BeginとEndの間にDrawメソッドは何回呼んでもかまいません。


        spriteBatch.Begin();
        spriteBatch.Draw(texture, new Rectangle(0, 0, 100, 100), Color.White);
        spriteBatch.Draw(texture, new Rectangle(200, 200, 80, 80), Color.White);
        spriteBatch.End();

xna4.0SimplestTextureTwoRectangle.jpg
(※2回呼べばとうぜん2つの四角が表示されます)

ちなみにこのColor.Whiteというのは描かれるテクスチャに色付けされる色のことです。
ここに指定する色が青ならばテクスチャは青みがかって描かれます。

さて、今回のこの方法はあまりスマートではありません。
画像はなるべく外部のファイルにしておきたいものです。
次回はその方法について扱います。

 

拍手[2回]


かんたんXNA4.0 その2 背景のクリア

前回では、ウィンドウが全く描画を行っていませんでした。
そのため、XP以前の環境だと他のウィンドウに隠されると跡が残ってたいへん見苦しいものでした。

これではいけません!

このような跡はすぐに消せるようにならなければなりません。
それが出来るようになったのが、以下のコードです:

using Microsoft.Xna.Framework;

class MyGame : Game
{
    GraphicsDeviceManager graphics;

    public MyGame()
    {
        graphics = new GraphicsDeviceManager(this);
    }

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

    static void Main()
    {
        using (MyGame game = new MyGame())
        {
            game.Run();
        }
    }
}

xna4.0SimplestClearable.jpg

このプログラムは、一秒間に60回背景を空色で塗りつぶしています。
それをやっているのがDrawメソッドですが、こいつは一秒間に大体60回呼ばれるのです。

これだけひんぱんに背景を消しているので、もう他のウィンドウに隠されても大丈夫です。
(Vista以降だと何もしなくても大丈夫ですが)

さて、注目すべきは一秒間に60回塗りつぶしているという部分です。
XP以前の普通のプログラム、WindowsFormsなどでは、他のウィンドウに隠れたときなど、描画すべきときにしか描画しませんでした。
しかし、XNAではそのようにはしません。
狂ったように、何も変化が無くても、とりあえず60回描画します
ゲームプログラミングとは、そのようなものだということです。

もう少し細かいところに注目してみましょう。
このプログラムでは、まずMicrosoft.Xna.Framework.GraphicsDeviceManagerのインスタンスを作成しています。
これは、ゲームのウィンドウのサイズやフルスクリーン、それと描画全般を行うGraphicsDeviceを管理するマネージャーです。
(管理(マネージ)するマネージャーとは変な表現ですが、まぁ大目に見てください)

重要なのはこのクラスというよりも、そのプロパティからアクセスできる、GraphicsDeviceです。
このGraphicsDeviceは役割としてはちょうどWin32でいうHDCやWindowsFormsやJavaのGraphicsクラスのようなものです。
つまり、描画はこれを通じて行うのです。
点を表示するにも、線を表示するにも、2D画像を表示するにも、3Dのモデルを表示するにもこのGraphicsDeviceが必要です。
(もっとも、2Dの場合はカプセル化されていて、描画の時には意識する必要はありませんが)

このプログラムではGraphicsDevice.Clearを使って、ウィンドウのクライアント領域を空色で塗りつぶしています。
もちろん色は別に空色でなくてもかまいません。
でもでも黒でもかまいませんが、Xnaの多くのサンプルでは、空の色に似ているからでしょうか、Color.CornflowerBlueを使っています。
多数決の原則(?)により、次回からもColor.CornflowerBlueを使うことにします。

 

拍手[2回]


かんたんXNA4.0 その1 ウィンドウの表示

XNAを簡単なレベルから少しずつ整理していこうと思います。
まずは一番簡単なプログラムから。
class MyGame : Microsoft.Xna.Framework.Game
{
    static void Main()
    {
        using (MyGame game = new MyGame())
        {
            game.Run();
        }
    }
}

これはおそらくXNAの最も簡単なプログラムで、
Gameクラス(を継承したMyGameクラス)のオブジェクトを作り、Runメソッドを呼んでいます。
実行すると(ゲームの)ウィンドウが表示されるだけです。

xna4.0Simplest.jpg

Vista以降では何の面白みもないウィンドウですが、XP以前だと面白い現象が見られます。
他のウィンドウで覆い隠すとこんな風に跡が残ってしまうのです。
これは描画を全くしていないからですね。

xnaSimplestCovered.JPG

※ちなみに(どうでもいいことですが)、このウィンドウのタイトルはMyGameとなっていますが、これはクラスの名前ではなくて実行ファイルの名前に関係しているようです。
というのも、このコードをコンパイルして出来たプログラムは"MyGame.exe"だったのですが、この名前を"Game.exe"に変更するとタイトルはGameになったからです。

ただ、これはコマンドラインから普通にコンパイルした場合の話で、VisualC#を使った場合には、AssemblyInfo.csのAssemblyTitleがタイトルを表しているようです。

拍手[0回]