1

在我的项目中,我展示了很多球体。

装置

为了显示球体,我加载了一个包含一些值的文件。因此它可能是 1600 个球体。现在我在渲染时遇到性能问题...... :(

在这一部分中,我初始化了我的设备对象:

        try
        {
            meshList = new List<Sphere>();

            // Erstellt die PresentParameters für weitere Einstellungen des Device
            PresentParameters presParams = new PresentParameters()
            {
                Windowed = true,                            // Device nur innerhalbe des Fensterhandels benutzen
                SwapEffect = SwapEffect.Discard,            // Grafikkarte entscheidet selbst wie sie den Backbuffer zur anzeige bringt
                EnableAutoDepthStencil = true,              // Boolean zum Merken der Tiefe
                AutoDepthStencilFormat = DepthFormat.D16    // Format der Tiefe
            };

            // Erzeugt eine Instanz von dem Device
            device = new Device(0,                                      // Nummer fuer den Grafikadapter der verwendet wird                  
                                DeviceType.Hardware,                    // Parameter über die Garfikkarte oder CPU ausführen
                                panel1,                 // Fensterhadel für das Device 
                                CreateFlags.HardwareVertexProcessing,   // Einstellung des Device. Gibt an, dass die Vertices nur per Software verarbeitet werden 
                                presParams);                            // Gibt die weiteren Einstellungen mit

            // Wenn das Device neupositioniert wird
            device.DeviceReset += new System.EventHandler(this.OnResetDevice);
            // Führt das Reset aus
            OnResetDevice(device, null);

            // Definiert keine Vor und Rückseite
            device.RenderState.CullMode = Cull.Clockwise;
            // Direct3D-Beleuchtung deaktivieren
            device.RenderState.Lighting = false;
            // Beschreibt einen festen Füllmodus
            device.RenderState.FillMode = FillMode.Solid;

            // Erstellt den Buffer für die Vertices (Lab Koordinatensystem)
            vertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionColored),   // Typ der Vertices
                                            18,                                     // Anzahl der Vertices
                                            device,                                 // Gerätekontext unser device
                                            0,                                      // Anzahl der Flags zur Verarbeitung der Vertice
                                            CustomVertex.PositionColored.Format,    // Typ der Vertices (Weil man auch eigene Strukturen definieren kann)
                                            Pool.Default);                          // Speicherung der Vertices

            // Event welches aufgerufen wird wenn der Vertexbuffer erstellt wurde
            vertexBuffer.Created += new System.EventHandler(this.OnCreateVertexBuffer);
            // Event wird von Hand aufgerufen
            this.OnCreateVertexBuffer(vertexBuffer, null);

            return true;    // Device wurde erstellt
        }
        catch { return false; } // Device konnte nicht erstellt werden 

在这一部分中,我渲染所有顶点:

    public void Render()
    {
        // Fragt ob das Device erstellt wurde und noch gültig ist
        if (device == null)
            return;

        // Inhalt des Backbuffers löschen und das ganze mit einer Farbe einfärben
        device.Clear(ClearFlags.Target | ClearFlags.ZBuffer,    // Die entsprechende Oberfläche
                     System.Drawing.Color.Black,                // Die Farbe 
                     1.0f,                                      // Abstand vom Betrachter, an dem die Oberfläche gelöscht wird und einen Wert, ...
                     0);                                        // ...der in jedem Stencil-Buffer-Eintrag gespeichert wird.

        // Anfang der Szene
        device.BeginScene();
        // Matrizen aufsetzen
        SetupMatrices();

        // Bindet den Buffer an das Device
        device.SetStreamSource(0,           // Nummer des Streams
                               vertexBuffer,// Der Buffer
                               0);          // StartOffset in dem Buffer

        // Teilt dem Device das Format der Vertices mit
        device.VertexFormat = CustomVertex.PositionColored.Format;
        // Zeichnet die Dreiecke
        device.DrawPrimitives(PrimitiveType.LineList,   // Typ der Primitive
                              0,                        // Eintrag des ersten Vertex
                              3);                       // Anzahl der Primetive

        // Zeichnet jedes einzelne Sphere
        foreach (Sphere mesh in meshList)
        {
            mesh.labMesh.DrawSubset(0);
        }

        // Ende der Szene
        device.EndScene();
        // Bringt die Zeichnung auf das Fensterhandle
        device.Present();
    }

这是创建每个球体的类:

/// <summary>
/// Die Klasse Sphere
/// </summary>
public class Sphere
{
    // Radius der Kugel
    private const float radius = 4f;
    // Die Anzahl der Ebenen einer Kugel
    private const int slices = 40;
    // Die Anzalh der Flächen einer Ebene
    private const int stacks = 40;

    // Das Mesh zum Darstellen der Kugel
    private Mesh mesh = null;
    private Vector3 vec;
    public Vector3 min;
    public Vector3 max;


    /// <summary>
    /// Gibt den Mesh zurück
    /// </summary>
    public Mesh labMesh
    {
        get { return mesh; }
    }

    public Vector3 labVector 
    {
        get { return vec; }
    }

    /// <summary>
    /// Erstellt das Mesh
    /// </summary>
    /// <param name="device">Das 3D Device</param>
    /// <param name="color">Die Farbe der Kugel</param>
    /// <param name="labValues">Die Lab Werte der Kugel</param>
    public void createMesh(Device device, Color color, params float[] labValues)
    {
        // Erstellt die Kugel mit der Anbindung an das Device
        mesh = Mesh.Sphere(device, radius, slices, stacks);
        // Kopiert das Mesh zum Erstellen des VertexArrays
        Mesh tempMesh = mesh.Clone(mesh.Options.Value, Vertex.FVF_Flags, device);
        // Erstellt den VertexArray
        Vertex[] vertData = (Vertex[])tempMesh.VertexBuffer.Lock(0, typeof(Vertex), LockFlags.None, tempMesh.NumberVertices);

        // Weist jedem Vertex die Farbe und die Position zu
        for (int i = 0; i < vertData.Length; ++i)
        {
            vertData[i].color = color.ToArgb();
            vertData[i].x += labValues[1];
            vertData[i].y += labValues[0] - 50f;
            vertData[i].z += labValues[2];
        }
        min = new Vector3(labValues[1], labValues[0] + 100f, labValues[2]);
        max = new Vector3(labValues[1], labValues[0] - 100f, labValues[2]);

        // Gibt den VertexBuffer in der Kopie frei
        tempMesh.VertexBuffer.Unlock();
        // Löscht den Mesh aus dem Speicher
        mesh.Dispose();
        // Legt die Kopie in der Meshinstanz ab
        mesh = tempMesh;

        Vector3 v = new Vector3(labValues[1], labValues[0], labValues[2]);
        vec = v;
    }
}

/// <summary>
/// Vertex für die Kugel
/// </summary>
struct Vertex
{
    public float x, y, z; // Position of vertex in 3D space
    public int color;     // Diffuse color of vertex

    /// <summary>
    /// Konstruktor der Vertex
    /// </summary>
    /// <param name="_x">X(A) - Position</param>
    /// <param name="_y">Y(L) - Position</param>
    /// <param name="_z">Z(B) - Position</param>
    /// <param name="_color">Die Farbe</param>
    public Vertex(float _x, float _y, float _z, int _color)
    {
        x = _x; y = _y; z = _z;
        color = _color;
    }

    // Das Format des Vertex
    public static readonly VertexFormats FVF_Flags = VertexFormats.Position | VertexFormats.Diffuse;
}

我不知道如何通过渲染 1600 个球体来提高性能!我认为在游戏中也必须是一个解决方案。

我希望你有一个想法来帮助我!

4

3 回答 3

2

我建议通过分析器运行您的代码,并查看代码中的瓶颈所在并对其进行优化。

查看这个问题,了解 C# 有哪些分析器。

于 2013-03-22T18:30:47.890 回答
2

首先我不得不说微软不支持托管 DirectX。你最好使用 XNA 之类的东西,或者更好的是SlimDX

一种方法是仅使用一个球体,然后设置包含矩阵数据的第二个顶点流。然后,您可以使用一个单独的绘制调用来渲染实例化的球体。这应该会显着提高性能。

另一种方法是构建一个巨大的顶点缓冲区,其中包含尽可能多的球体,这样你就可以DrawSubset减少调用次数。这将提高性能。

也就是说,每帧 1600 次绘制调用很高,但并不是那么重要,因此应该可以按原样获得不错的性能。

可以尝试的几件事是在您的mesh.Clone通话中添加以下标志:

  1. 只写
  2. 优化顶点缓存
  3. 共享

还要确保关闭 alpha 混合(可能但值得一试)。

理想情况下,按从前到后的顺序渲染球体将优化溢出(写入像素的次数也应尽可能低),但这通常会使用比您节省的 GPU 时间更多的 CPU。

其他要记住的事情是你的领域有多复杂。他们可以减少tris的数量吗?

除此之外,使用 Caesar 建议的某种调试器(尽管我从不输入;))是一个很好的前进方式。可能只是托管 DirectX 的性能不足以为您提供您想要的结果......

于 2013-03-22T18:47:42.020 回答
0

在此示例中,您的 Draw Call 数量是瓶颈,而不是您的 GPU 性能。您绝对应该减少绘图调用的数量。对于一款完整的高端游戏,在 PC 上的 draw call 数量很少超过 2000,这是一个非常优化的管道。在笔记本电脑上,这仍然是一个非常高的数字。使用 C# 时的目标是最多 1000 个绘图调用。

为了解决您的问题,有一些选择。

第一种选择是将所有数据放入一个缓冲区。您应该将顶点缓冲区视为发送到 gpu 的数据块,并且您希望向 gpu 发送尽可能少的东西,因为这里的开销很大。您应该将所有球体放入单个缓冲区或几个缓冲区中。这将解决您的性能问题。合并静态对象是游戏引擎中的常见做法。

第二种选择是,如果您需要可移动的球体,您可以使用实例化。实例化是一种技术,您可以在其中多次渲染相同的数据,每个实例都有一些额外的数据。这只需要一个绘图调用。现在所有 GPU 通常都支持实例化,因此如果您需要移动对象或参数化对象,请使用它。一个快速的谷歌肯定会提供更多的信息。

最后一点,就像已经提到的那样,托管 Directx 已经死了好几年了。它很慢,只有 dx9。使用 c# 时应该切换到 SlimDX(XNA 也已死)。

于 2013-03-23T14:53:28.497 回答