Qt Quick 3D - 自定义实例渲染
演示使用自定义材料和 C++ 实例表进行实例化。
此示例展示了如何在 C++ 中程序化地创建实例数据,以及如何使用自定义材料进行实例渲染。
自定义实例表
我们将表定义为 QQuick3DInstancing 的子类,并添加一些属性,以便我们可以从 QML 中控制它
class CppInstanceTable : public QQuick3DInstancing { Q_OBJECT QML_ELEMENT Q_PROPERTY(int gridSize READ gridSize WRITE setGridSize NOTIFY gridSizeChanged) Q_PROPERTY(float gridSpacing READ gridSpacing WRITE setGridSpacing NOTIFY gridSpacingChanged) Q_PROPERTY(int randomSeed READ randomSeed WRITE setRandomSeed NOTIFY randomSeedChanged)
虚函数 getInstanceBuffer 被重新实现以返回实例数据
QByteArray CppInstanceTable::getInstanceBuffer(int *instanceCount) { if (m_dirty) { BlockTable blocks(m_gridSize, m_randomSeed); m_instanceData.resize(0); auto idxToPos = [this](int i) -> float { return m_gridSpacing * (i - m_gridSize / 2); }; int instanceNumber = 0; for (int i = 0; i < m_gridSize; ++i) { float xPos = idxToPos(i); for (int j = 0; j < m_gridSize; ++j) { float zPos = idxToPos(j); int lowest = blocks.lowestVisible(i, j); int highest = blocks.highestBlock(i, j); for (int k = lowest; k <= highest; ++k) { float yPos = idxToPos(k); QColor color = blocks.getBlockColor(i, j, k); float waterAnimation = blocks.isWaterSurface(i, j, k) ? 1.0 : 0.0; auto entry = calculateTableEntry({ xPos, yPos, zPos }, { 1.0, 1.0, 1.0 }, {}, color, { waterAnimation, 0, 0, 0 }); m_instanceData.append(reinterpret_cast<const char *>(&entry), sizeof(entry)); instanceNumber++; } } } m_instanceCount = instanceNumber; m_dirty = false; } if (instanceCount) *instanceCount = m_instanceCount; return m_instanceData; }
自定义材料
我们使用带有阴影的自定义材料,这意味着 Qt 提供了基本实现,我们只需指定额外的逻辑。
对于顶点着色器,我们所需的唯一定制的自定义是传递信息到片元着色器。默认情况下,Qt 只向顶点着色器提供实例数据,因此我们将其传递为 vCustomData
。我们还计算顶点的全局位置并将其作为 vGlobalPosition
提供出来。
// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause VARYING vec4 vCustomData; VARYING vec3 vGlobalPosition; void MAIN() { vCustomData = INSTANCE_DATA; // MODEL_MATRIX does not exist when instancing vec4 pos = INSTANCE_MODEL_MATRIX * vec4(VERTEX, 1.0); vGlobalPosition = pos.xyz; }
片元着色器对水面执行简单的波动动画。其他所有东西都获得微妙的径向渐变。差异由自定义数据的第一个元素确定。
VARYING vec4 vCustomData; VARYING vec3 vGlobalPosition; void MAIN() { METALNESS = 0.0; ROUGHNESS = 1.0; FRESNEL_POWER = 5.0; float c; if (vCustomData.x > 0) c = 1.0 - (1.0 + sin(sqrt(vGlobalPosition.x*vGlobalPosition.x + vGlobalPosition.z*vGlobalPosition.z) - uTime/200.0)) * 0.2; else c = 1.0 - 0.25 * (UV0.x*UV0.x + UV0.y*UV0.y); BASE_COLOR = vec4(c, c, c, 1.0); }
在 QML 中使用自定义表和材料
我们使用之前制作的着色器创建自定义材料,并添加一个新属性 uTime
。此属性将自动映射到片元着色器中的相应统一变量。
CustomMaterial { id: cubeMaterial property real uTime: frametimer.elapsedTime FrameAnimation { id: frametimer running: true } vertexShader: "cubeMaterial.vert" fragmentShader: "cubeMaterial.frag" }
最后,我们创建我们的模型并应用自定义材料和实例表
Model { id: instancedCube property real cubeSize: 15 scale: Qt.vector3d(cubeSize/100, cubeSize/100, cubeSize/100) source: "#Cube" instancing: CppInstanceTable { gridSize: 65 gridSpacing: instancedCube.cubeSize randomSeed: 1522562186 } materials: [ cubeMaterial ] }
请注意,我们只创建了一个立方体:所有繁重的工作都是由 GPU 执行的。
文件
© 2024 The Qt Company Ltd. 此处包含的文档贡献的版权属于其各自的所有者。此处提供的文档是根据由自由软件基金会发布的 GNU 自由文档许可证版本 1.3 的条款许可的。Qt 和相应的标志是芬兰的 The Qt Company Ltd 以及/或世界其他国家的商标。所有其他商标均为其各自所有者的财产。