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 XML 只向顶点着色器提供实例数据,因此我们将其作为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 完成。
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.