[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();
}
}