差分

ナビゲーションに移動 検索に移動

MonoGameでハードウェアインスタンシングしてみる

3,245 バイト除去, 2020年4月15日 (水) 05:54
編集の要約なし
これがついにMonoGame 3.7で[[OpenGL]]環境でも[[ハードウェアインスタンシング]]が使えるようになったそうだ。さっそく[[MacOS]]上で試してみた。[[ハードウェアインスタンシング]]が使えるとなると[[ボリュームレンダリング]]の実装が捗る可能性がある。
==HLSLを書くMacでシェーダーをコンパイルできるようにする ==[[HLSL]]はこんな感じ。今回は移動だけで回転はしていない。 [[Mac]]上で[[HLSL]]を[[コンパイル]]する方法は「 Mac上でHLSLをコンパイルする方法は以下を参照。* [[MonoGameでプログラマブルシェーダーを使う]]」および「* [[InfinitespaceStudios.Pipeline]]」の項目を参照。
<syntaxhighlight lang="text">= HLSL側を書く ==// file: effect4.fxワールド座標(ワールド空間内での位置や傾きなど)は、
// ----------------------------------------------------------------// MonoGame PipelineでOpenGL環境の場合は「OPENGL」シンボルが立っている。一般的なシェーダーではユニフィード変数で受け取る。float4x4 World; // 以下は定型文だと思ってコピペしとけ。#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_1float4x4 View;#endiffloat4x4 Projection;
// ----------------------------------------------------------------一方、ハードウェアインスタンシングを使う場合はダイナミック頂点バッファとして複数の値を受け取る。// 呼び出し側(C#)から設定されるグローバル変数(シェーダー内では実質定数)<source lang="hlsl">float4x4 myView : VIEWView;float4x4 myProjection : PROJECTIONProjection;
// ----------------------------------------------------------------これがワールド座標群を格納したダイナミック頂点バッファ。// 入出力用の構造体C#側にfloat4x4の送信手段がないので「float4が4個」で代用している。struct VertexPositionColorVSInstance
{
float4 Position w1 : POSITION0BLENDWEIGHT0; float4 Color w2 : COLORBLENDWEIGHT1; float4 w3 : BLENDWEIGHT2; float4 w4 : BLENDWEIGHT3;
};
// ----------------------------------------------------------------「float4が4個」をシェーダー関数内でfloat4x4に再構成して利用する。// バーテックスシェーダーVertexPositionColor MyVertexShaderVSOutput VSFunc( VertexPositionColor VSInput input, float3 position : POSITION1 VSInstance instance)
{
// inputの位置をpositionほど移動させる。 // 今回は移動だけ。 // 回転したい場合なども同じ要領でここで変形させる。 input.Position.xyz +float4x4 world = position;  // 頂点をカメラから見た座標に変換する。 inputfloat4x4(instance.Position = mul( inputw1, instance.Positionw2, mul(myViewinstance.w3, myProjection)instance.w4); return input;
}
<// ----------------------------------------------------------------// ピクセルシェーダーfloat4 MyPixelShader(float4 color : COLOR) : COLOR0{ return color;}source>
technique HardwareInstancing== C#側を書く ==まずインスタンスを格納するクラスを作る。<source lang="csharp">public struct VertexDynamicInstance : IVertexType
{
pass Pass1 { VertexShader = compile VS_SHADERMODEL MyVertexShader() public Matrix World; PixelShader = compile PS_SHADERMODEL MyPixelShader(); }}</syntaxhighlight>==C#で呼び出してみる==この[[コード]]は「[[MonoGameのカメラを作る]]」で作ったカメラを使っている。Cameraクラスは[[MonoGame]]標準物ではないので注意。
<syntaxhighlight lang public static readonly VertexDeclaration VertexDeclaration ="csharp"> using System;new VertexDeclaration( using System new VertexElement( 0, VertexElementFormat.Collections.Generic; using SystemVector4, VertexElementUsage.Text;BlendWeight, 0), using Microsoft new VertexElement(16, VertexElementFormat.XnaVector4, VertexElementUsage.Framework;BlendWeight, 1), using Microsoft.Xna new VertexElement(32, VertexElementFormat.FrameworkVector4, VertexElementUsage.Graphics;BlendWeight, 2), using Microsoft.Xna new VertexElement(48, VertexElementFormat.FrameworkVector4, VertexElementUsage.InputBlendWeight, 3));
public class Game1 : GameVertexDeclaration IVertexType.VertexDeclaration
{
GraphicsDeviceManager graphicsget { return VertexDynamicInstance.VertexDeclaration;} }}</source>
Camera cameraとりあえず動作確認用に1000個のテストデータを作る。<source lang="csharp">var rand = new System.Random();var instances = new VertexDynamicInstance[1000]; Effect effectfor (int i = 0; i < instances.Length;i++){ instances[i].World = Matrix.CreateTranslation( VertexBuffer triangleVertexBuffer;(float)rand.NextDouble(), IndexBuffer indexBuffer;(float)rand.NextDouble(), DynamicVertexBuffer instanceVertexBuffer(float)rand.NextDouble());}</source>
VertexPositionColor[] vertices ダイナミック頂点バッファを生成する。<source lang= {"csharp"> new VertexPositionColor(var instanceVertexBuffer = new Vector3DynamicVertexBuffer(-1graphicsDevice, 1, 0), Color VertexDynamicInstance.Blue )VertexDeclaration, new VertexPositionColor(new Vector3( 1, 1, 0), Color instances.White)Length, new VertexPositionColor(new Vector3( 1, -1, 0), Color BufferUsage.Red WriteOnly) };
Vector3[] instanceVertexBuffer.SetData(instances = new Vector3[] { new Vector3(0f, 0f, 0f),; new Vector3(1f, 1f, 0f), new Vector3(2f, 2f, 0f) };</source>
public Game1描画する。<source lang="csharp">// 頂点バッファとダイナミック頂点バッファの2つを指定する。graphicsDevice.SetVertexBuffers( new VertexBufferBinding( meshPart.VertexBuffer, meshPart.VertexOffset, 0), { graphics = new GraphicsDeviceManagerVertexBufferBinding(thisinstanceVertexBuffer , 0, 1)); Content.RootDirectory = "Content"; }
protected override void Initialize()// インデックスバッファは普通に指定する。 { IsMouseVisible graphicsDevice.Indices = true; basemeshPart.Initialize()IndexBuffer; }
protected override void LoadContentforeach (EffectPass pass in effect.CurrentTechnique.Passes) { // 即席カメラ camera = new Camera(this); camera.Position = new Vector3(0, 0, 10); camera pass.Target = new Vector3Apply(0, 0, 0);
// エフェクト読み込みドローコールはDrawInstancedPrimitivesで呼び出す。 effect = Content graphicsDevice.Load<Effect>("effect4");  // バーテックスバッファーを生成 triangleVertexBuffer = new VertexBuffer( GraphicsDevice, VertexPositionColor.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly); triangleVertexBuffer.SetData<VertexPositionColor>(vertices);  // インデックスバッファーを生成 var index = new ushort[] { 0, 1, 2 }; indexBuffer = new IndexBuffer( GraphicsDevice, IndexElementSize.SixteenBits, index.Length, BufferUsage.WriteOnly); indexBuffer.SetData<ushort>(index);  // インスタンスバッファーを生成 instanceVertexBuffer = new DynamicVertexBufferDrawInstancedPrimitives( GraphicsDevice, new VertexDeclaration( new VertexElement( offset: 0, elementFormat: VertexElementFormat.Vector3, elementUsage: VertexElementUsage.Position, usageIndex: 1)), instances.Length, BufferUsage.WriteOnly); instanceVertexBuffer.SetData(instances);  }  protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) Exit();  // TODO: Add your update logic here 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);  // バーテックスバッファーを転送 GraphicsDevice.SetVertexBuffers( new VertexBufferBinding(triangleVertexBuffer, 0, 0), new VertexBufferBinding(instanceVertexBuffer, 0, 1));  // インデックスバッファーを転送 GraphicsDevice.Indices = indexBuffer;  // インスタンスバッファーを転送 instanceVertexBuffer.SetData(instances, 0, instances.Length, SetDataOptions.Discard);  // 描画 foreach (EffectPass pass in effect.CurrentTechnique.Passes) { pass.Apply();  graphics.GraphicsDevice.DrawInstancedPrimitives( primitiveType: PrimitiveType.TriangleList, baseVertex: 0, startIndex: 0, primitiveCount: verticesmeshPart.Length / 3PrimitiveCount, instanceCount: instances.Length);}</source>
}  base.Draw(gameTime); } }</syntaxhighlight> ==Mac上で動かしてみる==
こんな感じ。[[Mac]]でも[[HLSL]]で書いた[[シェーダー]]が使えてる。なお、[[OpenGL]]系の環境では[[シェーダーモデル]]は3.0までだ。まあSM3.0もあれば元気があれば何でもできる。
<movie>https://youtu.be/qU-toUOhvRI</movie>
==関連項目==
 == 関連項目 ==* [[MonoGameでHLSLにMatrixを渡す]]*[[MonoGameでプログラマブルシェーダーを使う]]*[[MonoGameでピクセルシェーダーを使ってテクスチャを貼る‎]]*[[ハードウェアインスタンシング]]*[[疑似インスタンシング]]*[[メッシュベイカー]]*[[ドローコール]]
[[category: MonoGame]]
[[category: HLSL]]

案内メニュー