「MonoGameでハードウェアインスタンシングしてみる」の版間の差分
ナビゲーションに移動
検索に移動
imported>Administrator |
|||
1行目: | 1行目: | ||
− | 1つの[[ポリゴンメッシュ]]を[[GPU]] | + | 1つの[[ポリゴンメッシュ]]を[[GPU]]側で複製することで[[Drawコール]]を減らして描画を高速化する「[[ハードウェアインスタンシング]]」という技術がある。 |
− | ただ[[MonoGame]]と[[OpenGL]] | + | ただ[[MonoGame]]と[[OpenGL]]の組み合わせの環境では長らく「new NotImplementedException()」であった。 |
これがついにMonoGame 3.7で[[OpenGL]]環境でも[[ハードウェアインスタンシング]]が使えるようになったそうだ。さっそく[[MacOS]]上で試してみた。[[ハードウェアインスタンシング]]が使えるとなると[[ボリュームレンダリング]]の実装が捗る可能性がある。 | これがついにMonoGame 3.7で[[OpenGL]]環境でも[[ハードウェアインスタンシング]]が使えるようになったそうだ。さっそく[[MacOS]]上で試してみた。[[ハードウェアインスタンシング]]が使えるとなると[[ボリュームレンダリング]]の実装が捗る可能性がある。 | ||
− | == HLSLを書く== | + | ==HLSLを書く== |
[[HLSL]]はこんな感じ。今回は移動だけで回転はしていない。 [[Mac]]上で[[HLSL]]を[[コンパイル]]する方法は「 [[MonoGameでプログラマブルシェーダーを使う]]」および「[[InfinitespaceStudios.Pipeline]]」の項目を参照。 | [[HLSL]]はこんな感じ。今回は移動だけで回転はしていない。 [[Mac]]上で[[HLSL]]を[[コンパイル]]する方法は「 [[MonoGameでプログラマブルシェーダーを使う]]」および「[[InfinitespaceStudios.Pipeline]]」の項目を参照。 | ||
69行目: | 69行目: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | == C#で呼び出してみる== | + | ==C#で呼び出してみる== |
この[[コード]]は「[[MonoGameのカメラを作る]]」で作ったカメラを使っている。Cameraクラスは[[MonoGame]]標準物ではないので注意。 | この[[コード]]は「[[MonoGameのカメラを作る]]」で作ったカメラを使っている。Cameraクラスは[[MonoGame]]標準物ではないので注意。 | ||
205行目: | 205行目: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | == 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でピクセルシェーダーを使ってテクスチャを貼る]] | *[[MonoGameでピクセルシェーダーを使ってテクスチャを貼る]] | ||
+ | *[[ハードウェアインスタンシング]] | ||
+ | *[[疑似インスタンシング]] | ||
+ | *[[メッシュベイカー]] | ||
+ | *[[ドローコール]] | ||
[[category: MonoGame]] | [[category: MonoGame]] | ||
[[category: HLSL]] | [[category: HLSL]] |
2019年10月27日 (日) 00:01時点における版
1つのポリゴンメッシュをGPU側で複製することでDrawコールを減らして描画を高速化する「ハードウェアインスタンシング」という技術がある。
ただMonoGameとOpenGLの組み合わせの環境では長らく「new NotImplementedException()」であった。
これがついにMonoGame 3.7でOpenGL環境でもハードウェアインスタンシングが使えるようになったそうだ。さっそくMacOS上で試してみた。ハードウェアインスタンシングが使えるとなるとボリュームレンダリングの実装が捗る可能性がある。
HLSLを書く
HLSLはこんな感じ。今回は移動だけで回転はしていない。 Mac上でHLSLをコンパイルする方法は「 MonoGameでプログラマブルシェーダーを使う」および「InfinitespaceStudios.Pipeline」の項目を参照。
// file: effect4.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
// ----------------------------------------------------------------
// 呼び出し側(C#)から設定されるグローバル変数(シェーダー内では実質定数)
float4x4 myView : VIEW;
float4x4 myProjection : PROJECTION;
// ----------------------------------------------------------------
// 入出力用の構造体
struct VertexPositionColor
{
float4 Position : POSITION0;
float4 Color : COLOR;
};
// ----------------------------------------------------------------
// バーテックスシェーダー
VertexPositionColor MyVertexShader(
VertexPositionColor input,
float3 position : POSITION1
)
{
// inputの位置をpositionほど移動させる。
// 今回は移動だけ。
// 回転したい場合なども同じ要領でここで変形させる。
input.Position.xyz += position;
// 頂点をカメラから見た座標に変換する。
input.Position = mul( input.Position, mul(myView, myProjection));
return input;
}
// ----------------------------------------------------------------
// ピクセルシェーダー
float4 MyPixelShader(float4 color : COLOR) : COLOR0
{
return color;
}
technique HardwareInstancing
{
pass Pass1
{
VertexShader = compile VS_SHADERMODEL MyVertexShader();
PixelShader = compile PS_SHADERMODEL MyPixelShader();
}
}
C#で呼び出してみる
このコードは「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;
Camera camera;
Effect effect;
VertexBuffer triangleVertexBuffer;
IndexBuffer indexBuffer;
DynamicVertexBuffer instanceVertexBuffer;
VertexPositionColor[] vertices = {
new VertexPositionColor(new Vector3(-1, 1, 0), Color.Blue ),
new VertexPositionColor(new Vector3( 1, 1, 0), Color.White),
new VertexPositionColor(new Vector3( 1, -1, 0), Color.Red )
};
Vector3[] instances = new Vector3[] {
new Vector3(0f, 0f, 0f),
new Vector3(1f, 1f, 0f),
new Vector3(2f, 2f, 0f)
};
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
IsMouseVisible = true;
base.Initialize();
}
protected override void LoadContent()
{
// 即席カメラ
camera = new Camera(this);
camera.Position = new Vector3(0, 0, 10);
camera.Target = new Vector3(0, 0, 0);
// エフェクト読み込み
effect = Content.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 DynamicVertexBuffer(
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: vertices.Length / 3,
instanceCount: instances.Length);
}
base.Draw(gameTime);
}
}
Mac上で動かしてみる
こんな感じ。MacでもHLSLで書いたシェーダーが使えてる。なお、OpenGL系の環境ではシェーダーモデルは3.0までだ。まあSM3.0もあれば元気があれば何でもできる。