Qt Quick 3D - 程序化纹理示例
演示如何从 C++ 或 QML 提供自定义纹理数据。
此示例使用 QQuick3DTextureData 和纹理的 textureData 属性 来提供在运行时动态生成的纹理数据,而不是从静态资源中加载。出于演示目的,此示例分别使用 C++ 和 QML 生成两个渐变纹理。
首先,我们定义一个 C++ 类来处理我们的纹理数据。我们将其设为 QQuick3DTextureData 的子类。尽管这不是必须的,因为没有虚拟函数,但将所有内容放入一个类中会更方便。我们定义将要使用的属性,并使用 QML_NAMED_ELEMENT 确保它可以从 QML 访问。
class GradientTexture : public QQuick3DTextureData { Q_OBJECT Q_PROPERTY(int height READ height WRITE setHeight NOTIFY heightChanged) Q_PROPERTY(int width READ width WRITE setWidth NOTIFY widthChanged) Q_PROPERTY(QColor startColor READ startColor WRITE setStartColor NOTIFY startColorChanged) Q_PROPERTY(QColor endColor READ endColor WRITE setEndColor NOTIFY endColorChanged) QML_NAMED_ELEMENT(GradientTexture) ...
我们添加了一个更新纹理的功能。它使用 setSize 和 setFormat 来配置纹理,并使用 setTextureData 来设置图像数据。
void GradientTexture::updateTexture() { setSize(QSize(m_width, m_height)); setFormat(QQuick3DTextureData::RGBA8); setHasTransparency(false); setTextureData(generateTexture()); }
generateTexture
函数创建正确大小的 QByteArray,并填充图像数据。
QByteArray GradientTexture::generateTexture() { QByteArray imageData; // Create a horizontal gradient between startColor and endColor // Create a single scanline and reuse that data for each QByteArray gradientScanline; gradientScanline.resize(m_width * 4); // RGBA8 for (int x = 0; x < m_width; ++x) { QColor color = linearInterpolate(m_startColor, m_endColor, x / float(m_width)); int offset = x * 4; gradientScanline.data()[offset + 0] = char(color.red()); gradientScanline.data()[offset + 1] = char(color.green()); gradientScanline.data()[offset + 2] = char(color.blue()); gradientScanline.data()[offset + 3] = char(255); } for (int y = 0; y < m_height; ++y) imageData += gradientScanline; return imageData; }
每次属性改变时,我们都调用 updateTexture
。
void GradientTexture::setStartColor(QColor startColor) { if (m_startColor == startColor) return; m_startColor = startColor; emit startColorChanged(m_startColor); updateTexture(); }
最后,我们可以从 QML 使用我们的新纹理。
Texture { id: textureFromCpp minFilter: applicationState.filterMode magFilter: applicationState.filterMode textureData: gradientTexture GradientTexture { id: gradientTexture startColor: applicationState.startColor endColor: applicationState.endColor width: applicationState.size height: width } }
在 QML 中也可以生成相同的纹理数据。在这种情况下,我们使用 ProceduralTextureData 组件。
Texture { id: textureFromQML minFilter: applicationState.filterMode magFilter: applicationState.filterMode textureData: gradientTextureDataQML ProceduralTextureData { id: gradientTextureDataQML property color startColor: applicationState.startColor property color endColor: applicationState.endColor width: applicationState.size height: width textureData: generateTextureData() function linearInterpolate(startColor : color, endColor : color, fraction : real) : color{ return Qt.rgba( startColor.r + (endColor.r - startColor.r) * fraction, startColor.g + (endColor.g - startColor.g) * fraction, startColor.b + (endColor.b - startColor.b) * fraction, startColor.a + (endColor.a - startColor.a) * fraction ); } function generateTextureData() { let dataBuffer = new ArrayBuffer(width * height * 4) let data = new Uint8Array(dataBuffer) let gradientScanline = new Uint8Array(width * 4); for (let x = 0; x < width; ++x) { let color = linearInterpolate(startColor, endColor, x / width); let offset = x * 4; gradientScanline[offset + 0] = color.r * 255; gradientScanline[offset + 1] = color.g * 255; gradientScanline[offset + 2] = color.b * 255; gradientScanline[offset + 3] = color.a * 255; } for (let y = 0; y < height; ++y) { data.set(gradientScanline, y * width * 4); } return dataBuffer; } } }
与 C++ 类似,我们在 QByteArray 中填充反映纹理大小和格式的图像数据。当从 QML 进行此操作时,使用 ArrayBuffer 类型以避免不必要的数据类型转换。
文件
© 2024 Qt 公司 Ltd. 本文档的贡献包括其中各自所有者的版权。本文档根据 Free Software Foundation 发布的 GNU 自由文档许可证版本 1.3 的条款提供许可。Qt 和相应的标志是芬兰以及世界的商标,商标权属于 Qt 公司 Ltd。所有其他商标均为其各自所有者的财产。