From 05e378b2fcbe72725b4736946d65c1290c291d25 Mon Sep 17 00:00:00 2001 From: Sean Harmer Date: Wed, 24 Jun 2015 14:05:53 +0200 Subject: Doc: Add some docs for the shadow map example Change-Id: Iea29c9f654fc57518bf3d428b30fa5955aa1230d Reviewed-by: Sean Harmer Reviewed-by: Leena Miettinen --- .../doc/images/shadowmapping-depth.png | Bin 0 -> 23712 bytes .../doc/images/shadowmapping-qt3d.png | Bin 0 -> 39159 bytes .../shadow-map-qml/doc/src/shadow-map-qml.qdoc | 240 ++++++++++++++++++++- src/core/doc/qt3dcore.qdocconf | 3 +- src/core/doc/src/qt3d-overview.qdoc | 86 +++++++- 5 files changed, 324 insertions(+), 5 deletions(-) create mode 100644 examples/qt3d/shadow-map-qml/doc/images/shadowmapping-depth.png create mode 100644 examples/qt3d/shadow-map-qml/doc/images/shadowmapping-qt3d.png diff --git a/examples/qt3d/shadow-map-qml/doc/images/shadowmapping-depth.png b/examples/qt3d/shadow-map-qml/doc/images/shadowmapping-depth.png new file mode 100644 index 000000000..d2b501412 Binary files /dev/null and b/examples/qt3d/shadow-map-qml/doc/images/shadowmapping-depth.png differ diff --git a/examples/qt3d/shadow-map-qml/doc/images/shadowmapping-qt3d.png b/examples/qt3d/shadow-map-qml/doc/images/shadowmapping-qt3d.png new file mode 100644 index 000000000..bac6ac75c Binary files /dev/null and b/examples/qt3d/shadow-map-qml/doc/images/shadowmapping-qt3d.png differ diff --git a/examples/qt3d/shadow-map-qml/doc/src/shadow-map-qml.qdoc b/examples/qt3d/shadow-map-qml/doc/src/shadow-map-qml.qdoc index 4e55da3b1..fd3377a8b 100644 --- a/examples/qt3d/shadow-map-qml/doc/src/shadow-map-qml.qdoc +++ b/examples/qt3d/shadow-map-qml/doc/src/shadow-map-qml.qdoc @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the documentation of the Qt Toolkit. @@ -29,4 +29,242 @@ \example shadow-map-qml \title Qt3D: Shadow Map QML Example \ingroup qt3d-examples-qml + + \brief A Qt3D QML application that illustrates how to render a scene in Qt3D + with shadows. + + \image shadowmapping-qt3d.png + + \e {Qt3D 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 + \printuntil Renderer + + 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 framegraph, 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 Light.qml. + + Again, we import the necessary modules: + + \quotefromfile shadow-map-qml/Light.qml + \skipto import Qt3D + \printuntil Qt3D.Renderer + + 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 Qt3D, the framegraph is the data-driven configuration for the rendering. + We implement the framegraph in the \e ShadowMapFrameGraph.qml file. + + In addition to the Qt3D and Qt3D Renderer modules, we also import the + Qt Quick module: + + \quotefromfile shadow-map-qml/ShadowMapFrameGraph.qml + \skipto import Qt3D + \printuntil QtQuick + + The code defines a \l FrameGraph entity that has a tree of entities as the + active framegraph: + + \printuntil clearColor + + Any path from the leaves of this tree to the root is a viable framegraph + configuration. Filter entities can enable or disable such paths, and + selector entities can alter the configuration. + + In our case, the tree looks like this: + + \code + Viewport + RenderPassFilter + RenderTargetSelector + ClearBuffer + CameraSelector + RenderPassFilter + ClearBuffer + CameraSelector + \endcode + + So we have two paths from the topmost \l Viewport entity. Each path + corresponds to a pass of the shadow map technique. The paths are enabled and + disabled using a RenderPassFilter, an entity that can filter depending on + arbitrary values defined in a given render pass. In this example, it is a + string: + + \printuntil ] + + The actual passes are not defined here. The framegraph simply modifies its + configuration when a given pass is rendered. + + \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 Qt3D, 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. + + \section1 Using Effects + + The bulk of the magic happens in the \e AdsEffect.qml file, where our main + \l Effect entity is defined. It implements the Ambient, Diffuse and Specular + (ADS) Lightning Model Phong shading with the addition of shadow mapped + generated shadows. + + 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 OpenGL shader program uniforms, so that in the + shaders we can access them. 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 framegraph: + + \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 Techniques. This allows a + \l Material instance to override defaults in an \l Effect or \l Technique. + The bindings array provides the same thing, except that it also allows us to + rename some parameters. In this example, it renames the \c ambient, + \c diffuse, and \c specular values defined in the material to the actual + uniform names used by the shader programs: + + \skipto bindings: + \printuntil ] + + 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 an \l Annotation entity, matching the ones + we specified in the framegraph 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/src/core/doc/qt3dcore.qdocconf b/src/core/doc/qt3dcore.qdocconf index 28f985029..923cc1345 100644 --- a/src/core/doc/qt3dcore.qdocconf +++ b/src/core/doc/qt3dcore.qdocconf @@ -48,7 +48,8 @@ exampledirs += src/snippets #excludedirs += -imagedirs += images +imagedirs += images \ + ../../../examples/qt3d/shadow-map-qml/doc/images Cpp.ignoretokens += QT3DCORE_PRIVATE_EXPORT \ QT3DINPUTSHARED_EXPORT \ diff --git a/src/core/doc/src/qt3d-overview.qdoc b/src/core/doc/src/qt3d-overview.qdoc index 74a4c2949..133f6fd14 100644 --- a/src/core/doc/src/qt3d-overview.qdoc +++ b/src/core/doc/src/qt3d-overview.qdoc @@ -61,14 +61,14 @@ \li 2D and 3D rendering for C++ and Qt Quick applications \li Meshes \li \l {Materials} - \li Shadows \li \l {Shaders} + \li \l {Shadow Mapping}{Shadow mapping} \li Ambient occlusion \li High dynamic range \li Deferred rendering \li Multitexturing - \li Instancing - \li Uniform Buffer Objects + \li \l {Instanced Rendering}{Instanced rendering} + \li \l {Uniform Buffer Objects} \endlist \section2 Materials @@ -98,6 +98,86 @@ \l {Qt3D: Shadow Map QML Example}, \l{Qt3D: Wireframe QML Example}, and \l {Qt3D: Wave QML Example}. + \section2 Shadow Mapping + + Shadows are not directly supported by OpenGL, but there are countless + techniques that can be employed to generate them. Shadow mapping is simple + to use for generating good-looking shadows, while having a very small + performance cost. + + Shadow mapping is typically implemented using a two pass rendering. In the + first pass, the shadow information is generated. In the second pass, the + scene is generated using a particular rendering technique, while at the + same time using the information gathered in the first pass to draw the + shadows. + + The idea behind shadow mapping is that only the closest fragments to the + light are lit. Fragments \e behind other fragments are occluded, and + therefore in shadow. + + Therefore, in the first pass, the scene is drawn from the point of view of + the light. The information that is stored is simply the distance of the + closest fragment in this \e {light space}. In OpenGL terms, this corresponds + to having a Framebuffer Object, or FBO, with a depth texture attached to it. + In fact, the \e {distance from the eye} is the definition of the depth, + and the default depth testing done by OpenGL will actually store only the + depth for the closest fragment. + + A color texture attachment is not even needed, because there is no need to + shade fragments, only to calculate their depth. + + The following image displays a scene with a self-shadowed plane and trefoil + knot: + + \image shadowmapping-qt3d.png + + The following image shows an exaggerated shadow map texture of the scene: + + \image shadowmapping-depth.png + + The image indicates the depth stored when rendering the scene from the light + point of view. Darker colors represent a shallow depth (that is, closer to + the camera). In this scene, the light is placed somewhere above the objects + in the scene, on the right side with respect to the main camera (compare + this with the first screenshot). This matches with the fact that the toy + plane is closer to the camera than the other objects. + + Once the shadow map has been generated, the second rendering pass is done. + In this second pass, rendering is done using the normal scene's camera. Any + effect can be used here, such as Phong shading. It is important that the + shadow map algorithm is applied in the fragment shader. That is, the + fragment that is closest to the light is drawn lit, whereas the other + fragments are drawn in shadow. + + The shadow map generated in the first pass provides the necessary + information about the distance of fragments to light. It then suffices to + remap the fragment in light space, thereby calculating its depth from the + light point of view, as well as where its coordinates are on the shadow map + texture. The shadow map texture can then be sampled at the given coordinates + and the fragment's depth can be compared with the result of the sampling. If + the fragment is further away, then it is in shadow; otherwise it is lit. + + For example code, see the \l {Qt3D: Shadow Map QML Example}. + + \section2 Instanced Rendering + + \e Instancing is a way of getting the GPU to draw many copies (instances) of + a base object that varies in some way for each copy. Often, in position, + orientation, color, material properties, scale, and so on. Qt3D provides an + API similar to the Qt Quick \l Repeater element. In this case, the delegate + is the base object and the model provides the per-instance data. So whereas + an entity with a \l Mesh component attached eventually gets transformed into + a call to glDrawElements, an entity with a instanced component will be + translated into a call to glDrawElementsInstanced. + + Instanced rendering is planned for a future release. + + \section2 Uniform Buffer Objects + + A Uniform Buffer Object (UBO) can be bound to OpenGL shader programs to make + large amounts of data readily available. Typical use cases for UBOs are for + sets of material or lighting parameters. + \section1 Configurable Renderer To combine support for both C++ and QML APIs with having a fully -- cgit v1.2.3