summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJean-Michaël Celerier <jean-michael.celerier@kdab.com>2020-03-13 15:23:50 +0100
committerJean-Michaël Celerier <jean-michael.celerier@kdab.com>2020-04-21 16:36:47 +0200
commita01dbe5b0e1b912c7210abc304700020e685aff0 (patch)
tree7516339103b75068133654e6984c8bc45bd6cb7f /src
parent18b319f919f71c6b476675d832d1b8a2bda118c2 (diff)
rhi: Handle RHI-required information in QShaderGraph
Change-Id: I705843bbb1f6928c2e36b327469882e11fb9613e Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
Diffstat (limited to 'src')
-rw-r--r--src/extras/defaults/qt3dwindow.cpp6
-rw-r--r--src/extras/extras.qrc17
-rw-r--r--src/extras/shaders/graphs/phong.graph466
-rw-r--r--src/extras/shaders/rhi/coordinatesystems.inc70
-rw-r--r--src/extras/shaders/rhi/default.vert82
-rw-r--r--src/extras/shaders/rhi/distancefieldtext.frag39
-rw-r--r--src/extras/shaders/rhi/distancefieldtext.vert19
-rw-r--r--src/extras/shaders/rhi/gooch.frag63
-rw-r--r--src/extras/shaders/rhi/gooch.vert19
-rw-r--r--src/extras/shaders/rhi/light.inc.frag26
-rw-r--r--src/extras/shaders/rhi/metalrough.inc.frag346
-rw-r--r--src/extras/shaders/rhi/morphphong.vert34
-rw-r--r--src/extras/shaders/rhi/pervertexcolor.frag17
-rw-r--r--src/extras/shaders/rhi/pervertexcolor.vert22
-rw-r--r--src/extras/shaders/rhi/phong.inc.frag138
-rw-r--r--src/extras/shaders/rhi/skybox.frag24
-rw-r--r--src/extras/shaders/rhi/skybox.vert16
-rw-r--r--src/extras/shaders/rhi/unlittexture.frag13
-rw-r--r--src/extras/shaders/rhi/unlittexture.vert20
-rw-r--r--src/plugins/renderers/rhi/renderer/renderview.cpp1
-rw-r--r--src/render/materialsystem/prototypes/default.json72
-rw-r--r--src/render/materialsystem/shaderbuilder.cpp3
-rw-r--r--src/render/shadergraph/qshadergenerator.cpp375
-rw-r--r--src/render/shadergraph/qshadernodesloader.cpp3
24 files changed, 1811 insertions, 80 deletions
diff --git a/src/extras/defaults/qt3dwindow.cpp b/src/extras/defaults/qt3dwindow.cpp
index eb0961113..4cf3af250 100644
--- a/src/extras/defaults/qt3dwindow.cpp
+++ b/src/extras/defaults/qt3dwindow.cpp
@@ -183,6 +183,12 @@ Qt3DWindow::Qt3DWindow(QScreen *screen, Qt3DRender::API api)
format.setProfile(QSurfaceFormat::CoreProfile);
}
#endif
+
+ if (!userRequestedApi.isEmpty()) {
+ // This is used for RHI
+ format.setVersion(1, 0);
+ }
+
format.setDepthBufferSize(24);
format.setSamples(4);
format.setStencilBufferSize(8);
diff --git a/src/extras/extras.qrc b/src/extras/extras.qrc
index 2aedc6622..8d7085264 100644
--- a/src/extras/extras.qrc
+++ b/src/extras/extras.qrc
@@ -39,5 +39,22 @@
<file>shaders/es2/distancefieldtext.vert</file>
<file>shaders/gl3/morphphong.vert</file>
<file>shaders/es2/morphphong.vert</file>
+ <file>shaders/graphs/phong.graph</file>
+ <file>shaders/rhi/unlittexture.vert</file>
+ <file>shaders/rhi/unlittexture.frag</file>
+ <file>shaders/rhi/skybox.vert</file>
+ <file>shaders/rhi/skybox.frag</file>
+ <file>shaders/rhi/phong.inc.frag</file>
+ <file>shaders/rhi/pervertexcolor.vert</file>
+ <file>shaders/rhi/pervertexcolor.frag</file>
+ <file>shaders/rhi/morphphong.vert</file>
+ <file>shaders/rhi/metalrough.inc.frag</file>
+ <file>shaders/rhi/light.inc.frag</file>
+ <file>shaders/rhi/gooch.vert</file>
+ <file>shaders/rhi/gooch.frag</file>
+ <file>shaders/rhi/distancefieldtext.vert</file>
+ <file>shaders/rhi/distancefieldtext.frag</file>
+ <file>shaders/rhi/default.vert</file>
+ <file>shaders/rhi/coordinatesystems.inc</file>
</qresource>
</RCC>
diff --git a/src/extras/shaders/graphs/phong.graph b/src/extras/shaders/graphs/phong.graph
new file mode 100644
index 000000000..dedeb1067
--- /dev/null
+++ b/src/extras/shaders/graphs/phong.graph
@@ -0,0 +1,466 @@
+{
+ "nodes": [
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000001}",
+ "type": "input",
+ "parameters": {
+ "name": "worldPosition",
+ "qualifier": {
+ "type": "QShaderLanguage::StorageQualifier",
+ "value": "QShaderLanguage::Input"
+ },
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000002}",
+ "type": "eyePosition"
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000003}",
+ "type": "input",
+ "parameters": {
+ "name": "worldNormal",
+ "qualifier": {
+ "type": "QShaderLanguage::StorageQualifier",
+ "value": "QShaderLanguage::Input"
+ },
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000004}",
+ "type": "input",
+ "layers": ["normalTexture"],
+ "parameters": {
+ "name": "worldTangent",
+ "qualifier": {
+ "type": "QShaderLanguage::StorageQualifier",
+ "value": "QShaderLanguage::Input"
+ },
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec4"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000005}",
+ "type": "input",
+ "layers": ["diffuseTexture", "specularTexture", "normalTexture"],
+ "parameters": {
+ "name": "texCoord",
+ "qualifier": {
+ "type": "QShaderLanguage::StorageQualifier",
+ "value": "QShaderLanguage::Input"
+ },
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec2"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000006}",
+ "type": "input",
+ "parameters": {
+ "name": "ka",
+ "qualifier": {
+ "type": "QShaderLanguage::StorageQualifier",
+ "value": "QShaderLanguage::Uniform"
+ },
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec4"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000007}",
+ "type": "input",
+ "layers": ["diffuse"],
+ "parameters": {
+ "name": "kd",
+ "qualifier": {
+ "type": "QShaderLanguage::StorageQualifier",
+ "value": "QShaderLanguage::Uniform"
+ },
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec4"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000008}",
+ "type": "sampleTexture",
+ "layers": ["diffuseTexture"],
+ "parameters": {
+ "name": "diffuseTexture"
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000009}",
+ "type": "input",
+ "layers": ["specular"],
+ "parameters": {
+ "name": "ks",
+ "qualifier": {
+ "type": "QShaderLanguage::StorageQualifier",
+ "value": "QShaderLanguage::Uniform"
+ },
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec4"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000010}",
+ "layers": ["specularTexture"],
+ "type": "sampleTexture",
+ "parameters": {
+ "name": "specularTexture"
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000011}",
+ "type": "input",
+ "parameters": {
+ "name": "shininess",
+ "qualifier": {
+ "type": "QShaderLanguage::StorageQualifier",
+ "value": "QShaderLanguage::Uniform"
+ },
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Float"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000012}",
+ "type": "subtract",
+ "parameters": {
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000013}",
+ "type": "normalize",
+ "parameters": {
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000014}",
+ "type": "normalize",
+ "parameters": {
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000015}",
+ "type": "worldSpaceToTangentSpaceMatrix",
+ "layers": ["normalTexture"]
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000016}",
+ "type": "transpose",
+ "layers": ["normalTexture"],
+ "parameters": {
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Mat3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000017}",
+ "type": "sampleTexture",
+ "layers": ["normalTexture"],
+ "parameters": {
+ "name": "normalTexture"
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000018}",
+ "type": "swizzle",
+ "layers": ["normalTexture"],
+ "parameters": {
+ "fields": "rgb",
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000019}",
+ "type": "constant",
+ "layers": ["normalTexture"],
+ "parameters": {
+ "constant": "2.0",
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Float"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000020}",
+ "type": "multiply",
+ "layers": ["normalTexture"],
+ "parameters": {
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000021}",
+ "type": "constant",
+ "layers": ["normalTexture"],
+ "parameters": {
+ "constant": "1.0",
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000022}",
+ "type": "subtract",
+ "parameters": {
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000023}",
+ "type": "multiply",
+ "parameters": {
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000024}",
+ "type": "phongFunction"
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000025}",
+ "type": "fragColor"
+ }
+ ],
+ "edges": [
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000001}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000024}",
+ "targetPort": "worldPosition"
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000001}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000012}",
+ "targetPort": "subtrahend"
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000002}",
+ "sourcePort": "eyePosition",
+ "targetUuid": "{00000000-0000-0000-0000-000000000012}",
+ "targetPort": "minuend"
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000012}",
+ "sourcePort": "difference",
+ "targetUuid": "{00000000-0000-0000-0000-000000000013}",
+ "targetPort": "input"
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000013}",
+ "sourcePort": "output",
+ "targetUuid": "{00000000-0000-0000-0000-000000000024}",
+ "targetPort": "worldView"
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000003}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000014}",
+ "targetPort": "input",
+ "layers": ["normal"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000003}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000015}",
+ "targetPort": "worldNormal",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000004}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000015}",
+ "targetPort": "worldTangent",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000015}",
+ "sourcePort": "matrix",
+ "targetUuid": "{00000000-0000-0000-0000-000000000016}",
+ "targetPort": "input",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000016}",
+ "sourcePort": "output",
+ "targetUuid": "{00000000-0000-0000-0000-000000000023}",
+ "targetPort": "first",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000005}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000017}",
+ "targetPort": "coord",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000017}",
+ "sourcePort": "color",
+ "targetUuid": "{00000000-0000-0000-0000-000000000018}",
+ "targetPort": "input",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000018}",
+ "sourcePort": "output",
+ "targetUuid": "{00000000-0000-0000-0000-000000000020}",
+ "targetPort": "first",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000019}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000020}",
+ "targetPort": "second",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000020}",
+ "sourcePort": "product",
+ "targetUuid": "{00000000-0000-0000-0000-000000000022}",
+ "targetPort": "minuend",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000021}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000022}",
+ "targetPort": "subtrahend",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000022}",
+ "sourcePort": "difference",
+ "targetUuid": "{00000000-0000-0000-0000-000000000023}",
+ "targetPort": "second",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000023}",
+ "sourcePort": "product",
+ "targetUuid": "{00000000-0000-0000-0000-000000000014}",
+ "targetPort": "input",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000014}",
+ "sourcePort": "output",
+ "targetUuid": "{00000000-0000-0000-0000-000000000024}",
+ "targetPort": "worldNormal"
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000006}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000024}",
+ "targetPort": "ambient"
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000007}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000024}",
+ "targetPort": "diffuse",
+ "layers": ["diffuse"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000005}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000008}",
+ "targetPort": "coord",
+ "layers": ["diffuseTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000008}",
+ "sourcePort": "color",
+ "targetUuid": "{00000000-0000-0000-0000-000000000024}",
+ "targetPort": "diffuse",
+ "layers": ["diffuseTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000009}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000024}",
+ "targetPort": "specular",
+ "layers": ["specular"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000005}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000010}",
+ "targetPort": "coord",
+ "layers": ["specularTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000010}",
+ "sourcePort": "color",
+ "targetUuid": "{00000000-0000-0000-0000-000000000024}",
+ "targetPort": "specular",
+ "layers": ["specularTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000011}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000024}",
+ "targetPort": "shininess"
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000024}",
+ "sourcePort": "outputColor",
+ "targetUuid": "{00000000-0000-0000-0000-000000000025}",
+ "targetPort": "fragColor"
+ }
+ ]
+}
diff --git a/src/extras/shaders/rhi/coordinatesystems.inc b/src/extras/shaders/rhi/coordinatesystems.inc
new file mode 100644
index 000000000..ed3d2cb92
--- /dev/null
+++ b/src/extras/shaders/rhi/coordinatesystems.inc
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module 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$
+**
+****************************************************************************/
+
+mat3 calcWorldSpaceToTangentSpaceMatrix(const in vec3 wNormal, const in vec4 wTangent)
+{
+ // Make the tangent truly orthogonal to the normal by using Gram-Schmidt.
+ // This allows to build the tangentMatrix below by simply transposing the
+ // tangent -> eyespace matrix (which would now be orthogonal)
+ vec3 wFixedTangent = normalize(wTangent.xyz - dot(wTangent.xyz, wNormal) * wNormal);
+
+ // Calculate binormal vector. No "real" need to renormalize it,
+ // as built by crossing two normal vectors.
+ // To orient the binormal correctly, use the fourth coordinate of the tangent,
+ // which is +1 for a right hand system, and -1 for a left hand system.
+ vec3 wBinormal = cross(wNormal, wFixedTangent.xyz) * wTangent.w;
+
+ // Construct matrix to transform from world space to tangent space
+ // This is the transpose of the tangentToWorld transformation matrix
+ mat3 tangentToWorldMatrix = mat3(wFixedTangent, wBinormal, wNormal);
+ mat3 worldToTangentMatrix = transpose(tangentToWorldMatrix);
+ return worldToTangentMatrix;
+}
+
diff --git a/src/extras/shaders/rhi/default.vert b/src/extras/shaders/rhi/default.vert
new file mode 100644
index 000000000..f97cd099d
--- /dev/null
+++ b/src/extras/shaders/rhi/default.vert
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module 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$
+**
+****************************************************************************/
+
+#version 150 core
+
+in vec3 vertexPosition;
+in vec3 vertexNormal;
+in vec4 vertexTangent;
+in vec2 vertexTexCoord;
+
+out vec3 worldPosition;
+out vec3 worldNormal;
+out vec4 worldTangent;
+out vec2 texCoord;
+
+uniform mat4 modelMatrix;
+uniform mat3 modelNormalMatrix;
+uniform mat4 modelViewProjection;
+
+uniform float texCoordScale;
+
+void main()
+{
+ // Pass through scaled texture coordinates
+ texCoord = vertexTexCoord * texCoordScale;
+
+ // Transform position, normal, and tangent to world space
+ worldPosition = vec3(modelMatrix * vec4(vertexPosition, 1.0));
+ worldNormal = normalize(modelNormalMatrix * vertexNormal);
+ worldTangent.xyz = normalize(vec3(modelMatrix * vec4(vertexTangent.xyz, 0.0)));
+ worldTangent.w = vertexTangent.w;
+
+ // Calculate vertex position in clip coordinates
+ gl_Position = modelViewProjection * vec4(vertexPosition, 1.0);
+}
diff --git a/src/extras/shaders/rhi/distancefieldtext.frag b/src/extras/shaders/rhi/distancefieldtext.frag
new file mode 100644
index 000000000..998fa6e79
--- /dev/null
+++ b/src/extras/shaders/rhi/distancefieldtext.frag
@@ -0,0 +1,39 @@
+#version 150 core
+
+uniform sampler2D distanceFieldTexture;
+uniform float minAlpha;
+uniform float maxAlpha;
+uniform float textureSize;
+uniform vec4 color;
+
+in vec2 texCoord;
+in float zValue;
+
+out vec4 fragColor;
+
+void main()
+{
+ // determine the scale of the glyph texture within pixel-space coordinates
+ // (that is, how many pixels are drawn for each texel)
+ vec2 texelDeltaX = abs(dFdx(texCoord));
+ vec2 texelDeltaY = abs(dFdy(texCoord));
+ float avgTexelDelta = textureSize * 0.5 * (texelDeltaX.x + texelDeltaX.y + texelDeltaY.x + texelDeltaY.y);
+ float texScale = 1.0 / avgTexelDelta;
+
+ // scaled to interval [0.0, 0.15]
+ float devScaleMin = 0.00;
+ float devScaleMax = 0.15;
+ float scaled = (clamp(texScale, devScaleMin, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin);
+
+ // thickness of glyphs should increase a lot for very small glyphs to make them readable
+ float base = 0.5;
+ float threshold = base * scaled;
+ float range = 0.06 / texScale;
+
+ float minAlpha = threshold - range;
+ float maxAlpha = threshold + range;
+
+ float distVal = texture(distanceFieldTexture, texCoord).r;
+ fragColor = vec4(color.rgb, color.a * smoothstep(minAlpha, maxAlpha, distVal));
+ gl_FragDepth = gl_FragCoord.z - zValue * 0.000001;
+}
diff --git a/src/extras/shaders/rhi/distancefieldtext.vert b/src/extras/shaders/rhi/distancefieldtext.vert
new file mode 100644
index 000000000..f6743001c
--- /dev/null
+++ b/src/extras/shaders/rhi/distancefieldtext.vert
@@ -0,0 +1,19 @@
+#version 150 core
+
+in vec3 vertexPosition;
+in vec2 vertexTexCoord;
+
+out vec2 texCoord;
+out float zValue;
+
+uniform mat4 modelView;
+uniform mat4 mvp;
+
+void main()
+{
+ texCoord = vertexTexCoord;
+ zValue = vertexPosition.z;
+
+ gl_Position = mvp * vec4(vertexPosition.xy, 0.0, 1.0);
+}
+
diff --git a/src/extras/shaders/rhi/gooch.frag b/src/extras/shaders/rhi/gooch.frag
new file mode 100644
index 000000000..168a862f8
--- /dev/null
+++ b/src/extras/shaders/rhi/gooch.frag
@@ -0,0 +1,63 @@
+#version 150 core
+
+// TODO: Replace with a struct
+uniform vec3 kd; // Diffuse reflectivity
+uniform vec3 ks; // Specular reflectivity
+uniform vec3 kblue; // Cool color
+uniform vec3 kyellow; // Warm color
+uniform float alpha; // Fraction of diffuse added to kblue
+uniform float beta; // Fraction of diffuse added to kyellow
+uniform float shininess; // Specular shininess factor
+
+uniform vec3 eyePosition;
+
+in vec3 worldPosition;
+in vec3 worldNormal;
+
+out vec4 fragColor;
+
+#pragma include light.inc.frag
+
+vec3 goochModel( const in vec3 pos, const in vec3 n )
+{
+ // Based upon the original Gooch lighting model paper at:
+ // http://www.cs.northwestern.edu/~ago820/SIG98/abstract.html
+
+ // Calculate kcool and kwarm from equation (3)
+ vec3 kcool = clamp(kblue + alpha * kd, 0.0, 1.0);
+ vec3 kwarm = clamp(kyellow + beta * kd, 0.0, 1.0);
+
+ vec3 result = vec3(0.0);
+ for (int i = 0; i < lightCount; ++i) {
+ // Calculate the vector from the light to the fragment
+ vec3 s = normalize( vec3( lights[i].position ) - pos );
+
+ // Calculate the cos theta factor mapped onto the range [0,1]
+ float sDotNFactor = ( 1.0 + dot( s, n ) ) / 2.0;
+
+ // Calculate the tone by blending the kcool and kwarm contributions
+ // as per equation (2)
+ vec3 intensity = mix( kcool, kwarm, sDotNFactor );
+
+ // Calculate the vector from the fragment to the eye position
+ vec3 v = normalize( eyePosition - pos );
+
+ // Reflect the light beam using the normal at this fragment
+ vec3 r = reflect( -s, n );
+
+ // Calculate the specular component
+ float specular = 0.0;
+ if ( dot( s, n ) > 0.0 )
+ specular = pow( max( dot( r, v ), 0.0 ), shininess );
+
+ // Sum the blended tone and specular highlight
+ result += intensity + ks * specular;
+ }
+
+ return result;
+}
+
+void main()
+{
+ fragColor = vec4( goochModel( worldPosition, normalize( worldNormal ) ), 1.0 );
+}
diff --git a/src/extras/shaders/rhi/gooch.vert b/src/extras/shaders/rhi/gooch.vert
new file mode 100644
index 000000000..5230fb70e
--- /dev/null
+++ b/src/extras/shaders/rhi/gooch.vert
@@ -0,0 +1,19 @@
+#version 150 core
+
+in vec3 vertexPosition;
+in vec3 vertexNormal;
+
+out vec3 worldPosition;
+out vec3 worldNormal;
+
+uniform mat4 modelMatrix;
+uniform mat3 modelNormalMatrix;
+uniform mat4 mvp;
+
+void main()
+{
+ worldNormal = normalize( modelNormalMatrix * vertexNormal );
+ worldPosition = vec3( modelMatrix * vec4( vertexPosition, 1.0 ) );
+
+ gl_Position = mvp * vec4( vertexPosition, 1.0 );
+}
diff --git a/src/extras/shaders/rhi/light.inc.frag b/src/extras/shaders/rhi/light.inc.frag
new file mode 100644
index 000000000..861e1ba4a
--- /dev/null
+++ b/src/extras/shaders/rhi/light.inc.frag
@@ -0,0 +1,26 @@
+const int MAX_LIGHTS = 8;
+const int TYPE_POINT = 0;
+const int TYPE_DIRECTIONAL = 1;
+const int TYPE_SPOT = 2;
+struct Light {
+ int type;
+ vec3 position;
+ vec3 color;
+ float intensity;
+ vec3 direction;
+ float constantAttenuation;
+ float linearAttenuation;
+ float quadraticAttenuation;
+ float cutOffAngle;
+};
+
+
+layout(std140, binding = auto) uniform qt3d_light_uniforms {
+ uniform Light lights[MAX_LIGHTS];
+ uniform int lightCount;
+ uniform int envLightCount;
+};
+
+// Pre-convolved environment maps
+layout(binding = auto) uniform samplerCube envLight_irradiance; // For diffuse contribution
+layout(binding = auto) uniform samplerCube envLight_specular; // For specular contribution
diff --git a/src/extras/shaders/rhi/metalrough.inc.frag b/src/extras/shaders/rhi/metalrough.inc.frag
new file mode 100644
index 000000000..cf85f3b22
--- /dev/null
+++ b/src/extras/shaders/rhi/metalrough.inc.frag
@@ -0,0 +1,346 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module 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$
+**
+****************************************************************************/
+
+// Exposure correction
+uniform float exposure = 0.0;
+// Gamma correction
+uniform float gamma = 2.2;
+
+#pragma include light.inc.frag
+
+int mipLevelCount(const in samplerCube cube)
+{
+ int baseSize = textureSize(cube, 0).x;
+ int nMips = int(log2(float(baseSize > 0 ? baseSize : 1))) + 1;
+ return nMips;
+}
+
+float remapRoughness(const in float roughness)
+{
+ // As per page 14 of
+ // http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf
+ // we remap the roughness to give a more perceptually linear response
+ // of "bluriness" as a function of the roughness specified by the user.
+ // r = roughness^2
+ const float maxSpecPower = 999999.0;
+ const float minRoughness = sqrt(2.0 / (maxSpecPower + 2));
+ return max(roughness * roughness, minRoughness);
+}
+
+float alphaToMipLevel(float alpha)
+{
+ float specPower = 2.0 / (alpha * alpha) - 2.0;
+
+ // We use the mip level calculation from Lys' default power drop, which in
+ // turn is a slight modification of that used in Marmoset Toolbag. See
+ // https://docs.knaldtech.com/doku.php?id=specular_lys for details.
+ // For now we assume a max specular power of 999999 which gives
+ // maxGlossiness = 1.
+ const float k0 = 0.00098;
+ const float k1 = 0.9921;
+ float glossiness = (pow(2.0, -10.0 / sqrt(specPower)) - k0) / k1;
+
+ // TODO: Optimize by doing this on CPU and set as
+ // uniform int envLight.specularMipLevels say (if present in shader).
+ // Lookup the number of mips in the specular envmap
+ int mipLevels = mipLevelCount(envLight_specular);
+
+ // Offset of smallest miplevel we should use (corresponds to specular
+ // power of 1). I.e. in the 32x32 sized mip.
+ const float mipOffset = 5.0;
+
+ // The final factor is really 1 - g / g_max but as mentioned above g_max
+ // is 1 by definition here so we can avoid the division. If we make the
+ // max specular power for the spec map configurable, this will need to
+ // be handled properly.
+ float mipLevel = (mipLevels - 1.0 - mipOffset) * (1.0 - glossiness);
+ return mipLevel;
+}
+
+float normalDistribution(const in vec3 n, const in vec3 h, const in float alpha)
+{
+ // Blinn-Phong approximation - see
+ // http://graphicrants.blogspot.co.uk/2013/08/specular-brdf-reference.html
+ float specPower = 2.0 / (alpha * alpha) - 2.0;
+ return (specPower + 2.0) / (2.0 * 3.14159) * pow(max(dot(n, h), 0.0), specPower);
+}
+
+vec3 fresnelFactor(const in vec3 color, const in float cosineFactor)
+{
+ // Calculate the Fresnel effect value
+ vec3 f = color;
+ vec3 F = f + (1.0 - f) * pow(1.0 - cosineFactor, 5.0);
+ return clamp(F, f, vec3(1.0));
+}
+
+float geometricModel(const in float lDotN,
+ const in float vDotN,
+ const in vec3 h)
+{
+ // Implicit geometric model (equal to denominator in specular model).
+ // This currently assumes that there is no attenuation by geometric shadowing or
+ // masking according to the microfacet theory.
+ return lDotN * vDotN;
+}
+
+vec3 specularModel(const in vec3 F0,
+ const in float sDotH,
+ const in float sDotN,
+ const in float vDotN,
+ const in vec3 n,
+ const in vec3 h)
+{
+ // Clamp sDotN and vDotN to small positive value to prevent the
+ // denominator in the reflection equation going to infinity. Balance this
+ // by using the clamped values in the geometric factor function to
+ // avoid ugly seams in the specular lighting.
+ float sDotNPrime = max(sDotN, 0.001);
+ float vDotNPrime = max(vDotN, 0.001);
+
+ vec3 F = fresnelFactor(F0, sDotH);
+ float G = geometricModel(sDotNPrime, vDotNPrime, h);
+
+ vec3 cSpec = F * G / (4.0 * sDotNPrime * vDotNPrime);
+ return clamp(cSpec, vec3(0.0), vec3(1.0));
+}
+
+vec3 pbrModel(const in int lightIndex,
+ const in vec3 wPosition,
+ const in vec3 wNormal,
+ const in vec3 wView,
+ const in vec3 baseColor,
+ const in float metalness,
+ const in float alpha,
+ const in float ambientOcclusion)
+{
+ // Calculate some useful quantities
+ vec3 n = wNormal;
+ vec3 s = vec3(0.0);
+ vec3 v = wView;
+ vec3 h = vec3(0.0);
+
+ float vDotN = dot(v, n);
+ float sDotN = 0.0;
+ float sDotH = 0.0;
+ float att = 1.0;
+
+ if (lights[lightIndex].type != TYPE_DIRECTIONAL) {
+ // Point and Spot lights
+ vec3 sUnnormalized = vec3(lights[lightIndex].position) - wPosition;
+ s = normalize(sUnnormalized);
+
+ // Calculate the attenuation factor
+ sDotN = dot(s, n);
+ if (sDotN > 0.0) {
+ if (lights[lightIndex].constantAttenuation != 0.0
+ || lights[lightIndex].linearAttenuation != 0.0
+ || lights[lightIndex].quadraticAttenuation != 0.0) {
+ float dist = length(sUnnormalized);
+ att = 1.0 / (lights[lightIndex].constantAttenuation +
+ lights[lightIndex].linearAttenuation * dist +
+ lights[lightIndex].quadraticAttenuation * dist * dist);
+ }
+
+ // The light direction is in world space already
+ if (lights[lightIndex].type == TYPE_SPOT) {
+ // Check if fragment is inside or outside of the spot light cone
+ if (degrees(acos(dot(-s, lights[lightIndex].direction))) > lights[lightIndex].cutOffAngle)
+ sDotN = 0.0;
+ }
+ }
+ } else {
+ // Directional lights
+ // The light direction is in world space already
+ s = normalize(-lights[lightIndex].direction);
+ sDotN = dot(s, n);
+ }
+
+ h = normalize(s + v);
+ sDotH = dot(s, h);
+
+ // Calculate diffuse component
+ vec3 diffuseColor = (1.0 - metalness) * baseColor * lights[lightIndex].color;
+ vec3 diffuse = diffuseColor * max(sDotN, 0.0) / 3.14159;
+
+ // Calculate specular component
+ vec3 dielectricColor = vec3(0.04);
+ vec3 F0 = mix(dielectricColor, baseColor, metalness);
+ vec3 specularFactor = vec3(0.0);
+ if (sDotN > 0.0) {
+ specularFactor = specularModel(F0, sDotH, sDotN, vDotN, n, h);
+ specularFactor *= normalDistribution(n, h, alpha);
+ }
+ vec3 specularColor = lights[lightIndex].color;
+ vec3 specular = specularColor * specularFactor;
+
+ // Blend between diffuse and specular to conserver energy
+ vec3 color = att * lights[lightIndex].intensity * (specular + diffuse * (vec3(1.0) - specular));
+
+ // Reduce by ambient occlusion amount
+ color *= ambientOcclusion;
+
+ return color;
+}
+
+vec3 pbrIblModel(const in vec3 wNormal,
+ const in vec3 wView,
+ const in vec3 baseColor,
+ const in float metalness,
+ const in float alpha,
+ const in float ambientOcclusion)
+{
+ // Calculate reflection direction of view vector about surface normal
+ // vector in world space. This is used in the fragment shader to sample
+ // from the environment textures for a light source. This is equivalent
+ // to the l vector for punctual light sources. Armed with this, calculate
+ // the usual factors needed
+ vec3 n = wNormal;
+ vec3 l = reflect(-wView, n);
+ vec3 v = wView;
+ vec3 h = normalize(l + v);
+ float vDotN = dot(v, n);
+ float lDotN = dot(l, n);
+ float lDotH = dot(l, h);
+
+ // Calculate diffuse component
+ vec3 diffuseColor = (1.0 - metalness) * baseColor;
+ vec3 diffuse = diffuseColor * texture(envLight_irradiance, l).rgb;
+
+ // Calculate specular component
+ vec3 dielectricColor = vec3(0.04);
+ vec3 F0 = mix(dielectricColor, baseColor, metalness);
+ vec3 specularFactor = specularModel(F0, lDotH, lDotN, vDotN, n, h);
+
+ float lod = alphaToMipLevel(alpha);
+//#define DEBUG_SPECULAR_LODS
+#ifdef DEBUG_SPECULAR_LODS
+ if (lod > 7.0)
+ return vec3(1.0, 0.0, 0.0);
+ else if (lod > 6.0)
+ return vec3(1.0, 0.333, 0.0);
+ else if (lod > 5.0)
+ return vec3(1.0, 1.0, 0.0);
+ else if (lod > 4.0)
+ return vec3(0.666, 1.0, 0.0);
+ else if (lod > 3.0)
+ return vec3(0.0, 1.0, 0.666);
+ else if (lod > 2.0)
+ return vec3(0.0, 0.666, 1.0);
+ else if (lod > 1.0)
+ return vec3(0.0, 0.0, 1.0);
+ else if (lod > 0.0)
+ return vec3(1.0, 0.0, 1.0);
+#endif
+ vec3 specularSkyColor = textureLod(envLight_specular, l, lod).rgb;
+ vec3 specular = specularSkyColor * specularFactor;
+
+ // Blend between diffuse and specular to conserve energy
+ vec3 color = specular + diffuse * (vec3(1.0) - specularFactor);
+
+ // Reduce by ambient occlusion amount
+ color *= ambientOcclusion;
+
+ return color;
+}
+
+vec3 toneMap(const in vec3 c)
+{
+ return c / (c + vec3(1.0));
+}
+
+vec3 gammaCorrect(const in vec3 color)
+{
+ return pow(color, vec3(1.0 / gamma));
+}
+
+vec4 metalRoughFunction(const in vec4 baseColor,
+ const in float metalness,
+ const in float roughness,
+ const in float ambientOcclusion,
+ const in vec3 worldPosition,
+ const in vec3 worldView,
+ const in vec3 worldNormal)
+{
+ vec3 cLinear = vec3(0.0);
+
+ // Remap roughness for a perceptually more linear correspondence
+ float alpha = remapRoughness(roughness);
+
+ for (int i = 0; i < envLightCount; ++i) {
+ cLinear += pbrIblModel(worldNormal,
+ worldView,
+ baseColor.rgb,
+ metalness,
+ alpha,
+ ambientOcclusion);
+ }
+
+ for (int i = 0; i < lightCount; ++i) {
+ cLinear += pbrModel(i,
+ worldPosition,
+ worldNormal,
+ worldView,
+ baseColor.rgb,
+ metalness,
+ alpha,
+ ambientOcclusion);
+ }
+
+ // Apply exposure correction
+ cLinear *= pow(2.0, exposure);
+
+ // Apply simple (Reinhard) tonemap transform to get into LDR range [0, 1]
+ vec3 cToneMapped = toneMap(cLinear);
+
+ // Apply gamma correction prior to display
+ vec3 cGamma = gammaCorrect(cToneMapped);
+
+ return vec4(cGamma, 1.0);
+}
diff --git a/src/extras/shaders/rhi/morphphong.vert b/src/extras/shaders/rhi/morphphong.vert
new file mode 100644
index 000000000..7a8bdd097
--- /dev/null
+++ b/src/extras/shaders/rhi/morphphong.vert
@@ -0,0 +1,34 @@
+#version 150 core
+
+in vec3 vertexPosition;
+in vec3 vertexNormal;
+in vec3 vertexPositionTarget;
+in vec3 vertexNormalTarget;
+
+out vec3 worldPosition;
+out vec3 worldNormal;
+
+uniform mat4 modelMatrix;
+uniform mat3 modelNormalMatrix;
+uniform mat4 modelViewProjection;
+uniform float interpolator;
+
+void main()
+{
+ vec3 morphPos;
+ vec3 morphNormal;
+ if (interpolator > 0.0) {
+ // normalized
+ morphPos = mix(vertexPosition, vertexPositionTarget, interpolator);
+ morphNormal = normalize(mix(vertexNormal, vertexNormalTarget, interpolator));
+ } else {
+ // relative
+ morphPos = vertexPosition + vertexPositionTarget * abs(interpolator);
+ morphNormal = normalize(vertexNormal + vertexNormalTarget * abs(interpolator));
+ }
+
+ worldNormal = normalize( modelNormalMatrix * morphNormal );
+ worldPosition = vec3( modelMatrix * vec4( morphPos, 1.0 ) );
+
+ gl_Position = modelViewProjection * vec4( morphPos, 1.0 );
+}
diff --git a/src/extras/shaders/rhi/pervertexcolor.frag b/src/extras/shaders/rhi/pervertexcolor.frag
new file mode 100644
index 000000000..40fc066d6
--- /dev/null
+++ b/src/extras/shaders/rhi/pervertexcolor.frag
@@ -0,0 +1,17 @@
+#version 150 core
+
+in vec3 worldPosition;
+in vec3 worldNormal;
+in vec4 color;
+
+out vec4 fragColor;
+
+uniform vec3 eyePosition;
+
+#pragma include phong.inc.frag
+
+void main()
+{
+ vec3 worldView = normalize(eyePosition - worldPosition);
+ fragColor = phongFunction(color, color, vec4(0.0), 0.0, worldPosition, worldView, worldNormal);
+}
diff --git a/src/extras/shaders/rhi/pervertexcolor.vert b/src/extras/shaders/rhi/pervertexcolor.vert
new file mode 100644
index 000000000..1d721e945
--- /dev/null
+++ b/src/extras/shaders/rhi/pervertexcolor.vert
@@ -0,0 +1,22 @@
+#version 150 core
+
+in vec3 vertexPosition;
+in vec3 vertexNormal;
+in vec4 vertexColor;
+
+out vec3 worldPosition;
+out vec3 worldNormal;
+out vec4 color;
+
+uniform mat4 modelMatrix;
+uniform mat3 modelNormalMatrix;
+uniform mat4 mvp;
+
+void main()
+{
+ worldNormal = normalize( modelNormalMatrix * vertexNormal );
+ worldPosition = vec3( modelMatrix * vec4( vertexPosition, 1.0 ) );
+ color = vertexColor;
+
+ gl_Position = mvp * vec4( vertexPosition, 1.0 );
+}
diff --git a/src/extras/shaders/rhi/phong.inc.frag b/src/extras/shaders/rhi/phong.inc.frag
new file mode 100644
index 000000000..47a6ecd4a
--- /dev/null
+++ b/src/extras/shaders/rhi/phong.inc.frag
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module 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$
+**
+****************************************************************************/
+
+#pragma include light.inc.frag
+
+void adsModel(const in vec3 worldPos,
+ const in vec3 worldNormal,
+ const in vec3 worldView,
+ const in float shininess,
+ out vec3 diffuseColor,
+ out vec3 specularColor)
+{
+ diffuseColor = vec3(0.0);
+ specularColor = vec3(0.0);
+
+ // We perform all work in world space
+ vec3 n = normalize(worldNormal);
+ vec3 s = vec3(0.0);
+
+ for (int i = 0; i < lightCount; ++i) {
+ float att = 1.0;
+ float sDotN = 0.0;
+
+ if (lights[i].type != TYPE_DIRECTIONAL) {
+ // Point and Spot lights
+
+ // Light position is already in world space
+ vec3 sUnnormalized = lights[i].position - worldPos;
+ s = normalize(sUnnormalized); // Light direction
+
+ // Calculate the attenuation factor
+ sDotN = dot(s, n);
+ if (sDotN > 0.0) {
+ if (lights[i].constantAttenuation != 0.0
+ || lights[i].linearAttenuation != 0.0
+ || lights[i].quadraticAttenuation != 0.0) {
+ float dist = length(sUnnormalized);
+ att = 1.0 / (lights[i].constantAttenuation +
+ lights[i].linearAttenuation * dist +
+ lights[i].quadraticAttenuation * dist * dist);
+ }
+
+ // The light direction is in world space already
+ if (lights[i].type == TYPE_SPOT) {
+ // Check if fragment is inside or outside of the spot light cone
+ if (degrees(acos(dot(-s, lights[i].direction))) > lights[i].cutOffAngle)
+ sDotN = 0.0;
+ }
+ }
+ } else {
+ // Directional lights
+ // The light direction is in world space already
+ s = normalize(-lights[i].direction);
+ sDotN = dot(s, n);
+ }
+
+ // Calculate the diffuse factor
+ float diffuse = max(sDotN, 0.0);
+
+ // Calculate the specular factor
+ float specular = 0.0;
+ if (diffuse > 0.0 && shininess > 0.0) {
+ float normFactor = (shininess + 2.0) / 2.0;
+ vec3 r = reflect(-s, n); // Reflection direction in world space
+ specular = normFactor * pow(max(dot(r, worldView), 0.0), shininess);
+ }
+
+ // Accumulate the diffuse and specular contributions
+ diffuseColor += att * lights[i].intensity * diffuse * lights[i].color;
+ specularColor += att * lights[i].intensity * specular * lights[i].color;
+ }
+}
+
+vec4 phongFunction(const in vec4 ambient,
+ const in vec4 diffuse,
+ const in vec4 specular,
+ const in float shininess,
+ const in vec3 worldPosition,
+ const in vec3 worldView,
+ const in vec3 worldNormal)
+{
+ // Calculate the lighting model, keeping the specular component separate
+ vec3 diffuseColor, specularColor;
+ adsModel(worldPosition, worldNormal, worldView, shininess, diffuseColor, specularColor);
+
+ // Combine spec with ambient+diffuse for final fragment color
+ vec3 color = (ambient.rgb + diffuseColor) * diffuse.rgb
+ + specularColor * specular.rgb;
+
+ return vec4(color, diffuse.a);
+}
diff --git a/src/extras/shaders/rhi/skybox.frag b/src/extras/shaders/rhi/skybox.frag
new file mode 100644
index 000000000..ceb13b628
--- /dev/null
+++ b/src/extras/shaders/rhi/skybox.frag
@@ -0,0 +1,24 @@
+#version 330
+
+in vec3 texCoord0;
+out vec4 fragColor;
+uniform samplerCube skyboxTexture;
+
+// Gamma correction
+uniform float gamma = 2.2;
+
+uniform float gammaStrength;
+
+vec3 gammaCorrect(const in vec3 color)
+{
+ return pow(color, vec3(1.0 / gamma));
+}
+
+void main()
+{
+ vec4 baseColor = texture(skyboxTexture, texCoord0);
+ vec4 gammaColor = vec4(gammaCorrect(baseColor.rgb), 1.0);
+ // This is an odd way to enable or not gamma correction,
+ // but this is a way to avoid branching until we can generate shaders
+ fragColor = mix(baseColor, gammaColor, gammaStrength);
+}
diff --git a/src/extras/shaders/rhi/skybox.vert b/src/extras/shaders/rhi/skybox.vert
new file mode 100644
index 000000000..cac49893a
--- /dev/null
+++ b/src/extras/shaders/rhi/skybox.vert
@@ -0,0 +1,16 @@
+#version 330
+
+in vec3 vertexPosition;
+out vec3 texCoord0;
+
+uniform mat4 modelMatrix;
+uniform mat4 viewMatrix;
+uniform mat4 projectionMatrix;
+
+void main()
+{
+ texCoord0 = vertexPosition.xyz;
+ // Converting the viewMatrix to a mat3, then back to a mat4
+ // removes the translation component from it
+ gl_Position = vec4(projectionMatrix * mat4(mat3(viewMatrix)) * modelMatrix * vec4(vertexPosition, 1.0)).xyww;
+}
diff --git a/src/extras/shaders/rhi/unlittexture.frag b/src/extras/shaders/rhi/unlittexture.frag
new file mode 100644
index 000000000..8abbeee8f
--- /dev/null
+++ b/src/extras/shaders/rhi/unlittexture.frag
@@ -0,0 +1,13 @@
+#version 150 core
+
+uniform sampler2D diffuseTexture;
+
+in vec3 position;
+in vec2 texCoord;
+
+out vec4 fragColor;
+
+void main()
+{
+ fragColor = texture( diffuseTexture, texCoord );
+}
diff --git a/src/extras/shaders/rhi/unlittexture.vert b/src/extras/shaders/rhi/unlittexture.vert
new file mode 100644
index 000000000..7e245bd7f
--- /dev/null
+++ b/src/extras/shaders/rhi/unlittexture.vert
@@ -0,0 +1,20 @@
+#version 150 core
+
+in vec3 vertexPosition;
+in vec2 vertexTexCoord;
+
+out vec3 position;
+out vec2 texCoord;
+
+uniform mat4 modelView;
+uniform mat4 mvp;
+uniform mat3 texCoordTransform;
+
+void main()
+{
+ vec3 tt = texCoordTransform * vec3(vertexTexCoord, 1.0);
+ texCoord = (tt / tt.z).xy;
+ position = vec3( modelView * vec4( vertexPosition, 1.0 ) );
+
+ gl_Position = mvp * vec4( vertexPosition, 1.0 );
+}
diff --git a/src/plugins/renderers/rhi/renderer/renderview.cpp b/src/plugins/renderers/rhi/renderer/renderview.cpp
index f3cc1b811..df6345896 100644
--- a/src/plugins/renderers/rhi/renderer/renderview.cpp
+++ b/src/plugins/renderers/rhi/renderer/renderview.cpp
@@ -1121,7 +1121,6 @@ void RenderView::setShaderAndUniforms(RenderCommand *command,
shaderData->updateWorldTransform(*worldTransform);
setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, LIGHT_STRUCT_NAMES[lightIdx]);
- setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, LIGHT_STRUCT_UNROLL_NAMES[lightIdx]);
++lightIdx;
}
}
diff --git a/src/render/materialsystem/prototypes/default.json b/src/render/materialsystem/prototypes/default.json
index 597de41c3..6a25477e0 100644
--- a/src/render/materialsystem/prototypes/default.json
+++ b/src/render/materialsystem/prototypes/default.json
@@ -32,6 +32,15 @@
},
"substitution": "$type $value = $name;",
"headerSnippets": [ "$qualifier $type $name;" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 4,
+ "minor": 5
+ },
+ "substitution": "$type $value = $name;",
+ "headerSnippets": [ "add-input $qualifier $type $name" ]
}
]
},
@@ -102,6 +111,15 @@
},
"substitution": "vec4 $color = texture($name, $coord);",
"headerSnippets": [ "uniform sampler2D $name;" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 4,
+ "minor": 5
+ },
+ "substitution": "vec4 $color = texture($name, $coord);",
+ "headerSnippets": [ "add-sampler sampler2D $name" ]
}
]
},
@@ -135,6 +153,15 @@
},
"substitution": "fragColor = $fragColor;",
"headerSnippets": [ "out vec4 fragColor;" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 4,
+ "minor": 5
+ },
+ "substitution": "fragColor = $fragColor;",
+ "headerSnippets": [ "layout(location = 0) out vec4 fragColor;" ]
}
]
},
@@ -160,6 +187,15 @@
},
"substitution": "vec3 $eyePosition = eyePosition;",
"headerSnippets": [ "uniform vec3 eyePosition;" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 4,
+ "minor": 5
+ },
+ "substitution": "vec3 $eyePosition = eyePosition;",
+ "headerSnippets": [ "add-uniform vec3 eyePosition" ]
}
]
},
@@ -185,6 +221,15 @@
},
"substitution": "float $time = time;",
"headerSnippets": [ "uniform float time;" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 4,
+ "minor": 5
+ },
+ "substitution": "float $time = time;",
+ "headerSnippets": [ "add-uniform float time" ]
}
]
},
@@ -419,6 +464,15 @@
},
"substitution": "mat3 $matrix = calcWorldSpaceToTangentSpaceMatrix($worldNormal, $worldTangent);",
"headerSnippets": [ "#pragma include :/shaders/gl3/coordinatesystems.inc" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 4,
+ "minor": 5
+ },
+ "substitution": "mat3 $matrix = calcWorldSpaceToTangentSpaceMatrix($worldNormal, $worldTangent);",
+ "headerSnippets": [ "#pragma include :/shaders/rhi/coordinatesystems.inc" ]
}
]
},
@@ -453,6 +507,15 @@
},
"substitution": "vec4 $outputColor = phongFunction($ambient, $diffuse, $specular, $shininess, $worldPosition, $worldView, $worldNormal);",
"headerSnippets": [ "#pragma include :/shaders/gl3/phong.inc.frag" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 4,
+ "minor": 5
+ },
+ "substitution": "vec4 $outputColor = phongFunction($ambient, $diffuse, $specular, $shininess, $worldPosition, $worldView, $worldNormal);",
+ "headerSnippets": [ "#pragma include :/shaders/rhi/phong.inc.frag" ]
}
]
},
@@ -487,6 +550,15 @@
},
"substitution": "vec4 $outputColor = metalRoughFunction($baseColor, $metalness, $roughness, $ambientOcclusion, $worldPosition, $worldView, $worldNormal);",
"headerSnippets": [ "#pragma include :/shaders/gl3/metalrough.inc.frag" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 4,
+ "minor": 5
+ },
+ "substitution": "vec4 $outputColor = metalRoughFunction($baseColor, $metalness, $roughness, $ambientOcclusion, $worldPosition, $worldView, $worldNormal);",
+ "headerSnippets": [ "#pragma include :/shaders/rhi/metalrough.inc.frag" ]
}
]
},
diff --git a/src/render/materialsystem/shaderbuilder.cpp b/src/render/materialsystem/shaderbuilder.cpp
index c0bf21f44..996270b9e 100644
--- a/src/render/materialsystem/shaderbuilder.cpp
+++ b/src/render/materialsystem/shaderbuilder.cpp
@@ -246,7 +246,8 @@ void ShaderBuilder::generateCode(QShaderProgram::ShaderType type)
generator.graph = graph;
const auto code = generator.createShaderCode(m_enabledLayers);
- m_codes.insert(type, QShaderProgramPrivate::deincludify(code, graphPath + QStringLiteral(".glsl")));
+ const auto deincludified = QShaderProgramPrivate::deincludify(code, graphPath + QStringLiteral(".glsl"));
+ m_codes.insert(type, deincludified);
m_dirtyTypes.remove(type);
m_pendingUpdates.push_back({ peerId(),
diff --git a/src/render/shadergraph/qshadergenerator.cpp b/src/render/shadergraph/qshadergenerator.cpp
index 4f37cfef1..15178fc71 100644
--- a/src/render/shadergraph/qshadergenerator.cpp
+++ b/src/render/shadergraph/qshadergenerator.cpp
@@ -43,17 +43,17 @@
#include <QRegularExpression>
#include <cctype>
+#include <qshaderprogram_p.h>
QT_BEGIN_NAMESPACE
-namespace Qt3DRender
-{
+namespace Qt3DRender {
Q_LOGGING_CATEGORY(ShaderGenerator, "ShaderGenerator", QtWarningMsg)
namespace
{
- QByteArray toGlsl(QShaderLanguage::StorageQualifier qualifier, const QShaderFormat &format)
+ QByteArray toGlsl(QShaderLanguage::StorageQualifier qualifier, const QShaderFormat &format) noexcept
{
- if (format.version().majorVersion() <= 2) {
+ if (format.version().majorVersion() <= 2 && format.api() != QShaderFormat::RHI) {
// Note we're assuming fragment shader only here, it'd be different
// values for vertex shader, will need to be fixed properly at some
// point but isn't necessary yet (this problem already exists in past
@@ -91,7 +91,7 @@ namespace
Q_UNREACHABLE();
}
- QByteArray toGlsl(QShaderLanguage::VariableType type)
+ QByteArray toGlsl(QShaderLanguage::VariableType type) noexcept
{
switch (type) {
case QShaderLanguage::Bool:
@@ -267,7 +267,8 @@ namespace
Q_UNREACHABLE();
}
- QByteArray replaceParameters(const QByteArray &original, const QShaderNode &node, const QShaderFormat &format)
+ QByteArray replaceParameters(const QByteArray &original, const QShaderNode &node,
+ const QShaderFormat &format) noexcept
{
QByteArray result = original;
@@ -276,11 +277,13 @@ namespace
const QByteArray placeholder = QByteArray(QByteArrayLiteral("$") + parameterName.toUtf8());
const QVariant parameter = node.parameter(parameterName);
if (parameter.userType() == qMetaTypeId<QShaderLanguage::StorageQualifier>()) {
- const QShaderLanguage::StorageQualifier qualifier = qvariant_cast<QShaderLanguage::StorageQualifier>(parameter);
+ const QShaderLanguage::StorageQualifier qualifier =
+ qvariant_cast<QShaderLanguage::StorageQualifier>(parameter);
const QByteArray value = toGlsl(qualifier, format);
result.replace(placeholder, value);
} else if (parameter.userType() == qMetaTypeId<QShaderLanguage::VariableType>()) {
- const QShaderLanguage::VariableType type = qvariant_cast<QShaderLanguage::VariableType>(parameter);
+ const QShaderLanguage::VariableType type =
+ qvariant_cast<QShaderLanguage::VariableType>(parameter);
const QByteArray value = toGlsl(type);
result.replace(placeholder, value);
} else {
@@ -291,64 +294,275 @@ namespace
return result;
}
-}
-QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) const
-{
- auto code = QByteArrayList();
-
- if (format.isValid()) {
- const bool isGLES = format.api() == QShaderFormat::OpenGLES;
- const int major = format.version().majorVersion();
- const int minor = format.version().minorVersion();
-
- const int version = major == 2 && isGLES ? 100
- : major == 3 && isGLES ? 300
- : major == 2 ? 100 + 10 * (minor + 1)
- : major == 3 && minor <= 2 ? 100 + 10 * (minor + 3)
- : major * 100 + minor * 10
- ;
-
- const QByteArray profile = isGLES && version > 100 ? QByteArrayLiteral(" es")
- : version >= 150 && format.api() == QShaderFormat::OpenGLCoreProfile ? QByteArrayLiteral(" core")
- : version >= 150 && format.api() == QShaderFormat::OpenGLCompatibilityProfile ? QByteArrayLiteral(" compatibility")
- : QByteArray();
-
- code << (QByteArrayLiteral("#version ") + QByteArray::number(version) + profile);
- code << QByteArray();
+ bool intersectsEnabledLayers(const QStringList &enabledLayers, const QStringList &layers) noexcept
+ {
+ return layers.isEmpty()
+ || std::any_of(layers.cbegin(), layers.cend(),
+ [enabledLayers](const QString &s) { return enabledLayers.contains(s); });
}
- const auto intersectsEnabledLayers = [enabledLayers] (const QStringList &layers) {
- return layers.isEmpty()
- || std::any_of(layers.cbegin(), layers.cend(),
- [enabledLayers] (const QString &s) { return enabledLayers.contains(s); });
+ struct ShaderGenerationState
+ {
+ ShaderGenerationState(const QShaderGenerator & gen, QStringList layers, QVector<QShaderNode> nodes)
+ : generator{gen}
+ , enabledLayers{layers}
+ , nodes{nodes}
+ {
+
+ }
+
+ const QShaderGenerator &generator;
+ QStringList enabledLayers;
+ QVector<QShaderNode> nodes;
+ QByteArrayList code;
+
+ QVector<QString> globalInputVariables;
+ const QRegularExpression globalInputExtractRegExp { QStringLiteral("^.*\\s+(\\w+).*;$") };
};
- QVector<QString> globalInputVariables;
- const QRegularExpression globalInputExtractRegExp(QStringLiteral("^.*\\s+(\\w+).*;$"));
+ class GLSL45HeaderWriter
+ {
+ public:
+ void writeHeader(ShaderGenerationState &state)
+ {
+ const auto &format = state.generator.format;
+ auto &code = state.code;
+ for (const QShaderNode &node : state.nodes) {
+ if (intersectsEnabledLayers(state.enabledLayers, node.layers())) {
+ const QByteArrayList& headerSnippets = node.rule(format).headerSnippets;
+ for (const QByteArray &snippet : headerSnippets) {
+ auto replacedSnippet = replaceParameters(snippet, node, format).trimmed();
+
+ if (replacedSnippet.startsWith(QByteArrayLiteral("add-input"))) {
+ onInOut(code, replacedSnippet);
+ } else if (replacedSnippet.startsWith(QByteArrayLiteral("add-uniform"))) {
+ onNamedUniform(ubo, replacedSnippet);
+ } else if (replacedSnippet.startsWith(QByteArrayLiteral("add-sampler"))) {
+ onNamedSampler(code, replacedSnippet);
+ } else if (replacedSnippet.startsWith(QByteArrayLiteral("#pragma include "))) {
+ onInclude(code, replacedSnippet);
+ } else {
+ code << replacedSnippet;
+ }
+ // If node is an input, record the variable name into the globalInputVariables
+ // vector
+ if (node.type() == QShaderNode::Input) {
+ const QRegularExpressionMatch match = state.globalInputExtractRegExp.match(
+ QString::fromUtf8(code.last()));
+ if (match.hasMatch())
+ state.globalInputVariables.push_back(match.captured(1));
+ }
+ }
+ }
+ }
- const QVector<QShaderNode> nodes = graph.nodes();
- for (const QShaderNode &node : nodes) {
- if (intersectsEnabledLayers(node.layers())) {
- const QByteArrayList headerSnippets = node.rule(format).headerSnippets;
- for (const QByteArray &snippet : headerSnippets) {
- code << replaceParameters(snippet, node, format);
-
- // If node is an input, record the variable name into the globalInputVariables vector
- if (node.type() == QShaderNode::Input) {
- const QRegularExpressionMatch match = globalInputExtractRegExp.match(QString::fromUtf8(code.last()));
- if (match.hasMatch())
- globalInputVariables.push_back(match.captured(1));
+ if (!ubo.isEmpty()) {
+ code << QByteArrayLiteral("layout(std140, binding = ")
+ + QByteArray::number(currentBinding++)
+ + QByteArrayLiteral(") uniform qt3d_shadergraph_generated_uniforms {");
+ code << ubo;
+ code << "};";
+ }
+ }
+
+ private:
+ void onInOut(QByteArrayList &code, const QByteArray &snippet) noexcept
+ {
+ const auto split = snippet.split(' ');
+ if (split.size() < 4) {
+ qDebug() << "Invalid header snippet: " << snippet;
+ return;
+ }
+ const auto &qualifier = split[1];
+ const auto &type = split[2];
+ const auto &name = split[3];
+
+ if (qualifier == QByteArrayLiteral("in")) {
+ code << (QByteArrayLiteral("layout(location = ")
+ + QByteArray::number(currentInputLocation++) + QByteArrayLiteral(") in ")
+ + type + ' ' + name + QByteArrayLiteral(";"));
+ } else if (qualifier == QByteArrayLiteral("out")) {
+ code << (QByteArrayLiteral("layout(location = ")
+ + QByteArray::number(currentOutputLocation++) + QByteArrayLiteral(") out ")
+ + type + ' ' + name + QByteArrayLiteral(";"));
+ } else if (qualifier == QByteArrayLiteral("uniform")) {
+ ubo << (type + ' ' + name + ';');
+ }
+ }
+
+ void onNamedUniform(QByteArrayList &ubo, const QByteArray &snippet) noexcept
+ {
+ const auto split = snippet.split(' ');
+ if (split.size() < 3) {
+ qDebug() << "Invalid header snippet: " << snippet;
+ return;
+ }
+
+ const auto &type = split[1];
+ const auto &name = split[2];
+
+ ubo << (type + ' ' + name + ';');
+ }
+
+ void onNamedSampler(QByteArrayList &code, const QByteArray &snippet) noexcept
+ {
+ const auto split = snippet.split(' ');
+ if (split.size() < 3) {
+ qDebug() << "Invalid header snippet: " << snippet;
+ return;
+ }
+ const auto binding = QByteArray::number(currentBinding++);
+ const auto &type = split[1];
+ const auto &name = split[2];
+
+ code << (QByteArrayLiteral("layout(binding = ") + binding + QByteArrayLiteral(") uniform ")
+ + type + ' ' + name + QByteArrayLiteral(";"));
+ }
+
+ void onInclude(QByteArrayList &code, const QByteArray &snippet) noexcept
+ {
+ const auto filepath = QString::fromUtf8(snippet.mid(strlen("#pragma include ")));
+ QString deincluded = QString::fromUtf8(QShaderProgramPrivate::deincludify(filepath));
+
+ // This lambda will replace all occurrences of a string (e.g. "binding = auto") by another,
+ // with the incremented int passed as argument (e.g. "binding = 1", "binding = 2" ...)
+ const auto replaceAndIncrement = [&deincluded](const QRegularExpression &regexp,
+ int &variable,
+ const QString &replacement) noexcept {
+ int matchStart = 0;
+ do {
+ matchStart = deincluded.indexOf(regexp, matchStart);
+ if (matchStart != -1) {
+ const auto match = regexp.match(deincluded.midRef(matchStart));
+ const auto length = match.capturedLength(0);
+
+ deincluded.replace(matchStart, length, replacement.arg(variable++));
+ }
+ } while (matchStart != -1);
+ };
+
+ // 1. Handle uniforms
+ {
+ thread_local const QRegularExpression bindings(
+ QStringLiteral("binding\\s?+=\\s?+auto"));
+
+ replaceAndIncrement(bindings, currentBinding, QStringLiteral("binding = %1"));
+ }
+
+ // 2. Handle inputs
+ {
+ thread_local const QRegularExpression inLocations(
+ QStringLiteral("location\\s?+=\\s?+auto\\s?+\\)\\s?+in\\s+"));
+
+ replaceAndIncrement(inLocations, currentInputLocation,
+ QStringLiteral("location = %1) in "));
+ }
+
+ // 3. Handle outputs
+ {
+ thread_local const QRegularExpression outLocations(
+ QStringLiteral("location\\s?+=\\s?+auto\\s?+\\)\\s?+out\\s+"));
+
+ replaceAndIncrement(outLocations, currentOutputLocation,
+ QStringLiteral("location = %1) out "));
+ }
+
+ code << deincluded.toUtf8();
+ }
+
+ int currentInputLocation { 0 };
+ int currentOutputLocation { 0 };
+ int currentBinding { 0 };
+ QByteArrayList ubo;
+ };
+
+ struct GLSLHeaderWriter
+ {
+ void writeHeader(ShaderGenerationState &state)
+ {
+ const auto &format = state.generator.format;
+ auto &code = state.code;
+ for (const QShaderNode &node : state.nodes) {
+ if (intersectsEnabledLayers(state.enabledLayers, node.layers())) {
+ const QByteArrayList& headerSnippets = node.rule(format).headerSnippets;
+ for (const QByteArray &snippet : headerSnippets) {
+ code << replaceParameters(snippet, node, format);
+
+ // If node is an input, record the variable name into the globalInputVariables
+ // vector
+ if (node.type() == QShaderNode::Input) {
+ const QRegularExpressionMatch match = state.globalInputExtractRegExp.match(
+ QString::fromUtf8(code.last()));
+ if (match.hasMatch())
+ state.globalInputVariables.push_back(match.captured(1));
+ }
+ }
}
}
}
+ };
+
+ QByteArray versionString(const QShaderFormat &format) noexcept
+ {
+ if (!format.isValid())
+ return {};
+
+ switch (format.api()) {
+ case QShaderFormat::RHI: {
+ return QByteArrayLiteral("#version 450");
+ }
+ case QShaderFormat::VulkanFlavoredGLSL: {
+ const int major = format.version().majorVersion();
+ const int minor = format.version().minorVersion();
+ return (QByteArrayLiteral("#version ") + QByteArray::number(major * 100 + minor * 10));
+ }
+ default: {
+ const bool isGLES = format.api() == QShaderFormat::OpenGLES;
+ const int major = format.version().majorVersion();
+ const int minor = format.version().minorVersion();
+
+ const int version = major == 2 && isGLES ? 100
+ : major == 3 && isGLES ? 300
+ : major == 2 ? 100 + 10 * (minor + 1)
+ : major == 3 && minor <= 2 ? 100 + 10 * (minor + 3)
+ : major * 100 + minor * 10;
+
+ const QByteArray profile =
+ isGLES && version > 100 ? QByteArrayLiteral(" es")
+ : version >= 150 && format.api() == QShaderFormat::OpenGLCoreProfile ? QByteArrayLiteral(" core")
+ : version >= 150 && format.api() == QShaderFormat::OpenGLCompatibilityProfile ? QByteArrayLiteral(" compatibility")
+ : QByteArray();
+
+ return (QByteArrayLiteral("#version ") + QByteArray::number(version) + profile);
+ }
+ }
+ }
+}
+
+QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) const
+{
+ const QVector<QShaderNode> nodes = graph.nodes();
+ ShaderGenerationState state(*this, enabledLayers, nodes);
+ QByteArrayList &code = state.code;
+
+ code << versionString(format);
+ code << QByteArray();
+
+ if (format.api() == QShaderFormat::VulkanFlavoredGLSL || format.api() == QShaderFormat::RHI) {
+ GLSL45HeaderWriter builder;
+ builder.writeHeader(state);
+ } else {
+ GLSLHeaderWriter builder;
+ builder.writeHeader(state);
}
code << QByteArray();
code << QByteArrayLiteral("void main()");
code << QByteArrayLiteral("{");
- const QRegularExpression temporaryVariableToAssignmentRegExp(QStringLiteral("([^;]*\\s+(v\\d+))\\s*=\\s*([^;]*);"));
+ const QRegularExpression temporaryVariableToAssignmentRegExp(
+ QStringLiteral("([^;]*\\s+(v\\d+))\\s*=\\s*([^;]*);"));
const QRegularExpression temporaryVariableInAssignmentRegExp(QStringLiteral("\\W*(v\\d+)\\W*"));
const QRegularExpression statementRegExp(QStringLiteral("\\s*(\\w+)\\s*=\\s*([^;]*);"));
@@ -362,11 +576,7 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
struct Variable
{
- enum Type {
- GlobalInput,
- TemporaryAssignment,
- Output
- };
+ enum Type { GlobalInput, TemporaryAssignment, Output };
QString name;
QString declaration;
@@ -380,7 +590,8 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
if (v->substituted)
return;
- qCDebug(ShaderGenerator) << "Begin Substituting " << v->name << " = " << v->assignment.expression;
+ qCDebug(ShaderGenerator)
+ << "Begin Substituting " << v->name << " = " << v->assignment.expression;
for (Variable *ref : qAsConst(v->assignment.referencedVariables)) {
// Recursively substitute
Variable::substitute(ref);
@@ -390,14 +601,15 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
if (ref->referenceCount == 1 || ref->type == Variable::GlobalInput) {
const QRegularExpression r(QStringLiteral("(.*\\b)(%1)(\\b.*)").arg(ref->name));
if (v->assignment.referencedVariables.size() == 1)
- v->assignment.expression.replace(r,
- QStringLiteral("\\1%2\\3").arg(ref->assignment.expression));
+ v->assignment.expression.replace(
+ r, QStringLiteral("\\1%2\\3").arg(ref->assignment.expression));
else
- v->assignment.expression.replace(r,
- QStringLiteral("(\\1%2\\3)").arg(ref->assignment.expression));
+ v->assignment.expression.replace(
+ r, QStringLiteral("(\\1%2\\3)").arg(ref->assignment.expression));
}
}
- qCDebug(ShaderGenerator) << "Done Substituting " << v->name << " = " << v->assignment.expression;
+ qCDebug(ShaderGenerator)
+ << "Done Substituting " << v->name << " = " << v->assignment.expression;
v->substituted = true;
}
};
@@ -438,13 +650,16 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
return nullptr;
};
- auto gatherTemporaryVariablesFromAssignment = [&] (Variable *v, const QString &assignmentContent) {
- QRegularExpressionMatchIterator subMatchIt = temporaryVariableInAssignmentRegExp.globalMatch(assignmentContent);
+ auto gatherTemporaryVariablesFromAssignment = [&](Variable *v,
+ const QString &assignmentContent) {
+ QRegularExpressionMatchIterator subMatchIt =
+ temporaryVariableInAssignmentRegExp.globalMatch(assignmentContent);
while (subMatchIt.hasNext()) {
const QRegularExpressionMatch subMatch = subMatchIt.next();
const QString variableName = subMatch.captured(1);
- // Variable we care about should already exists -> an expression cannot reference a variable that hasn't been defined
+ // Variable we care about should already exists -> an expression cannot reference a
+ // variable that hasn't been defined
Variable *u = findVariable(variableName);
Q_ASSERT(u);
@@ -460,7 +675,8 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
QByteArray line = node.rule(format).substitution;
const QVector<QShaderNodePort> ports = node.ports();
- struct VariableReplacement {
+ struct VariableReplacement
+ {
QByteArray placeholder;
QByteArray variable;
};
@@ -477,8 +693,8 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
Q_ASSERT(portIndex >= 0);
- const int variableIndex = isInput ? statement.inputs.at(portIndex)
- : statement.outputs.at(portIndex);
+ const int variableIndex =
+ isInput ? statement.inputs.at(portIndex) : statement.outputs.at(portIndex);
if (variableIndex < 0)
continue;
@@ -502,10 +718,11 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
const int placeholderLength = end - begin;
const QByteArray variableName = line.mid(begin, placeholderLength);
- const auto replacementIt = std::find_if(variableReplacements.cbegin(), variableReplacements.cend(),
- [&variableName](const VariableReplacement &replacement) {
- return variableName == replacement.placeholder;
- });
+ const auto replacementIt =
+ std::find_if(variableReplacements.cbegin(), variableReplacements.cend(),
+ [&variableName](const VariableReplacement &replacement) {
+ return variableName == replacement.placeholder;
+ });
if (replacementIt != variableReplacements.cend()) {
line.replace(begin, placeholderLength, replacementIt->variable);
@@ -526,7 +743,8 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
matches = statementRegExp.globalMatch(QString::fromUtf8(substitutionedLine));
break;
case QShaderNode::Function:
- matches = temporaryVariableToAssignmentRegExp.globalMatch(QString::fromUtf8(substitutionedLine));
+ matches = temporaryVariableToAssignmentRegExp.globalMatch(
+ QString::fromUtf8(substitutionedLine));
break;
case QShaderNode::Invalid:
break;
@@ -606,7 +824,8 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
if (v != nullptr) {
Variable::substitute(v);
- qCDebug(ShaderGenerator) << "Line " << lineContent.rawContent << "is assigned to temporary" << v->name;
+ qCDebug(ShaderGenerator)
+ << "Line " << lineContent.rawContent << "is assigned to temporary" << v->name;
// Check number of occurrences a temporary variable is referenced
if (v->referenceCount == 1 || v->type == Variable::GlobalInput) {
@@ -615,9 +834,10 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
lineContent.rawContent.clear();
// We assume expression that were referencing vN will have vN properly substituted
} else {
- lineContent.rawContent = QStringLiteral(" %1 = %2;").arg(v->declaration)
- .arg(v->assignment.expression)
- .toUtf8();
+ lineContent.rawContent = QStringLiteral(" %1 = %2;")
+ .arg(v->declaration)
+ .arg(v->assignment.expression)
+ .toUtf8();
}
qCDebug(ShaderGenerator) << "Updated Line is " << lineContent.rawContent;
@@ -636,5 +856,6 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
return code.join('\n');
}
+
}
QT_END_NAMESPACE
diff --git a/src/render/shadergraph/qshadernodesloader.cpp b/src/render/shadergraph/qshadernodesloader.cpp
index 8346ed3d5..df34fd1f7 100644
--- a/src/render/shadergraph/qshadernodesloader.cpp
+++ b/src/render/shadergraph/qshadernodesloader.cpp
@@ -220,9 +220,10 @@ void QShaderNodesLoader::load(const QJsonObject &prototypesObject)
: api == QStringLiteral("OpenGLCoreProfile") ? QShaderFormat::OpenGLCoreProfile
: api == QStringLiteral("OpenGLCompatibilityProfile") ? QShaderFormat::OpenGLCompatibilityProfile
: api == QStringLiteral("VulkanFlavoredGLSL") ? QShaderFormat::VulkanFlavoredGLSL
+ : api == QStringLiteral("RHI") ? QShaderFormat::RHI
: QShaderFormat::NoApi);
if (format.api() == QShaderFormat::NoApi) {
- qWarning() << "Format API must be one of: OpenGLES, OpenGLNoProfile, OpenGLCoreProfile or OpenGLCompatibilityProfile, VulkanFlavoredGLSL";
+ qWarning() << "Format API must be one of: OpenGLES, OpenGLNoProfile, OpenGLCoreProfile OpenGLCompatibilityProfile, VulkanFlavoredGLSL or RHI";
hasError = true;
break;
}