忍者ブログ

Memeplexes

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

かんたん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