忍者ブログ

Memeplexes

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

[PR]

×

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


かんたんXNA4.0 その27 ビューポートで分割スクリーン

今回はゲーム画面を2つに分割する方法です。

2人のプレイヤーでゲーム対戦する場合を考えましょう。
その場合、2Dならともかく、3Dなら
プレイヤーによって画面が2つに分かれているべきでしょう。
画面が左と右の二つに分かれていて、
プレイヤーはそれぞれ自分の画面を見て相手を倒すのです!

そういったことをする場合、ゲームのモデルを全て
それぞれの画面に1回ずつ、計2回描画することになります。

その際、単に描画したのでは二人の画面が重なってしまいます。
別々の場所にモデルを描画しなければなりません。

そのように、別々の場所に描画するのに使うのが、ビューポートです。
これを使うと、描画する領域の位置や大きさを変更することが出来ます。




ビューポートはGraphicsDevice.Viewportプロパティで設定します。

public Viewport Viewport { get; set; }

このプロパティが扱っているのは
Microsoft.Xna.Framework.Graphics.Viewport構造体です。

[Serializable]
public struct Viewport


この構造体はモデルを描画する画面の位置やサイズを
表すプロパティを持っています。

public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
...


この構造体があらわす画面は普通は
ウィンドウのクライアントエリアと同じです。
しかし、今回のように半分に分割したい場合には
プロパティをいろいろと変更しないといけないでしょう。

  左スクリーン 右スクリーン
X 0 左スクリーンの横幅
Width 画面の横幅の半分 画面の横幅の半分

また、分割スクリーンをやるさいには、ビューポート以外にも
変更しなければいけないところがあります。
それはBasicEffect.Projectionプロパティです。
幅を半分にしたので、このプロパティにセットするマトリックスも
変えなければならないのです。
描画する画面の四角形の形をあらわすアスペクト比(横/縦)
を半分にしなければなりません。
そうしなければ、表示するモデルが横に2倍に引き伸ばされてしまいます。


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



class MyGame : Game
{
    GraphicsDeviceManager graphics;
    BasicEffect basicEffect;

    VertexPositionColor[] vertices =
    {
        new VertexPositionColor(new Vector3(0, 1, 0), Color.White),
        new VertexPositionColor(new Vector3(1, 0, 0), Color.Blue),
        new VertexPositionColor(new Vector3(-1, 0, 0), Color.Red)
    };

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

    protected override void LoadContent()
    {
        basicEffect = new BasicEffect(GraphicsDevice)
        {
            VertexColorEnabled = true,
            View = Matrix.CreateLookAt
            (
                new Vector3(0, 0, 2),  //カメラの位置
                new Vector3(0, 0, 0),   //カメラの見る点
                new Vector3(0, 1, 0)    //カメラの上向きベクトル
            ),
            Projection = Matrix.CreatePerspectiveFieldOfView
            (
                MathHelper.ToRadians(45),   //視野の角度。ここでは45°
                GraphicsDevice.Viewport.AspectRatio / 2,//画面のアスペクト比(=横/縦)
                1,      //カメラからこれより近い物体は画面に映らない
                100     //カメラからこれより遠い物体は画面に映らない
            )
        };
    }

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

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

        Viewport fullViewport = graphics.GraphicsDevice.Viewport;
        Viewport leftViewport = fullViewport;
        leftViewport.Width /= 2;
        graphics.GraphicsDevice.Viewport = leftViewport;

        drawTriangle();

        Viewport rightViewport = leftViewport;
        rightViewport.X = leftViewport.Width;
        graphics.GraphicsDevice.Viewport = rightViewport;

        drawTriangle();

        graphics.GraphicsDevice.Viewport = fullViewport;

    }

    private void drawTriangle()
    {
        foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
        {
            pass.Apply();

            GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(
                PrimitiveType.TriangleList,
                vertices,
                0,
                vertices.Length / 3
                );
        }
    }
}




xna4.0SimplestViewport.jpg
このサンプルプログラムでは、1つの同じ三角形を
左右2つのビューポートに描画しています。
そのため、画面が真ん中で切れていて、
分割スクリーンになっているのです。

ここで強調したいのは、三角形の座標やカメラの位置は
左右で全く変わっていないということです。
違うのは、描画している領域、ビューポートだけです。
ビューポートの違いにより2つの三角形がずれているのです。

これを応用するといろいろと面白いことが出来ます。
カメラを2つの画面で全く違ったものにして二人対戦に使うことも出来るでしょうし、
あるいはカメラを左右で少しだけずらして
交差法や平行法で立体視をしてみるのも面白いかもしれません。

拍手[0回]

PR

かんたんXNA4.0 その26 ステンシル・バッファ

ステンシル・バッファ

3Dの映像を描画する時には、
描画する部分を限定したい時があります。
例えば水や鏡にモデルが映りこんでいるときです。

そういうときにはモデルを鏡に対して反転してもう一回
描画するというやり方がありますが、
それでは鏡をはみ出してしまうかもしれません。

出来るなら鏡の領域の中でだけ
描画できるようにしたいものです。

あるいは物体の影を描く時も、そのように描画する
領域を制限したくなる場合があります。
影で暗くなる所だけ描画するようにして
半透明の黒で描画するのです。
(ここで言っているのは、
面が斜めになっていることによって出来る「陰」ではなくて、
他の物体によって光がさえぎられることによって出来る「影」です。)


こういったときに描画する領域を限定するために使うのが、
ステンシル・バッファです。
(ステンシルというのは、何か模様の形をした穴の開いた型があって、
そこにスプレーをプシューとやるとその模様が写るあれです。)


使い方は深度バッファやアルファ・テストに似ています。
とくに深度バッファとはかなり似ているので
ひとくくりにされることもあるくらいです。
深度バッファと同じように、各ピクセルにステンシルの値を表すデータがあって、
そのデータに基づいて描画するかしないかを制御することが出来るのです。
(ちなみに最初の値は深度バッファと違って0です。
深度バッファの最初の値は1でした。)


例えばあるモデルを四角形の中でだけ描画されるようにしたいとしましょう。
まず「型」を作るようにステンシル関連の設定をして、とりあえず四角形を描画します。
そうするとその四角形の形の「型」ができます。
四角形のそれぞれのピクセルのステンシル値が書き換えられたのです。

その後ステンシルの設定を「スプレーのプシュー」が出来るようにします。
そしてモデルを描画すると、きれいに「型」の中でだけ描画されるのです。
四角形の外には描画されません。

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

class Triangle
{
    VertexPositionColor[] vertices = new VertexPositionColor[3];

    public Triangle(Vector3 p1, Vector3 p2, Vector3 p3, Color color)
    {
        vertices[0] = new VertexPositionColor(p1, color);
        vertices[1] = new VertexPositionColor(p2, color);
        vertices[2] = new VertexPositionColor(p3, color);
    }

    public void Draw(GraphicsDevice graphicsDevice, BasicEffect effect)
    {
        effect.VertexColorEnabled = true;

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

            graphicsDevice.DrawUserPrimitives<VertexPositionColor>(
                PrimitiveType.TriangleList,
                vertices,
                0,
                1
                );
        }
    }
}

class MyGame : Game
{
    GraphicsDeviceManager graphics;
    BasicEffect effect;

    Triangle whiteTriangle = new Triangle(
        new Vector3(0, 1, 1),
        new Vector3(1, 0, 1),
        new Vector3(-1, 0, 1),
        Color.White
        );
    Triangle blueTriangle = new Triangle(
        new Vector3(1, 1, 0),
        new Vector3(0, 0, 0),
        new Vector3(-1, 1, 0),
        Color.Blue
        );

    DepthStencilState stencilTemplateCreator = new DepthStencilState
    {
        StencilEnable = true,
        StencilFunction = CompareFunction.Always,
        StencilPass = StencilOperation.Replace,

        ReferenceStencil = 1,
        DepthBufferWriteEnable = false,
    };
    BlendState noColorWriter = new BlendState
    {
        ColorWriteChannels = ColorWriteChannels.None
    };
    DepthStencilState sprayer = new DepthStencilState
    {
        StencilEnable = true,
        ReferenceStencil = 0,
        StencilFunction = CompareFunction.Less
    };


    public MyGame()
    {
        graphics = new GraphicsDeviceManager(this);
        graphics.PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8;
    }

    protected override void LoadContent()
    {
        effect = new BasicEffect(GraphicsDevice)
        {
            View = Matrix.CreateLookAt
            (
                new Vector3(0, 0, 3),  //カメラの位置
                new Vector3(0, 0, 0),   //カメラの見る点
                new Vector3(0, 1, 0)    //カメラの上向きベクトル
            ),
            Projection = Matrix.CreatePerspectiveFieldOfView
            (
                MathHelper.ToRadians(45),   //視野の角度。ここでは45°
                GraphicsDevice.Viewport.AspectRatio,//画面のアスペクト比(=横/縦)
                1,      //カメラからこれより近い物体は画面に映らない
                100     //カメラからこれより遠い物体は画面に映らない
            )
        };
    }

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

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

        GraphicsDevice.DepthStencilState = stencilTemplateCreator;
        GraphicsDevice.BlendState = noColorWriter;
        whiteTriangle.Draw(GraphicsDevice, effect);

        GraphicsDevice.DepthStencilState = sprayer;
        GraphicsDevice.BlendState = BlendState.Opaque;
        blueTriangle.Draw(GraphicsDevice, effect);
    }
}

stencilBuffer.JPG
このサンプルは大きな三角形でステンシルの型を作って、
そのあとその型に対して青い三角形を描画しています。
そのため、ちょっと領域外の部分が描画されず、
ちょっといびつな形になっています。

stencil.JPG「型」を作るのに使った三角形
triangleInStencil.JPGその「型」の中に描画した三角形

初期化の前に

さて、実はステンシル・バッファはそのままでは使えません。
Drawメソッド内でどんなにあがいても使えないのです。
まずは一番最初の、グラフィックスデバイスの設定をいじって、
ステンシルバッファを使えるようにしなければなりません。



それにはGraphicsDeviceManager.PreferredDepthStencilFormatプロパティを使います。

public DepthFormat PreferredDepthStencilFormat { get; set; }

このプロパティを使って、深度バッファやステンシルバッファのフォーマットを設定します。
なお、これのプロパティを使うのはグラフィックスデバイスが実際に
作られる前でなければなりません。
(その後、例えばLoadContentメソッド内で呼んでも効果はありません)
なので、コンストラクタ中やInitialize()中でセットするといいでしょう。

深度バッファとステンシルバッファが一緒になっているのは、
この二つはセットになって確保されるからです。

このプロパティの型であるMicrosoft.Xna.Framework.Graphics.DepthFormat列挙体は、
どんなフォーマットの深度バッファを使うのかを表します。
このメンバによってはステンシルバッファを扱うようにもできるのです。

メンバ名 説明
Depth16 深度バッファは16bitです。
Depth24 深度バッファは24bitです。ただし、実際には32bit確保されます。そのうち使える分が24bitということです。
Depth24Stencil8 深度バッファは24bitで、ステンシルバッファは8bitです。
None 深度バッファを作りません。




Drawメソッド内ではステンシル・バッファを使うにはまず
DepthStencilState.StencilEnableプロパティを使います。

public bool StencilEnable { get; set; }

これをtrueにするとステンシル・バッファが有効になります。

ステンシル・バッファに書き込むには
DepthStencilState.StencilFunctionプロパティと
DepthStencilState.StencilPassプロパティを設定する必要があります。

public CompareFunction StencilFunction { get; set; }

このプロパティはステンシルバッファに書き込むかどうかの条件を表します。
ちょうど深度バッファ関数のようなものです(使ってる列挙体も同じですしね)
サンプルではポリゴンを描いたら必ずそこに
ステンシル・バッファが書き込まれるようにAlwaysをセットしてあります。

さて、ここで「書き込む」という表現をしましたが、
これはあまり正確ではなくて、実はStencilFunctionプロパティの条件に合格したからといって
ステンシルの値が新しく書き込まれるれるとは限りません。
デフォルトの設定では書き込まれません。
きちんと条件に合格した時に「書き換える」と明示してやらなければならないのです。

それをやるのがStencilPassプロパティです。

public StencilOperation StencilPass { get; set; }

このプロパティはStencilFunctionの条件に合格した時に(Passした時に)
どのような操作を行うかを表します。

この型はMicrosoft.Xna.Framework.Graphics.StencilOperation列挙体で、
いくつかのメンバがあります。

メンバ名 説明
Decrement ステンシル・バッファの値をデクリメントします。0より小さくなったらMaxの値になります。
DecrementSaturation ステンシル・バッファの値をデクリメントします。ただし、0になるまでです。
Increment ステンシル・バッファの値をインクリメントします。Maxの値を超えたら0に戻ります。
IncrementSaturation ステンシル・バッファの値をインクリメントします。ただし、Maxの値になるまでです。
Invert ステンシル・バッファのビットを反転します。
Keep ステンシル・バッファの値を書き換えません。デフォルトの値です。
Replace ステンシル・バッファをRenderState.ReferenceStencilプロパティの値に書き換えます。
Zero ステンシル・バッファの値を0にセットします。

サンプルではReplaceを使って書き込んでいます。
これは、DepthStencilState.ReferenceStencilプロパティの値を
ステンシル・バッファに書き込むものです。

DepthStencilState.ReferenceStencilはこのように書き込みに使われたり、
あるいは後で、「スプレーのプシュー」をする時の条件に使われたりします。

public int ReferenceStencil { get; set; }

これはアルファ・テストのReferenceAlphaにも似ていますし、
あるいは深度バッファで言うならそれぞれの頂点の変換後のz座標に相当します。
ようするにこの値を使って描画するかしないかを決めているということです。

拍手[0回]


かんたんXNA4.0 その25 ブレンド関数

物を半透明にするアルファ・ブレンディングですが、
ブレンド関数を使うことによってもっと別のことも出来ます。

アルファ・ブレンディングとはもう少し一般的にいうと、
色を混ぜる(ブレンドする)ということです。
ある混ぜ方をすると物が半透明になりますが、
別の混ぜ方をすれば別の効果が得られます。

前回では2つの色は常に足されましたが、(変わったのは係数だけ)
これは逆に引いたり、あるいは最大値をとったり、
最小値をとったりするようにも変更できます。
(そのため、アルファは単なる不透明度ではない場合だってたくさんあり、
むしろそれがほとんどなのです。)


このような、混ぜ方の関数を設定するのが
BlendState.ColorBlendFunctionプロパティです。


public BlendFunction CoplorBlendFunction { get; set; }

これはMicrosoft.Xna.Framework.Graphics.BlendFunction列挙体を
セットまたはゲットします。

BlendFunction列挙体
メンバ名 説明
Add "最終的な色" = ("これから描画するポリゴンの色" × SourceBlend) + ("描画する前の色" × DestinationBlend)
Max "最終的な色" = max( ("これから描画するポリゴンの色" × SourceBlend), ("描画する前の色" × DestinationBlend) )
Min "最終的な色" = min( ("これから描画するポリゴンの色" × SourceBlend), ("描画する前の色" × DestinationBlend) )
ReverseSubtract "最終的な色" = ("描画する前の色" × DestinationBlend) - ("これから描画するポリゴンの色" × SourceBlend)
Subtract "最終的な色" = ("これから描画するポリゴンの色" × SourceBlend) - ("描画する前の色" × DestinationBlend)


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


class Triangle
{
    VertexPositionColor[] vertices = new VertexPositionColor[3];

    public Triangle(Vector3 p1, Vector3 p2, Vector3 p3, Color color)
    {
        vertices[0] = new VertexPositionColor(p1, color);
        vertices[1] = new VertexPositionColor(p2, color);
        vertices[2] = new VertexPositionColor(p3, color);
    }

    public void Draw(GraphicsDevice graphicsDevice, BasicEffect effect)
    {
        effect.VertexColorEnabled = true;

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

            graphicsDevice.DrawUserPrimitives<VertexPositionColor>(
                PrimitiveType.TriangleList,
                vertices,
                0,
                1
                );
        }
    }
}

class MyGame : Game
{
    GraphicsDeviceManager graphics;
    BasicEffect effect;

    Triangle redTriangle = new Triangle
    (
        new Vector3(1, 0.5f, -1),
        new Vector3(0, -1, -1),
        new Vector3(-1, 0.5f, -1),
        Color.Red
    );

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

    protected override void LoadContent()
    {
        effect = new BasicEffect(GraphicsDevice)
        {
            View = Matrix.CreateLookAt
            (
                new Vector3(0, 0, 3),  //カメラの位置
                new Vector3(0, 0, 0),   //カメラの見る点
                new Vector3(0, 1, 0)    //カメラの上向きベクトル
            ),
            Projection = Matrix.CreatePerspectiveFieldOfView
            (
                MathHelper.ToRadians(45),   //視野の角度。ここでは45°
                GraphicsDevice.Viewport.AspectRatio,//画面のアスペクト比(=横/縦)
                1,      //カメラからこれより近い物体は画面に映らない
                100     //カメラからこれより遠い物体は画面に映らない
            )
        };

        GraphicsDevice.BlendState = new BlendState
        {
            ColorSourceBlend = Blend.SourceAlpha,
            ColorDestinationBlend = Blend.One,
            ColorBlendFunction = BlendFunction.ReverseSubtract,
        };
    }

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

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


        redTriangle.Draw(GraphicsDevice, effect);
    }
}

blendFunctionReverseSubtract.JPG
さて、赤い三角形を描画したはずなのに青っぽくなっています。
どういうことでしょう?

これは、背景の色から赤い成分が引かれたのです。

"最終的な色(0, 149, 237)" = (コーンフラワーブルー(100, 149, 237) × 1) - (赤(255, 0, 0) × 1)

デフォルトでは成分は足されるのですが、ここでは引かれています。
このように、BlendState.ColorBlendFunctionを使うと
詳しい色の混ぜ方を設定することが出来るのです。

拍手[0回]


かんたんXNA4.0 その24 アルファ・ブレンディング

XNAでは物を半透明に描くことが出来ます。
ゼリーのように、後ろのものが透けて見えるのです。

これをアルファ・ブレンディングと言います。
不透明度(アルファ)を考慮する描画だからです。

ポリゴンをスライムのように透明にするには、
BlendState.ColorDestinationBlendプロパティを使います。

public Blend ColorDestinationBlend { get; set; }

Blend列挙体
メンバ名 説明
Zero × 0
One × 1
SourceColor × "これから描くポリゴンの色"

※正確には掛け算と言うより、「それぞれの色の要素が”これから書くポリゴンの色”の対応する色の要素倍される」です。

例えば、(1, 1, 1)と(0.1, 0.2, 0.3)なら(0.1, 0.2, 0.3)。
(0.1, 0.2, 0.3)と(0.1, 0.2, 0.3)なら(0.01, 0.04, 0.09)
InverseSourceColor × (白 - "これから描くポリゴンの色")
SourceAlpha × "これから描くポリゴンの不透明度"
InverseSourceAlpha × (1 - "これから描くポリゴンの不透明度")
DestinationAlpha × "背景の不透明度"
InverseDestinationAlpha × (1 - "背景の不透明度")
DestinationColor × 背景の色
InverseDestinationColor × (白 - "背景の色")
SourceAlphaSaturation × Min("これから描くポリゴンの不透明度", 1 - 描画前の不透明度)
BothInverseSourceAlpha これはDestinationBlendプロパティにセットする必要はありません。これをSourceBlendプロパティにセットするとDestinationBlendの中身は上書きされます(ただしプロパティの値には反映されず、Zeroのままですが)。

この値をセットすると、
最終的な色 =
描画前の色 × 描画するポリゴンの不透明度
+ 描画するポリゴンの色 × (1 - 描画するポリゴンの不透明度)

となります。
BlendFactor × "RenderState.BlendFactorプロパティにセットした色"
InverseBlendFactor × (白 - "RenderState.BlendFactorプロパティにセットした色")
ただしこのモードは、GraphicsDeviceCapabilitiesクラスでSourceBlendCapabilitiesプロパティかDestinationBlendCapabilitiesプロパティのSupportsBlendFactorプロパティ(こいつはgetだけでsetはできません)がtrueのときにしか使えません。


このプロパティは、ポリゴンをその上に描画する前の背景が
どの程度結果に影響するかを表します。
デフォルトではBlend.Zeroで全く影響されません。
つまり、背景は全く描画結果に現れないのです。(不透明に見えます)

"最終的な色" = "これから描画するポリゴンの色"

もし透明なポリゴンを描こうと思えば、
これを、例えばBlend.Oneに指定してやればいいでしょう。
そうすれば、

"最終的な色" = "描画する前の色" + "描画するポリゴンの色"

となり、半透明に見えます。
実際にはBlendState.Additiveで同じことができます。
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;


class Triangle
{
    VertexPositionColor[] vertices = new VertexPositionColor[3];

    public Triangle(Vector3 p1, Vector3 p2, Vector3 p3, Color color)
    {
        vertices[0] = new VertexPositionColor(p1, color);
        vertices[1] = new VertexPositionColor(p2, color);
        vertices[2] = new VertexPositionColor(p3, color);
    }

    public void Draw(GraphicsDevice graphicsDevice, BasicEffect effect)
    {
        effect.VertexColorEnabled = true;

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

            graphicsDevice.DrawUserPrimitives<VertexPositionColor>(
                PrimitiveType.TriangleList,
                vertices,
                0,
                1
                );
        }
    }
}

class MyGame : Game
{
    GraphicsDeviceManager graphics;
    BasicEffect effect;

    Triangle redTriangle = new Triangle
    (
        new Vector3(1, 0.5f, -1),
        new Vector3(0, -1, -1),
        new Vector3(-1, 0.5f, -1),
        Color.Red
    );

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

    }

    protected override void LoadContent()
    {
        effect = new BasicEffect(GraphicsDevice)
        {
            View = Matrix.CreateLookAt
            (
                new Vector3(0, 0, 3),  //カメラの位置
                new Vector3(0, 0, 0),   //カメラの見る点
                new Vector3(0, 1, 0)    //カメラの上向きベクトル
            ),
            Projection = Matrix.CreatePerspectiveFieldOfView
            (
                MathHelper.ToRadians(45),   //視野の角度。ここでは45°
                GraphicsDevice.Viewport.AspectRatio,//画面のアスペクト比(=横/縦)
                1,      //カメラからこれより近い物体は画面に映らない
                100     //カメラからこれより遠い物体は画面に映らない
            )
        };
    }

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

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

        GraphicsDevice.BlendState = BlendState.Additive;
        redTriangle.Draw(GraphicsDevice, effect);
    }
}


destBlendOne.JPG
ここでは赤い三角形を描画しているのですが、
アルファ・ブレンディングの効果で半透明になり、
ピンクっぽくなっています。

背景のコーンフラワーブルーと赤が
加算されているのです。

さて、このサンプルプログラムはアルファ・ブレンディングを
銘打っておきながら不透明度(アルファ)を利用していません。
ただ単純にポリゴンの色を加算しているだけです。

サギです。

不透明値を変更しても実際に描画されるポリゴンの
色には変わりがないのですから!

ポリゴンの色の不透明度のデータを変更したら、
実際に描画される色も変更されて欲しいものです。

それに、この描画の仕方はゼリーやスライムというよりもむしろ
自分で光っている透明な物体、光るゼリーに近いのです。
(光るゼリーなんて見たことありませんが、
他に形容するのは難しいですからね)


アルファが本当に考慮されるアルファ・ブレンディングを行うには、
BlendState.ColorSourceBlendプロパティも使います。

public Blend ColorSourceBlend [ get; set; }

このプロパティは、これから描画するポリゴンの色が
どの程度最終的な色に反映されるかを表します。

デフォルトはBlend.Oneで、これから描くポリゴンの色が
100%反映されるようになっています。

実際には、BlendState.AlphaBlendを使うだけで構いません。
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;


class Triangle
{
    VertexPositionColor[] vertices = new VertexPositionColor[3];

    public Triangle(Vector3 p1, Vector3 p2, Vector3 p3, Color color)
    {
        vertices[0] = new VertexPositionColor(p1, color);
        vertices[1] = new VertexPositionColor(p2, color);
        vertices[2] = new VertexPositionColor(p3, color);
    }

    public void Draw(GraphicsDevice graphicsDevice, BasicEffect effect)
    {
        effect.VertexColorEnabled = true;

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

            graphicsDevice.DrawUserPrimitives<VertexPositionColor>(
                PrimitiveType.TriangleList,
                vertices,
                0,
                1
                );
        }
    }
}

class MyGame : Game
{
    GraphicsDeviceManager graphics;
    BasicEffect effect;

    Triangle redTriangle = new Triangle
    (
        new Vector3(1, 0.5f, -1),
        new Vector3(0, -1, -1),
        new Vector3(-1, 0.5f, -1),
        new Color(255, 0, 0, 250)
    );

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

    }

    protected override void LoadContent()
    {
        effect = new BasicEffect(GraphicsDevice)
        {
            View = Matrix.CreateLookAt
            (
                new Vector3(0, 0, 3),  //カメラの位置
                new Vector3(0, 0, 0),   //カメラの見る点
                new Vector3(0, 1, 0)    //カメラの上向きベクトル
            ),
            Projection = Matrix.CreatePerspectiveFieldOfView
            (
                MathHelper.ToRadians(45),   //視野の角度。ここでは45°
                GraphicsDevice.Viewport.AspectRatio,//画面のアスペクト比(=横/縦)
                1,      //カメラからこれより近い物体は画面に映らない
                100     //カメラからこれより遠い物体は画面に映らない
            )
        };

    }

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

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

        GraphicsDevice.BlendState = BlendState.AlphaBlend;
        redTriangle.Draw(GraphicsDevice, effect);
    }
}
alpha20.JPG
このサンプルプログラムは、不透明度が255中20の
赤い三角形を描画しています。

「最終的に描画される色」 
        = 不透明度 × 「描画前の色」 + (1 - 不透明度)×「描画するポリゴンの色」


ぼんやりとしていますが、不透明度を上げていくと
徐々にはっきりしてきます。

alpha40.JPG40/255
alpha80.JPG80/255
alpha160.JPG160/255
alpha255.JPG255/255

不透明度が255の三角形は普通に描画したものと変わりません。
これが前のサンプルとは違うところです。

前のサンプルは背景と三角の色を単純に足していましたが、
このサンプルではポリゴンの色の不透明度が考慮慮されて足されます。

拍手[0回]


かんたんXNA4.0 その23 深度バッファ関数

さて、深度バッファとは物体の前後関係から
適切に描画するためのデータでした。

カメラからの距離がより小さいときには描画し、
より大きいときには描画しないのです。

こうすることによって近くにあるものは遠くにあるものを隠し、
遠くにあるものは近くにあるものに隠されるのです。

実はこの挙動は変更することが出来ます。
例えば全く逆、カメラからの距離がより大きいときには描画し、
より小さいときには描画しないということができます。

近くにあるものは遠くにあるものに隠れ、
遠くにあるものは近くにあるものを隠すことが可能となるのです!!
(そんなことをして何が楽しいのかは知りませんが)
こういった挙動はDepthStencilState.DepthBufferFunctionプロパティで
設定します。

public CompareFunction DepthBufferFunction { get; set; }

このプロパティで表される条件を満たしたときのみ描画し、
満たさないときは描画しません。
そしてデフォルトの条件は「より小さいとき」です。

このプロパティの型は
Microsoft.Xna.Framework.CompareFunction列挙体です。

メンバ名 説明
Always 常に描画
Equal ==
Greater >
GreaterEqual >=
Less <
LessEqual <=
Never 常に描画しない
NotEqual !=

デフォルトの値はLessEqualです。
カメラからの距離が「より小さいとき」、
つまりカメラに近いときは描画すると言うことです。
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;


class Triangle
{
    VertexPositionColor[] vertices = new VertexPositionColor[3];

    public Triangle(Vector3 p1, Vector3 p2, Vector3 p3, Color color)
    {
        vertices[0] = new VertexPositionColor(p1, color);
        vertices[1] = new VertexPositionColor(p2, color);
        vertices[2] = new VertexPositionColor(p3, color);
    }

    public void Draw(GraphicsDevice graphicsDevice, BasicEffect effect)
    {
        effect.VertexColorEnabled = true;

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

            graphicsDevice.DrawUserPrimitives<VertexPositionColor>(
                PrimitiveType.TriangleList,
                vertices,
                0,
                1
                );
        }
    }
}

class MyGame : Game
{
    GraphicsDeviceManager graphics;
    BasicEffect effect;

    Triangle blueTriangle = new Triangle
    (
        new Vector3(0, 1, 0),
        new Vector3(1, -0.5f, 0),
        new Vector3(-1, -0.5f, 0),
        Color.Blue
    );
    Triangle redTriangle = new Triangle
    (
        new Vector3(1, 0.5f, -1),
        new Vector3(0, -1, -1),
        new Vector3(-1, 0.5f, -1),
        Color.Red
    );

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

    }

    protected override void LoadContent()
    {
        effect = new BasicEffect(GraphicsDevice)
        {
            View = Matrix.CreateLookAt
            (
                new Vector3(0, 0, 3),  //カメラの位置
                new Vector3(0, 0, 0),   //カメラの見る点
                new Vector3(0, 1, 0)    //カメラの上向きベクトル
            ),
            Projection = Matrix.CreatePerspectiveFieldOfView
            (
                MathHelper.ToRadians(45),   //視野の角度。ここでは45°
                GraphicsDevice.Viewport.AspectRatio,//画面のアスペクト比(=横/縦)
                1,      //カメラからこれより近い物体は画面に映らない
                100     //カメラからこれより遠い物体は画面に映らない
            )
        };
        GraphicsDevice.DepthStencilState = new DepthStencilState
        {
            DepthBufferFunction = CompareFunction.GreaterEqual
        };
    }

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

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(
            ClearOptions.Target | ClearOptions.DepthBuffer, //色と深度バッファをクリア
            Color.CornflowerBlue,   //色の初期値
            0,  //深度バッファの初期値(一番近いところから始める)
            0   //ステンシルバッファの初期値
            );


        blueTriangle.Draw(GraphicsDevice, effect);
        redTriangle.Draw(GraphicsDevice, effect);
    }
}

depthBufferFunctionGreaterEqual.jpg
このサンプルプログラムは、奇妙なことに
「近くのものが遠くのものに隠される」ようになっています。

カメラからの距離が「大きいか等しい」ときのみ描画するようなっているのです。

実際にこれがなんの役に立つかはさっぱりわかりませんが
こういうのもあるということです。

拍手[0回]