I'm stuck trying to implement instanced mesh rendering in my project. Particularly because I am failing to understand how certain components actually function - and how the drawing routine is actually associating an effect with the instanced geometry. Being new to 3D devlopment this is turning into a pain in the ass for me - all of my previous experience had never expanded beyond 2D until now.
Although I understand C# Syntax - VB.net is a bit of a sentimental preference for me , so anything written in VB below is my code, and C# are sample references.
Before I carry on - I have tried to use http://www.float4x4.net/index.php/2011/07/hardware-instancing-for-pc-in-xna-4-with-textures/ as reference. However I am stuck trying to understand the 'InstanceBuffer', Vertex Bindings, and how I pass the effect I want to use - as im trying to do the same thing - but not with an atlas texture. I only want the transforms to be passed to my shader - and not with the generated cube geometry from the example - but instead copied from a model.
As seen in the above link - the author demonstrates instancing using generated cubes and "atlas' textures. I am trying to generate instanced geometry by first initializing from an existing model.
Firstly: Most examples I have found - always use a custom model importer , including Microsoft's sample - which in my case - I don't need or want - I instead have a class named InstancedModel - which contains a list of InstancedMeshPart objects - and is initialized from a Microsoft.XNA.Framework.Graphics.Model using the following methods:
Friend Shared Function G_GET_MODEL_VERTICES_AND_INDICES(Model As Model, ByRef Out_InstancedMeshParts As List(Of InstancedMeshPart)) As Integer
Dim Parts As Integer = 0
Out_InstancedMeshParts = New List(Of InstancedMeshPart)
For Each Mesh As ModelMesh In Model.Meshes
For Each Part As ModelMeshPart In Mesh.MeshParts
Dim Part_Vertices As List(Of VertexPositionTexture),
Part_Indices As List(Of Integer)
G_GET_PART_VERTICES_AND_INDICES(Part, Part_Vertices, Part_Indices)
Out_InstancedMeshParts.Add(
New InstancedMeshPart With {
.m_VERTICES = Part_Vertices.ToArray,
.m_INDICES = Part_Indices.ToArray,
.m_OriginalVertexDeclaration = Part.VertexBuffer.VertexDeclaration
}
)
Parts += 1
Next
Next
Return Parts
End Function
Friend Shared Sub G_GET_PART_VERTICES_AND_INDICES(Part As ModelMeshPart, ByRef Out_Vertices As List(Of VertexPositionTexture), ByRef Out_Indices As List(Of Integer))
Dim verticeslist As New List(Of VertexPositionTexture)
Dim indiceslist As New List(Of Integer)
Dim OffsetInBytes = Part.VertexBuffer.VertexDeclaration.VertexStride * Part.VertexOffset
Dim PartVertices(Part.NumVertices) As VertexPositionNormalTexture
Dim PartIndices(Part.PrimitiveCount * 3) As UShort
Part.VertexBuffer.GetData(OffsetInBytes, PartVertices, 0, Part.NumVertices, Part.VertexBuffer.VertexDeclaration.VertexStride)
OffsetInBytes = Part.StartIndex * SizeOfShort
Part.IndexBuffer.GetData(OffsetInBytes, PartIndices, 0, Part.PrimitiveCount * 3)
indiceslist.AddRange(PartVertices)
verticeslist.AddRange(PartVertices)
Out_Vertices = verticeslist
Out_Indices = indiceslist
End Sub
After Vertices and Indices are copied from a model , in my InstancedMeshPart object I then initialize my Vertex buffer, Index Buffer and my Vertex Declaration
Friend Sub InitializeGeometry(GraphicsDevice As GraphicsDevice)
m_VertexCount = m_VERTICES.Length
m_IndexCount = m_INDICES.Length
m_GeometryBuffer = New VertexBuffer(GraphicsDevice, VertexPositionTexture.VertexDeclaration, m_VertexCount, BufferUsage.WriteOnly)
m_GeometryBuffer.SetData(m_VERTICES)
m_IndexBuffer = New IndexBuffer(GraphicsDevice, New Integer().GetType, m_IndexCount, BufferUsage.WriteOnly)
m_IndexBuffer.SetData(m_INDICES)
Const SizeOfFloat = 4
Dim StreamElements As VertexElement() = {
New VertexElement(0, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 1),
New VertexElement(SizeOfFloat * 4, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 2),
New VertexElement(SizeOfFloat * 8, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 3),
New VertexElement(SizeOfFloat * 12, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 4),
New VertexElement(SizeOfFloat * 16, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 5)
}
m_VertexDeclaration = New VertexDeclaration(StreamElements)
End Sub
My StreamElements array is initialized the same way as the example in the link I mentioned above - however im not exactly sure if this is right since I don't want to pass the atlas texture stuff. I only need the transforms for the instances, so I assume I need to remove something - but im not sure how many elements I actually need.
Instead of using the "InstanceInfo" structure the article describes - I only want to pass the World Matrices in my instance buffer - so I am totally brainmashed on what I should be changing in my StreamElements arrray.
' Positions is an array of matrices - not a structure.
m_InstanceBuffer = New VertexBuffer(GraphicsDevice, m_VertexDeclaration, Positions.Length, BufferUsage.WriteOnly)
m_InstanceBuffer.SetData(Positions)
I am also struggling to understand exactly how the VertexBufferBindings work :
// from sample:
VertexBufferBinding[] bindings;
bindings = new VertexBufferBinding[2];
bindings[0] = new VertexBufferBinding(geometryBuffer);
bindings[1] = new VertexBufferBinding(instanceBuffer, 0, 1);
Why am I passing the transormation matrices stored in the InstanceBuffer to the bindings and not through an EffectParameter? Were or How are the parameters being passed to my shader (eg the transforms for each mesh instance)?
In his example - im not sure how the graphics device knows that it has to use the effect specified below: I can't see any relation to the effect object and the DrawInstancedPrimitives. How is his code assigning this effect to the instance mesh he is rendering? Especially since there is no Meshpart.Effect property? Were am I telling the graphics device that I want to draw this instanced geometry with "MyEffect"?
// from sample:
GraphicsDevice.Clear(Color.CornflowerBlue);
effect.CurrentTechnique = effect.Techniques["Instancing"];
effect.Parameters["WVP"].SetValue(view * projection);
effect.Parameters["cubeTexture"].SetValue(texture);
GraphicsDevice.Indices = indexBuffer;
effect.CurrentTechnique.Passes[0].Apply();
GraphicsDevice.SetVertexBuffers(bindings);
GraphicsDevice.DrawInstancedPrimitives(PrimitiveType.TriangleList, 0, 0, 24, 0, 12, count);
and similarly from http://blogs.msdn.com/b/shawnhar/archive/2010/06/17/drawinstancedprimitives-in-xna-game-studio-4-0.aspx
// from sample:
instanceVertexBuffer.SetData(instanceTransformMatrices, 0, numInstances, SetDataOptions.Discard);
graphicsDevice.SetVertexBuffers(modelVertexBuffer, new VertexBufferBinding(instanceVertexBuffer, 0, 1));
graphicsDevice.Indices = indexBuffer;
instancingEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.DrawInstancedPrimitives(PrimitiveType.TriangleList, 0, 0,
modelVertexBuffer.VertexCount, 0,
indexBuffer.IndexCount / 3,
numInstances);
In the above - It is obvious we are setting common EffectParameters for the effect and then applying it, but the issue being that I have no idea how or were the GraphicsDevice instance would know to use my effect.
Basic overview of what i cannot understand:
- How are my transforms ( in InstanceBuffer ) being passed to the shader?
- How or were does the graphics device know that I want to draw using a specific effect when trying to draw instanced geometry? It was easy when I was just looping ModelMeshParts before... but now my brain has been sucked into a black hole the moment I need to render several thousands of identical objects.
- Exactly what should I be removing from my 'VertexElements' variable - what happens to each element? Are they being passed to a shader? Am I right to assume I dont need the 5th element?
When I began trying to implement instancing I wanted to implement my instanced draw routine that accepts parameters like this. However because of what im stuck on - I cant fill this routine.
DrawInstanceModel(GraphicsDevice as GraphicsDevice, Model as InstancedModel, Effect as Effect , Positions as Matrix() )