MonoGameでピクセルシェーダーを使ってテクスチャを貼る

提供: MonoBook
ナビゲーションに移動 検索に移動

MonoGameでプログラマブルシェーダーを使う」の実践編として以下をやってみる。Macでやっているのがミソ。

準備[編集 | ソースを編集]

HLSLを書く[編集 | ソースを編集]

iOSAndroid向けの場合は強制的に「OPENGL」シンボルが定義されており、HLSLシェーダーモデルは3.0固定にしておけ。

// file: effect1.fx

// ----------------------------------------------------------------
// MonoGame PipelineでOpenGL環境の場合は「OPENGL」シンボルが立っている。
// 以下は定型文だと思ってコピペしとけ。
#if OPENGL
	#define SV_POSITION POSITION
	#define VS_SHADERMODEL vs_3_0
	#define PS_SHADERMODEL ps_3_0
#else
	#define VS_SHADERMODEL vs_4_0_level_9_1
	#define PS_SHADERMODEL ps_4_0_level_9_1
#endif

// ----------------------------------------------------------------
// 呼び出し側から設定されるグローバル変数(シェーダー内では実質定数)

// カメラ座標
float4x4 myView;
float4x4 myProjection;

// テクスチャ
texture  myTexture;

// ----------------------------------------------------------------
// tex2D関数で使うテクスチャサンプラー
// tex2D関数はサンプラーと座標を入力すると、テクスチャからその位置の
// テクセルを抜き出してCOLORが返ってくる。
sampler mySampler = sampler_state 
{
	Texture = <myTexture>;
};

// ----------------------------------------------------------------
// 入出力用の構造体
struct VertexShaderInput
{
	float4 Position : POSITION0;
	float4 TextureCoordinate : TEXCOORD;
};

struct VertexShaderOutput
{
	float4 Position : SV_POSITION;
	float4 TextureCoordinate : TEXCOORD;
};

// ----------------------------------------------------------------
// バーテックスシェーダー
VertexShaderOutput MainVS(in VertexShaderInput input)
{
	// 出力用の頂点
	VertexShaderOutput output = (VertexShaderOutput)0;

    // 頂点をカメラから見た座標に変換
	output.Position = mul(input.Position, mul(myView, myProjection));
	
	// テクスチャ座標をコピー
	output.TextureCoordinate = input.TextureCoordinate;

	return output;
}

// ----------------------------------------------------------------
// ピクセルシェーダー
float4 MainPS(VertexShaderOutput input) : COLOR
{
	// テクスチャサンプラーからピクセルに対応する色を抜き出す
	return tex2D( mySampler, input.TextureCoordinate);
}

// ----------------------------------------------------------------
// テクニック、いわゆるエントリーポイント
technique MyTechnique
{
	pass MyPass
	{
		VertexShader = compile VS_SHADERMODEL MainVS();
		PixelShader  = compile PS_SHADERMODEL MainPS();
	}
};

HLSLをコンパイルする[編集 | ソースを編集]

macOS上のVisual Studio for MacではHLSLをコンパイルするには準備の際にいれた「InfinitespaceStudios.Pipeline」を用いる。

この際にProjectツリーの「Content」を開き「Platform」の設定が間違っていないことを確認すること。iOSAndroid向けなのに「Windows」のままコンパイルするとOpenGLではなくDirectX向けのモノができあがり残念なことになる。なお、iOSAndroidを間違えてもどっちもOpenGL系なので動く。

コンテンツファイルを準備[編集 | ソースを編集]

このサンプルで使っているコンテンツファイルは以下のとおり。

  • texture1.xnb (texture1.pngをコンパイルしたもの。画像は自分で用意してください)
  • effect1.xnb (上記のHLSLをコンパイルしたもの)

C#で呼び出してみる[編集 | ソースを編集]

即席コードなのでバーテックスバッファーインデックスバッファーは使用せずにDrawUserPrimitivesメソッドで描画を行っている。

また、「MonoGameのカメラを作る」で作ったカメラを使っている。CameraクラスはMonoGame標準物ではないので注意。

    using System;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;

    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Camera camera;
        Effect effect;
        Texture texture;

        VertexPositionTexture[] vertices = {
            new VertexPositionTexture(new Vector3( 0, 1, 0), new Vector2(0.5f, 0.0f)),
            new VertexPositionTexture(new Vector3( 1, 0, 0), new Vector2(1.0f, 1.0f)),
            new VertexPositionTexture(new Vector3(-1, 0, 0), new Vector2(0.0f, 1.0f))
        };

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            IsMouseVisible = true;
            base.Initialize();
        }

        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // 即席カメラ
            camera = new Camera(this);
            camera.Position = new Vector3(0, -2, 2);
            camera.Target = new Vector3(0, 0, 0);

            // テクスチャ読み込み
            texture = Content.Load<Texture2D>("texture1");

            // エフェクト読み込み
            effect = Content.Load<Effect>("effect1");
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();

            // カメラを回転させる
            camera.Angle += 1f;

            base.Update(gameTime);
        }

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

            // シェーダーのグローバル変数に値をセットする
            effect.Parameters["myView"].SetValue(camera.View);
            effect.Parameters["myProjection"].SetValue(camera.Projection);
            effect.Parameters["myTexture"].SetValue(texture);

            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                // パス適用開始(シェーダー有効化)
                pass.Apply();

                // 描画
                graphics.GraphicsDevice.DrawUserPrimitives(
                    primitiveType: PrimitiveType.TriangleList,
                    vertexData: vertices,
                    vertexOffset: 0,
                    primitiveCount: vertices.Length / 3);
            }

            base.Draw(gameTime);
        }
    }

動かしてみる[編集 | ソースを編集]

こんな感じ。MacでもHLSLで書いたシェーダーが使えてる。なお、シェーダーモデル3.0までだがSM3.0まであればそこそこなことはできる。。

動画

関連項目[編集 | ソースを編集]