diff options
Diffstat (limited to 'tests/manual/shadow-map-qml')
26 files changed, 1281 insertions, 0 deletions
diff --git a/tests/manual/shadow-map-qml/AdsEffect.qml b/tests/manual/shadow-map-qml/AdsEffect.qml new file mode 100644 index 000000000..8050ff01c --- /dev/null +++ b/tests/manual/shadow-map-qml/AdsEffect.qml @@ -0,0 +1,129 @@ +// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import Qt3D.Core 2.0 +import Qt3D.Render 2.0 + +Effect { + id: root + + property Texture2D shadowTexture + property ShadowMapLight light + + // These parameters act as default values for the effect. They take + // priority over any parameters specified in the RenderPasses below + // (none provided in this example). In turn these parameters can be + // overwritten by specifying them in a Material that references this + // effect. + // The priority order is: + // + // Material -> Effect -> Technique -> RenderPass -> GLSL default values + parameters: [ + Parameter { name: "lightViewProjection"; value: root.light.lightViewProjection }, + Parameter { name: "lightPosition"; value: root.light.lightPosition }, + Parameter { name: "lightIntensity"; value: root.light.lightIntensity }, + Parameter { name: "shadowMapTexture"; value: root.shadowTexture } + ] + + techniques: [ + Technique { + graphicsApiFilter { + api: GraphicsApiFilter.OpenGL + profile: GraphicsApiFilter.CoreProfile + majorVersion: 3 + minorVersion: 2 + } + + renderPasses: [ + RenderPass { + filterKeys: [ FilterKey { name: "pass"; value: "shadowmap" } ] + + shaderProgram: ShaderProgram { + vertexShaderCode: loadSource("qrc:/shaders/shadowmap.vert") + fragmentShaderCode: loadSource("qrc:/shaders/shadowmap.frag") + } + + renderStates: [ + PolygonOffset { scaleFactor: 4; depthSteps: 4 }, + DepthTest { depthFunction: DepthTest.Less } + ] + }, + + RenderPass { + filterKeys: [ FilterKey { name : "pass"; value : "forward" } ] + + shaderProgram: ShaderProgram { + vertexShaderCode: loadSource("qrc:/shaders/ads.vert") + fragmentShaderCode: loadSource("qrc:/shaders/ads.frag") + } + + // no special render state set => use the default set of states + } + ] + }, + Technique { + graphicsApiFilter { + api: GraphicsApiFilter.OpenGLES + majorVersion: 3 + minorVersion: 0 + } + + renderPasses: [ + RenderPass { + filterKeys: [ FilterKey { name: "pass"; value: "shadowmap" } ] + + shaderProgram: ShaderProgram { + vertexShaderCode: loadSource("qrc:/shaders/es3/shadowmap.vert") + fragmentShaderCode: loadSource("qrc:/shaders/es3/shadowmap.frag") + } + + renderStates: [ + PolygonOffset { scaleFactor: 4; depthSteps: 4 }, + DepthTest { depthFunction: DepthTest.Less } + ] + }, + + RenderPass { + filterKeys: [ FilterKey { name : "pass"; value : "forward" } ] + + shaderProgram: ShaderProgram { + vertexShaderCode: loadSource("qrc:/shaders/es3/ads.vert") + fragmentShaderCode: loadSource("qrc:/shaders/es3/ads.frag") + } + } + ] + }, + Technique { + graphicsApiFilter { + api: GraphicsApiFilter.RHI + majorVersion: 1 + minorVersion: 0 + } + + renderPasses: [ + RenderPass { + filterKeys: [ FilterKey { name: "pass"; value: "shadowmap" } ] + + shaderProgram: ShaderProgram { + vertexShaderCode: loadSource("qrc:/shaders/rhi/shadowmap.vert") + fragmentShaderCode: loadSource("qrc:/shaders/rhi/shadowmap.frag") + } + + renderStates: [ + PolygonOffset { scaleFactor: 4; depthSteps: 4 }, + DepthTest { depthFunction: DepthTest.Less } + ] + }, + + RenderPass { + filterKeys: [ FilterKey { name : "pass"; value : "forward" } ] + + shaderProgram: ShaderProgram { + vertexShaderCode: loadSource("qrc:/shaders/rhi/ads.vert") + fragmentShaderCode: loadSource("qrc:/shaders/rhi/ads.frag") + } + } + ] + } + ] +} diff --git a/tests/manual/shadow-map-qml/AdsMaterial.qml b/tests/manual/shadow-map-qml/AdsMaterial.qml new file mode 100644 index 000000000..2ac53b987 --- /dev/null +++ b/tests/manual/shadow-map-qml/AdsMaterial.qml @@ -0,0 +1,21 @@ +// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick 2.1 +import Qt3D.Core 2.0 +import Qt3D.Render 2.0 + +Material { + id: root + property color ambientColor: Qt.rgba(0.1, 0.1, 0.1, 1.0) + property color diffuseColor: Qt.rgba(0.7, 0.7, 0.9, 1.0) + property color specularColor: Qt.rgba(0.1, 0.1, 0.1, 1.0) + property real shininess: 150.0 + + parameters: [ + Parameter { name: "ka"; value: Qt.vector3d(root.ambientColor.r, root.ambientColor.g, root.ambientColor.b) }, + Parameter { name: "kd"; value: Qt.vector3d(root.diffuseColor.r, root.diffuseColor.g, root.diffuseColor.b) }, + Parameter { name: "ks"; value: Qt.vector3d(root.specularColor.r, root.specularColor.g, root.specularColor.b) }, + Parameter { name: "shininess"; value: root.shininess } + ] +} diff --git a/tests/manual/shadow-map-qml/CMakeLists.txt b/tests/manual/shadow-map-qml/CMakeLists.txt new file mode 100644 index 000000000..d7fe2232b --- /dev/null +++ b/tests/manual/shadow-map-qml/CMakeLists.txt @@ -0,0 +1,90 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(shadow-map-qml LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}") + +find_package(Qt6 REQUIRED COMPONENTS 3DCore 3DInput 3DQuick 3DQuickExtras 3DRender Core Gui Qml Quick) + +qt_add_executable(shadow-map-qml + main.cpp +) + +set_target_properties(shadow-map-qml PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(shadow-map-qml PUBLIC + Qt::3DCore + Qt::3DInput + Qt::3DQuick + Qt::3DQuickExtras + Qt::3DRender + Qt::Core + Qt::Gui + Qt::Qml + Qt::Quick +) + +# Resources: +set(shadow-map-qml_resource_files + "AdsEffect.qml" + "AdsMaterial.qml" + "GroundPlane.qml" + "ShadowMapFrameGraph.qml" + "ShadowMapLight.qml" + "Toyplane.qml" + "Trefoil.qml" + "main.qml" + "shaders/ads.frag" + "shaders/ads.vert" + "shaders/es3/ads.frag" + "shaders/es3/ads.vert" + "shaders/es3/shadowmap.frag" + "shaders/es3/shadowmap.vert" + "shaders/rhi/ads.frag" + "shaders/rhi/ads.vert" + "shaders/rhi/shadowmap.frag" + "shaders/rhi/shadowmap.vert" + "shaders/shadowmap.frag" + "shaders/shadowmap.vert" +) + +qt6_add_resources(shadow-map-qml "shadow-map-qml" + PREFIX + "/" + FILES + ${shadow-map-qml_resource_files} +) + +set(obj_resource_files + "../exampleresources/assets/obj/ball.obj" + "../exampleresources/assets/obj/material-sphere.obj" + "../exampleresources/assets/obj/plane-10x10.obj" + "../exampleresources/assets/obj/toyplane.obj" + "../exampleresources/assets/obj/trefoil.obj" +) + +qt6_add_resources(shadow-map-qml "obj" + PREFIX + "/" + BASE + "../exampleresources" + FILES + ${obj_resource_files} +) + +install(TARGETS shadow-map-qml + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/tests/manual/shadow-map-qml/GroundPlane.qml b/tests/manual/shadow-map-qml/GroundPlane.qml new file mode 100644 index 000000000..278f50432 --- /dev/null +++ b/tests/manual/shadow-map-qml/GroundPlane.qml @@ -0,0 +1,29 @@ +// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import Qt3D.Core 2.0 +import Qt3D.Render 2.0 +import Qt3D.Extras 2.0 + +Entity { + id: root + property Material material + + PlaneMesh { + id: groundMesh + width: 50 + height: width + meshResolution: Qt.size(2, 2) + } + + Transform { + id: groundTransform + translation: Qt.vector3d(0, -5, 0) + } + + components: [ + groundMesh, + groundTransform, + material + ] +} diff --git a/tests/manual/shadow-map-qml/ShadowMapFrameGraph.qml b/tests/manual/shadow-map-qml/ShadowMapFrameGraph.qml new file mode 100644 index 000000000..c534ae7d2 --- /dev/null +++ b/tests/manual/shadow-map-qml/ShadowMapFrameGraph.qml @@ -0,0 +1,71 @@ +// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick 2.2 as QQ2 +import Qt3D.Core 2.0 +import Qt3D.Render 2.0 + +RenderSettings { + id: root + + property alias viewCamera: viewCameraSelector.camera + property alias lightCamera: lightCameraSelector.camera + readonly property Texture2D shadowTexture: depthTexture + + activeFrameGraph: Viewport { + normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0) + + RenderSurfaceSelector { + RenderPassFilter { + matchAny: [ FilterKey { name: "pass"; value: "shadowmap" } ] + + RenderTargetSelector { + target: RenderTarget { + attachments: [ + RenderTargetOutput { + objectName: "depth" + attachmentPoint: RenderTargetOutput.Depth + texture: Texture2D { + id: depthTexture + width: 1024 + height: 1024 + format: Texture.DepthFormat + generateMipMaps: false + magnificationFilter: Texture.Linear + minificationFilter: Texture.Linear + wrapMode { + x: WrapMode.ClampToEdge + y: WrapMode.ClampToEdge + } + comparisonFunction: Texture.CompareLessEqual + comparisonMode: Texture.CompareRefToTexture + } + } + ] + } + + ClearBuffers { + buffers: ClearBuffers.DepthBuffer + + CameraSelector { + id: lightCameraSelector + } + } + } + } + + RenderPassFilter { + matchAny: [ FilterKey { name: "pass"; value: "forward" } ] + + ClearBuffers { + clearColor: Qt.rgba(0.0, 0.4, 0.7, 1.0) + buffers: ClearBuffers.ColorDepthBuffer + + CameraSelector { + id: viewCameraSelector + } + } + } + } + } +} diff --git a/tests/manual/shadow-map-qml/ShadowMapLight.qml b/tests/manual/shadow-map-qml/ShadowMapLight.qml new file mode 100644 index 000000000..797aeb769 --- /dev/null +++ b/tests/manual/shadow-map-qml/ShadowMapLight.qml @@ -0,0 +1,28 @@ +// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import Qt3D.Core 2.0 +import Qt3D.Render 2.0 + +Entity { + id: root + + property vector3d lightPosition: Qt.vector3d(30.0, 30.0, 0.0) + property vector3d lightIntensity: Qt.vector3d(1.0, 1.0, 1.0) + + readonly property Camera lightCamera: lightCamera + readonly property matrix4x4 lightViewProjection: lightCamera.projectionMatrix.times(lightCamera.viewMatrix) + + Camera { + id: lightCamera + objectName: "lightCameraLens" + projectionType: CameraLens.PerspectiveProjection + fieldOfView: 45 + aspectRatio: 1 + nearPlane : 0.1 + farPlane : 200.0 + position: root.lightPosition + viewCenter: Qt.vector3d(0.0, 0.0, 0.0) + upVector: Qt.vector3d(0.0, 1.0, 0.0) + } +} diff --git a/tests/manual/shadow-map-qml/Toyplane.qml b/tests/manual/shadow-map-qml/Toyplane.qml new file mode 100644 index 000000000..d0ae66a4e --- /dev/null +++ b/tests/manual/shadow-map-qml/Toyplane.qml @@ -0,0 +1,94 @@ +// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick 2.1 as QQ2 +import Qt3D.Core 2.0 +import Qt3D.Render 2.0 + +Entity { + id: root + property Material material + + Mesh { + id: toyplaneMesh + source: "qrc:///assets/obj/toyplane.obj" + } + + Transform { + id: toyplaneTransform + + property real rollAngle: 0 + property real pitchAngle: 15 + property real altitude: 5 + property real angle: 0 + property real scaleFactor: 10 + + QQ2.Behavior on rollAngle { QQ2.SpringAnimation { spring: 2; damping: 0.2} } + + matrix: { + var m = Qt.matrix4x4(); + m.translate(Qt.vector3d(Math.sin(angle * Math.PI / 180) * scaleFactor, + altitude, + Math.cos(angle * Math.PI / 180) * scaleFactor)); + m.rotate(angle, Qt.vector3d(0, 1, 0)); + m.rotate(pitchAngle, Qt.vector3d(0, 0, 1)); + m.rotate(rollAngle, Qt.vector3d(1, 0, 0)); + m.scale(1.0 / toyplaneTransform.scaleFactor); + return m; + } + } + + QQ2.NumberAnimation { + target: toyplaneTransform + + running: true + loops: QQ2.Animation.Infinite + + property: "angle" + duration: 10000 + from: 0 + to: 360 + } + + // Altitude / Pitch animation + QQ2.SequentialAnimation { + running: true + loops: QQ2.Animation.Infinite + QQ2.ParallelAnimation { + QQ2.SequentialAnimation { + QQ2.NumberAnimation { target: toyplaneTransform; property: "pitchAngle"; from: 0; to: 30; duration: 2000; easing.type: QQ2.Easing.OutQuad } + QQ2.NumberAnimation { target: toyplaneTransform; property: "pitchAngle"; from: 30; to: 0; duration: 2000; easing.type: QQ2.Easing.OutSine } + } + QQ2.NumberAnimation { target: toyplaneTransform; property: "altitude"; to: 5; duration: 4000; easing.type: QQ2.Easing.InOutCubic } + } + QQ2.PauseAnimation { duration: 1500 } + QQ2.ParallelAnimation { + QQ2.SequentialAnimation { + QQ2.NumberAnimation { target: toyplaneTransform; property: "pitchAngle"; from: 0; to: -30; duration: 1000; easing.type: QQ2.Easing.OutQuad } + QQ2.NumberAnimation { target: toyplaneTransform; property: "pitchAngle"; from: -30; to: 0; duration: 5000; easing.type: QQ2.Easing.OutSine } + } + QQ2.NumberAnimation { target: toyplaneTransform; property: "altitude"; to: 0; duration: 6000; easing.type: QQ2.Easing.InOutCubic} + } + QQ2.PauseAnimation { duration: 1500 } + } + + // Roll Animation + QQ2.SequentialAnimation { + running: true + loops: QQ2.Animation.Infinite + QQ2.NumberAnimation { target: toyplaneTransform; property: "rollAngle"; to: 360; duration: 1500; easing.type: QQ2.Easing.InOutQuad } + QQ2.PauseAnimation { duration: 1000 } + QQ2.NumberAnimation { target: toyplaneTransform; property: "rollAngle"; from: 0; to: 30; duration: 1000; easing.type: QQ2.Easing.OutQuart } + QQ2.PauseAnimation { duration: 1500 } + QQ2.NumberAnimation { target: toyplaneTransform; property: "rollAngle"; from: 30; to: -30; duration: 1000; easing.type: QQ2.Easing.OutQuart } + QQ2.PauseAnimation { duration: 1500 } + QQ2.NumberAnimation { target: toyplaneTransform; property: "rollAngle"; from: -30; to: 0; duration: 750; easing.type: QQ2.Easing.OutQuart } + QQ2.PauseAnimation { duration: 2000 } + } + + components: [ + toyplaneMesh, + toyplaneTransform, + material + ] +} diff --git a/tests/manual/shadow-map-qml/Trefoil.qml b/tests/manual/shadow-map-qml/Trefoil.qml new file mode 100644 index 000000000..338fa0e48 --- /dev/null +++ b/tests/manual/shadow-map-qml/Trefoil.qml @@ -0,0 +1,40 @@ +// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick 2.1 as QQ2 +import Qt3D.Core 2.0 +import Qt3D.Render 2.0 + +Entity { + id: root + property Material material + + Mesh { + id: trefoilMesh + source: "qrc:///assets/obj/trefoil.obj" + } + + Transform { + id: trefoilMeshTransform + property real userAngle: 0.0 + rotation: fromAxisAndAngle(Qt.vector3d(0, 1, 0), userAngle) + } + + QQ2.NumberAnimation { + target: trefoilMeshTransform + + running: true + loops: QQ2.Animation.Infinite + + property: "userAngle" + duration: 5000 + from: 360 + to: 0 + } + + components: [ + trefoilMesh, + trefoilMeshTransform, + material + ] +} diff --git a/tests/manual/shadow-map-qml/doc/images/shadowmapping-depth.png b/tests/manual/shadow-map-qml/doc/images/shadowmapping-depth.png Binary files differnew file mode 100644 index 000000000..d2b501412 --- /dev/null +++ b/tests/manual/shadow-map-qml/doc/images/shadowmapping-depth.png diff --git a/tests/manual/shadow-map-qml/doc/images/shadowmapping-qt3d.png b/tests/manual/shadow-map-qml/doc/images/shadowmapping-qt3d.png Binary files differnew file mode 100644 index 000000000..bac6ac75c --- /dev/null +++ b/tests/manual/shadow-map-qml/doc/images/shadowmapping-qt3d.png diff --git a/tests/manual/shadow-map-qml/doc/src/shadow-map-qml.qdoc b/tests/manual/shadow-map-qml/doc/src/shadow-map-qml.qdoc new file mode 100644 index 000000000..29cb4d391 --- /dev/null +++ b/tests/manual/shadow-map-qml/doc/src/shadow-map-qml.qdoc @@ -0,0 +1,252 @@ +// Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \example shadow-map-qml + \title Qt 3D: Shadow Map QML Example + \ingroup qt3d-examples-qml + + \brief A Qt 3D QML application that illustrates how to render a scene in Qt 3D + with shadows. + + \image shadowmapping-qt3d.png + + \e {Qt 3D Shadow Map} illustrates how to configure the renderer in order to + accommodate custom rendering techniques. The example application displays a + self-shadowed plane and trefoil knot. + + We implement \l{Shadow Mapping}{shadow mapping} using a two pass rendering. + In the first pass, we generate the shadow information. In the second pass, + we generate the scene using the forward rendering technique with Phong + shading, while at the same time using the information gathered in the first + pass to draw the shadows. + + The entire rendering is configured using QML, but it is possible to use C++ + to achieve the very same result. + + \include examples-run.qdocinc + + \section1 Setting Up the Scene + + We set up the entire scene in the \e main.qml file. + + To be able to use the types in the Q3D and Q3D Renderer modules, we must + import the modules: + + \quotefromfile shadow-map-qml/main.qml + \skipto import Qt3D.Core + \printuntil Render 2.0 + + The first entities we create are a \l Camera, which represents the camera + used for the final rendering, and a \l Configuration, which allows us to + control this camera using the keyboard or the mouse: + + \printuntil } + \printuntil } + + We then create a Light custom entity, which represents our light. It is a + directional spotlight, placed somewhere above the plane and looking down at + the scene’s origin: + + \printuntil } + + This light entity is used by our custom frame graph, ShadowMapFrameGraph, + and our rendering effect, AdsEffect, whose instances are created just after + the light: + + \printuntil ] + \printuntil } + + Last, we create three entities for the meshes in the scene: a trefoil knot, + a toy plane, and a ground plane. They aggregate a mesh, a transformation, + and a material that uses the AdsEffect. The toy plane and the trefoil knot + transformations are animated: + + \printuntil /^\}/ + + \section1 Specifying the Light + + We specify the Light custom entity in \e ShadowMapLight.qml. + + Again, we import the necessary modules: + + \quotefromfile shadow-map-qml/ShadowMapLight.qml + \skipto import Qt3D + \printuntil Qt3D.Render + + We then use an \l Entity type as the root element of the custom QML type. + The light is a directional spotlight that exposes as properties a position, + intensity, and a 4×4 transformation matrix: + + \printuntil matrix4x4 + + In the first rendering pass, we use the light as a camera, and therefore we + use a \l Camera entity within the light and expose it as a property: + + \printuntil /^\}/ + + \section1 Configuring the Framegraph + + In Qt 3D, the frame graph is the data-driven configuration for the rendering. + We implement the frame graph in the \e ShadowMapFrameGraph.qml file. + + In addition to the Qt 3D and Qt 3D Render modules, we also import the + Qt Quick module: + + \quotefromfile shadow-map-qml/ShadowMapFrameGraph.qml + \skipto import QtQuick + \printuntil Render 2.0 + + The code defines a \l RenderSettings node that has a tree of nodes as the + active frame graph: + + \badcode + RenderSettings { + activeFrameGraph: Viewport {...} + } + \endcode + + Any path from the leaves of this tree to the root is a viable frame graph + configuration. Filter entities can enable or disable such paths, and + selector entities can alter the configuration. + + In our case, the tree looks like this: + + \badcode + Viewport + RenderSurfaceSelector + RenderPassFilter + RenderTargetSelector + ClearBuffers + CameraSelector + RenderPassFilter + ClearBuffers + CameraSelector + \endcode + + So we have two paths from the topmost \l Viewport entity. Each path + corresponds to a pass, or phase, of the shadow map technique. The paths are + enabled and disabled using a RenderPassFilter, a node that can filter + depending on arbitrary values defined in a given render pass. In this + example, it is a string: + + \skipto RenderPassFilter + \printuntil ] + + The actual passes are not defined within the frame graph. Instead the + available passes are declared in the Materials used in the scene graph. The + frame graph is only used to select which passes are used when rendering. + + \section1 Generating the Shadow Map + + In the shadow map generation pass, we must render to an offscreen surface + (Framebuffer Object) which has a depth texture attachment. In Qt 3D, it is + represented by the RenderTarget entity, which has a number of attachments. + + In this example, we need only a depth attachment. We define it as a + RenderAttachment entity using the RenderAttachment.DepthAttachment \c type + that stores the depth and a Texture2D entity that actually configures the + exture storage used to store the depth information: + + \printuntil ] + \printuntil } + + Moreover, in this first pass, we must render using the light’s camera. + Therefore, we have a CameraSelector entity that sets the camera to the one + exported by the Light: + + \skipto CameraSelector + \printuntil } + + The second pass is more straightforward, because we simply render to the + screen using the main camera: + + \skipto RenderPassFilter + \printuntil } + \printuntil } + \printuntil } + \printuntil } + + \section1 Using Effects + + The bulk of the magic happens in the \e AdsEffect.qml file, where our main + \l Effect is defined. It implements the Ambient, Diffuse and Specular + (ADS) Lighting Model using Phong shading with the addition of shadow mapping. + + An effect contains the implementation of a particular rendering strategy. In + this example, shadow mapping using two passes: + + \quotefromfile shadow-map-qml/AdsEffect.qml + \skipto Effect + \printuntil Light + + The \c parameters list defines some default values for the effect. The + values will get mapped to shader program uniform variables, so that in the + shaders we can access their values. In this example, we expose some information from + the Light entity (position, intensity, view or projection matrix defined by + the internal camera) and the shadow map texture exposed by the frame graph: + + \skipto parameters: + \printuntil ] + + It is possible to put such parameters all the way down, from a \l Material, + to its \l Effect, to one of the effect’s \l {Technique}{Techniques} and a + \l RenderPass within a \l Technique. This allows a \l Material instance to + override defaults in an \l Effect, \l Technique or \l RenderPass. + + To adapt the implementation to different hardware or OpenGL versions, we + could use one or more \l Technique elements. In this example, only one + technique is provided, targeting OpenGL 3.2 Core, or later: + + \quotefromfile shadow-map-qml/AdsEffect.qml + \skipto techniques: + \printuntil } + + Inside the technique, we finally have the definition of our two rendering + passes. We \e tag each pass with a \l FilterKey object, matching the ones + we specified in the frame graph configuration, so that each pass will have + different rendering settings: + + \printuntil ] + + The first pass is the shadow map generation. We load a suitable set of GLSL + shaders, which are actually extremely simple. They do only MVP (Model, View, + Projection) to bring meshes from their model space into clip space (and, + remember, in this first pass, the light is the camera). The fragment shader + is totally empty, because there is no color to be generated, and the depth + will be automatically captured for us by OpenGL: + + \printuntil } + + In this first pass, we also set some custom OpenGL state in the form of a + polygon offset and depth testing mode: + + \printuntil ] + + \section1 Rendering Using Phong Shading + + The second pass is a normal forward rendering using Phong shading. The code + in the effect entity is extremely simple. We simply configure some + parameters and load a pair of shaders which will be used when drawing. + + The first part of the shadow mapping happens in the vertex shader defined in + \e ads.vert file, where we output towards the fragment shader the + coordinates of each vertex in light space: + + \quotefromfile shadow-map-qml/shaders/ads.vert + \skipto mat4( + \skipto positionInLightSpace + \printuntil ; + + Actually, the coordinates get adjusted a little to allow us to easily sample + the shadow map texture. + + The second part happens in the fragment shader defined in the \e ads.frag + file, where we sample the shadow map. If the currently processed fragment is + behind the one closest to the light, then the current fragment is in shadow + (and only gets ambient contribution). Otherwise, it gets full Phong shading: + + \quotefromfile shadow-map-qml/shaders/ads.frag + \skipto main + \printuntil } +*/ diff --git a/tests/manual/shadow-map-qml/main.cpp b/tests/manual/shadow-map-qml/main.cpp new file mode 100644 index 000000000..dbdf4610d --- /dev/null +++ b/tests/manual/shadow-map-qml/main.cpp @@ -0,0 +1,22 @@ +// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <Qt3DQuickExtras/qt3dquickwindow.h> +#include <Qt3DQuick/QQmlAspectEngine> +#include <QGuiApplication> +#include <QQmlContext> +#include <QQmlEngine> + +int main(int argc, char* argv[]) +{ + QGuiApplication app(argc, argv); + + Qt3DExtras::Quick::Qt3DQuickWindow view; + view.resize(1600, 800); + view.engine()->qmlEngine()->rootContext()->setContextProperty("_window", &view); + view.setSource(QUrl("qrc:/main.qml")); + view.show(); + + return app.exec(); +} + diff --git a/tests/manual/shadow-map-qml/main.qml b/tests/manual/shadow-map-qml/main.qml new file mode 100644 index 000000000..635557b65 --- /dev/null +++ b/tests/manual/shadow-map-qml/main.qml @@ -0,0 +1,75 @@ +// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick 2.1 as QQ2 +import Qt3D.Core 2.0 +import Qt3D.Render 2.0 +import Qt3D.Input 2.0 +import Qt3D.Extras 2.0 + +Entity { + id: sceneRoot + + Camera { + id: camera + projectionType: CameraLens.PerspectiveProjection + fieldOfView: 45 + aspectRatio: _window.width / _window.height + nearPlane: 0.1 + farPlane: 1000.0 + position: Qt.vector3d(0.0, 10.0, 20.0) + viewCenter: Qt.vector3d(0.0, 0.0, 0.0) + upVector: Qt.vector3d(0.0, 1.0, 0.0) + } + + FirstPersonCameraController { camera: camera } + + ShadowMapLight { + id: light + } + + components: [ + ShadowMapFrameGraph { + id: framegraph + viewCamera: camera + lightCamera: light.lightCamera + }, + // Event Source will be set by the Qt3DQuickWindow + InputSettings { } + ] + + + AdsEffect { + id: shadowMapEffect + + shadowTexture: framegraph.shadowTexture + light: light + } + + + // Trefoil knot entity + Trefoil { + material: AdsMaterial { + effect: shadowMapEffect + specularColor: Qt.rgba(0.5, 0.5, 0.5, 1.0) + } + } + + // Toyplane entity + Toyplane { + material: AdsMaterial { + effect: shadowMapEffect + diffuseColor: Qt.rgba(0.9, 0.5, 0.3, 1.0) + shininess: 75 + } + } + + // Plane entity + GroundPlane { + material: AdsMaterial { + effect: shadowMapEffect + diffuseColor: Qt.rgba(0.2, 0.5, 0.3, 1.0) + specularColor: Qt.rgba(0, 0, 0, 1.0) + } + } +} diff --git a/tests/manual/shadow-map-qml/shaders/ads.frag b/tests/manual/shadow-map-qml/shaders/ads.frag new file mode 100644 index 000000000..2d8318282 --- /dev/null +++ b/tests/manual/shadow-map-qml/shaders/ads.frag @@ -0,0 +1,60 @@ +// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#version 150 core + +uniform mat4 viewMatrix; + +uniform vec3 lightPosition; +uniform vec3 lightIntensity; + +uniform vec3 ka; // Ambient reflectivity +uniform vec3 kd; // Diffuse reflectivity +uniform vec3 ks; // Specular reflectivity +uniform float shininess; // Specular shininess factor + +uniform sampler2DShadow shadowMapTexture; + +in vec4 positionInLightSpace; + +in vec3 position; +in vec3 normal; + +out vec4 fragColor; + +vec3 dsModel(const in vec3 pos, const in vec3 n) +{ + // Calculate the vector from the light to the fragment + vec3 s = normalize(vec3(viewMatrix * vec4(lightPosition, 1.0)) - pos); + + // Calculate the vector from the fragment to the eye position + // (origin since this is in "eye" or "camera" space) + vec3 v = normalize(-pos); + + // Reflect the light beam using the normal at this fragment + vec3 r = reflect(-s, n); + + // Calculate the diffuse component + float diffuse = max(dot(s, n), 0.0); + + // Calculate the specular component + float specular = 0.0; + if (dot(s, n) > 0.0) + specular = pow(max(dot(r, v), 0.0), shininess); + + // Combine the diffuse and specular contributions (ambient is taken into account by the caller) + return lightIntensity * (kd * diffuse + ks * specular); +} + +void main() +{ + float shadowMapSample = textureProj(shadowMapTexture, positionInLightSpace); + + vec3 ambient = lightIntensity * ka; + + vec3 result = ambient; + if (shadowMapSample > 0) + result += dsModel(position, normalize(normal)); + + fragColor = vec4(result, 1.0); +} diff --git a/tests/manual/shadow-map-qml/shaders/ads.vert b/tests/manual/shadow-map-qml/shaders/ads.vert new file mode 100644 index 000000000..017fa0082 --- /dev/null +++ b/tests/manual/shadow-map-qml/shaders/ads.vert @@ -0,0 +1,33 @@ +// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#version 150 core + +in vec3 vertexPosition; +in vec3 vertexNormal; + +out vec4 positionInLightSpace; +out vec3 position; +out vec3 normal; + +uniform mat4 lightViewProjection; +uniform mat4 modelMatrix; +uniform mat4 modelView; +uniform mat3 modelViewNormal; +uniform mat4 mvp; + +void main() +{ + // positionInLightSpace = lightViewProjection * modelMatrix * vec4(vertexPosition, 1.0); + const mat4 shadowMatrix = mat4(0.5, 0.0, 0.0, 0.0, + 0.0, 0.5, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.5, 0.5, 0.5, 1.0); + + positionInLightSpace = shadowMatrix * lightViewProjection * modelMatrix * vec4(vertexPosition, 1.0); + + normal = normalize(modelViewNormal * vertexNormal); + position = vec3(modelView * vec4(vertexPosition, 1.0)); + + gl_Position = mvp * vec4(vertexPosition, 1.0); +} diff --git a/tests/manual/shadow-map-qml/shaders/es3/ads.frag b/tests/manual/shadow-map-qml/shaders/es3/ads.frag new file mode 100644 index 000000000..8a59268ac --- /dev/null +++ b/tests/manual/shadow-map-qml/shaders/es3/ads.frag @@ -0,0 +1,59 @@ +#version 300 es + +precision highp float; + +uniform mat4 viewMatrix; + +uniform vec3 lightPosition; +uniform vec3 lightIntensity; + +uniform vec3 ka; // Ambient reflectivity +uniform vec3 kd; // Diffuse reflectivity +uniform vec3 ks; // Specular reflectivity +uniform float shininess; // Specular shininess factor + +uniform highp sampler2DShadow shadowMapTexture; + +in vec4 positionInLightSpace; + +in vec3 position; +in vec3 normal; + +out vec4 fragColor; + +vec3 dsModel(const in vec3 pos, const in vec3 n) +{ + // Calculate the vector from the light to the fragment + vec3 s = normalize(vec3(viewMatrix * vec4(lightPosition, 1.0)) - pos); + + // Calculate the vector from the fragment to the eye position + // (origin since this is in "eye" or "camera" space) + vec3 v = normalize(-pos); + + // Reflect the light beam using the normal at this fragment + vec3 r = reflect(-s, n); + + // Calculate the diffuse component + float diffuse = max(dot(s, n), 0.0); + + // Calculate the specular component + float specular = 0.0; + if (dot(s, n) > 0.0) + specular = pow(max(dot(r, v), 0.0), shininess); + + // Combine the diffuse and specular contributions (ambient is taken into account by the caller) + return lightIntensity * (kd * diffuse + ks * specular); +} + +void main() +{ + float shadowMapSample = textureProj(shadowMapTexture, positionInLightSpace); + + vec3 ambient = lightIntensity * ka; + + vec3 result = ambient; + if (shadowMapSample > 0.0) + result += dsModel(position, normalize(normal)); + + fragColor = vec4(result, 1.0); +} diff --git a/tests/manual/shadow-map-qml/shaders/es3/ads.vert b/tests/manual/shadow-map-qml/shaders/es3/ads.vert new file mode 100644 index 000000000..9c16eb07d --- /dev/null +++ b/tests/manual/shadow-map-qml/shaders/es3/ads.vert @@ -0,0 +1,30 @@ +#version 300 es + +in vec3 vertexPosition; +in vec3 vertexNormal; + +out vec4 positionInLightSpace; +out vec3 position; +out vec3 normal; + +uniform mat4 lightViewProjection; +uniform mat4 modelMatrix; +uniform mat4 modelView; +uniform mat3 modelViewNormal; +uniform mat4 mvp; + +void main() +{ + // positionInLightSpace = lightViewProjection * modelMatrix * vec4(vertexPosition, 1.0); + const mat4 shadowMatrix = mat4(0.5, 0.0, 0.0, 0.0, + 0.0, 0.5, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.5, 0.5, 0.5, 1.0); + + positionInLightSpace = shadowMatrix * lightViewProjection * modelMatrix * vec4(vertexPosition, 1.0); + + normal = normalize(modelViewNormal * vertexNormal); + position = vec3(modelView * vec4(vertexPosition, 1.0)); + + gl_Position = mvp * vec4(vertexPosition, 1.0); +} diff --git a/tests/manual/shadow-map-qml/shaders/es3/shadowmap.frag b/tests/manual/shadow-map-qml/shaders/es3/shadowmap.frag new file mode 100644 index 000000000..93ced265c --- /dev/null +++ b/tests/manual/shadow-map-qml/shaders/es3/shadowmap.frag @@ -0,0 +1,7 @@ +#version 300 es + +precision highp float; + +void main() +{ +} diff --git a/tests/manual/shadow-map-qml/shaders/es3/shadowmap.vert b/tests/manual/shadow-map-qml/shaders/es3/shadowmap.vert new file mode 100644 index 000000000..4745c7214 --- /dev/null +++ b/tests/manual/shadow-map-qml/shaders/es3/shadowmap.vert @@ -0,0 +1,10 @@ +#version 300 es + +in vec3 vertexPosition; + +uniform mat4 mvp; + +void main() +{ + gl_Position = mvp * vec4(vertexPosition, 1.0); +} diff --git a/tests/manual/shadow-map-qml/shaders/rhi/ads.frag b/tests/manual/shadow-map-qml/shaders/rhi/ads.frag new file mode 100644 index 000000000..c30b34a78 --- /dev/null +++ b/tests/manual/shadow-map-qml/shaders/rhi/ads.frag @@ -0,0 +1,89 @@ +// Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#version 450 + +layout(location = 0) in vec4 positionInLightSpace; +layout(location = 1) in vec3 position; +layout(location = 2) in vec3 normal; + +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform qt3d_render_view_uniforms { + mat4 viewMatrix; + mat4 projectionMatrix; + mat4 viewProjectionMatrix; + mat4 inverseViewMatrix; + mat4 inverseProjectionMatrix; + mat4 inverseViewProjectionMatrix; + mat4 viewportMatrix; + mat4 inverseViewportMatrix; + vec4 textureTransformMatrix; + vec3 eyePosition; + float aspectRatio; + float gamma; + float exposure; + float time; +}; + +layout(std140, binding = 1) uniform qt3d_command_uniforms { + mat4 modelMatrix; + mat4 inverseModelMatrix; + mat4 modelView; + mat3 modelNormalMatrix; + mat4 inverseModelViewMatrix; + mat4 mvp; + mat4 inverseModelViewProjectionMatrix; + mat3 modelViewNormal; +}; + +layout(std140, binding = 2) uniform qt3d_custom_uniforms { + mat4 lightViewProjection; + vec3 lightPosition; + vec3 lightIntensity; + + vec3 ka; // Ambient reflectivity + vec3 kd; // Diffuse reflectivity + vec3 ks; // Specular reflectivity + float shininess; // Specular shininess factor +}; + +layout(binding = 3) uniform sampler2DShadow shadowMapTexture; + + +vec3 dsModel(const in vec3 pos, const in vec3 n) +{ + // Calculate the vector from the light to the fragment + vec3 s = normalize(vec3(viewMatrix * vec4(lightPosition, 1.0)) - pos); + + // Calculate the vector from the fragment to the eye position + // (origin since this is in "eye" or "camera" space) + vec3 v = normalize(-pos); + + // Reflect the light beam using the normal at this fragment + vec3 r = reflect(-s, n); + + // Calculate the diffuse component + float diffuse = max(dot(s, n), 0.0); + + // Calculate the specular component + float specular = 0.0; + if (dot(s, n) > 0.0) + specular = pow(max(dot(r, v), 0.0), shininess); + + // Combine the diffuse and specular contributions (ambient is taken into account by the caller) + return lightIntensity * (kd * diffuse + ks * specular); +} + +void main() +{ + float shadowMapSample = textureProj(shadowMapTexture, positionInLightSpace); + + vec3 ambient = lightIntensity * ka; + + vec3 result = ambient; + if (shadowMapSample > 0) + result += dsModel(position, normalize(normal)); + + fragColor = vec4(result, 1.0); +} diff --git a/tests/manual/shadow-map-qml/shaders/rhi/ads.vert b/tests/manual/shadow-map-qml/shaders/rhi/ads.vert new file mode 100644 index 000000000..c4e484883 --- /dev/null +++ b/tests/manual/shadow-map-qml/shaders/rhi/ads.vert @@ -0,0 +1,67 @@ +// Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#version 450 + +layout(location = 0) in vec3 vertexPosition; +layout(location = 1) in vec3 vertexNormal; + +layout(location = 0) out vec4 positionInLightSpace; +layout(location = 1) out vec3 position; +layout(location = 2) out vec3 normal; + + +layout(std140, binding = 0) uniform qt3d_render_view_uniforms { + mat4 viewMatrix; + mat4 projectionMatrix; + mat4 viewProjectionMatrix; + mat4 inverseViewMatrix; + mat4 inverseProjectionMatrix; + mat4 inverseViewProjectionMatrix; + mat4 viewportMatrix; + mat4 inverseViewportMatrix; + vec4 textureTransformMatrix; + vec3 eyePosition; + float aspectRatio; + float gamma; + float exposure; + float time; +}; + +layout(std140, binding = 1) uniform qt3d_command_uniforms { + mat4 modelMatrix; + mat4 inverseModelMatrix; + mat4 modelView; + mat3 modelNormalMatrix; + mat4 inverseModelViewMatrix; + mat4 mvp; + mat4 inverseModelViewProjectionMatrix; + mat3 modelViewNormal; +}; + +layout(std140, binding = 2) uniform qt3d_custom_uniforms { + mat4 lightViewProjection; + vec3 lightPosition; + vec3 lightIntensity; + + vec3 ka; // Ambient reflectivity + vec3 kd; // Diffuse reflectivity + vec3 ks; // Specular reflectivity + float shininess; // Specular shininess factor +}; + +void main() +{ + // positionInLightSpace = lightViewProjection * modelMatrix * vec4(vertexPosition, 1.0); + const mat4 shadowMatrix = mat4(0.5, 0.0, 0.0, 0.0, + 0.0, 0.5, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.5, 0.5, 0.5, 1.0); + + positionInLightSpace = shadowMatrix * lightViewProjection * modelMatrix * vec4(vertexPosition, 1.0); + + normal = normalize(modelViewNormal * vertexNormal); + position = vec3(modelView * vec4(vertexPosition, 1.0)); + + gl_Position = mvp * vec4(vertexPosition, 1.0); +} diff --git a/tests/manual/shadow-map-qml/shaders/rhi/shadowmap.frag b/tests/manual/shadow-map-qml/shaders/rhi/shadowmap.frag new file mode 100644 index 000000000..6e0b8a03a --- /dev/null +++ b/tests/manual/shadow-map-qml/shaders/rhi/shadowmap.frag @@ -0,0 +1,8 @@ +// Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#version 450 + +void main() +{ +} diff --git a/tests/manual/shadow-map-qml/shaders/rhi/shadowmap.vert b/tests/manual/shadow-map-qml/shaders/rhi/shadowmap.vert new file mode 100644 index 000000000..277819aef --- /dev/null +++ b/tests/manual/shadow-map-qml/shaders/rhi/shadowmap.vert @@ -0,0 +1,22 @@ +// Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#version 450 + +layout(location = 0) in vec3 vertexPosition; + +layout(std140, binding = 1) uniform qt3d_command_uniforms { + mat4 modelMatrix; + mat4 inverseModelMatrix; + mat4 modelView; + mat3 modelNormalMatrix; + mat4 inverseModelViewMatrix; + mat4 mvp; + mat4 inverseModelViewProjectionMatrix; + mat3 modelViewNormal; +}; + +void main() +{ + gl_Position = mvp * vec4(vertexPosition, 1.0); +} diff --git a/tests/manual/shadow-map-qml/shaders/shadowmap.frag b/tests/manual/shadow-map-qml/shaders/shadowmap.frag new file mode 100644 index 000000000..31b39e148 --- /dev/null +++ b/tests/manual/shadow-map-qml/shaders/shadowmap.frag @@ -0,0 +1,8 @@ +// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#version 150 core + +void main() +{ +} diff --git a/tests/manual/shadow-map-qml/shaders/shadowmap.vert b/tests/manual/shadow-map-qml/shaders/shadowmap.vert new file mode 100644 index 000000000..d245cd127 --- /dev/null +++ b/tests/manual/shadow-map-qml/shaders/shadowmap.vert @@ -0,0 +1,13 @@ +// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#version 150 core + +in vec3 vertexPosition; + +uniform mat4 mvp; + +void main() +{ + gl_Position = mvp * vec4(vertexPosition, 1.0); +} diff --git a/tests/manual/shadow-map-qml/shadow-map-qml.qrc b/tests/manual/shadow-map-qml/shadow-map-qml.qrc new file mode 100644 index 000000000..838766c4d --- /dev/null +++ b/tests/manual/shadow-map-qml/shadow-map-qml.qrc @@ -0,0 +1,24 @@ +<RCC> + <qresource prefix="/"> + <file>main.qml</file> + <file>AdsMaterial.qml</file> + <file>AdsEffect.qml</file> + <file>ShadowMapLight.qml</file> + <file>ShadowMapFrameGraph.qml</file> + <file>Trefoil.qml</file> + <file>Toyplane.qml</file> + <file>GroundPlane.qml</file> + <file>shaders/ads.frag</file> + <file>shaders/ads.vert</file> + <file>shaders/shadowmap.frag</file> + <file>shaders/shadowmap.vert</file> + <file>shaders/es3/ads.frag</file> + <file>shaders/es3/ads.vert</file> + <file>shaders/es3/shadowmap.frag</file> + <file>shaders/es3/shadowmap.vert</file> + <file>shaders/rhi/ads.frag</file> + <file>shaders/rhi/ads.vert</file> + <file>shaders/rhi/shadowmap.frag</file> + <file>shaders/rhi/shadowmap.vert</file> + </qresource> +</RCC> |