「MonoGameでハードウェアインスタンシングしてみる」の版間の差分
imported>Administrator 編集の要約なし |
Administrator (トーク | 投稿記録) 編集の要約なし |
||
| 5行目: | 5行目: | ||
これがついにMonoGame 3.7で[[OpenGL]]環境でも[[ハードウェアインスタンシング]]が使えるようになったそうだ。さっそく[[MacOS]]上で試してみた。[[ハードウェアインスタンシング]]が使えるとなると[[ボリュームレンダリング]]の実装が捗る可能性がある。 | これがついにMonoGame 3.7で[[OpenGL]]環境でも[[ハードウェアインスタンシング]]が使えるようになったそうだ。さっそく[[MacOS]]上で試してみた。[[ハードウェアインスタンシング]]が使えるとなると[[ボリュームレンダリング]]の実装が捗る可能性がある。 | ||
== | == Macでシェーダーをコンパイルできるようにする == | ||
Mac上でHLSLをコンパイルする方法は以下を参照。 | |||
* [[MonoGameでプログラマブルシェーダーを使う]] | |||
* [[InfinitespaceStudios.Pipeline]] | |||
== HLSL側を書く == | |||
ワールド座標(ワールド空間内での位置や傾きなど)は、 | |||
一般的なシェーダーではユニフィード変数で受け取る。 | |||
float4x4 World; //これがワールド座標 | |||
// | float4x4 View; | ||
float4x4 Projection; | |||
一方、ハードウェアインスタンシングを使う場合はダイナミック頂点バッファとして複数の値を受け取る。 | |||
<source lang="hlsl"> | |||
float4x4 | float4x4 View; | ||
float4x4 | float4x4 Projection; | ||
// | // これがワールド座標群を格納したダイナミック頂点バッファ。 | ||
// | // C#側にfloat4x4の送信手段がないので「float4が4個」で代用している。 | ||
struct | struct VSInstance | ||
{ | { | ||
float4 | float4 w1 : BLENDWEIGHT0; | ||
float4 | float4 w2 : BLENDWEIGHT1; | ||
float4 w3 : BLENDWEIGHT2; | |||
float4 w4 : BLENDWEIGHT3; | |||
}; | }; | ||
// | // 「float4が4個」をシェーダー関数内でfloat4x4に再構成して利用する。 | ||
VSOutput VSFunc(VSInput input, VSInstance instance) | |||
{ | { | ||
float4x4 world = float4x4(instance.w1, instance.w2, instance.w3, instance.w4); | |||
} | } | ||
/ | </source> | ||
== C#側を書く == | |||
まずインスタンスを格納するクラスを作る。 | |||
<source lang="csharp"> | |||
public struct VertexDynamicInstance : IVertexType | |||
{ | { | ||
public Matrix World; | |||
public static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration( | |||
new VertexElement( 0, VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 0), | |||
new VertexElement(16, VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 1), | |||
new VertexElement(32, VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 2), | |||
new VertexElement(48, VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 3)); | |||
VertexDeclaration IVertexType.VertexDeclaration | |||
{ | { | ||
get { return VertexDynamicInstance.VertexDeclaration; } | |||
} | |||
} | |||
</source> | |||
とりあえず動作確認用に1000個のテストデータを作る。 | |||
<source lang="csharp"> | |||
var rand = new System.Random(); | |||
var instances = new VertexDynamicInstance[1000]; | |||
for (int i = 0; i < instances.Length; i++) | |||
{ | |||
instances[i].World = Matrix.CreateTranslation( | |||
(float)rand.NextDouble(), | |||
(float)rand.NextDouble(), | |||
(float)rand.NextDouble()); | |||
} | |||
</source> | |||
ダイナミック頂点バッファを生成する。 | |||
<source lang="csharp"> | |||
var instanceVertexBuffer = new DynamicVertexBuffer(graphicsDevice, | |||
VertexDynamicInstance.VertexDeclaration, | |||
instances.Length, | |||
BufferUsage.WriteOnly); | |||
instanceVertexBuffer.SetData(instances); | |||
</source> | |||
描画する。 | |||
<source lang="csharp"> | |||
// 頂点バッファとダイナミック頂点バッファの2つを指定する。 | |||
graphicsDevice.SetVertexBuffers( | |||
new VertexBufferBinding( meshPart.VertexBuffer, meshPart.VertexOffset, 0), | |||
new VertexBufferBinding( instanceVertexBuffer , 0, 1)); | |||
// インデックスバッファは普通に指定する。 | |||
graphicsDevice.Indices = meshPart.IndexBuffer; | |||
foreach (EffectPass pass in effect.CurrentTechnique.Passes) | |||
{ | |||
pass.Apply(); | |||
// ドローコールはDrawInstancedPrimitivesで呼び出す。 | |||
graphicsDevice.DrawInstancedPrimitives( | |||
primitiveType: PrimitiveType.TriangleList, | |||
baseVertex: 0, | |||
startIndex: 0, | |||
primitiveCount: meshPart.PrimitiveCount, | |||
instanceCount: instances.Length); | |||
} | |||
</source> | |||
== Mac上で動かしてみる == | |||
==Mac上で動かしてみる== | |||
こんな感じ。[[Mac]]でも[[HLSL]]で書いた[[シェーダー]]が使えてる。なお、[[OpenGL]]系の環境では[[シェーダーモデル]]は3.0までだ。まあSM3.0もあれば元気があれば何でもできる。 | こんな感じ。[[Mac]]でも[[HLSL]]で書いた[[シェーダー]]が使えてる。なお、[[OpenGL]]系の環境では[[シェーダーモデル]]は3.0までだ。まあSM3.0もあれば元気があれば何でもできる。 | ||
<movie>https://youtu.be/qU-toUOhvRI</movie> | <movie>https://youtu.be/qU-toUOhvRI</movie> | ||
*[[MonoGameでプログラマブルシェーダーを使う]] | |||
*[[MonoGameでピクセルシェーダーを使ってテクスチャを貼る]] | == 関連項目 == | ||
*[[ハードウェアインスタンシング]] | * [[MonoGameでHLSLにMatrixを渡す]] | ||
*[[疑似インスタンシング]] | * [[MonoGameでプログラマブルシェーダーを使う]] | ||
*[[メッシュベイカー]] | * [[MonoGameでピクセルシェーダーを使ってテクスチャを貼る]] | ||
*[[ドローコール]] | * [[ハードウェアインスタンシング]] | ||
* [[疑似インスタンシング]] | |||
* [[メッシュベイカー]] | |||
* [[ドローコール]] | |||
[[category: MonoGame]] | [[category: MonoGame]] | ||
[[category: HLSL]] | [[category: HLSL]] | ||
2020年4月15日 (水) 05:54時点における版
1つのポリゴンメッシュをGPU側で複製することでDrawコールを減らして描画を高速化する「ハードウェアインスタンシング」という技術がある。
ただMonoGameとOpenGLの組み合わせの環境では長らく「new NotImplementedException()」であった。
これがついにMonoGame 3.7でOpenGL環境でもハードウェアインスタンシングが使えるようになったそうだ。さっそくMacOS上で試してみた。ハードウェアインスタンシングが使えるとなるとボリュームレンダリングの実装が捗る可能性がある。
Macでシェーダーをコンパイルできるようにする
Mac上でHLSLをコンパイルする方法は以下を参照。
HLSL側を書く
ワールド座標(ワールド空間内での位置や傾きなど)は、
一般的なシェーダーではユニフィード変数で受け取る。 float4x4 World; //これがワールド座標 float4x4 View; float4x4 Projection;
一方、ハードウェアインスタンシングを使う場合はダイナミック頂点バッファとして複数の値を受け取る。
float4x4 View;
float4x4 Projection;
// これがワールド座標群を格納したダイナミック頂点バッファ。
// C#側にfloat4x4の送信手段がないので「float4が4個」で代用している。
struct VSInstance
{
float4 w1 : BLENDWEIGHT0;
float4 w2 : BLENDWEIGHT1;
float4 w3 : BLENDWEIGHT2;
float4 w4 : BLENDWEIGHT3;
};
// 「float4が4個」をシェーダー関数内でfloat4x4に再構成して利用する。
VSOutput VSFunc(VSInput input, VSInstance instance)
{
float4x4 world = float4x4(instance.w1, instance.w2, instance.w3, instance.w4);
}
C#側を書く
まずインスタンスを格納するクラスを作る。
public struct VertexDynamicInstance : IVertexType
{
public Matrix World;
public static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration(
new VertexElement( 0, VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 0),
new VertexElement(16, VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 1),
new VertexElement(32, VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 2),
new VertexElement(48, VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 3));
VertexDeclaration IVertexType.VertexDeclaration
{
get { return VertexDynamicInstance.VertexDeclaration; }
}
}
とりあえず動作確認用に1000個のテストデータを作る。
var rand = new System.Random();
var instances = new VertexDynamicInstance[1000];
for (int i = 0; i < instances.Length; i++)
{
instances[i].World = Matrix.CreateTranslation(
(float)rand.NextDouble(),
(float)rand.NextDouble(),
(float)rand.NextDouble());
}
ダイナミック頂点バッファを生成する。
var instanceVertexBuffer = new DynamicVertexBuffer(graphicsDevice,
VertexDynamicInstance.VertexDeclaration,
instances.Length,
BufferUsage.WriteOnly);
instanceVertexBuffer.SetData(instances);
描画する。
// 頂点バッファとダイナミック頂点バッファの2つを指定する。
graphicsDevice.SetVertexBuffers(
new VertexBufferBinding( meshPart.VertexBuffer, meshPart.VertexOffset, 0),
new VertexBufferBinding( instanceVertexBuffer , 0, 1));
// インデックスバッファは普通に指定する。
graphicsDevice.Indices = meshPart.IndexBuffer;
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
// ドローコールはDrawInstancedPrimitivesで呼び出す。
graphicsDevice.DrawInstancedPrimitives(
primitiveType: PrimitiveType.TriangleList,
baseVertex: 0,
startIndex: 0,
primitiveCount: meshPart.PrimitiveCount,
instanceCount: instances.Length);
}
Mac上で動かしてみる
こんな感じ。MacでもHLSLで書いたシェーダーが使えてる。なお、OpenGL系の環境ではシェーダーモデルは3.0までだ。まあSM3.0もあれば元気があれば何でもできる。