忍者ブログ

Memeplexes

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

XNA爆発チュートリアル その5 HLSLへ

XNA爆発チュートリアル その4 透過のつづきです。

前回までに、爆炎っぽいものをそれらしく表示することが出来ました。
しかしまだ本物には及びません。
本物の爆発は「ぶわっ」という感じに広がっていくものだからです。
今のところ爆炎は動きません。
サイズも変わりません。

こういった、ポイントのいろんな変化を行うには、
HLSLを使うべきでしょう。
BasicEffectとはおさらばです。
HLSLでエフェクトファイルを直接書くことによって、
より詳しく描画をコントロールすることが出来ます。


ただ……、より詳しく描画をコントロールすることが出来るのはいいのですが、
そのかわり代償として、やらなければならないことが多くなり、
面倒になったり、一時的に見た目が逆戻りする(お粗末になる)ことがあるでしょう。

やる気を保つためにも、見た目をあんまりお粗末にしないことが大切です。
ですから、この回が終わるころにはBasicEffectで
やったものに追いついておくべきでしょうね。

心構えはこのくらいにして、さっそくプロジェクトにエフェクトファイルを追加しましょう。
ExplosionShader.fxという名前で、Contentフォルダの中に作ることにします。
それをContentManagerから"Content/ExplosionShader"という風に読み込みます。
こうして出来たEffectはただのEffectなのでTextureプロパティは使えません。
この関連は削除することになるでしょう。

ExplosionTest.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;


public class ExplosionTest : Microsoft.Xna.Framework.Game
{
    private GraphicsDeviceManager graphics;

    private Effect effect;
    private VertexPositionColor[] vertices = {
        new VertexPositionColor(new Vector3(0, 0.1f, 0), Color.White),
        new VertexPositionColor(new Vector3(0.1f, 0, 0), Color.White),
        new VertexPositionColor(new Vector3(-0.1f, 0, 0), Color.White)
    };

    private ContentManager content;


    public ExplosionTest()
    {
        graphics = new GraphicsDeviceManager(this);
        content = new ContentManager(Services);
    }

    protected override void LoadGraphicsContent(bool loadAllContent)
    {
        if (loadAllContent)
        {
            effect = content.Load<Effect>("Content/ExplosionShader");
            graphics.GraphicsDevice.VertexDeclaration = new VertexDeclaration(
                graphics.GraphicsDevice,
                VertexPositionColor.VertexElements
                );
        }
    }

    protected override void UnloadGraphicsContent(bool unloadAllContent)
    {
        if (unloadAllContent) { content.Unload(); }
    }

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

        graphics.GraphicsDevice.RenderState.PointSize = 50;
        graphics.GraphicsDevice.RenderState.PointSpriteEnable = true;

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

        effect.Begin();

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

            graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(
                PrimitiveType.PointList,
                vertices,
                0,
                vertices.Length
                );

            pass.End();
        }

        effect.End();
    }
}

HLSLで書いたエフェクトファイルは考えられる限りもっともシンプルなものにします。
頂点シェーダは何もしてませんし、ピクセルシェーダは常に白を返します。
もちろんこんなのですから、結果は炎から真っ白タイルに戻ることが予想できます。

ExplosionShader.fx
float4 ExplosionVertexShader(float4 position:POSITION):POSITION
{
	return position;
}

float4 ExplosionPixelShader():COLOR
{
	return 1;   //white
}

technique ExplosionShaderTechnique
{
	pass P0
	{
		VertexShader = compile vs_2_0 ExplosionVertexShader();
		PixelShader = compile ps_2_0 ExplosionPixelShader();
	}
}

explosionTutorialHLSLSimplest.jpg

あいたたた……見た目がただの白タイルに戻ってしまいました。
わかっていたこととはいえ、これは精神的にきついですね。
今まで築き上げてきたものが失われる、いやーな気分です。
でもすぐに建て直しできるので、気を取り直していきましょう。

テクスチャ

ここで白タイルから爆炎に復活させるにはHLSLで
ポイントにテクスチャを貼るようにしなければなりません。

これは簡単で、ポイントスプライトを使っているときには
ピクセルシェーダに自動的にテクスチャ座標が引数として入ってくるので
それを元にピクセルシェーダの戻り値をだせばいいのです。

C#側ではまたさっきまでのようにテクスチャをロードして
エフェクトにセットするようにします。

ExplosionTest.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;


public class ExplosionTest : Microsoft.Xna.Framework.Game
{
    private GraphicsDeviceManager graphics;

    private Effect effect;
    private VertexPositionColor[] vertices = {
        new VertexPositionColor(new Vector3(0, 0.1f, 0), Color.White),
        new VertexPositionColor(new Vector3(0.1f, 0, 0), Color.White),
        new VertexPositionColor(new Vector3(-0.1f, 0, 0), Color.White)
    };

    private ContentManager content;


    public ExplosionTest()
    {
        graphics = new GraphicsDeviceManager(this);
        content = new ContentManager(Services);
    }

    protected override void LoadGraphicsContent(bool loadAllContent)
    {
        if (loadAllContent)
        {
            effect = content.Load<Effect>("Content/ExplosionShader");
            effect.Parameters["ExplosionTexture"].SetValue(
                content.Load<Texture2D>("Content/explosion")
                );

            graphics.GraphicsDevice.VertexDeclaration = new VertexDeclaration(
                graphics.GraphicsDevice,
                VertexPositionColor.VertexElements
                );
        }
    }

    protected override void UnloadGraphicsContent(bool unloadAllContent)
    {
        if (unloadAllContent) { content.Unload(); }
    }

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

        graphics.GraphicsDevice.RenderState.PointSize = 50;
        graphics.GraphicsDevice.RenderState.PointSpriteEnable = true;

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

        effect.Begin();

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

            graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(
                PrimitiveType.PointList,
                vertices,
                0,
                vertices.Length
                );

            pass.End();
        }

        effect.End();
    }
}

ExplosionShader.fx
texture ExplosionTexture;

sampler explosionSampler = sampler_state
{
	Texture = <ExplosionTexture>;
};

float4 ExplosionVertexShader(float4 position:POSITION):POSITION
{
	return position;
}

float4 ExplosionPixelShader(float2 textureCoordinate:TEXCOORD):COLOR
{
	return tex2D(explosionSampler, textureCoordinate);
}

technique ExplosionShaderTechnique
{
	pass P0
	{
		VertexShader = compile vs_2_0 ExplosionVertexShader();
		PixelShader = compile ps_2_0 ExplosionPixelShader();
	}
}


explosionTutorialHLSLTextured.jpg

復活できました。
めでたく、HLSLでBasicEffectを使っていたときと同じことが出来ました。
きちんと、白タイルではなく爆炎が表示されています。

「このまま白いタイルのままだったらどうしよう」
という恐怖があったのですが、安心しました。
これは気分としては「冤罪でつかまったけれど無事疑いが晴れた」
というのに似ている気がしますね!

さて、今回無事にBasicEffectからHLSLに移行できたので
次回からはいろいろなことが詳しく調節できるようになるはずです。
(BasicEffectは柔軟性に欠けるため、さらなる進歩は難しいのです)

たとえばそれぞれの爆炎を時間の経過とともに周囲に「ぶわっ」と広げたり、
フェードアウトさせたり、回転させたり、
遠近法のようにカメラからの距離によって大きさを調節したり、
いろいろなことが出来るようになり、
さらに本物の爆炎に近づくことが出来るのです。
(というか、そうなる予定です)

拍手[0回]

PR