Compartilhar via


消えたRenderState

カテゴリ分けされたレンダーステート

XNA Game Studio 3.1では描画するときに欠かせないレンダーステート情報を変更するには、GraphicsDevice.RenderStateプロパティを介する必要がありました。プロパティ化したことにより、デバッガ上で即座にレンダーステートを確認できるというのはC++でDirectXを使っていた時よりも格段に使いやすくなっていました。

ですが、RenderStateクラスには実に70近いプロパティがあり、中には複数のプロパティを正しく設定しないと思い通りに描画できないといった問題があったり、Visual Studio上で編集しているときでもインテリセンスに表示される候補が多すぎるという問題がありました。また、パフォーマンス的にも複数のレンダーステートを設定するのは時間が掛かってしまいます。さらにコードのメンテナンスしやすさの面から見ても、これだけの量のレンダーステートを人力で管理するのは難しく、かといって安易にStateBlockを使用すると更なるパフォーマンス低下の原因になってしまうという問題がありました。

そこで、XNA Framework 4.0ではこれらの問題を解決する為に、レンダーステートを以下の4つのステートにカテゴリ分けしています。

  • BlendState
    • 半透明処理など、カラーブレンディングに関するステート
  • DepthStencilState
    • 深度バッファやステンシルバッファに関するステート
  • RasterizerState
    • ポリゴンのフィルモード、マルチサンプル設定などラスタライズ時に関するステート
  • SamplerState
    • テクスチャのサンプリングフィルターやアドレスモードなどのステート

また、GraphicsDeviceにはBlendFactorプロパティがあり、BlendState.BlendFactorと同じものです。これはBlendFactorを使用するBlendStateを設定した後にBlendFactorの値だけを変えたい時の為に用意されています。GraphicsDevice.ReferenceStencilとDepthStencilState.ReferenceStecilの関係も同じものです。

各ステートの初期化と設定方法

それぞれのレンダーステートはあらかじめオブジェクトとして生成する必要があります。例えばBlendStateの場合は以下のように生成します。

 // 一般的な半透明ステートオブジェクトを生成する
BlendState myState1 = new BlendState();
myState1.ColorSourceBlend = Blend.SourceAlpha;
myState1.AlphaSourceBlend = Blend.SourceAlpha;
myState1.ColorDestinationBlend = Blend.InverseSourceAlpha;
myState1.AlphaDestinationBlend = Blend.InverseSourceAlpha;

もしくは、C# 3.0から使えるようになったオブジェクト初期化子を使用して

 BlendState myState2 = new BlendState
{
    ColorSourceBlend = Blend.SourceAlpha,
    AlphaSourceBlend = Blend.SourceAlpha,
    ColorDestinationBlend = Blend.InverseSourceAlpha,
    AlphaDestinationBlend = Blend.InverseSourceAlpha,
};

のように書くこともできます。この書き方のが一行ごとにオブジェクト名を書かなくて良いのですっきりとした感じになります。

こうしてできたステートオブジェクトはGraphicsDeviceクラスのプロパティに設定することでレンダーステートを変更することができます。

 GraphicsDevice.BlendState = myState1;

あらかじめ宣言されたステート

このままだと、レンダーステートを設定する時にあらかじめ使用するステートオブジェクト作らないといけないので、それらを管理する手間が増えてしまいます。確かに、その方がパフォーマンス的には有利なのですが、良く使われるレンダーステートくらいは直ぐに使いたいと思う人が多いでしょう。

そこで、それぞれのレンダーステートクラスには良く使われるステートの組み合わせが静的プロパティとしてあらかじめ用意されています。

BlendStateであらかじめ宣言されているBlendStateプロパティ

プロパティ名 意味 設定値
Addtive 加算半透明用ブレンドステート ColorSourceBlend = Blend.SourceAlpha, AlphaSourceBlend = Blend.Blend.SourceAlpha, ColorDestinationBlend = Blend.One, AlphaDestinationBlend = Blend.One,
AlphaBlend 半透明用ブレンドステート (アルファ値乗算済み) ColorSourceBlend = Blend.One, AlphaSourceBlend = Blend.One, ColorDestinationBlend = Blend.InverseSourceAlpha, AlphaDestinationBlend = Blend.InverseSourceAlpha,
NonPremultiplied 半透明用ブレンドステート (一般的な半透明) ColorSourceBlend = Blend.SourceAlpha, AlphaSourceBlend = Blend.SourceAlpha, ColorDestinationBlend = Blend.InverseSourceAlpha, AlphaDestinationBlend = Blend.InverseSourceAlpha,
Opaque 不透明用ブレンドステート(規定値) ColorSourceBlend = Blend.One, AlphaSourceBlend = Blend.One, ColorDestinationBlend = Blend.Zero, AlphaDestinationBlend = Blend.Zero,

DepthStencilStateであらかじめ宣言されているDepthStencilStateプロパティ

プロパティ名 意味 設定値
Default 規定値、深度バッファの読み込み、更新あり DepthBufferEnable = true, DepthBufferWriteEnable = true,
DepthRead 読み込みのみ 深度バッファとの比較はするが、深度バッファの更新はしない。パーティクル等の半透明描画時に使用する DepthBufferEnable = true, DepthBufferWriteEnable = false,
None 深度バッファを使用しない DepthBufferEnable = false, DepthBufferWriteEnable = false,

RasterizerStateであらかじめ宣言されているRasterizerStateプロパティ

プロパティ名 意味 設定値
CullClockwise 時計方向回りのプリミティブをカリングする CullMode = CullMode.CullClockwiseFace,
CullCounterClockwise 反時計方向回りのプリミティブをカリングする(規定値) CullMode = CullMode.CullCounterClockwiseFace,
CullNoneNone カリングなし CullMode = CullMode.None,

SamplerStateであらかじめ宣言されているSamplerStateプロパティ

プロパティ名 意味 設定値
AnisotropicClamp アニソトロピックフィルタリング(異方向性フィルタリング)、クランプ Filter = TextureFilter.Anisotropic, AddressU = TextureAddressMode.Clamp, AddressV = TextureAddressMode.Clamp, AddressW = TextureAddressMode.Clamp,
AnisotropicWrap アニソトロピックフィルタリング(異方向性フィルタリング)、ラッピングあり Filter = TextureFilter.Anisotropic, AddressU = TextureAddressMode.Wrap, AddressV = TextureAddressMode.Wrap, AddressW = TextureAddressMode.Wrap,
LinearClamp リニアフィルタリング、クランプ Filter = TextureFilter.Linear, AddressU = TextureAddressMode.Clamp, AddressV = TextureAddressMode.Clamp, AddressW = TextureAddressMode.Clamp,
LinearWrap リニアフィルタリング、ラッピングあり(規定値) Filter = TextureFilter.Linear, AddressU = TextureAddressMode.Wrap, AddressV = TextureAddressMode.Wrap, AddressW = TextureAddressMode.Wrap,
PointClamp ポイントフィルタリング、クランプ Filter = TextureFilter.Point, AddressU = TextureAddressMode.Clamp, AddressV = TextureAddressMode.Clamp, AddressW = TextureAddressMode.Clamp,
PointWrap ポイントフィルタリング、ラッピングあり Filter = TextureFilter.Point, AddressU = TextureAddressMode.Wrap, AddressV = TextureAddressMode.Wrap, AddressW = TextureAddressMode.Wrap,

レンダーステートの管理が簡単になった

これら四種類のレンダーステートオブジェクトにカテゴリ分けと、あらかじめ用意されているステートの組み合わせを使用することでレンダーステートの管理がしやすくなりました。どれぐらい簡単になったかというと、例えば全てのレンダーステートを初期値に戻したいという場合があったとします(普段はあまりないけれど)。このケースの場合は以下の数行を書き加えるだけで実現することができます。

 // レンダーステートを初期値に戻す
GraphicsDevice.BlendState = BlendState.Opaque;
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;

// サンプラーステート数はReach、HiDefともに16個
for (int i = 0; i < 16; ++i)
    GraphicsDevice.SamplerStates[i] = SamplerState.LinearWrap;