消えた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;