diff options
author | Jean-Michaël Celerier <jean-michael.celerier@kdab.com> | 2020-03-13 15:23:50 +0100 |
---|---|---|
committer | Jean-Michaël Celerier <jean-michael.celerier@kdab.com> | 2020-04-21 16:36:47 +0200 |
commit | a01dbe5b0e1b912c7210abc304700020e685aff0 (patch) | |
tree | 7516339103b75068133654e6984c8bc45bd6cb7f /src | |
parent | 18b319f919f71c6b476675d832d1b8a2bda118c2 (diff) |
rhi: Handle RHI-required information in QShaderGraph
Change-Id: I705843bbb1f6928c2e36b327469882e11fb9613e
Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
Diffstat (limited to 'src')
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 ®exp, + 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; } |