Qt Quick 3D - Beispiel für prozedurale Texturen
Zeigt, wie man benutzerdefinierte Texturdaten aus C++ oder QML bereitstellt.
Dieses Beispiel nutzt QQuick3DTextureData und the textureData property von Texture, um Texturdaten bereitzustellen, die zur Laufzeit dynamisch generiert werden, anstatt sie aus einem statischen Asset zu laden. Zu Demonstrationszwecken werden in diesem Beispiel zwei Farbverlaufstexturen in C++ bzw. QML erzeugt.
Zunächst definieren wir eine C++-Klasse für unsere Texturdaten. Wir machen sie zu einer Unterklasse von QQuick3DTextureData. Das ist nicht unbedingt notwendig, da es keine virtuellen Funktionen gibt, aber es ist viel bequemer, alles in einer Klasse zu haben. Wir definieren die Eigenschaften, die wir verwenden werden, und fügen QML_NAMED_ELEMENT hinzu, um sie von QML aus verfügbar zu machen:
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) ...
Wir fügen eine Funktion zum Aktualisieren der Textur hinzu. Sie verwendet setSize und setFormat, um die Textur zu konfigurieren, und setTextureData, um die Bilddaten zu setzen:
void GradientTexture::updateTexture() { setSize(QSize(m_width, m_height)); setFormat(QQuick3DTextureData::RGBA8); setHasTransparency(false); setTextureData(generateTexture()); }
Die Funktion generateTexture
erstellt eine QByteArray in der richtigen Größe und füllt sie mit Bilddaten:
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; }
Wir rufen updateTexture
jedes Mal auf, wenn eine Eigenschaft geändert wird:
void GradientTexture::setStartColor(QColor startColor) { if (m_startColor == startColor) return; m_startColor = startColor; emit startColorChanged(m_startColor); updateTexture(); }
Schließlich können wir unsere neue Textur aus QML verwenden:
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 } }
Es ist auch möglich, die gleichen Texturdaten in QML zu erzeugen. In diesem Fall verwenden wir die Komponente 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; } } }
Genau wie in C++ füllen wir ein QByteArray mit Bilddaten, die die Größe und das Format der Textur widerspiegeln. Wenn Sie dies von QML aus tun, verwenden Sie den Typ ArrayBuffer, um unnötige Typkonvertierungen zu vermeiden.
© 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.