[PR]
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
プログラミング、3DCGとその他いろいろについて
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
前回はハードウェア・インスタンシングをやりましたが、これはGPUシェーダー・モデル3.0以上でなければ動きません。
どうやらまだ2.0までのものがよく使われているそうなので、これでは困る場合もあるでしょう。
実は、シェーダーモデルが2.0でも上手くいく方法があります。
グラフィックスカードのメモリを少々食うのですが、モデルそのものはたくさん複製して1つのVertexBufferに入れておいて、インスタンスの情報は配列にしてHLSLのグローバル変数としてセットしてしまうというものです。
これをシェーダー・インスタンシングといいます。
ハードウェア・インスタンシングではVertexBufferにインスタンスの情報を格納しましたが、こちらはHLSLのグローバル変数に格納します。
そして、本来のVertexBufferの中にあるそれぞれのモデルに、インデックスを振ります。
そのインデックスから対応するインスタンスの情報を特定して、頂点に適用するのです。(どうもわかりにくいですね・・・)
こうすることによって、1つのVertexBufferから、自由に動かせる複数のモデルを一度に描画することが出来ます。
モデルのデータを複製して1つのVertexBufferに入れるため、その分無駄なグラフィックス・カードのメモリを食うのですが、それでも一つ一つモデルを描画するよりもパフォーマンスが良くなる(CPUの負荷が減るので)そうです。
HLSLのコードは次のようになります:
ShaderInstancing.fx
float4x4 InstanceTransforms[10]; struct VertexPositionColor { float4 Position : POSITION; float4 Color : COLOR; }; VertexPositionColor VertexShader( VertexPositionColor input, float instanceIndex : TEXCOORD1 ) { VertexPositionColor output; output.Position = mul(input.Position, InstanceTransforms[instanceIndex]); output.Color = input.Color; return output; } float4 PixelShader(float4 color : COLOR):COLOR { return color; } technique ShaderInstancing { pass ShaderInstancingPass { VertexShader = compile vs_2_0 VertexShader(); PixelShader = compile ps_2_0 PixelShader(); } }
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Content; struct VertexPositionColorIndex { public Vector3 Position; public Color Color; public float Index; public static readonly int SizeInBytes = System.Runtime.InteropServices.Marshal.SizeOf( typeof(VertexPositionColorIndex) ); public static readonly VertexElement[] VertexElements = new VertexElement[]{ new VertexElement( 0, 0, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Position, 0 ), new VertexElement( 0, sizeof(float)*3, VertexElementFormat.Color, VertexElementMethod.Default, VertexElementUsage.Color, 0 ), new VertexElement( 0, sizeof(float)*3 + 4, VertexElementFormat.Single, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 1 ) }; } class MyGame : Game { GraphicsDeviceManager graphics; ContentManager content; const int instanceCount = 10; VertexPositionColorIndex[] vertices = new VertexPositionColorIndex[3 * instanceCount]; Matrix[] instanceTransforms = new Matrix[instanceCount]; Effect effect; VertexBuffer vertexBuffer; public MyGame() { graphics = new GraphicsDeviceManager(this); content = new ContentManager(Services); for (int i = 0; i < instanceCount; i++) { vertices[3 * i].Color = Color.Blue; vertices[3 * i].Position = new Vector3(-0.1f, 0.1f, 0); vertices[3 * i].Index = i; vertices[3 * i + 1].Color = Color.White; vertices[3 * i + 1].Position = new Vector3(0.1f, 0.1f, 0); vertices[3 * i + 1].Index = i; vertices[3 * i + 2].Color = Color.Red; vertices[3 * i + 2].Position = new Vector3(0.1f, -0.1f, 0); vertices[3 * i + 2].Index = i; } for (int i = 0; i < instanceTransforms.Length; i++) { instanceTransforms[i] = Matrix.CreateTranslation(0.1f * i, 0, 0); } } protected override void LoadGraphicsContent(bool loadAllContent) { if (loadAllContent) { effect = content.Load<Effect>("ShaderInstancing"); vertexBuffer = new VertexBuffer( graphics.GraphicsDevice, vertices.Length * VertexPositionColorIndex.SizeInBytes, ResourceUsage.None ); vertexBuffer.SetData<VertexPositionColorIndex>(vertices); graphics.GraphicsDevice.VertexDeclaration = new VertexDeclaration( graphics.GraphicsDevice, VertexPositionColorIndex.VertexElements ); } } protected override void Draw(GameTime gameTime) { graphics.GraphicsDevice.Clear(Color.CornflowerBlue); graphics.GraphicsDevice.Vertices[0].SetSource( vertexBuffer, 0, VertexPositionColorIndex.SizeInBytes ); effect.Parameters["InstanceTransforms"].SetValue(instanceTransforms); effect.Begin(); foreach (EffectPass pass in effect.CurrentTechnique.Passes) { pass.Begin(); graphics.GraphicsDevice.DrawPrimitives( PrimitiveType.TriangleList, 0, vertices.Length / 3 ); pass.End(); } effect.End(); } }