忍者ブログ

Memeplexes

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

モデルを膨らます

Xna Creators ClubのShatterサンプルを簡略化してやってみようとしたのですが、失敗してしまいました(笑)。
でも結果は・・・・・・これはこれで面白いので書いておきます。(バラバラになるのではなく、風船のように膨らんでしまいました)

まず、Shatterサンプルと言うのが一体どういうものかを説明しておきます。
これは、モデルをリアルタイムで粉々にするサンプルです。

shatterSample1.JPG
shatterSample2.JPG
shatterSample3.JPG

shatterSample4.JPG
(クリックで画像を拡大)

最初に戦車のモデルが表示されるのですが、キーボードの↑キーを押すとバラバラになっていきます(↓キーで元に戻す)。

これは、「Updateメソッド内で頂点データを書き換えている」とかそういうのではなく、HLSLで書いたエフェクトファイルの中でバラバラにする操作をやっています。
モデルを粉々に砕いているのはCPUではなくGPUです。

ではエフェクトファイルの中でどのようにしてモデルをバラしているのかというと、これはモデルの法線を利用しています。
基本的なアイデアは、「法線ベクトルの方向に頂点の位置を動かせばバラバラになるだろう」というものです。(ただし、Shatterサンプルではそれぞれのポリゴンを回転させてもいます。たぶんこの回転がないと、今回のように失敗するんでしょう。)

そのため次のようにエフェクトファイルを書いてみました:

ShatterEffect.fx
float4x4 World, View, Projection;
float Shatter;

texture Texture;
sampler TextureSampler = sampler_state
{
	Texture = (Texture);
};


struct VertexPositionTexture
{
	float4 Position : POSITION;
	float2 TextureCoordinate : TEXCOORD;
};


VertexPositionTexture ShatterVertexShader(
	float4 position:POSITION,
	float4 normal:NORMAL,
	float2 textureCoordinate :TEXCOORD
	)
{
	VertexPositionTexture output;
	
	position.xyz += normal.xyz * Shatter;
	float4x4 transform = mul(World, mul(View, Projection));
	output.Position = mul(position, transform);
	
	output.TextureCoordinate = textureCoordinate;
	
	return output;
}


float4 ShatterPixelShader(float2 textureCoordinate : TEXCOORD) : COLOR
{
	return tex2D(TextureSampler, textureCoordinate);
}

technique ShatterEffect
{
	pass
	{
		VertexShader = compile vs_2_0 ShatterVertexShader();
		PixelShader = compile ps_2_0 ShatterPixelShader();
	}
}

最初に「失敗した」と書きましたが、これにはミスは無いはずです(たぶん)!

ミスがないというか不十分みたいですね。
これは頂点のPositionを法線の向きに移動するようなシェーダなのですが、これだとモデルは風船のように膨らむことになります。

Creators ClubのShatterサンプルでは、法線の向きへの移動だけではなくポリゴンの回転もやっているので、きちんと粉々に割れてるように見えるのだと思います。

で、結果ですが、風船のようにモデルが膨らんでしまいました:

shatterFailed.JPG
そういえばDirectXのサンプルにこんなのがあったような気がします。
たしか人間が風船のように膨らんでいたような……


参考までにC#側のコードも書いておきます。

今回の失敗の原因はあまりに単純化しすぎたということでしょうか・・・
モデルを粉々にするのはまたの機会にしましょうかね…。

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

class MyGame : Game
{
    GraphicsDeviceManager graphics;
    ContentManager content;

    //Graphics Device Objects
    Effect effect;
    Model model;


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


    protected override void LoadGraphicsContent(bool loadAllContent)
    {
        if (loadAllContent)
        {
            effect = content.Load<Effect>("ShatterEffect");
            model = loadModel("Ship", effect);
        }
    }

    Model loadModel(string modelName, Effect effect)
    {
        Model result = content.Load<Model>(modelName);

        foreach (ModelMesh mesh in result.Meshes)
        {
            foreach (ModelMeshPart part in mesh.MeshParts)
            {
                effect.Parameters["Texture"].SetValue(
                    ((BasicEffect)part.Effect).Texture
                    );
                part.Effect = effect.Clone(graphics.GraphicsDevice);
            }
        }

        return result;
    }


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


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


        foreach (ModelMesh mesh in model.Meshes)
        {
            foreach (Effect effect in mesh.Effects)
            {
                setEffectParameters(
                    effect,
                    gameTime.TotalGameTime.TotalSeconds
                    );
            }

            mesh.Draw();
        }
    }

    private void setEffectParameters(Effect effect, double totalSeconds)
    {
        Matrix world = Matrix.CreateRotationY(
            (float)totalSeconds * 2
            );

        Matrix view = Matrix.CreateLookAt(
            new Vector3(0, 0, 3000), new Vector3(), Vector3.UnitY
            );

        Matrix projection = Matrix.CreatePerspectiveFieldOfView(
            MathHelper.ToRadians(90),
            (float)graphics.GraphicsDevice.Viewport.Width / graphics.GraphicsDevice.Viewport.Height,
            1, 10000
            );

        effect.Parameters["Shatter"].SetValue(
            (float)(1 + System.Math.Sin(totalSeconds)) * 100
            );
        effect.Parameters["World"].SetValue(world);
        effect.Parameters["View"].SetValue(view);
        effect.Parameters["Projection"].SetValue(projection);
    }
}



ちなみに、モデルはChaseCameraサンプルShip.fbx、そのモデルのテクスチャはShipDiffuse.tgaを使っています。

拍手[0回]

PR