「MonoGameでハードウェアインスタンシングしてみる」の版間の差分
ナビゲーションに移動
検索に移動
imported>Administrator |
Administrator (トーク | 投稿記録) (→C#側を書く) |
||
(同じ利用者による、間の3版が非表示) | |||
5行目: | 5行目: | ||
これがついにMonoGame 3.7で[[OpenGL]]環境でも[[ハードウェアインスタンシング]]が使えるようになったそうだ。さっそく[[MacOS]]上で試してみた。[[ハードウェアインスタンシング]]が使えるとなると[[ボリュームレンダリング]]の実装が捗る可能性がある。 | これがついにMonoGame 3.7で[[OpenGL]]環境でも[[ハードウェアインスタンシング]]が使えるようになったそうだ。さっそく[[MacOS]]上で試してみた。[[ハードウェアインスタンシング]]が使えるとなると[[ボリュームレンダリング]]の実装が捗る可能性がある。 | ||
− | == | + | == Macでシェーダーをコンパイルできるようにする == |
− | + | Mac上でHLSLをコンパイルする方法は以下を参照。 | |
+ | * [[MonoGameでプログラマブルシェーダーを使う]] | ||
+ | * [[InfinitespaceStudios.Pipeline]] | ||
− | + | == HLSL側を書く == | |
− | + | [[ワールド座標]](ワールド空間内での位置や傾きなど)は、 | |
− | + | 一般的な[[シェーダー]]では[[ユニフォーム変数]]で受け取る。 | |
− | + | <source lang="hlsl"> | |
− | // | + | float4x4 World; //これがワールド座標 |
− | + | float4x4 View; | |
− | + | float4x4 Projection; | |
− | + | </source> | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | 一方、[[ハードウェアインスタンシング]]を使う場合はダイナミック頂点バッファとして複数の値を受け取る。 | |
− | + | <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#側を書く == | |
+ | まず3Dモデルのインスタンス([[ワールド座標]])を格納する[[クラス]]を作る。 | ||
+ | <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:58時点における最新版
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#側を書く[編集 | ソースを編集]
まず3Dモデルのインスタンス(ワールド座標)を格納するクラスを作る。
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もあれば元気があれば何でもできる。