实例化渲染

简介

Qt Quick 3D 支持实例化 模型 对象。实例化是指一个对象通过单个绘图调用来渲染多次(例如 OpenGL 函数 glDrawElementsInstanced。)

实例化允许对具有不同变体的模型进行复制。与使用 Repeater3D 相比,模型及其图形资源只分配一次。复制的实例的渲染由 GPU 在低级别上进行。根据模型复杂性的不同,这可以提高几个数量级的性能。

在实践中,通过定义一个表来指定每个实例相对于基本模型的修改来执行实例化。

实例化 API

实例化 API 的主要原则是它很明确:它不会尝试自动检测现有 API 中的实例化机会。相反,通过将模型的 instancing 属性设置为引用一个 Instancing 对象来分别为每个模型标记。同一个 Instancing 对象可以同时用于多个模型。

Instancing 对象指定一个表,该表定义了每个副本的渲染方式。可用的修改包括

  • 转换:位置、旋转和比例
  • 颜色:与模型的材质混合的颜色
  • 自定义数据:可以由自定义材质使用的其他数据

Qt 提供了三个继承自 Instancing 的 QML 类型

  • InstanceList 列出了所有实例,并允许绑定到每个实例的属性。
  • RandomInstancing 提供了一种快速测试和原型的快捷方式,可以在定义的边界内生成随机的实例。
  • FileInstancing 从外部文件读取实例表。

实例化示例 展示了如何使用 QML API 创建场景。

可以通过在 C++ 中创建 QQuick3DInstancing 的子类来定义其他类型的实例化表。例如,粒子系统使用它自己的内部实例化表。它作为 ModelParticle3D.instanceTable 可用。

通过编写自定义着色器代码,可以使用实例化来控制其他属性,例如基于物理的渲染、骨骼动画权重、扭曲或可以用自定义材质表述的一切。实例化表中的自定义数据由四个浮点数字组成。

自定义实例化示例自定义实例化示例演示了如何结合自定义材料和C++中实现的实例化表格。

透明混合和实例化

正确的透明混合要求半透明对象从后向前渲染。因此,QtQuick3D将不透明和半透明对象分别排序,并按正确顺序渲染。但是,对于实例化,如果未开启深度排序,GPU将按照实例化表格中指定的顺序渲染实例。出于性能考虑,QtQuick3D默认不按顺序排序表格,因为对于大量实例,这可能需要很长时间。这意味着如果半透明实例互相重叠,或者与其他半透明对象重叠,结果可能看起来不正确。一般来说,当不透明度较低时,错误不太明显。

与无重叠的半透明对象一起的完全不透明对象将始终被正确渲染,因为Qt使用深度缓冲区测试来避免在不透明对象后面绘制。然而,未排序有潜在的性能影响,对于完全不透明对象:它们可能会被以非最佳顺序渲染,这意味着相同的像素可能被多次写入,使片段着色器的工作量更大。

渲染器不会检查实例化表的内容,因此必须在实例化表包含半透明alpha值时显式指定:将hasTransparency属性设置为true,以确保渲染器启用透明混合。这适用于所有实例:即使完全不透明的实例也将使用深度测试以外的顺序渲染,可能会造成明显的错误。

可以通过设置模型的depthBias来调整与场景其他部分的相对渲染顺序。

变换和实例化

每个实例在实例化表中都有自己的变换。这与实例化模型的变换结合在一起。这略微复杂,因为有几个用例

  • 对模型执行变换,该变换应用于每个单独的实例。这使得动画变得便宜,例如一次旋转所有实例,而不必更改实例化表格。
  • 一次变换整个实例组。
  • 实例化模型层次结构。

为了支持所有这些情况,将模型的变换分为两部分:《局部实例变换》和《全局实例变换》。从概念上来说,实例化是这样执行的

  • 首先根据局部实例变换对模型进行变换。
  • 然后根据实例化表变换对每个实例进行计算。
  • 最后,根据全局实例变换变换整个实例化对象组。

默认情况下,模型的局部实例变换由模型的缩放和旋转组成,其余部分进入全局实例变换。

可以通过设置模型的instanceRoot属性来控制。这定义了实例坐标系的起点。最常见的使用是在实例化模型层次结构时。例如,围绕立方体旋转的球体。

Model {
    id: cube
    instancing: someInstanceTable
    source: "#Cube"
    materials: DefaultMaterial { diffuseColor: "lightgray" }
    Node {
        Model {
            source: "#Sphere"
            instanceRoot: cube
            instancing: cube.instancing
            x: 150
            materials: DefaultMaterial { diffuseColor: "gray" }
        }
        NumberAnimation on eulerRotation.y {
            from: 0
            to: 360
            duration: 4000
            loops: Animation.Infinite
        }
    }
}

必须指定instanceRoot,以便将球体实例定位为立方体元素。层次结构中的每个模型仍然需要指定instancing属性:在正常情况下,它们应设置相同的Instancing对象。

在实例化单个模型时也可以使用instanceRoot。例如,围绕偏心点旋转的圆柱体。

 Node {
    id: parentNode
    Model {
        source: "#Cylinder"
        instanceRoot: parentNode
        instancing: anotherInstanceTable
        x: 25
        materials: DefaultMaterial { diffuseColor: "white" }
    }
    NumberAnimation on eulerRotation.y {
        from: 0
        to: 360
        duration: 1000
        loops: Animation.Infinite
    }
}

拾取和实例化

拾取是一种机制,它允许用户通过用户界面交互选择一个模型。使用实例渲染时,同一模型有多个表示,因此拾取结果将包括一个实例索引。通过在基本模型上设置可拾取属性来启用实例拾取。

© 2024 The Qt Company Ltd. 本文档中的文档贡献属于其各自的版权所有者。本文件中提供的文档是根据免费软件基金会公布的GNU自由文档许可证版本1.3许可的。Qt及其相关标志是芬兰和/或全球其他地区的The Qt Company Ltd.的商标。所有其他商标均为其各自所有者的财产。