メインメニューを開く

差分

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

7,384 バイト追加, 2018年9月3日 (月) 05:42
ページの作成:「「MonoGameでプログラマブルシェーダーを使う」の実践編として以下をやってみる。 * バーテックスシェーダーでジオメ...」
「[[MonoGameでプログラマブルシェーダーを使う]]」の実践編として以下をやってみる。
* [[バーテックスシェーダー]]で[[ジオメトリ変換]]してみる
* [[ピクセルシェーダー]]で[[テクスチャマッピング]]してみる。

== 準備 ==
* [[NuGet]]で「[[InfinitespaceStudios.Pipeline]]」を入れる。

== HLSLを書く ==
[[iOS]]や[[Android]]向けの場合は強制的に「OPENGL」シンボルが定義されており、[[HLSL]]の[[シェーダーモデル]]は3.0固定にしておけ。<syntaxhighlight lang="text">
// 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();
}
};
</syntaxhighlight>

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

== コンテンツファイルを準備 ==
このサンプルで使っているコンテンツファイルは以下のとおり。
* texture1.xnb (texture1.pngをコンパイルしたもの。画像は自分で用意してください)
* effect1.xnb (上記のHLSLをコンパイルしたもの)

== C#で呼び出してみる ==
即席[[コード]]なので[[バーテックスバッファー]]と[[インデックスバッファー]]は使用せずにDrawUserPrimitivesメソッドで描画を行っている。

また、「[[MonoGameのカメラを作る]]」で作ったカメラを使っている。Cameraクラスは[[MonoGame]]標準物ではないので注意。<syntaxhighlight lang="csharp">
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);
}
}
</syntaxhighlight>こんな感じ。[[Mac]]でも[[HLSL]]が使えるとか感動的だね。
[[ファイル:MonoGameでピクセルシェーダーを使ってテクスチャを貼ってみる.png|なし|サムネイル|640x640ピクセル]]

== 関連項目 ==
* [[MonoGameでプログラマブルシェーダーを使う]]
* [[MonoGameの3Dモデルを管理描画するクラスを作る]]
* [[MonoGameでSkiaSharpを使う]]
* [[シェーダー]]
* [[プログラマブルシェーダー]]
* [[シェーダーモデル]]
* [[テクスチャマッピング]]
* [[法線マッピング]]
* [[スリープソート]]

[[category: HLSL]]
匿名利用者