「MonoGameでハードウェアインスタンシングしてみる」を編集中
ナビゲーションに移動
検索に移動
この編集を取り消せます。 下記の差分を確認して、本当に取り消していいか検証してください。よろしければ変更を保存して取り消しを完了してください。
最新版 | 編集中の文章 | ||
5行目: | 5行目: | ||
これがついにMonoGame 3.7で[[OpenGL]]環境でも[[ハードウェアインスタンシング]]が使えるようになったそうだ。さっそく[[MacOS]]上で試してみた。[[ハードウェアインスタンシング]]が使えるとなると[[ボリュームレンダリング]]の実装が捗る可能性がある。 | これがついにMonoGame 3.7で[[OpenGL]]環境でも[[ハードウェアインスタンシング]]が使えるようになったそうだ。さっそく[[MacOS]]上で試してみた。[[ハードウェアインスタンシング]]が使えるとなると[[ボリュームレンダリング]]の実装が捗る可能性がある。 | ||
− | == | + | ==HLSLを書く== |
− | + | [[HLSL]]はこんな感じ。今回は移動だけで回転はしていない。 [[Mac]]上で[[HLSL]]を[[コンパイル]]する方法は「 [[MonoGameでプログラマブルシェーダーを使う]]」および「[[InfinitespaceStudios.Pipeline]]」の項目を参照。 | |
− | |||
− | |||
− | = | + | <syntaxhighlight lang="text"> |
− | + | // 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 | + | float4x4 myView : VIEW; |
− | float4x4 | + | float4x4 myProjection : PROJECTION; |
− | // | + | // ---------------------------------------------------------------- |
− | // | + | // 入出力用の構造体 |
− | struct | + | struct VertexPositionColor |
{ | { | ||
− | float4 | + | float4 Position : POSITION0; |
− | float4 | + | 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(); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | ==C#で呼び出してみる== | ||
+ | この[[コード]]は「[[MonoGameのカメラを作る]]」で作ったカメラを使っている。Cameraクラスは[[MonoGame]]標準物ではないので注意。 | ||
− | + | <syntaxhighlight lang="csharp"> | |
− | + | 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); | + | 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上で動かしてみる== |
こんな感じ。[[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]] |