aboutsummaryrefslogtreecommitdiffstats
path: root/examples/quick/scenegraph/custommaterial
diff options
context:
space:
mode:
Diffstat (limited to 'examples/quick/scenegraph/custommaterial')
-rw-r--r--examples/quick/scenegraph/custommaterial/customitem.cpp267
-rw-r--r--examples/quick/scenegraph/custommaterial/customitem.h113
-rw-r--r--examples/quick/scenegraph/custommaterial/custommaterial.pro16
-rw-r--r--examples/quick/scenegraph/custommaterial/custommaterial.qrc7
-rw-r--r--examples/quick/scenegraph/custommaterial/doc/images/custom-material-example.jpgbin0 -> 72603 bytes
-rw-r--r--examples/quick/scenegraph/custommaterial/doc/src/custommaterial.qdoc104
-rw-r--r--examples/quick/scenegraph/custommaterial/main.cpp69
-rw-r--r--examples/quick/scenegraph/custommaterial/main.qml99
-rw-r--r--examples/quick/scenegraph/custommaterial/shaders/mandelbrot.frag48
-rw-r--r--examples/quick/scenegraph/custommaterial/shaders/mandelbrot.frag.qsbbin0 -> 2605 bytes
-rw-r--r--examples/quick/scenegraph/custommaterial/shaders/mandelbrot.vert22
-rw-r--r--examples/quick/scenegraph/custommaterial/shaders/mandelbrot.vert.qsbbin0 -> 1643 bytes
12 files changed, 745 insertions, 0 deletions
diff --git a/examples/quick/scenegraph/custommaterial/customitem.cpp b/examples/quick/scenegraph/custommaterial/customitem.cpp
new file mode 100644
index 0000000000..62dc962455
--- /dev/null
+++ b/examples/quick/scenegraph/custommaterial/customitem.cpp
@@ -0,0 +1,267 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "customitem.h"
+
+#include <QtCore/QPointer>
+
+#include <QtQuick/QSGMaterial>
+#include <QtQuick/QSGTexture>
+#include <QtQuick/QSGGeometryNode>
+#include <QtQuick/QSGTextureProvider>
+
+//! [2]
+class CustomShader : public QSGMaterialShader
+{
+public:
+ CustomShader()
+ {
+ setShaderFileName(VertexStage, QLatin1String(":/scenegraph/custommaterial/shaders/mandelbrot.vert.qsb"));
+ setShaderFileName(FragmentStage, QLatin1String(":/scenegraph/custommaterial/shaders/mandelbrot.frag.qsb"));
+ }
+ bool updateUniformData(RenderState &state,
+ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
+//! [2]
+
+//! [1]
+class CustomMaterial : public QSGMaterial
+{
+public:
+ CustomMaterial();
+ QSGMaterialType *type() const override;
+ int compare(const QSGMaterial *other) const override;
+
+ QSGMaterialShader *createShader(QSGRendererInterface::RenderMode) const override
+ {
+ return new CustomShader;
+ }
+
+ struct {
+ float center[2];
+ float zoom;
+ int limit;
+ bool dirty;
+ } uniforms;
+};
+//! [1]
+
+CustomMaterial::CustomMaterial()
+{
+}
+
+QSGMaterialType *CustomMaterial::type() const
+{
+ static QSGMaterialType type;
+ return &type;
+}
+
+int CustomMaterial::compare(const QSGMaterial *o) const
+{
+ Q_ASSERT(o && type() == o->type());
+ const auto *other = static_cast<const CustomMaterial *>(o);
+ return other == this ? 0 : 1; // ### TODO: compare state???
+}
+
+//! [3]
+bool CustomShader::updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
+{
+ bool changed = false;
+ QByteArray *buf = state.uniformData();
+ Q_ASSERT(buf->size() >= 84);
+
+ if (state.isMatrixDirty()) {
+ const QMatrix4x4 m = state.combinedMatrix();
+ memcpy(buf->data(), m.constData(), 64);
+ changed = true;
+ }
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(buf->data() + 64, &opacity, 4);
+ changed = true;
+ }
+
+ auto *customMaterial = static_cast<CustomMaterial *>(newMaterial);
+ if (oldMaterial != newMaterial || customMaterial->uniforms.dirty) {
+ memcpy(buf->data() + 68, &customMaterial->uniforms.zoom, 4);
+ memcpy(buf->data() + 72, &customMaterial->uniforms.center, 8);
+ memcpy(buf->data() + 80, &customMaterial->uniforms.limit, 4);
+ customMaterial->uniforms.dirty = false;
+ changed = true;
+ }
+ return changed;
+}
+//! [3]
+
+//! [4]
+class CustomNode : public QSGGeometryNode
+{
+public:
+ CustomNode()
+ {
+ auto *m = new CustomMaterial;
+ setMaterial(m);
+ setFlag(OwnsMaterial, true);
+
+ QSGGeometry *g = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
+ QSGGeometry::updateTexturedRectGeometry(g, QRect(), QRect());
+ setGeometry(g);
+ setFlag(OwnsGeometry, true);
+ }
+
+ void setRect(const QRectF &bounds)
+ {
+ QSGGeometry::updateTexturedRectGeometry(geometry(), bounds, QRectF(0, 0, 1, 1));
+ markDirty(QSGNode::DirtyGeometry);
+ }
+
+ void setZoom(qreal zoom)
+ {
+ auto *m = static_cast<CustomMaterial *>(material());
+ m->uniforms.zoom = zoom;
+ m->uniforms.dirty = true;
+ markDirty(DirtyMaterial);
+ }
+
+ void setLimit(int limit)
+ {
+ auto *m = static_cast<CustomMaterial *>(material());
+ m->uniforms.limit = limit;
+ m->uniforms.dirty = true;
+ markDirty(DirtyMaterial);
+ }
+
+ void setCenter(const QPointF &center)
+ {
+ auto *m = static_cast<CustomMaterial *>(material());
+ m->uniforms.center[0] = center.x();
+ m->uniforms.center[1] = center.y();
+ m->uniforms.dirty = true;
+ markDirty(DirtyMaterial);
+ }
+};
+//! [4]
+
+CustomItem::CustomItem(QQuickItem *parent)
+ : QQuickItem(parent)
+{
+ setFlag(ItemHasContents, true);
+}
+
+//! [5]
+void CustomItem::setZoom(qreal zoom)
+{
+ if (qFuzzyCompare(m_zoom, zoom))
+ return;
+
+ m_zoom = zoom;
+ m_zoomChanged = true;
+ emit zoomChanged(m_zoom);
+ update();
+}
+
+void CustomItem::setIterationLimit(int limit)
+{
+ if (m_limit == limit)
+ return;
+
+ m_limit = limit;
+ m_limitChanged = true;
+ emit iterationLimitChanged(m_limit);
+ update();
+}
+
+void CustomItem::setCenter(QPointF center)
+{
+ if (m_center == center)
+ return;
+
+ m_center = center;
+ m_centerChanged = true;
+ emit centerChanged(m_center);
+ update();
+}
+//! [5]
+
+void CustomItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ m_geometryChanged = true;
+ update();
+ QQuickItem::geometryChange(newGeometry, oldGeometry);
+}
+
+//! [6]
+QSGNode *CustomItem::updatePaintNode(QSGNode *old, UpdatePaintNodeData *)
+{
+ auto *node = static_cast<CustomNode *>(old);
+
+ if (!node)
+ node = new CustomNode;
+
+ if (m_geometryChanged)
+ node->setRect(boundingRect());
+ m_geometryChanged = false;
+
+ if (m_zoomChanged)
+ node->setZoom(m_zoom);
+ m_zoomChanged = false;
+
+ if (m_limitChanged)
+ node->setLimit(m_limit);
+ m_limitChanged = false;
+
+ if (m_centerChanged)
+ node->setCenter(m_center);
+ m_centerChanged = false;
+
+ return node;
+}
+//! [6]
diff --git a/examples/quick/scenegraph/custommaterial/customitem.h b/examples/quick/scenegraph/custommaterial/customitem.h
new file mode 100644
index 0000000000..5cc1fa09f4
--- /dev/null
+++ b/examples/quick/scenegraph/custommaterial/customitem.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CUSTOMITEM_H
+#define CUSTOMITEM_H
+
+//! [1]
+#include <QQuickItem>
+
+class CustomItem : public QQuickItem
+{
+ Q_OBJECT
+//! [2]
+ Q_PROPERTY(qreal zoom READ zoom WRITE setZoom NOTIFY zoomChanged)
+ Q_PROPERTY(int iterationLimit READ iterationLimit WRITE setIterationLimit NOTIFY iterationLimitChanged)
+ Q_PROPERTY(QPointF center READ center WRITE setCenter NOTIFY centerChanged)
+//! [2]
+ QML_ELEMENT
+
+public:
+ explicit CustomItem(QQuickItem *parent = 0);
+
+ qreal zoom() const
+ {
+ return m_zoom;
+ }
+
+ int iterationLimit() const
+ {
+ return m_limit;
+ }
+
+ QPointF center() const
+ {
+ return m_center;
+ }
+
+public slots:
+ void setZoom(qreal zoom);
+
+ void setIterationLimit(int iterationLimit);
+
+ void setCenter(QPointF center);
+
+signals:
+ void zoomChanged(qreal zoom);
+
+ void iterationLimitChanged(int iterationLimit);
+
+ void centerChanged(QPointF center);
+
+protected:
+ QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override;
+ void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+
+private:
+ bool m_geometryChanged = true;
+ qreal m_zoom;
+ bool m_zoomChanged = true;
+ int m_limit;
+ bool m_limitChanged = true;
+ QPointF m_center;
+ bool m_centerChanged = true;
+};
+//! [1]
+#endif // CUSTOMITEM_H
diff --git a/examples/quick/scenegraph/custommaterial/custommaterial.pro b/examples/quick/scenegraph/custommaterial/custommaterial.pro
new file mode 100644
index 0000000000..46a1a6603c
--- /dev/null
+++ b/examples/quick/scenegraph/custommaterial/custommaterial.pro
@@ -0,0 +1,16 @@
+QT += qml quick
+
+CONFIG += qmltypes
+QML_IMPORT_NAME = ExampleCustomMaterial
+QML_IMPORT_MAJOR_VERSION = 1
+
+HEADERS += customitem.h
+SOURCES += customitem.cpp main.cpp
+
+RESOURCES += custommaterial.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/custommaterial
+INSTALLS += target
+
+OTHER_FILES += \
+ main.qml
diff --git a/examples/quick/scenegraph/custommaterial/custommaterial.qrc b/examples/quick/scenegraph/custommaterial/custommaterial.qrc
new file mode 100644
index 0000000000..18e586bf65
--- /dev/null
+++ b/examples/quick/scenegraph/custommaterial/custommaterial.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/scenegraph/custommaterial">
+ <file>main.qml</file>
+ <file>shaders/mandelbrot.frag.qsb</file>
+ <file>shaders/mandelbrot.vert.qsb</file>
+ </qresource>
+</RCC>
diff --git a/examples/quick/scenegraph/custommaterial/doc/images/custom-material-example.jpg b/examples/quick/scenegraph/custommaterial/doc/images/custom-material-example.jpg
new file mode 100644
index 0000000000..e4040cfcf2
--- /dev/null
+++ b/examples/quick/scenegraph/custommaterial/doc/images/custom-material-example.jpg
Binary files differ
diff --git a/examples/quick/scenegraph/custommaterial/doc/src/custommaterial.qdoc b/examples/quick/scenegraph/custommaterial/doc/src/custommaterial.qdoc
new file mode 100644
index 0000000000..5cb7b38ab0
--- /dev/null
+++ b/examples/quick/scenegraph/custommaterial/doc/src/custommaterial.qdoc
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \example scenegraph/custommaterial
+ \title Scene Graph - Custom Material
+ \ingroup qtquickexamples
+ \brief Shows how to implement a custom material in the Qt Quick Scene Graph.
+
+ The custom material example shows how to implement an item that is rendered
+ using a material with a custom vertex and fragment shader.
+
+ \image custom-material-example.jpg
+
+ \section1 Shader and material
+
+ The main functionality is in the fragment shader
+
+ \quotefile scenegraph/custommaterial/shaders/mandelbrot.frag
+
+ The fragment and vertex shaders are combined into a \l QSGMaterialShader subclass.
+
+ \snippet scenegraph/custommaterial/customitem.cpp 2
+
+ A QSGMaterial subclass encapsulates the shader together with the render state. In
+ this example, we add state information corresponding to the shader uniforms. The
+ material is responsible for creating the shader by reimplementing
+ \l QSGMaterial::createShader().
+
+ \snippet scenegraph/custommaterial/customitem.cpp 1
+
+
+ To update the uniform data, we reimplement \l QSGMaterialShader::updateUniformData().
+
+ \snippet scenegraph/custommaterial/customitem.cpp 3
+
+ \section1 Item and node
+
+ We create a custom item to show off our new material:
+
+ \snippet scenegraph/custommaterial/customitem.h 1
+
+ The CustomItem declaration adds three
+ properties corresponding to the uniforms that we want to expose to QML.
+
+ \snippet scenegraph/custommaterial/customitem.h 2
+
+ As with every custom Qt Quick item, the implementation is split in two:
+ in addition to \c CustomItem, which lives in the GUI thread, we create
+ a \l QSGNode subclass that lives in the render thread.
+
+ \snippet scenegraph/custommaterial/customitem.cpp 4
+
+ The node owns an instance of the material, and has logic to update
+ the material's state. The item maintains the corresponding QML properties.
+ It needs to duplicate the information from the material since the item and
+ material live on different threads.
+
+ \snippet scenegraph/custommaterial/customitem.cpp 5
+
+ The information is copied from the item to the scene graph in a
+ reimplementation of \l QQuickItem::updatePaintNode(). The two threads
+ are at a synchronization point when the function is called, so it is safe
+ to access both classes.
+
+ \snippet scenegraph/custommaterial/customitem.cpp 6
+
+ \section1 The rest of the example
+
+ The application is a straightforward QML application, with a
+ QGuiApplication and a QQuickView that we pass a .qml file.
+
+ In the QML file, we create the
+ customitem which we anchor to fill the root.
+
+ \snippet scenegraph/custommaterial/main.qml 1
+
+ To make the example a bit more interesting we add an animation to
+ change the zoom level and iteration limit. The center stays constant.
+ */
diff --git a/examples/quick/scenegraph/custommaterial/main.cpp b/examples/quick/scenegraph/custommaterial/main.cpp
new file mode 100644
index 0000000000..ce1bcd9dfe
--- /dev/null
+++ b/examples/quick/scenegraph/custommaterial/main.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+
+#include <QtQuick/QQuickView>
+
+#include "customitem.h"
+
+//! [1]
+int main(int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl("qrc:///scenegraph/custommaterial/main.qml"));
+ view.show();
+
+ return app.exec();
+}
+//! [1]
diff --git a/examples/quick/scenegraph/custommaterial/main.qml b/examples/quick/scenegraph/custommaterial/main.qml
new file mode 100644
index 0000000000..e552c4ee48
--- /dev/null
+++ b/examples/quick/scenegraph/custommaterial/main.qml
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.2
+import ExampleCustomMaterial 1.0
+
+Item {
+ id: root
+
+ width: 640
+ height: 480
+
+
+//! [1]
+ CustomItem {
+ property real t: 1
+ anchors.fill: parent
+ center: Qt.point(-0.748, 0.1);
+ iterationLimit: 3 * (zoom + 30)
+ zoom: t * t / 10
+ NumberAnimation on t {
+ from: 1
+ to: 60
+ duration: 30*1000;
+ running: true
+ loops: Animation.Infinite
+ }
+ }
+//! [1]
+
+ Rectangle {
+ id: labelFrame
+ anchors.margins: -10
+ radius: 10
+ color: "white"
+ border.color: "black"
+ opacity: 0.8
+ anchors.fill: description
+ }
+
+ Text {
+ id: description
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.margins: 20
+ wrapMode: Text.WordWrap
+ text: "This example shows how to create a custom material in C++ and use it in QML.\n"
+ + "The custom material uses a fragment shader that calculates the Mandelbrot set,"
+ + " and exposes the shader uniforms as QML properties."
+ }
+}
diff --git a/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.frag b/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.frag
new file mode 100644
index 0000000000..0e5f63e7a8
--- /dev/null
+++ b/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.frag
@@ -0,0 +1,48 @@
+//! [1]
+#version 440
+
+layout(location = 0) in vec2 vTexCoord;
+
+layout(location = 0) out vec4 fragColor;
+
+//! [2]
+// uniform block: 84 bytes
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix; // offset 0
+ float qt_Opacity; // offset 64
+ float zoom; // offset 68
+ vec2 center; // offset 72
+ int limit; // offset 80
+} ubuf;
+//! [2]
+
+void main()
+{
+ vec4 color1 = vec4(1.0, 0.85, 0.55, 1);
+ vec4 color2 = vec4(0.226, 0.0, 0.615, 1);
+
+ float aspect_ratio = -ubuf.qt_Matrix[0][0]/ubuf.qt_Matrix[1][1];
+ vec2 z, c;
+
+ c.x = (vTexCoord.x - 0.5) / ubuf.zoom + ubuf.center.x;
+ c.y = aspect_ratio * (vTexCoord.y - 0.5) / ubuf.zoom + ubuf.center.y;
+
+ int i;
+ z = c;
+ for (i = 0; i < ubuf.limit; i++) {
+ float x = (z.x * z.x - z.y * z.y) + c.x;
+ float y = (z.y * z.x + z.x * z.y) + c.y;
+
+ if ((x * x + y * y) > 4.0) break;
+ z.x = x;
+ z.y = y;
+ }
+
+ if (i == ubuf.limit) {
+ fragColor = vec4(0.0, 0.0, 0.0, 1.0);
+ } else {
+ float f = (i * 1.0) / ubuf.limit;
+ fragColor = mix(color1, color2, sqrt(f));
+ }
+}
+//! [1]
diff --git a/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.frag.qsb b/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.frag.qsb
new file mode 100644
index 0000000000..dce5a9d934
--- /dev/null
+++ b/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.frag.qsb
Binary files differ
diff --git a/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.vert b/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.vert
new file mode 100644
index 0000000000..00c519cd9d
--- /dev/null
+++ b/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.vert
@@ -0,0 +1,22 @@
+#version 440
+
+layout(location = 0) in vec4 aVertex;
+layout(location = 1) in vec2 aTexCoord;
+
+layout(location = 0) out vec2 vTexCoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 qt_Matrix;
+ float qt_Opacity;
+ float scale;
+ vec2 center;
+ int limit;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ gl_Position = ubuf.qt_Matrix * aVertex;
+ vTexCoord = aTexCoord;
+}
diff --git a/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.vert.qsb b/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.vert.qsb
new file mode 100644
index 0000000000..445b986ebd
--- /dev/null
+++ b/examples/quick/scenegraph/custommaterial/shaders/mandelbrot.vert.qsb
Binary files differ