|
|
| (3人の利用者による、間の5版が非表示) |
| 1行目: |
1行目: |
| 1つの[[ポリゴンメッシュ]]を[[GPU]]側で複製することでDrawコールを減らして描画を高速化する「[[ハードウェアインスタンシング]]」という技術がある。 | | 1つの[[ポリゴンメッシュ]]を[[GPU]]側で複製することで[[Drawコール]]を減らして描画を高速化する「[[ハードウェアインスタンシング]]」という技術がある。 |
|
| |
|
| ただ[[MonoGame]]と[[OpenGL]]の環境では長らく「new NotImplementedException()」であった。これがついにMonoGame 3.7で[[OpenGL]]環境でも[[ハードウェアインスタンシング]]が使えるようになったそうだ。さっそく[[MacOS]]上で試してみた。[[ハードウェアインスタンシング]]が使えるとなると[[ボリュームレンダリング]]の実装が捗る可能性がある。 | | ただ[[MonoGame]]と[[OpenGL]]の組み合わせの環境では長らく「new NotImplementedException()」であった。 |
| == HLSLを書く==
| |
| [[HLSL]]はこんな感じ。今回は移動だけで回転はしていない。 [[Mac]]上で[[HLSL]]を[[コンパイル]]する方法は「 [[MonoGameでプログラマブルシェーダーを使う]]」および「[[InfinitespaceStudios.Pipeline]]」の項目を参照。
| |
|
| |
|
| <syntaxhighlight lang="text">
| | これがついにMonoGame 3.7で[[OpenGL]]環境でも[[ハードウェアインスタンシング]]が使えるようになったそうだ。さっそく[[MacOS]]上で試してみた。[[ハードウェアインスタンシング]]が使えるとなると[[ボリュームレンダリング]]の実装が捗る可能性がある。 |
| // file: effect4.fx
| |
|
| |
|
| // ----------------------------------------------------------------
| | == Macでシェーダーをコンパイルできるようにする == |
| // MonoGame PipelineでOpenGL環境の場合は「OPENGL」シンボルが立っている。
| | Mac上でHLSLをコンパイルする方法は以下を参照。 |
| // 以下は定型文だと思ってコピペしとけ。
| | * [[MonoGameでプログラマブルシェーダーを使う]] |
| #if OPENGL
| | * [[InfinitespaceStudios.Pipeline]] |
| #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
| |
|
| |
|
| // ----------------------------------------------------------------
| | == HLSL側を書く == |
| // 呼び出し側(C#)から設定されるグローバル変数(シェーダー内では実質定数)
| | [[ワールド座標]](ワールド空間内での位置や傾きなど)は、 |
| float4x4 myView : VIEW;
| |
| float4x4 myProjection : PROJECTION;
| |
|
| |
|
| // ---------------------------------------------------------------- | | 一般的な[[シェーダー]]では[[ユニフォーム変数]]で受け取る。 |
| // 入出力用の構造体 | | <source lang="hlsl"> |
| struct VertexPositionColor | | float4x4 World; //これがワールド座標 |
| | float4x4 View; |
| | float4x4 Projection; |
| | </source> |
| | |
| | 一方、[[ハードウェアインスタンシング]]を使う場合はダイナミック頂点バッファとして複数の値を受け取る。 |
| | <source lang="hlsl"> |
| | float4x4 View; |
| | float4x4 Projection; |
| | |
| | // これがワールド座標群を格納したダイナミック頂点バッファ。 |
| | // C#側にfloat4x4の送信手段がないので「float4が4個」で代用している。 |
| | struct VSInstance |
| { | | { |
| float4 Position : POSITION0; | | float4 w1 : BLENDWEIGHT0; |
| float4 Color : COLOR; | | float4 w2 : BLENDWEIGHT1; |
| | float4 w3 : BLENDWEIGHT2; |
| | float4 w4 : BLENDWEIGHT3; |
| }; | | }; |
|
| |
|
| // ---------------------------------------------------------------- | | // 「float4が4個」をシェーダー関数内でfloat4x4に再構成して利用する。 |
| // バーテックスシェーダー
| | VSOutput VSFunc(VSInput input, VSInstance instance) |
| VertexPositionColor MyVertexShader(
| |
| VertexPositionColor input,
| |
| float3 position : POSITION1
| |
| )
| |
| { | | { |
| // inputの位置をpositionほど移動させる。 | | float4x4 world = float4x4(instance.w1, instance.w2, instance.w3, instance.w4); |
| // 今回は移動だけ。
| |
| // 回転したい場合なども同じ要領でここで変形させる。
| |
| input.Position.xyz += position;
| |
| | |
| // 頂点をカメラから見た座標に変換する。
| |
| input.Position = mul( input.Position, mul(myView, myProjection));
| |
| return input;
| |
| } | | } |
|
| |
|
| // ---------------------------------------------------------------- | | </source> |
| // ピクセルシェーダー
| |
| float4 MyPixelShader(float4 color : COLOR) : COLOR0
| |
| {
| |
| return color;
| |
| }
| |
|
| |
|
| technique HardwareInstancing
| | == C#側を書く == |
| | まず3Dモデルのインスタンス([[ワールド座標]])を格納する[[クラス]]を作る。 |
| | <source lang="csharp"> |
| | public struct VertexDynamicInstance : IVertexType |
| { | | { |
| pass Pass1
| | public Matrix World; |
| {
| |
| VertexShader = compile VS_SHADERMODEL MyVertexShader();
| |
| PixelShader = compile PS_SHADERMODEL MyPixelShader();
| |
| }
| |
| }
| |
| </syntaxhighlight>
| |
| == C#で呼び出してみる==
| |
| この[[コード]]は「[[MonoGameのカメラを作る]]」で作ったカメラを使っている。Cameraクラスは[[MonoGame]]標準物ではないので注意。
| |
|
| |
|
| <syntaxhighlight lang="csharp">
| | public static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration( |
| using System;
| | new VertexElement( 0, VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 0), |
| using System.Collections.Generic;
| | new VertexElement(16, VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 1), |
| using System.Text;
| | new VertexElement(32, VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 2), |
| using Microsoft.Xna.Framework;
| | new VertexElement(48, VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 3)); |
| using Microsoft.Xna.Framework.Graphics;
| |
| using Microsoft.Xna.Framework.Input;
| |
|
| |
|
| public class Game1 : Game | | VertexDeclaration IVertexType.VertexDeclaration |
| { | | { |
| GraphicsDeviceManager graphics; | | get { return VertexDynamicInstance.VertexDeclaration; } |
| | } |
| | } |
| | </source> |
|
| |
|
| Camera camera;
| | とりあえず動作確認用に1000個のテストデータを作る。 |
| Effect effect;
| | <source lang="csharp"> |
| VertexBuffer triangleVertexBuffer; | | var rand = new System.Random(); |
| IndexBuffer indexBuffer; | | var instances = new VertexDynamicInstance[1000]; |
| DynamicVertexBuffer instanceVertexBuffer; | | for (int i = 0; i < instances.Length; i++) |
| | { |
| | instances[i].World = Matrix.CreateTranslation( |
| | (float)rand.NextDouble(), |
| | (float)rand.NextDouble(), |
| | (float)rand.NextDouble()); |
| | } |
| | </source> |
|
| |
|
| VertexPositionColor[] vertices = {
| | インスタンス群を格納したダイナミック頂点バッファを生成する。 |
| new VertexPositionColor(new Vector3(-1, 1, 0), Color.Blue ),
| | <source lang="csharp"> |
| new VertexPositionColor(new Vector3( 1, 1, 0), Color.White),
| | // ダイナミック頂点バッファを作って |
| new VertexPositionColor(new Vector3( 1, -1, 0), Color.Red )
| | var instanceVertexBuffer = new DynamicVertexBuffer(graphicsDevice, |
| };
| | VertexDynamicInstance.VertexDeclaration, |
| | instances.Length, |
| | BufferUsage.WriteOnly); |
|
| |
|
| Vector3[] instances = new Vector3[] {
| | // インスタンス群を入れ込む |
| new Vector3(0f, 0f, 0f),
| | instanceVertexBuffer.SetData(instances); |
| new Vector3(1f, 1f, 0f),
| | </source> |
| new Vector3(2f, 2f, 0f)
| |
| };
| |
|
| |
|
| public Game1()
| | 描画する。 |
| {
| | <source lang="csharp"> |
| graphics = new GraphicsDeviceManager(this);
| | // 頂点バッファとダイナミック頂点バッファの2つを指定する。 |
| Content.RootDirectory = "Content";
| | graphicsDevice.SetVertexBuffers( |
| }
| | new VertexBufferBinding( meshPart.VertexBuffer, meshPart.VertexOffset, 0), |
| | new VertexBufferBinding( instanceVertexBuffer , 0, 1)); |
|
| |
|
| protected override void Initialize()
| | // インデックスバッファは普通に指定する。 |
| {
| | graphicsDevice.Indices = meshPart.IndexBuffer; |
| IsMouseVisible = true;
| |
| base.Initialize();
| |
| }
| |
|
| |
|
| protected override void LoadContent()
| | foreach (EffectPass pass in effect.CurrentTechnique.Passes) |
| {
| | { |
| // 即席カメラ
| | pass.Apply(); |
| camera = new Camera(this);
| |
| camera.Position = new Vector3(0, 0, 10);
| |
| camera.Target = new Vector3(0, 0, 0);
| |
|
| |
|
| // エフェクト読み込み
| | // ドローコールはDrawInstancedPrimitivesで呼び出す。 |
| effect = Content.Load<Effect>("effect4");
| | graphicsDevice.DrawInstancedPrimitives( |
| | primitiveType: PrimitiveType.TriangleList, |
| | baseVertex: 0, |
| | startIndex: 0, |
| | primitiveCount: meshPart.PrimitiveCount, |
| | instanceCount: instances.Length); |
| | } |
| | </source> |
|
| |
|
| // バーテックスバッファーを生成
| | == Mac上で動かしてみる == |
| triangleVertexBuffer = new VertexBuffer(
| | こんな感じ。[[Mac]]でも[[HLSL]]で書いた[[シェーダー]]が使えてる。なお、[[OpenGL]]系の環境では[[シェーダーモデル]]は3.0までだ。まあSM3.0もあれば元気があれば何でもできる。 |
| GraphicsDevice,
| |
| VertexPositionColor.VertexDeclaration,
| |
| vertices.Length,
| |
| BufferUsage.WriteOnly);
| |
| triangleVertexBuffer.SetData<VertexPositionColor>(vertices);
| |
|
| |
|
| // インデックスバッファーを生成
| | <movie>https://youtu.be/qU-toUOhvRI</movie> |
| 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);
| |
| }
| |
| }
| |
| </syntaxhighlight>
| |
|
| |
| == Mac上で動かしてみる==
| |
| こんな感じ。[[Mac]]でも[[HLSL]]で書いた[[シェーダー]]が使えてる。なお、[[OpenGL]]系の環境では[[シェーダーモデル]]は3.0までだ。まあSM3.0もあれば元気があれば何でもできる。
| |
|
| |
| <movie>https://youtu.be/qU-toUOhvRI</movie>
| |
|
| |
|
| == 関連項目== | | == 関連項目 == |
| *[[MonoGameでプログラマブルシェーダーを使う]] | | * [[MonoGameでHLSLにMatrixを渡す]] |
| *[[MonoGameでピクセルシェーダーを使ってテクスチャを貼る]] | | * [[MonoGameでプログラマブルシェーダーを使う]] |
| | * [[MonoGameでピクセルシェーダーを使ってテクスチャを貼る]] |
| | * [[ハードウェアインスタンシング]] |
| | * [[疑似インスタンシング]] |
| | * [[メッシュベイカー]] |
| | * [[ドローコール]] |
|
| |
|
| [[category: MonoGame]] | | [[category: MonoGame]] |
| [[category: HLSL]] | | [[category: HLSL]] |