Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>tl;dr;</p> <p>You can simply call <code>RenderModel</code> in a loop. Though, for better performance you would want to <em>sort your operations by update frequency</em>, <em>always dispose unneeded resources</em> and <em>don't keep multiple copies of the same data</em>.</p> <hr> <p>Well, a naive approach for rendering multiple objects with your setup would look like this:</p> <pre><code>foreach (var model in models) { RenderModel(model); } </code></pre> <p>I say naive, because there are quite a lot of things I would fix first.</p> <h1>Enhancing Performance</h1> <p>Real-time 3D applications are all about good performance. In order to achieve it, you have to really be careful about how often you do expensive tasks. For example, at the moment you are creating a new vertex and index buffer <em>everytime</em> the <code>RenderModel</code> method is called. (And I suppose this would happen each frame?) Also, the effect states (like view/projection matrices and lighting) would be set for every model individually, eventhough they probably stay they same for the whole frame.</p> <h2>Separate effect states by frequency of update.</h2> <p>Identify expensive operations (setting/creating shaders, constants, buffers; roughly anything that uses the GraphicsDevice) and think about how often they need to change. For optimal performance, don't do it more often than that. Examples:</p> <ul> <li><p><strong>Vertex buffer for a mesh:</strong> If the mesh is not dynamic and the vertices won't change for some time (or not at all), the vertex buffer will always contain the same data. It is totally sufficient to create it when you load the mesh and then reuse it over and over again. (Also applies to index buffers.) <em>(Update frequency: Once per level)</em></p></li> <li><p><strong>Setting lighting, view and projection matrices:</strong> Lighting usually stays the same during one frame. So do the camera matrices. Set them once at the beginning of the frame and you are done. <em>(Update frequency: Once per frame)</em></p></li> <li><p><strong>Binding textures:</strong> Usually you would have different <em>materials</em> in the scene, associated with individual models. Most of the time a material belongs to multiple objects (because you combined their textures in one large texture map). So binding these textures and constants once for all meshes which use it would save the most GPU capacity. <em>(Update frequency: Once per material)</em></p></li> <li><p><strong>Binding vertex and index buffers:</strong> Vertex and index data is usually unique for a mesh. So if you have more than one mesh in your scene, switching buffers on the device as soon you are done with one mesh is inevitable. <em>(Update frequency: Once per mesh)</em></p></li> <li><p><strong>Setting the world matrix:</strong> Your effect states are set and the mesh data is bound to the device. You can finally draw your object. So you set the world matrix and call the draw method of the device. But wait, you want another copy of the mesh somewhere else in the scene? Now is the best time for it. Simply bind the other world matrix and draw again. You just drew two objects with a minimum change of state on the device. Perfect!<sup>1</sup> <em>(Update frequency: Once per mesh instance)</em></p></li> </ul> <p><sup>1</sup> <sub>If you really need <em>a lot</em> of copies of the same mesh in the scene, I suggest taking a look into <a href="http://en.wikipedia.org/wiki/Geometry_instancing">Hardware instancing</a>, available starting from Direc3D 9.</sub></p> <h2>Dispose resources that are not needed anymore.</h2> <p>Usually the Garbage Collector takes care of your old objects and it does a <em>really</em> good job at that. But here you are working with <em>unamanged</em> resources. They are called unmanaged because they are not managed by the CLR and therefore not affected by garbage collection. </p> <h3>Why should that bother you?</h3> <p>Keeping such objects around will probably lead to a memory leak. Your memory will be cluttered with unneeded resources, resulting in a performance loss.<sup>2</sup></p> <p>.NET provides an interface for classes that use unmanaged resources, called the <a href="http://msdn.microsoft.com/en-us/library/system.idisposable.aspx">IDisposable</a> interface. It exposes a <code>Disposable</code> method from which the unmanaged resources should be released. All you have to do is call the method, as soon as you are done using the object.</p> <pre><code>if (myTexture != null) myTexture.Dispose(); </code></pre> <p>For example in your case you create a new vertex and index buffer every frame. You should definitely dispose them, before you overwrite them with new buffers and the old ones disappear into nirvana. (Not to mention, that you shouldn't create them so often anyways.) Just take a look at some of the Direct3D classes, and you will see that most of them implement IDisposable.</p> <p><sup>2</sup><sub>From what I understand, it is actually not <em>that</em> bad. Implementing IDisposable correctly usually means that the classes have a Destructor, which calls <code>Dispose</code> anyways as soon as the object is garbage collected. Still, since you cannot know <em>when</em> garbage collection happens, it is the general recommondation to dispose Disposable objects manually anyways.</sub></p> <h2>Don't keep multiple copies of the same data.</h2> <p>At the moment you are probably storing your vertices and indices in arrays. When you create the buffer, you are basically creating a copy of the data. As I mentioned before: The vertex and index buffer can be created once per mesh. So when you do that, write the vertices and indices to the buffers and then keep only those. Your model class would of course change slightly:</p> <pre><code>public class Model { public VertexBuffer Vertices { get; private set; } public IndexBuffer Indices { get; private set; } } </code></pre> <p>If you don't need access to the vertices and indices directly, this would be a better solution.</p> <p><sub>I know, this is a minor step, but I think it's good to be aware of it.</sub></p> <h1>Pseudo-Code Render Method</h1> <pre><code>public void RenderFrame(Model[] models) { // Per frame Bind(View); Bind(Projection); BindLighting(); // Per effect BindEffect(); foreach (var material in GetMaterials(models)) { // Per material Bind(material.Color); Bind(material.DiffuseMap); foreach (var model in GetModelsByMaterial(material, models)) { // Per mesh Bind(model.VertexBuffer); Bind(model.IndexBuffer); foreach (var instance in model.Instances) { // Per instance Bind(instance.World); // Draw the instance Draw(); } } } } </code></pre> <hr> <p><sub><strong>Disclaimer:</strong> I have been working with Direct3D 9 (XNA) only for some months. Most of the things I wrote should apply to both, D3D9 and Direct3D 10/11, which I am by far more familiar with.</sub></p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload