c# - 在一个场景中渲染多个对象DirectX C#

标签 c# directx direct3d

我并不是真的不懂DirectX,但是我会尽力解释。
首先,我有多个模型对象(带有顶点数据的自定义对象)。目前,我的代码(请参见下文)可以使用模型的vertexBuffer和indexBuffer渲染场景中的单个模型。我想做的是给出一系列模型。将它们全部渲染到一个场景中。这是我当前的代码:

    private void Render()
    {
        device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.White, 1.0f, 0);
        device.BeginScene();

        float x = (float)Math.Cos(0);
        float z = (float)Math.Sin(0);
        device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 1f, 50f);
        device.Transform.View = Matrix.LookAtLH(new Vector3(x, 6, z), new Vector3(0, 0, 0), new Vector3(0, 1, 0));
        device.RenderState.Lighting = true;
        device.Lights[0].Type = LightType.Directional;
        device.Lights[0].Diffuse = Color.White;
        device.Lights[0].Direction = new Vector3(-x, -6, -z);
        device.Lights[0].Position = new Vector3(x, 6, z);
        device.Lights[0].Enabled = true;

        device.Transform.World = model.transform;

        device.VertexFormat = CustomVertex.PositionNormalColored.Format;
        device.SetStreamSource(0, vertexBuffer, 0);
        device.Indices = indexBuffer;

        device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, model.getVertices().Length, 0, model.getIndices().Length / 3);

        device.EndScene();
        device.Present();
    }

    public void RenderModel(Model model)
    {
        this.model = model;
        vertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionNormalColored), model.getVertices().Length,
                                    device, Usage.Dynamic | Usage.WriteOnly, CustomVertex.PositionNormalColored.Format, Pool.Default);
        vertexBuffer.SetData(model.getVertices(), 0, LockFlags.None);

        indexBuffer = new IndexBuffer(typeof(ushort), model.getIndices().Length, device, Usage.WriteOnly, Pool.Default);
        indexBuffer.SetData(model.getIndices(), 0, LockFlags.None);

        Render();
    }

最佳答案

tl; dr;

您可以简单地循环调用RenderModel。但是,为了获得更好的性能,您可能希望按更新频率对操作进行排序,请始终处置不需要的资源,并且不要保留同一数据的多个副本。



好吧,用您的设置渲染多个对象的幼稚方法看起来像这样:

foreach (var model in models)
{
    RenderModel(model);
}


我说天真,因为我会首先解决很多问题。

提升绩效

实时3D应用程序都具有良好的性能。为了实现这一目标,您必须非常小心自己执行昂贵任务的频率。例如,当前您每次调用RenderModel方法时都在创建一个新的顶点和索引缓冲区。 (而且我想这会在每帧发生吗?)而且,效果状态(如视图/投影矩阵和照明)将分别为每个模型设置,即使它们可能在整个帧中保持不变。

根据更新频率将效果状态分开。

确定昂贵的操作(设置/创建着色器,常量,缓冲区;大致使用GraphicsDevice的任何对象),并考虑需要更改的频率。为了获得最佳性能,请不要经常这样做。例子:


网格的顶点缓冲区:如果网格不是动态的,并且顶点在一段时间内(或根本不会改变),顶点缓冲区将始终包含相同的数据。加载网格时创建它并一遍又一遍地重复使用就足够了。 (也适用于索引缓冲区。)(更新频率:每个级别一次)
设置照明,视图和投影矩阵:照明通常在一帧内保持不变。相机矩阵也是如此。在帧的开头将它们设置一次,就可以完成。 (更新频率:每帧一次)
绑定纹理:通常,场景中将具有与单个模型关联的不同材质。大多数情况下,一种材料属于多个对象(因为您将它们的纹理合并在一个大的纹理贴图中)。因此,为所有使用它的网格物体绑定一次这些纹理和常量将节省最多的GPU容量。 (更新频率:每种材料一次)
绑定顶点和索引缓冲区:顶点和索引数据通常对于网格是唯一的。因此,如果您的场景中有多个网格,那么一旦完成一个网格,就不可避免地要在设备上切换缓冲区。 (更新频率:每个网格一次)
设置世界矩阵:设置效果状态,并将网格数据绑定到设备。您终于可以绘制对象了。因此,您设置了世界矩阵并调用了设备的draw方法。但是,等等,您是否想要在场景中其他位置复制网格物体?现在是最佳时机。只需绑定另一个世界矩阵并再次绘制即可。您仅在设备上绘制了状态变化最小的两个对象。完美!1(更新频率:每个网格实例一次)


1如果场景中确实需要大量相同网格物体的副本,建议您查看Hardware instancing(可从Direc3D 9开始使用)。

处置不再需要的资源。

通常,垃圾收集器会处理您的旧物件,并且在这方面做得很好。但是,这里您正在使用未使用的资源。它们之所以称为非托管的,是因为它们不受CLR的管理,因此不受垃圾回收的影响。

为什么要打扰你?

保留此类对象可能会导致内存泄漏。不必要的资源将使您的内存混乱,从而导致性能下降。2

.NET为使用非托管资源的类提供一个接口,称为IDisposable接口。它公开了一个Disposable方法,从该方法中可以释放非托管资源。使用对象后,您要做的就是调用该方法。

if (myTexture != null)
    myTexture.Dispose();


例如,在您的情况下,每帧创建一个新的顶点和索引缓冲区。在用新的缓冲区覆盖它们之前,您一定应该处置它们,而旧的缓冲区会消失在必杀技中。 (更不用说,无论如何您都不应该经常创建它们。)仅看一些Direct3D类,您将看到其中大多数实现了IDisposable。

2据我了解,实际上还不错。正确实现IDisposable通常意味着这些类具有一个析构函数,该析构函数将在对象被垃圾回收后立即调用Dispose。但是,由于您不知道何时进行垃圾回收,因此通常建议手动手动处理Disposable对象。

不要保留同一数据的多个副本。

目前,您可能会将顶点和索引存储在数组中。创建缓冲区时,基本上是在创建数据副本。如前所述:每个网格可以创建一次顶点和索引缓冲区。因此,执行此操作时,请将顶点和索引写入缓冲区,然后仅保留这些顶点和索引。您的模型课程当然会稍有变化:

public class Model
{
    public VertexBuffer Vertices { get; private set; }
    public IndexBuffer Indices { get; private set; }
}


如果您不需要直接访问顶点和索引,这将是一个更好的解决方案。

我知道,这是一个很小的步骤,但是我认为意识到这一点很好。

伪代码渲染方法

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




免责声明:我仅使用Direct3D 9(XNA)已有几个月了。我编写的大部分内容都适用于D3D9和Direct3D 10/11,而我现在对它们更熟悉。

关于c# - 在一个场景中渲染多个对象DirectX C#,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14493230/

相关文章:

directx - HLSL 错误 X4507 : maximum constant register index exceeded

c# - 如何从 unix 更改为 linux 以通过 C# 连接(使用 telnet)?

c# - 为什么 Microsoft 建议跳过为引用类型实现相等运算符?

c++ - Directx:Obj文件解析到Index Buffer

c++ - 使用 C++ 开始 Direct X 的好书或教程

c++ - 如何在 3DS Max 中为 Direct3D 动画 Controller 定义动画集

opengl - 一步将剪切的图像与背景混合到目标中

c# - 如何将整数格式化为 ASCII/Unicode 字符(不只是转换为 char)

c# - MultiSelectable ListView - ItemSelected 未被调用

winapi - Windows 8 SDK 中的 DirectX