読者です 読者をやめる 読者になる 読者になる

XNAで3Dの三角形を描くにはバーテックスシェーダ必須なのか?

前に触れた(id:ABA:20060815#p1)Xbox 360用のゲームが作れるXNAフレームワークのベータ版が公開された。まだ360実機で動作させる方法は不明だが、とりあえずWindows上でどんな感じで書けばいいのかを試すことはできる。

あちこちでXNA用のサンプルプログラムとして挙げられているものは、たいていビットマップをスプライトとして表示してみましょう、とかいう内容なのだが、正直私にとってそれはどうでもいい。なぜなら私はビットマップが書けないからさ!悲しい。

なので私が知りたいのはアルファ付きの3Dプリミティブを書く方法だ。3Dの三角形さえ書ければとりあえずはなんとかなる。なのでその方法を探したのだが……よくわからん。そもそも私はDirectXを大昔に挫折した人間なので、その内容をまったく知らんのだ。まあ私が挫折した10年近く前のDirectXと今のManagedなDirectXはぜんぜん別物らしいが。XNAに付属するAPIドキュメントはそれなりに充実しているが、どこを見ればいいのか分からんし。

でもいろいろあさってたら以下のサンプルを見つけた。

おおこの三角形こそ私の求めていたものだ……なんとかshaderとか書いてあるけど、まあそんな高級なものに触らんでもなんとかなるだろう。とりあえずワールド座標系の扱い方でも見てみようか。

 CompiledEffect compiledEffect = Effect.CompileEffectFromFile
  (@"..\..\shaders\simple.fx", 
   null, null, CompilerOptions.None, TargetPlatform.Windows);
 effect = new Effect(graphics.GraphicsDevice, compiledEffect.GetShaderCode(),
                     CompilerOptions.None, null);
 worldViewProjParam = effect.Parameters["worldViewProj"];

???……'compiledEffect.GetShaderCode'してエフェクトを作ってパラメタを取って?

 worldViewProjParam.SetValue(worldMatrix * viewMatrix * projMatrix);
 effect.CommitChanges();

マトリクスを設定して'effect.CommitChanges'?なんじゃこりゃ。私シェーダに関しては何も知らないのですが……なんかいやな予感がするが、'simple.fx'を見てみよう。

// VERTEX SHADER
void vs(in a2v IN, out v2p OUT)
{
  OUT.position = mul(IN.position, worldViewProj);
  OUT.color = IN.color;
}

バーテックスシェーダで、設定したマトリクスを適用して出力している……えー、わざわざこんなことしないといかんの?単に視点を設定するだけで?

まあこれが最近の正しい3Dプログラミングだというならそれにならおう。シェーダなんか知らね、とかいうロートルプログラマじゃだめってことですか。まあこんくらいの基礎中の基礎ならなんとかなりそう、か?

あと三角形の描画自体は、

 Color c = new Color(50, 255, 50, 100);
 triangle[0] = new VertexPositionColor(new Vector3(1.0f, 0.0f, 0.0f), c);
 triangle[1] = new VertexPositionColor(new Vector3(0.0f, 1.0f, 0.0f), c);
 triangle[2] = new VertexPositionColor(new Vector3(-1.0f, 0.0f, 0.0f), c);

というようなVertexPositionColorの配列を用意して、

 graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>
  (PrimitiveType.TriangleList, 1, triangle);

DrawUserPrimitivesを呼び出せばよいみたい。三角形にアルファをかけるには、

 graphics.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
 graphics.GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
 graphics.GraphicsDevice.RenderState.AlphaBlendEnable = true;

てな具合にRenderStateを設定すればOK。あとアルファ値は直接Colorで設定してもいいし、ピクセルシェーダで'OUT.color.a'みたいなパラメタを変更してもいい。これってどっちを使った方が速いんだろ。アルファが逓減するパーティクルみたいのを実現する際にピクセルシェーダをうまいこと使う方法、みたいのがあるのかな。この辺もぜんぜん分からん。

ちなみに昔の私を挫折に追いやったDirectX周りの初期化のごちゃごちゃは、XNAでは'Microsoft.Xna.Framework.Game'クラスの中にきれいに隠蔽されていて影も形もない。ゲームライフサイクル管理周りはProcessing並みに簡単になっている感じ。この辺はよくできてそうだ。

あとここまで書いてから気づいたのだが、

ここにも3Dプリミティブを表示するサンプルがちゃんとあった……でも頂点シェーダは使っているなあ。やっぱり必須なのかな。