├── .gitignore ├── LICENSE ├── README.md ├── arrows-on-mesh ├── arrow.png ├── arrows-on-mesh.pro ├── arrows.frag ├── arrows.vert ├── main.cpp ├── main.qml ├── qml.qrc └── shaders.qrc ├── billboards ├── billboardgeometry.cpp ├── billboardgeometry.h ├── billboards.frag ├── billboards.geom ├── billboards.vert ├── fun3d-billboards.pro ├── main.cpp ├── main.qml ├── qml.qrc ├── shaders.qrc └── success-kid.png ├── edge-detection ├── EdgeMaterial.qml ├── MainMaterial.qml ├── MyScene.qml ├── PreprocessRenderTarget.qml ├── fun3d.pro ├── light.inc.frag ├── main.cpp ├── main.qml ├── phong.frag ├── phong.vert └── qml.qrc ├── instanced ├── fun3d-instanced.pro ├── instanced.frag ├── instanced.vert ├── instancedgeometry.cpp ├── instancedgeometry.h ├── light.inc.frag ├── main.cpp ├── main.qml ├── qml.qrc └── shaders.qrc ├── lines ├── drawdata.cpp ├── drawdata.h ├── fun3d-lines.pro ├── line-texture.png ├── lines.frag ├── lines.geom ├── lines.vert ├── main.cpp ├── main.qml ├── qml.qrc └── shaders.qrc ├── logdepth-bad.mp4 ├── logdepth-good.mp4 ├── logdepth ├── CMakeLists.txt ├── basic.frag ├── basic.vert ├── main.cpp └── resources.qrc ├── msaa-off.png ├── msaa-on.png ├── msaa ├── fun3d-msaa.pro ├── main.cpp ├── main.qml └── qml.qrc ├── qt3d-arrows.png ├── qt3d-billboards.png ├── qt3d-edge-detection.png ├── qt3d-instanced.png ├── qt3d-lines.png ├── qt3d-ssao.png ├── rtc-bad.mp4 ├── rtc-good.mp4 ├── rtc ├── CMakeLists.txt ├── basic.frag ├── basic.vert ├── main.cpp ├── matrix4x4.cpp ├── matrix4x4.h ├── resources.qrc ├── rtc.py ├── vector3d.cpp └── vector3d.h └── ssao ├── FinalMaterial.qml ├── MyScene.qml ├── SsaoBlurMaterial.qml ├── SsaoMaterial.qml ├── fun3d-ssao.pro ├── main.cpp ├── main.qml └── qml.qrc /.gitignore: -------------------------------------------------------------------------------- 1 | *.pro.user 2 | *.user 3 | *.o 4 | build-* 5 | build 6 | 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Martin Dobias 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Qt3D Experiments 2 | 3 | Here are few projects that make use of Qt3D framework - I have ended up creating them while working on rendering techniques for QGIS 3D. 4 | 5 | Qt3D framework is a really nice addition to Qt ecosystem, but the documentation is still quite sparse and sometimes even wrong. There are not that many code examples floating around that use Qt 3D to demonstrate various 3D rendering techniques, so this is my little contribution towards that. 6 | 7 | I have used QML as much as possible to keep the code concise. C++ was only used for bits where QML does not have the necessary bindings (yet?). 8 | 9 | ## Compiling on Ubuntu 10 | 11 | If you are using Ubuntu 18.04 or 20.10, then everything is great and the `qt3d5-dev` package contains all include files you need. But if you are on Ubuntu 18.10, 19.04, 19.10 or 20.04, that package does not contain include files for Qt3DExtras and Qt3DQuickExtras libraries. A workaround is to: 12 | 13 | 1. download source package for your system version - e.g. from here for 20.04: https://packages.ubuntu.com/source/focal/qt3d-opensource-src and unpack it 14 | 2. add to qmake `.pro` file a line to path to those missing includes, e.g. `INCLUDEPATH += /your/path/qt3d-everywhere-src-5.12.8/include` 15 | 16 | # Billboards 17 | 18 | Demonstrates billboards rendering technique - quads with constant screen size that are always facing the camera. This uses geometry shader to generate quads from points. 19 | 20 | ![](qt3d-billboards.png) 21 | 22 | # Multisample Anti-aliasing (MSAA) 23 | 24 | Shows how to turn on multisample anti-aliasing with Qt3D. We create a render target with multisample textures, render to it (with render state including `MultiSampleAntiAliasing`), and then "resolve" multisample textures with `BlitFramebuffer` framegraph node. Learn more about MSAA in [LearnOpenGL tutorial](https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing). 25 | 26 | | Anti-aliasing OFF | Anti-aliasing ON | 27 | |------|-----| 28 | | ![](msaa-off.png) | ![](msaa-on.png) | 29 | 30 | 31 | # Screen Space Ambient Occlusion (SSAO) 32 | 33 | Implementation of SSAO that is based on [John Chapman's tutorial](http://john-chapman-graphics.blogspot.com/2013/01/ssao-tutorial.html) and [LearnOpenGL tutorial](https://learnopengl.com/Advanced-Lighting/SSAO). It only uses depth buffer as the input and samples from full sphere. The tutorials also use normals (for each pixel) and therefore only use a hemisphere for sampling. But I needed this for rendering where point clouds (with no normal vectors) are used, so I went for this variant (probably lower quality). 34 | 35 | ![](qt3d-ssao.png) 36 | 37 | # Edge Detection 38 | 39 | Edge detection is done as a post-processing pass of scene rendering. In the first stage we generate depth texture and normal vectors texture which are then combined in the post-processing pass using Sobel filter. 40 | 41 | ![](qt3d-edge-detection.png) 42 | 43 | # Instanced Rendering 44 | 45 | Using instancing to render a single geometry at multiple different positions. 46 | 47 | ![](qt3d-instanced.png) 48 | 49 | # Lines 50 | 51 | Rendering of lines in 3D space with constant screen space thickness. Supports flat and miter joins. 52 | 53 | ![](qt3d-lines.png) 54 | 55 | # Arrows 56 | 57 | Drawing rotated and scaled textures of arrows on a mesh. (The end goal is that arrow angles/magnitudes of arrows would be read from another texture, not just based on world positions.) 58 | 59 | ![](qt3d-arrows.png) 60 | 61 | # Relative-to-center Rendering 62 | 63 | This code demonstrates the "relative to center" rendering technique with Qt3D. 64 | It is useful when working with large coordinates (e.g. when creating a virtual 65 | globe) without getting numerical issues (which would make objects jump around 66 | when zoomed in). The idea is that we make sure to do model-view-projection (MVP) 67 | matrix calculation in double precision, and only then convert it to single 68 | precision floats that will be used on the GPU. 69 | 70 | | Using double precision | Using single precision (without RTC) | 71 | |------|-----| 72 | | | | 73 | 74 | 75 | # Logarithmic Depth 76 | 77 | This code demonstrates logarithmic depth buffer rendering technique. 78 | It is a way to increase the precision of the depth buffer when using 79 | large depth range (e.g. when creating a virtual globe). The idea is 80 | that the fragment shader sets depth of fragments to make a better 81 | use of the range [0..1] instead of keeping the depth value that came 82 | out from the projection matrix. 83 | 84 | | Using logarithmic depth | Without logarithmic depth | 85 | |------|-----| 86 | | | | 87 | 88 | 89 | -------------------------------------------------------------------------------- /arrows-on-mesh/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wonder-sk/qt3d-experiments/eef89210f0cdee05443d62442a640c04978fc63c/arrows-on-mesh/arrow.png -------------------------------------------------------------------------------- /arrows-on-mesh/arrows-on-mesh.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | QT += 3dcore 3drender 3dinput 3dquick qml quick 3dquickextras 3dextras 3 | 4 | # The following define makes your compiler emit warnings if you use 5 | # any Qt feature that has been marked deprecated (the exact warnings 6 | # depend on your compiler). Refer to the documentation for the 7 | # deprecated API to know how to port your code away from it. 8 | DEFINES += QT_DEPRECATED_WARNINGS 9 | 10 | # You can also make your code fail to compile if it uses deprecated APIs. 11 | # In order to do so, uncomment the following line. 12 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 13 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 14 | 15 | SOURCES += \ 16 | main.cpp 17 | 18 | RESOURCES += qml.qrc \ 19 | shaders.qrc 20 | 21 | # Additional import path used to resolve QML modules in Qt Creator's code model 22 | QML_IMPORT_PATH = 23 | 24 | # Additional import path used to resolve QML modules just for Qt Quick Designer 25 | QML_DESIGNER_IMPORT_PATH = 26 | 27 | # Default rules for deployment. 28 | qnx: target.path = /tmp/$${TARGET}/bin 29 | else: unix:!android: target.path = /opt/$${TARGET}/bin 30 | !isEmpty(target.path): INSTALLS += target 31 | -------------------------------------------------------------------------------- /arrows-on-mesh/arrows.frag: -------------------------------------------------------------------------------- 1 | #version 150 core 2 | 3 | //uniform vec3 ka; // Ambient reflectivity 4 | //uniform vec3 kd; // Diffuse reflectivity 5 | //uniform vec3 ks; // Specular reflectivity 6 | //uniform float shininess; // Specular shininess factor 7 | 8 | uniform vec3 eyePosition; 9 | 10 | in vec3 worldPosition; 11 | in vec3 worldNormal; 12 | 13 | out vec4 fragColor; 14 | 15 | uniform sampler2D tex0; 16 | 17 | //#pragma include light.inc.frag 18 | 19 | void main() 20 | { 21 | //vec3 diffuseColor, specularColor; 22 | //adsModel(worldPosition, worldNormal, eyePosition, shininess, diffuseColor, specularColor); 23 | //fragColor = vec4( ka + kd * diffuseColor + ks * specularColor, 1.0 ); 24 | 25 | float cell_x = floor(worldPosition.x); 26 | float cell_y = floor(worldPosition.z); 27 | float pos_x = worldPosition.x - cell_x; 28 | float pos_y = worldPosition.z - cell_y; 29 | 30 | int num_cells = 20; 31 | 32 | // scale from [0..1] to [-1..1] 33 | pos_x = pos_x*2 - 1; 34 | pos_y = pos_y*2 - 1; 35 | 36 | // apply rotation 37 | float angle = cell_x * 3.14159 * 2 / num_cells; 38 | float x = pos_x * cos(angle) - pos_y * sin(angle); 39 | float y = pos_x * sin(angle) + pos_y * cos(angle); 40 | 41 | // apply scaling to the texture coordinates 42 | float scale = (cell_y + (num_cells/2)) / num_cells; 43 | x /= scale; 44 | y /= scale; 45 | 46 | // scale from [-1..1] back to [0..1] 47 | vec2 UV = vec2((x+1)/2, (y+1)/2); 48 | 49 | float alpha = 0.0; 50 | if (UV.x >= 0 && UV.y >= 0 && UV.x <= 1 && UV.y <= 1) 51 | { 52 | alpha = texture(tex0, UV).a; 53 | } 54 | 55 | fragColor = vec4(alpha,0.1,0.1,1.0); 56 | } 57 | -------------------------------------------------------------------------------- /arrows-on-mesh/arrows.vert: -------------------------------------------------------------------------------- 1 | #version 150 core 2 | 3 | in vec3 vertexPosition; 4 | in vec3 vertexNormal; 5 | 6 | out vec3 worldPosition; 7 | out vec3 worldNormal; 8 | 9 | uniform mat4 modelMatrix; 10 | uniform mat3 modelNormalMatrix; 11 | uniform mat4 modelViewProjection; 12 | 13 | void main() 14 | { 15 | worldNormal = normalize( modelNormalMatrix * vertexNormal ); 16 | worldPosition = vec3( modelMatrix * vec4( vertexPosition, 1.0 ) ); 17 | 18 | gl_Position = modelViewProjection * vec4( vertexPosition, 1.0 ); 19 | } 20 | -------------------------------------------------------------------------------- /arrows-on-mesh/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char* argv[]) 8 | { 9 | QGuiApplication app(argc, argv); 10 | 11 | Qt3DExtras::Quick::Qt3DQuickWindow view; 12 | view.setTitle("Arrows"); 13 | view.resize(1600, 800); 14 | view.engine()->qmlEngine()->rootContext()->setContextProperty("_window", &view); 15 | view.setSource(QUrl("qrc:/main.qml")); 16 | view.show(); 17 | 18 | return app.exec(); 19 | } 20 | -------------------------------------------------------------------------------- /arrows-on-mesh/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.1 as QQ2 2 | import Qt3D.Core 2.0 3 | import Qt3D.Render 2.0 4 | import Qt3D.Input 2.0 5 | import Qt3D.Extras 2.0 6 | 7 | Entity { 8 | 9 | components: [ 10 | rendSettings, 11 | inputSettings 12 | ] 13 | 14 | InputSettings { id: inputSettings } 15 | 16 | RenderSettings { 17 | id: rendSettings 18 | activeFrameGraph: RenderSurfaceSelector { 19 | Viewport { 20 | normalizedRect: Qt.rect(0,0,1,1) 21 | CameraSelector { 22 | camera: camera 23 | ClearBuffers { 24 | buffers: ClearBuffers.ColorDepthBuffer 25 | } 26 | } 27 | } 28 | } 29 | 30 | } 31 | 32 | Camera { 33 | id: camera 34 | projectionType: CameraLens.PerspectiveProjection 35 | fieldOfView: 45 36 | aspectRatio: _window.width / _window.height 37 | nearPlane: 0.1 38 | farPlane: 100.0 39 | position: Qt.vector3d(0.0, 20.0, 15.0) 40 | viewCenter: Qt.vector3d(0.0, 0.0, 0.0) 41 | upVector: Qt.vector3d(0.0, 1.0, 0.0) 42 | } 43 | 44 | FirstPersonCameraController { camera: camera } 45 | 46 | Entity { 47 | PlaneMesh { 48 | id: pm 49 | width: 20 50 | height: 20 51 | } 52 | /* 53 | PhongMaterial { 54 | id: pmm 55 | ambient: Qt.rgba(0.0,0.0,0.7,1) 56 | }*/ 57 | 58 | Material { 59 | id: pmm 60 | 61 | parameters: [ 62 | Parameter { name: "tex0"; value: txt } 63 | ] 64 | 65 | Texture2D { 66 | id : txt 67 | generateMipMaps : false 68 | magnificationFilter : Texture.Linear 69 | minificationFilter : Texture.Linear 70 | textureImages: [ 71 | TextureImage { 72 | source: "qrc:/arrow.png" 73 | } 74 | ] 75 | } 76 | 77 | effect: Effect { 78 | techniques: Technique { 79 | graphicsApiFilter { api: GraphicsApiFilter.OpenGL; profile: GraphicsApiFilter.CoreProfile; majorVersion: 3; minorVersion: 1 } 80 | renderPasses: [ 81 | RenderPass { 82 | shaderProgram: ShaderProgram { 83 | vertexShaderCode: loadSource("qrc:/shaders/arrows.vert") 84 | fragmentShaderCode: loadSource("qrc:/shaders/arrows.frag") 85 | } 86 | } 87 | ] 88 | } 89 | } 90 | } 91 | 92 | components: [ pm, pmm ] 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /arrows-on-mesh/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | arrow.png 5 | 6 | 7 | -------------------------------------------------------------------------------- /arrows-on-mesh/shaders.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | arrows.frag 4 | arrows.vert 5 | 6 | 7 | -------------------------------------------------------------------------------- /billboards/billboardgeometry.cpp: -------------------------------------------------------------------------------- 1 | #include "billboardgeometry.h" 2 | 3 | #include 4 | 5 | 6 | BillboardGeometry::BillboardGeometry( Qt3DCore::QNode *parent ) 7 | : Qt3DRender::QGeometry( parent ) 8 | , mPositionAttribute( new Qt3DRender::QAttribute( this ) ) 9 | , mVertexBuffer( new Qt3DRender::QBuffer( Qt3DRender::QBuffer::VertexBuffer, this ) ) 10 | { 11 | 12 | mPositionAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute ); 13 | mPositionAttribute->setBuffer( mVertexBuffer ); 14 | mPositionAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float ); 15 | mPositionAttribute->setVertexSize( 3 ); 16 | mPositionAttribute->setName( Qt3DRender::QAttribute::defaultPositionAttributeName() ); 17 | 18 | addAttribute( mPositionAttribute ); 19 | 20 | } 21 | 22 | int BillboardGeometry::count() 23 | { 24 | return mVertexCount; 25 | } 26 | 27 | void BillboardGeometry::setPoints(const QVector &vertices) 28 | { 29 | QByteArray vertexBufferData; 30 | vertexBufferData.resize( vertices.size() * 3 * sizeof( float ) ); 31 | float *rawVertexArray = reinterpret_cast( vertexBufferData.data() ); 32 | int idx = 0; 33 | for ( const auto &v : vertices ) 34 | { 35 | rawVertexArray[idx++] = v.x(); 36 | rawVertexArray[idx++] = v.y(); 37 | rawVertexArray[idx++] = v.z(); 38 | } 39 | 40 | mVertexCount = vertices.count(); 41 | mVertexBuffer->setData( vertexBufferData ); 42 | 43 | emit countChanged(mVertexCount); 44 | } 45 | -------------------------------------------------------------------------------- /billboards/billboardgeometry.h: -------------------------------------------------------------------------------- 1 | #ifndef BILLBOARDGEOMETRY_H 2 | #define BILLBOARDGEOMETRY_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | 10 | class BillboardGeometry : public Qt3DRender::QGeometry 11 | { 12 | Q_OBJECT 13 | 14 | Q_PROPERTY(int count READ count NOTIFY countChanged) 15 | 16 | public: 17 | BillboardGeometry( Qt3DCore::QNode *parent = nullptr ); 18 | 19 | void setPoints( const QVector &vertices ); 20 | 21 | int count(); 22 | 23 | signals: 24 | void countChanged(int count); 25 | 26 | private: 27 | Qt3DRender::QAttribute *mPositionAttribute = nullptr; 28 | Qt3DRender::QBuffer *mVertexBuffer = nullptr; 29 | int mVertexCount = 0; 30 | }; 31 | 32 | #endif // BILLBOARDGEOMETRY_H 33 | -------------------------------------------------------------------------------- /billboards/billboards.frag: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | uniform sampler2D tex0; 4 | 5 | in vec2 UV; 6 | 7 | out vec4 color; 8 | 9 | void main(void) 10 | { 11 | //color = vec4(0.5,0.1,0.1,1); 12 | color = texture(tex0, UV); 13 | } 14 | -------------------------------------------------------------------------------- /billboards/billboards.geom: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | layout (points) in; 4 | layout (triangle_strip, max_vertices = 4) out; 5 | 6 | uniform mat4 modelViewProjection; 7 | 8 | uniform vec2 BB_SIZE; // billboard size in pixels 9 | uniform vec2 WIN_SCALE; // the size of the viewport in pixels 10 | 11 | out vec2 UV; 12 | 13 | 14 | void main (void) 15 | { 16 | 17 | vec4 P = gl_in[0].gl_Position; 18 | P /= P.w; 19 | 20 | //vec2 size = vec2(0.5,0.5); 21 | vec2 size = BB_SIZE / WIN_SCALE; 22 | 23 | gl_Position = P; 24 | gl_Position.xy += vec2(-0.5,-0.5) * size; 25 | UV = vec2(0,0); 26 | EmitVertex(); 27 | 28 | gl_Position = P; 29 | gl_Position.xy += vec2(0.5,-0.5) * size; 30 | UV = vec2(1,0); 31 | EmitVertex(); 32 | 33 | gl_Position = P; 34 | gl_Position.xy += vec2(-0.5,+0.5) * size; 35 | UV = vec2(0,1); 36 | EmitVertex(); 37 | 38 | gl_Position = P; 39 | gl_Position.xy += vec2(+0.5,+0.5) * size; 40 | UV = vec2(1,1); 41 | EmitVertex(); 42 | 43 | EndPrimitive(); 44 | } 45 | -------------------------------------------------------------------------------- /billboards/billboards.vert: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | uniform mat4 modelViewProjection; 4 | 5 | in vec3 vertexPosition; 6 | 7 | void main(void) 8 | { 9 | gl_Position = modelViewProjection * vec4(vertexPosition, 1); 10 | } 11 | -------------------------------------------------------------------------------- /billboards/fun3d-billboards.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | QT += 3dcore 3drender 3dinput 3dquick qml quick 3dquickextras 3dextras 3 | 4 | # The following define makes your compiler emit warnings if you use 5 | # any Qt feature that has been marked deprecated (the exact warnings 6 | # depend on your compiler). Refer to the documentation for the 7 | # deprecated API to know how to port your code away from it. 8 | DEFINES += QT_DEPRECATED_WARNINGS 9 | 10 | # You can also make your code fail to compile if it uses deprecated APIs. 11 | # In order to do so, uncomment the following line. 12 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 13 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 14 | 15 | SOURCES += \ 16 | main.cpp \ 17 | billboardgeometry.cpp 18 | 19 | RESOURCES += qml.qrc \ 20 | shaders.qrc 21 | 22 | # Additional import path used to resolve QML modules in Qt Creator's code model 23 | QML_IMPORT_PATH = 24 | 25 | # Additional import path used to resolve QML modules just for Qt Quick Designer 26 | QML_DESIGNER_IMPORT_PATH = 27 | 28 | # Default rules for deployment. 29 | qnx: target.path = /tmp/$${TARGET}/bin 30 | else: unix:!android: target.path = /opt/$${TARGET}/bin 31 | !isEmpty(target.path): INSTALLS += target 32 | 33 | HEADERS += \ 34 | billboardgeometry.h 35 | -------------------------------------------------------------------------------- /billboards/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "billboardgeometry.h" 8 | 9 | int main(int argc, char* argv[]) 10 | { 11 | QGuiApplication app(argc, argv); 12 | 13 | QVector pos; 14 | pos << QVector3D(1, 1, 0); 15 | pos << QVector3D(-1, 2, 8); 16 | pos << QVector3D(1, 1, 7); 17 | pos << QVector3D(0, 0, 4); 18 | 19 | BillboardGeometry bbg; 20 | bbg.setPoints(pos); 21 | 22 | Qt3DExtras::Quick::Qt3DQuickWindow view; 23 | view.setTitle("Billboards"); 24 | view.resize(1600, 800); 25 | view.engine()->qmlEngine()->rootContext()->setContextProperty("_window", &view); 26 | view.engine()->qmlEngine()->rootContext()->setContextProperty("_bbg", &bbg); 27 | view.setSource(QUrl("qrc:/main.qml")); 28 | view.show(); 29 | 30 | return app.exec(); 31 | } 32 | -------------------------------------------------------------------------------- /billboards/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.1 as QQ2 2 | import Qt3D.Core 2.0 3 | import Qt3D.Render 2.0 4 | import Qt3D.Input 2.0 5 | import Qt3D.Extras 2.0 6 | 7 | Entity { 8 | 9 | components: [ 10 | rendSettings, 11 | inputSettings 12 | ] 13 | 14 | InputSettings { id: inputSettings } 15 | 16 | RenderSettings { 17 | id: rendSettings 18 | activeFrameGraph: RenderSurfaceSelector { 19 | Viewport { 20 | normalizedRect: Qt.rect(0,0,1,1) 21 | CameraSelector { 22 | camera: camera 23 | ClearBuffers { 24 | buffers: ClearBuffers.ColorDepthBuffer 25 | } 26 | } 27 | } 28 | } 29 | 30 | } 31 | 32 | Camera { 33 | id: camera 34 | projectionType: CameraLens.PerspectiveProjection 35 | fieldOfView: 45 36 | aspectRatio: _window.width / _window.height 37 | nearPlane: 0.1 38 | farPlane: 100.0 39 | position: Qt.vector3d(0.0, 10.0, 20.0) 40 | viewCenter: Qt.vector3d(0.0, 0.0, 0.0) 41 | upVector: Qt.vector3d(0.0, 1.0, 0.0) 42 | } 43 | 44 | FirstPersonCameraController { camera: camera } 45 | 46 | Entity { 47 | PlaneMesh { 48 | id: pm 49 | width: 20 50 | height: 20 51 | } 52 | PhongMaterial { 53 | id: pmm 54 | ambient: Qt.rgba(0.0,0.0,0.7,1) 55 | } 56 | components: [ pm, pmm ] 57 | } 58 | 59 | 60 | Entity { 61 | GeometryRenderer { 62 | id: gr 63 | primitiveType: GeometryRenderer.Points 64 | vertexCount: _bbg.count 65 | geometry: _bbg 66 | } 67 | 68 | Transform { 69 | id: trr 70 | translation: Qt.vector3d(0,1.5,0) 71 | } 72 | 73 | Material { 74 | id: grm 75 | 76 | parameters: [ 77 | Parameter { name: "tex0"; value: txt }, 78 | Parameter { name: "WIN_SCALE"; value: Qt.size(_window.width,_window.height) }, 79 | Parameter { name: "BB_SIZE"; value: Qt.size(100, 100) } 80 | ] 81 | 82 | Texture2D { 83 | id : txt 84 | generateMipMaps : false 85 | magnificationFilter : Texture.Linear 86 | minificationFilter : Texture.Linear 87 | textureImages: [ 88 | TextureImage { 89 | source: "qrc:/success-kid.png" 90 | } 91 | ] 92 | } 93 | 94 | effect: Effect { 95 | techniques: Technique { 96 | graphicsApiFilter { api: GraphicsApiFilter.OpenGL; profile: GraphicsApiFilter.CoreProfile; majorVersion: 3; minorVersion: 1 } 97 | renderPasses: [ 98 | RenderPass { 99 | shaderProgram: ShaderProgram { 100 | vertexShaderCode: loadSource("qrc:/shaders/billboards.vert") 101 | geometryShaderCode: loadSource("qrc:/shaders/billboards.geom") 102 | fragmentShaderCode: loadSource("qrc:/shaders/billboards.frag") 103 | } 104 | } 105 | ] 106 | } 107 | } 108 | } 109 | 110 | components: [ gr, grm, trr ] 111 | } 112 | 113 | 114 | Entity { 115 | PhongMaterial { 116 | id: redMat 117 | ambient: "red" 118 | } 119 | SphereMesh { 120 | id: sphereMesh 121 | } 122 | Transform { 123 | id: sphereTransform 124 | translation: Qt.vector3d(0,5,0) 125 | } 126 | 127 | components: [ redMat, sphereMesh, sphereTransform ] 128 | } 129 | 130 | Entity { 131 | PhongMaterial { 132 | id: grayMat 133 | ambient: "gray" 134 | } 135 | CuboidMesh { 136 | id: cubeMesh 137 | } 138 | Transform { 139 | id: cubeTransform 140 | translation: Qt.vector3d(2,2,5) 141 | scale: 2 142 | } 143 | 144 | components: [ grayMat, cubeMesh, cubeTransform ] 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /billboards/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | success-kid.png 5 | 6 | 7 | -------------------------------------------------------------------------------- /billboards/shaders.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | billboards.frag 4 | billboards.vert 5 | billboards.geom 6 | 7 | 8 | -------------------------------------------------------------------------------- /billboards/success-kid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wonder-sk/qt3d-experiments/eef89210f0cdee05443d62442a640c04978fc63c/billboards/success-kid.png -------------------------------------------------------------------------------- /edge-detection/EdgeMaterial.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.1 as QQ2 2 | import Qt3D.Core 2.0 3 | import Qt3D.Render 2.0 4 | import Qt3D.Input 2.0 5 | import Qt3D.Extras 2.0 6 | 7 | 8 | Material { 9 | 10 | property Texture2D textureColor 11 | property Texture2D textureNormal 12 | property Texture2D textureDepth 13 | property real cameraZNear 14 | property real cameraZFar 15 | property color edgeColor: "black" 16 | 17 | parameters: [ 18 | Parameter { name: "nor"; value: textureNormal }, 19 | Parameter { name: "dep"; value: textureDepth }, 20 | Parameter { name: "zNear"; value: cameraZNear }, 21 | Parameter { name: "zFar"; value: cameraZFar }, 22 | Parameter { name: "edgeColor"; value: edgeColor } 23 | ] 24 | effect: Effect { 25 | techniques: Technique { 26 | graphicsApiFilter { api: GraphicsApiFilter.OpenGL; profile: GraphicsApiFilter.CoreProfile; majorVersion: 3; minorVersion: 1 } 27 | renderPasses: [ 28 | RenderPass { 29 | shaderProgram: ShaderProgram { 30 | id: sp 31 | vertexShaderCode: " 32 | #version 140 33 | 34 | in vec3 vertexPosition; 35 | 36 | uniform mat4 modelViewProjection; 37 | 38 | void main() 39 | { 40 | gl_Position = modelViewProjection * vec4( vertexPosition, 1.0 ); 41 | }" 42 | fragmentShaderCode: " 43 | #version 140 44 | 45 | // name must be equal to parameter of the Material 46 | uniform sampler2D nor; 47 | uniform sampler2D dep; 48 | 49 | //in vec4 gl_FragCoord; // in window space (not normalized coords) 50 | out vec4 fragColor; 51 | 52 | uniform float zNear; 53 | uniform float zFar; 54 | uniform vec4 edgeColor; 55 | 56 | // result suitable for assigning to gl_FragDepth 57 | float depthSample(float linearDepth) 58 | { 59 | float nonLinearDepth = (zFar + zNear - 2.0 * zNear * zFar / linearDepth) / (zFar - zNear); 60 | nonLinearDepth = (nonLinearDepth + 1.0) / 2.0; 61 | return nonLinearDepth; 62 | } 63 | 64 | float depthSampleToDepth(float depthSample) 65 | { 66 | depthSample = 2.0 * depthSample - 1.0; 67 | float zLinear = 2.0 * zNear * zFar / (zFar + zNear - depthSample * (zFar - zNear)); 68 | return zLinear; 69 | } 70 | 71 | float get_info_depth(sampler2D s) 72 | { 73 | ivec2 baseCoord = ivec2(gl_FragCoord); 74 | float s00 = depthSampleToDepth(texelFetch(s, baseCoord + ivec2(-1,-1), 0).r); 75 | float s01 = depthSampleToDepth(texelFetch(s, baseCoord + ivec2(-1,0), 0).r); 76 | float s02 = depthSampleToDepth(texelFetch(s, baseCoord + ivec2(-1,1), 0).r); 77 | float s10 = depthSampleToDepth(texelFetch(s, baseCoord + ivec2(0,-1), 0).r); 78 | float s12 = depthSampleToDepth(texelFetch(s, baseCoord + ivec2(0,1), 0).r); 79 | float s20 = depthSampleToDepth(texelFetch(s, baseCoord + ivec2(1,-1), 0).r); 80 | float s21 = depthSampleToDepth(texelFetch(s, baseCoord + ivec2(1,0), 0).r); 81 | float s22 = depthSampleToDepth(texelFetch(s, baseCoord + ivec2(1,1), 0).r); 82 | 83 | float gx = -1*s00 + 1*s02 + -2*s10 + 2*s12 + (-1)*s20 + 1*s22; 84 | float gy = -1*s00 + 1*s20 + -2*s01 + 2*s21 + (-1)*s02 + 1*s22; 85 | float g = sqrt(pow(gx, 2.0)+pow(gy, 2.0)); 86 | return g; 87 | } 88 | 89 | float get_info_norm(sampler2D s) 90 | { 91 | ivec2 baseCoord = ivec2(gl_FragCoord); 92 | vec3 s00 = texelFetch(s, baseCoord + ivec2(-1,-1), 0).rgb; 93 | vec3 s01 = texelFetch(s, baseCoord + ivec2(-1,0), 0).rgb; 94 | vec3 s02 = texelFetch(s, baseCoord + ivec2(-1,1), 0).rgb; 95 | vec3 s10 = texelFetch(s, baseCoord + ivec2(0,-1), 0).rgb; 96 | vec3 s12 = texelFetch(s, baseCoord + ivec2(0,1), 0).rgb; 97 | vec3 s20 = texelFetch(s, baseCoord + ivec2(1,-1), 0).rgb; 98 | vec3 s21 = texelFetch(s, baseCoord + ivec2(1,0), 0).rgb; 99 | vec3 s22 = texelFetch(s, baseCoord + ivec2(1,1), 0).rgb; 100 | 101 | vec3 gx = -1*s00 + 1*s02 + -2*s10 + 2*s12 + (-1)*s20 + 1*s22; 102 | vec3 gy = -1*s00 + 1*s20 + -2*s01 + 2*s21 + (-1)*s02 + 1*s22; 103 | float g = sqrt(pow(length(gx), 2.0)+pow(length(gy), 2.0)); 104 | return g; 105 | } 106 | 107 | void main() 108 | { 109 | // for testing to display raw data 110 | //fragColor = vec4(texture(nor, texCoord).rgb, 1.0); 111 | //fragColor = vec4(texture(dep, texCoord).rrr, 1.0); 112 | 113 | // apply Sobel filter on both normals and depths and figure out strength of edges by combining those 114 | float edge_depth = get_info_depth(dep) / 2; 115 | float edge_norm = get_info_norm(nor) / 4; 116 | float edge_strength = edge_depth + edge_norm; 117 | if (edge_strength < 0.5) 118 | discard; 119 | fragColor = edgeColor; 120 | 121 | // use the same depth as was used by the original rendering 122 | // (this makes sure that if a part of our post-processed geometry is behind 123 | // an object that's not being post-processed, that part will be ignored 124 | // and no edges will be drawn) 125 | gl_FragDepth = texelFetch(dep, ivec2(gl_FragCoord), 0 ).r; 126 | } 127 | " 128 | 129 | onLogChanged: { 130 | console.warn("status", sp.status) 131 | console.log(sp.log) 132 | } 133 | } 134 | } 135 | ] 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /edge-detection/MainMaterial.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.1 as QQ2 2 | import Qt3D.Core 2.0 3 | import Qt3D.Render 2.0 4 | import Qt3D.Input 2.0 5 | import Qt3D.Extras 2.0 6 | 7 | // material that renders normals 8 | Material { 9 | id: normalMaterial 10 | 11 | property alias ambient: paramKA.value 12 | 13 | parameters: [ 14 | Parameter { id: paramKA; name: "ka"; value: Qt.rgba(0.05, 0.05, 0.05, 1.0) }, 15 | Parameter { name: "kd"; value: Qt.rgba(0.7, 0.7, 0.7, 1.0) }, 16 | Parameter { name: "ks"; value: Qt.rgba(0.01, 0.01, 0.01, 1.0) }, 17 | Parameter { name: "shininess"; value: 150. } 18 | ] 19 | 20 | effect: Effect { 21 | techniques: Technique { 22 | graphicsApiFilter { api: GraphicsApiFilter.OpenGL; profile: GraphicsApiFilter.CoreProfile; majorVersion: 3; minorVersion: 1 } 23 | renderPasses: [ 24 | RenderPass { 25 | filterKeys: [ FilterKey { name: "name"; value: "prepass" } ] 26 | shaderProgram: ShaderProgram { 27 | vertexShaderCode: " 28 | #version 140 29 | 30 | in vec3 vertexPosition; 31 | in vec3 vertexNormal; 32 | uniform mat4 modelViewProjection; 33 | 34 | out vec3 norm; 35 | 36 | void main() 37 | { 38 | gl_Position = modelViewProjection * vec4( vertexPosition, 1.0 ); 39 | norm = vertexNormal; 40 | }" 41 | fragmentShaderCode: " 42 | #version 140 43 | 44 | in vec3 norm; 45 | out vec4 normColor; 46 | 47 | void main() 48 | { 49 | normColor = vec4(norm, 1); 50 | } 51 | " 52 | } 53 | }, 54 | RenderPass { 55 | filterKeys: [ FilterKey { name: "name"; value: "forward" } ] 56 | shaderProgram: ShaderProgram { 57 | vertexShaderCode: loadSource("qrc:/phong.vert") 58 | fragmentShaderCode: loadSource("qrc:/phong.frag") 59 | } 60 | } 61 | ] 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /edge-detection/MyScene.qml: -------------------------------------------------------------------------------- 1 | import Qt3D.Core 2.0 2 | import Qt3D.Render 2.0 3 | import Qt3D.Input 2.0 4 | import Qt3D.Extras 2.0 5 | 6 | Entity { 7 | id: sceneRoot 8 | 9 | property var layerPrePass // only entities in this layer will be considered for edge detection 10 | 11 | Entity { 12 | 13 | SphereMesh { 14 | id: sm 15 | } 16 | MainMaterial { 17 | id: smm 18 | ambient: "red" 19 | } 20 | Transform { 21 | id: smt 22 | scale: 0.5 23 | } 24 | 25 | components: [ sm, smm, smt, layerPrePass ] 26 | } 27 | 28 | Entity { 29 | PlaneMesh { 30 | id: pm 31 | width: 20 32 | height: 20 33 | } 34 | MainMaterial { 35 | id: pmm 36 | ambient: Qt.rgba(0.3,0.3,0.3,1) 37 | } 38 | components: [ pm, pmm ] //, layerPrePass ] 39 | } 40 | 41 | 42 | Entity { 43 | CuboidMesh { 44 | id: cm 45 | } 46 | MainMaterial { 47 | id: cmm 48 | ambient: "yellow" 49 | } 50 | Transform { 51 | id: cmt 52 | translation: Qt.vector3d(0.8,0,0) 53 | } 54 | 55 | components: [ cm, cmm, cmt, layerPrePass ] 56 | } 57 | 58 | NodeInstantiator { 59 | 60 | model: 200 61 | 62 | delegate: Entity { 63 | property alias tform: tform 64 | property color color: "green" 65 | CuboidMesh { id: mesh } 66 | MainMaterial { id: material; ambient: color } 67 | Transform { id: tform; } 68 | components: [ mesh, material, tform, layerPrePass ] 69 | } 70 | 71 | onObjectAdded: { 72 | object.tform.translation = Qt.vector3d(Math.random()*10-5, Math.random()*10-5, Math.random()*10-5) 73 | object.color = ["green", "red", "yellow", "blue"][index % 4] 74 | } 75 | } 76 | 77 | 78 | } 79 | 80 | -------------------------------------------------------------------------------- /edge-detection/PreprocessRenderTarget.qml: -------------------------------------------------------------------------------- 1 | 2 | import QtQuick 2.1 as QQ2 3 | import Qt3D.Core 2.0 4 | import Qt3D.Render 2.0 5 | import Qt3D.Input 2.0 6 | import Qt3D.Extras 2.0 7 | 8 | 9 | RenderTarget { 10 | id: rt 11 | 12 | property int w: 1024 13 | property int h: 1024 14 | 15 | property alias textureNormal: normalAttachment 16 | property alias textureDepth: depthAttachment 17 | 18 | attachments: [ 19 | RenderTargetOutput { 20 | objectName : "normal" 21 | attachmentPoint : RenderTargetOutput.Color0 22 | texture : Texture2D { 23 | id : normalAttachment 24 | width : rt.w 25 | height : rt.h 26 | format : Texture.RGB16F 27 | generateMipMaps : false 28 | magnificationFilter : Texture.Linear 29 | minificationFilter : Texture.Linear 30 | wrapMode { 31 | x: WrapMode.ClampToEdge 32 | y: WrapMode.ClampToEdge 33 | } 34 | } 35 | }, 36 | RenderTargetOutput { 37 | objectName : "depth" 38 | attachmentPoint : RenderTargetOutput.Depth 39 | texture : Texture2D { 40 | id : depthAttachment 41 | width : rt.w 42 | height : rt.h 43 | 44 | format: Texture.DepthFormat 45 | // TODO: needed? 46 | comparisonFunction: Texture.CompareLessEqual 47 | comparisonMode: Texture.CompareRefToTexture 48 | 49 | generateMipMaps : false 50 | magnificationFilter : Texture.Linear 51 | minificationFilter : Texture.Linear 52 | wrapMode { 53 | x: WrapMode.ClampToEdge 54 | y: WrapMode.ClampToEdge 55 | } 56 | } 57 | } 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /edge-detection/fun3d.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | QT += 3dcore 3drender 3dinput 3dquick qml quick 3dquickextras 3dextras 3 | 4 | #CONFIG += c++11 5 | 6 | # The following define makes your compiler emit warnings if you use 7 | # any Qt feature that has been marked deprecated (the exact warnings 8 | # depend on your compiler). Refer to the documentation for the 9 | # deprecated API to know how to port your code away from it. 10 | #DEFINES += QT_DEPRECATED_WARNINGS 11 | 12 | # You can also make your code fail to compile if it uses deprecated APIs. 13 | # In order to do so, uncomment the following line. 14 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 15 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 16 | 17 | SOURCES += \ 18 | main.cpp 19 | 20 | RESOURCES += qml.qrc 21 | 22 | OTHER_FILES += \ 23 | main.qml 24 | 25 | # Additional import path used to resolve QML modules in Qt Creator's code model 26 | #QML_IMPORT_PATH = 27 | 28 | # Additional import path used to resolve QML modules just for Qt Quick Designer 29 | #QML_DESIGNER_IMPORT_PATH = 30 | 31 | # Default rules for deployment. 32 | #qnx: target.path = /tmp/$${TARGET}/bin 33 | #else: unix:!android: target.path = /opt/$${TARGET}/bin 34 | #!isEmpty(target.path): INSTALLS += target 35 | -------------------------------------------------------------------------------- /edge-detection/light.inc.frag: -------------------------------------------------------------------------------- 1 | const int MAX_LIGHTS = 8; 2 | const int TYPE_POINT = 0; 3 | const int TYPE_DIRECTIONAL = 1; 4 | const int TYPE_SPOT = 2; 5 | struct Light { 6 | int type; 7 | vec3 position; 8 | vec3 color; 9 | float intensity; 10 | vec3 direction; 11 | float constantAttenuation; 12 | float linearAttenuation; 13 | float quadraticAttenuation; 14 | float cutOffAngle; 15 | }; 16 | uniform Light lights[MAX_LIGHTS]; 17 | uniform int lightCount; 18 | 19 | // Pre-convolved environment maps 20 | struct EnvironmentLight { 21 | samplerCube irradiance; // For diffuse contribution 22 | samplerCube specular; // For specular contribution 23 | }; 24 | uniform EnvironmentLight envLight; 25 | uniform int envLightCount = 0; 26 | 27 | void adsModelNormalMapped(const in vec3 worldPos, 28 | const in vec3 tsNormal, 29 | const in vec3 worldEye, 30 | const in float shininess, 31 | const in mat3 tangentMatrix, 32 | out vec3 diffuseColor, 33 | out vec3 specularColor) 34 | { 35 | diffuseColor = vec3(0.0); 36 | specularColor = vec3(0.0); 37 | 38 | // We perform all work in tangent space, so we convert quantities from world space 39 | vec3 tsPos = tangentMatrix * worldPos; 40 | vec3 n = normalize(tsNormal); 41 | vec3 v = normalize(tangentMatrix * (worldEye - worldPos)); 42 | vec3 s = vec3(0.0); 43 | 44 | for (int i = 0; i < lightCount; ++i) { 45 | float att = 1.0; 46 | float sDotN = 0.0; 47 | 48 | if (lights[i].type != TYPE_DIRECTIONAL) { 49 | // Point and Spot lights 50 | 51 | // Transform the light position from world to tangent space 52 | vec3 tsLightPos = tangentMatrix * lights[i].position; 53 | vec3 sUnnormalized = tsLightPos - tsPos; 54 | s = normalize(sUnnormalized); // Light direction in tangent space 55 | 56 | // Calculate the attenuation factor 57 | sDotN = dot(s, n); 58 | if (sDotN > 0.0) { 59 | if (lights[i].constantAttenuation != 0.0 60 | || lights[i].linearAttenuation != 0.0 61 | || lights[i].quadraticAttenuation != 0.0) { 62 | float dist = length(sUnnormalized); 63 | att = 1.0 / (lights[i].constantAttenuation + 64 | lights[i].linearAttenuation * dist + 65 | lights[i].quadraticAttenuation * dist * dist); 66 | } 67 | 68 | // The light direction is in world space, convert to tangent space 69 | if (lights[i].type == TYPE_SPOT) { 70 | // Check if fragment is inside or outside of the spot light cone 71 | vec3 tsLightDirection = tangentMatrix * lights[i].direction; 72 | if (degrees(acos(dot(-s, tsLightDirection))) > lights[i].cutOffAngle) 73 | sDotN = 0.0; 74 | } 75 | } 76 | } else { 77 | // Directional lights 78 | // The light direction is in world space, convert to tangent space 79 | s = normalize(tangentMatrix * -lights[i].direction); 80 | sDotN = dot(s, n); 81 | } 82 | 83 | // Calculate the diffuse factor 84 | float diffuse = max(sDotN, 0.0); 85 | 86 | // Calculate the specular factor 87 | float specular = 0.0; 88 | if (diffuse > 0.0 && shininess > 0.0) { 89 | float normFactor = (shininess + 2.0) / 2.0; 90 | vec3 r = reflect(-s, n); // Reflection direction in tangent space 91 | specular = normFactor * pow(max(dot(r, v), 0.0), shininess); 92 | } 93 | 94 | // Accumulate the diffuse and specular contributions 95 | diffuseColor += att * lights[i].intensity * diffuse * lights[i].color; 96 | specularColor += att * lights[i].intensity * specular * lights[i].color; 97 | } 98 | } 99 | 100 | void adsModel(const in vec3 worldPos, 101 | const in vec3 worldNormal, 102 | const in vec3 worldEye, 103 | const in float shininess, 104 | out vec3 diffuseColor, 105 | out vec3 specularColor) 106 | { 107 | diffuseColor = vec3(0.0); 108 | specularColor = vec3(0.0); 109 | 110 | // We perform all work in world space 111 | vec3 n = normalize(worldNormal); 112 | vec3 v = normalize(worldEye - worldPos); 113 | vec3 s = vec3(0.0); 114 | 115 | for (int i = 0; i < lightCount; ++i) { 116 | float att = 1.0; 117 | float sDotN = 0.0; 118 | 119 | if (lights[i].type != TYPE_DIRECTIONAL) { 120 | // Point and Spot lights 121 | 122 | // Light position is already in world space 123 | vec3 sUnnormalized = lights[i].position - worldPos; 124 | s = normalize(sUnnormalized); // Light direction 125 | 126 | // Calculate the attenuation factor 127 | sDotN = dot(s, n); 128 | if (sDotN > 0.0) { 129 | if (lights[i].constantAttenuation != 0.0 130 | || lights[i].linearAttenuation != 0.0 131 | || lights[i].quadraticAttenuation != 0.0) { 132 | float dist = length(sUnnormalized); 133 | att = 1.0 / (lights[i].constantAttenuation + 134 | lights[i].linearAttenuation * dist + 135 | lights[i].quadraticAttenuation * dist * dist); 136 | } 137 | 138 | // The light direction is in world space already 139 | if (lights[i].type == TYPE_SPOT) { 140 | // Check if fragment is inside or outside of the spot light cone 141 | if (degrees(acos(dot(-s, lights[i].direction))) > lights[i].cutOffAngle) 142 | sDotN = 0.0; 143 | } 144 | } 145 | } else { 146 | // Directional lights 147 | // The light direction is in world space already 148 | s = normalize(-lights[i].direction); 149 | sDotN = dot(s, n); 150 | } 151 | 152 | // Calculate the diffuse factor 153 | float diffuse = max(sDotN, 0.0); 154 | 155 | // Calculate the specular factor 156 | float specular = 0.0; 157 | if (diffuse > 0.0 && shininess > 0.0) { 158 | float normFactor = (shininess + 2.0) / 2.0; 159 | vec3 r = reflect(-s, n); // Reflection direction in world space 160 | specular = normFactor * pow(max(dot(r, v), 0.0), shininess); 161 | } 162 | 163 | // Accumulate the diffuse and specular contributions 164 | diffuseColor += att * lights[i].intensity * diffuse * lights[i].color; 165 | specularColor += att * lights[i].intensity * specular * lights[i].color; 166 | } 167 | } 168 | 169 | void adModel(const in vec3 worldPos, 170 | const in vec3 worldNormal, 171 | out vec3 diffuseColor) 172 | { 173 | diffuseColor = vec3(0.0); 174 | 175 | // We perform all work in world space 176 | vec3 n = normalize(worldNormal); 177 | vec3 s = vec3(0.0); 178 | 179 | for (int i = 0; i < lightCount; ++i) { 180 | float att = 1.0; 181 | float sDotN = 0.0; 182 | 183 | if (lights[i].type != TYPE_DIRECTIONAL) { 184 | // Point and Spot lights 185 | 186 | // Light position is already in world space 187 | vec3 sUnnormalized = lights[i].position - worldPos; 188 | s = normalize(sUnnormalized); // Light direction 189 | 190 | // Calculate the attenuation factor 191 | sDotN = dot(s, n); 192 | if (sDotN > 0.0) { 193 | if (lights[i].constantAttenuation != 0.0 194 | || lights[i].linearAttenuation != 0.0 195 | || lights[i].quadraticAttenuation != 0.0) { 196 | float dist = length(sUnnormalized); 197 | att = 1.0 / (lights[i].constantAttenuation + 198 | lights[i].linearAttenuation * dist + 199 | lights[i].quadraticAttenuation * dist * dist); 200 | } 201 | 202 | // The light direction is in world space already 203 | if (lights[i].type == TYPE_SPOT) { 204 | // Check if fragment is inside or outside of the spot light cone 205 | if (degrees(acos(dot(-s, lights[i].direction))) > lights[i].cutOffAngle) 206 | sDotN = 0.0; 207 | } 208 | } 209 | } else { 210 | // Directional lights 211 | // The light direction is in world space already 212 | s = normalize(-lights[i].direction); 213 | sDotN = dot(s, n); 214 | } 215 | 216 | // Calculate the diffuse factor 217 | float diffuse = max(sDotN, 0.0); 218 | 219 | // Accumulate the diffuse contributions 220 | diffuseColor += att * lights[i].intensity * diffuse * lights[i].color; 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /edge-detection/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char* argv[]) 9 | { 10 | QGuiApplication app(argc, argv); 11 | 12 | Qt3DExtras::Quick::Qt3DQuickWindow view; 13 | view.setTitle("Edge Detection"); 14 | view.resize(1600, 800); 15 | view.engine()->qmlEngine()->rootContext()->setContextProperty("_window", &view); 16 | view.setSource(QUrl("qrc:/main.qml")); 17 | view.show(); 18 | 19 | return app.exec(); 20 | } 21 | -------------------------------------------------------------------------------- /edge-detection/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.1 as QQ2 2 | import Qt3D.Core 2.0 3 | import Qt3D.Render 2.0 4 | import Qt3D.Input 2.0 5 | import Qt3D.Extras 2.0 6 | 7 | Entity { 8 | 9 | components: [ 10 | rendSettings, 11 | inputSettings 12 | ] 13 | 14 | InputSettings { id: inputSettings } 15 | 16 | RenderSettings { 17 | id: rendSettings 18 | activeFrameGraph: RenderSurfaceSelector { 19 | //id: surfaceSelector 20 | Viewport { 21 | normalizedRect: Qt.rect(0,0,1,1) 22 | CameraSelector { 23 | camera: camera 24 | 25 | // "ordinary" pass to render scene with shading 26 | ClearBuffers { 27 | buffers: ClearBuffers.ColorDepthBuffer 28 | clearColor: Qt.rgba(0.3,0.3,0.3,1) 29 | RenderPassFilter { 30 | matchAny: [ FilterKey { name: "name"; value: "forward" } ] 31 | } 32 | } 33 | 34 | // pre-pass: rendering of the scene to a texture (normals, depths) 35 | RenderTargetSelector { 36 | target: rt 37 | ClearBuffers { 38 | buffers: ClearBuffers.ColorDepthBuffer 39 | clearColor: Qt.rgba(0,0,0,0) 40 | RenderPassFilter { 41 | matchAny: [ FilterKey { name: "name"; value: "prepass" } ] 42 | LayerFilter { 43 | layers: [layerPrePass] 44 | } 45 | } 46 | } 47 | } 48 | 49 | // second pass - addition of edges 50 | CameraSelector { 51 | camera: ortoCamera 52 | LayerFilter { 53 | layers: [layerQuad] 54 | /*RenderStateSet { 55 | renderStates: [ 56 | BlendEquation { blendFunction: BlendEquation.Add }, 57 | BlendEquationArguments { sourceAlpha: BlendEquationArguments.SourceAlpha; sourceRgb: BlendEquationArguments.One; destinationAlpha: BlendEquationArguments.OneMinusSourceAlpha; destinationRgb: BlendEquationArguments.One} 58 | ] 59 | }*/ 60 | } 61 | } 62 | 63 | // preview of what goes out of the edge filter 64 | Viewport { 65 | normalizedRect: Qt.rect( 0.0, 0.0, 0.2, 0.2 ) // starts in top-left corner 66 | CameraSelector { 67 | camera: ortoCamera 68 | LayerFilter { 69 | layers: [layerQuad] 70 | } 71 | } 72 | } 73 | 74 | // preview of normally shaded scene 75 | Viewport { 76 | normalizedRect: Qt.rect(0.8, 0.0, 0.2, 0.2) 77 | RenderPassFilter { 78 | matchAny: [ FilterKey { name: "name"; value: "forward" } ] 79 | } 80 | } 81 | 82 | } 83 | } 84 | } 85 | 86 | } 87 | 88 | Camera { 89 | id: ortoCamera 90 | projectionType: CameraLens.OrthographicProjection 91 | //aspectRatio: _window.width / _window.height 92 | nearPlane: 1 93 | farPlane: 100.0 94 | position: Qt.vector3d(0.0, 10.0, 0.0) 95 | viewCenter: Qt.vector3d(0.0, 0.0, 0.0) 96 | upVector: Qt.vector3d(0.0, 1.0, 0.0) 97 | } 98 | 99 | 100 | 101 | Camera { 102 | id: camera 103 | projectionType: CameraLens.PerspectiveProjection 104 | fieldOfView: 45 105 | aspectRatio: _window.width / _window.height 106 | nearPlane: 0.1 107 | farPlane: 1000.0 108 | position: Qt.vector3d(0.0, 10.0, 20.0) 109 | viewCenter: Qt.vector3d(0.0, 0.0, 0.0) 110 | upVector: Qt.vector3d(0.0, 1.0, 0.0) 111 | } 112 | 113 | FirstPersonCameraController { camera: camera } 114 | 115 | PreprocessRenderTarget { 116 | id: rt 117 | 118 | w: _window.width 119 | h: _window.height 120 | } 121 | 122 | 123 | Layer { id: layerPrePass } // rendering of normals 124 | Layer { id: layerQuad } // quad used for post-processing 125 | 126 | MyScene { 127 | id: sceneRoot 128 | 129 | layerPrePass: layerPrePass 130 | } 131 | 132 | ///// 133 | ///// 134 | ///// 135 | 136 | 137 | Entity { 138 | PlaneMesh { 139 | id: ortoMesh 140 | width: 1 141 | height: 1 142 | } 143 | Transform { 144 | id: ortoTr 145 | translation: Qt.vector3d(0, 2, 0) 146 | } 147 | // this material uses normal/depth textures and builds edges 148 | EdgeMaterial { 149 | id: edgeMaterial 150 | 151 | textureNormal: rt.textureNormal 152 | textureDepth: rt.textureDepth 153 | cameraZNear: camera.nearPlane 154 | cameraZFar: camera.farPlane 155 | } 156 | 157 | components: [ ortoMesh, edgeMaterial, ortoTr, layerQuad ] 158 | } 159 | 160 | 161 | } 162 | -------------------------------------------------------------------------------- /edge-detection/phong.frag: -------------------------------------------------------------------------------- 1 | #version 150 core 2 | 3 | uniform vec3 ka; // Ambient reflectivity 4 | uniform vec3 kd; // Diffuse reflectivity 5 | uniform vec3 ks; // Specular reflectivity 6 | uniform float shininess; // Specular shininess factor 7 | 8 | uniform vec3 eyePosition; 9 | 10 | in vec3 worldPosition; 11 | in vec3 worldNormal; 12 | 13 | out vec4 fragColor; 14 | 15 | #pragma include light.inc.frag 16 | 17 | void main() 18 | { 19 | vec3 diffuseColor, specularColor; 20 | adsModel(worldPosition, worldNormal, eyePosition, shininess, diffuseColor, specularColor); 21 | fragColor = vec4( ka + kd * diffuseColor + ks * specularColor, 1.0 ); 22 | } 23 | -------------------------------------------------------------------------------- /edge-detection/phong.vert: -------------------------------------------------------------------------------- 1 | #version 150 core 2 | 3 | in vec3 vertexPosition; 4 | in vec3 vertexNormal; 5 | 6 | out vec3 worldPosition; 7 | out vec3 worldNormal; 8 | 9 | uniform mat4 modelMatrix; 10 | uniform mat3 modelNormalMatrix; 11 | uniform mat4 modelViewProjection; 12 | 13 | void main() 14 | { 15 | worldNormal = normalize( modelNormalMatrix * vertexNormal ); 16 | worldPosition = vec3( modelMatrix * vec4( vertexPosition, 1.0 ) ); 17 | 18 | gl_Position = modelViewProjection * vec4( vertexPosition, 1.0 ); 19 | } 20 | -------------------------------------------------------------------------------- /edge-detection/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | MyScene.qml 5 | EdgeMaterial.qml 6 | PreprocessRenderTarget.qml 7 | light.inc.frag 8 | phong.frag 9 | phong.vert 10 | MainMaterial.qml 11 | 12 | 13 | -------------------------------------------------------------------------------- /instanced/fun3d-instanced.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | QT += 3dcore 3drender 3dinput 3dquick qml quick 3dquickextras 3dextras 3 | 4 | # The following define makes your compiler emit warnings if you use 5 | # any Qt feature that has been marked deprecated (the exact warnings 6 | # depend on your compiler). Refer to the documentation for the 7 | # deprecated API to know how to port your code away from it. 8 | DEFINES += QT_DEPRECATED_WARNINGS 9 | 10 | # You can also make your code fail to compile if it uses deprecated APIs. 11 | # In order to do so, uncomment the following line. 12 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 13 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 14 | 15 | SOURCES += \ 16 | main.cpp \ 17 | instancedgeometry.cpp 18 | 19 | RESOURCES += qml.qrc \ 20 | shaders.qrc 21 | 22 | # Additional import path used to resolve QML modules in Qt Creator's code model 23 | QML_IMPORT_PATH = 24 | 25 | # Additional import path used to resolve QML modules just for Qt Quick Designer 26 | QML_DESIGNER_IMPORT_PATH = 27 | 28 | # Default rules for deployment. 29 | qnx: target.path = /tmp/$${TARGET}/bin 30 | else: unix:!android: target.path = /opt/$${TARGET}/bin 31 | !isEmpty(target.path): INSTALLS += target 32 | 33 | HEADERS += \ 34 | instancedgeometry.h 35 | -------------------------------------------------------------------------------- /instanced/instanced.frag: -------------------------------------------------------------------------------- 1 | #version 150 core 2 | 3 | // copy of phong.frag from qt3d extras 4 | 5 | uniform vec3 ka; // Ambient reflectivity 6 | uniform vec3 kd; // Diffuse reflectivity 7 | uniform vec3 ks; // Specular reflectivity 8 | uniform float shininess; // Specular shininess factor 9 | 10 | uniform vec3 eyePosition; 11 | 12 | in vec3 worldPosition; 13 | in vec3 worldNormal; 14 | 15 | out vec4 fragColor; 16 | 17 | #pragma include light.inc.frag 18 | 19 | void main() 20 | { 21 | vec3 diffuseColor, specularColor; 22 | adsModel(worldPosition, worldNormal, eyePosition, shininess, diffuseColor, specularColor); 23 | fragColor = vec4( ka + kd * diffuseColor + ks * specularColor, 1.0 ); 24 | } 25 | -------------------------------------------------------------------------------- /instanced/instanced.vert: -------------------------------------------------------------------------------- 1 | #version 150 core 2 | 3 | in vec3 vertexPosition; 4 | in vec3 vertexNormal; 5 | in vec3 pos; 6 | 7 | out vec3 worldPosition; 8 | out vec3 worldNormal; 9 | 10 | uniform mat4 modelView; 11 | uniform mat3 modelViewNormal; 12 | uniform mat4 modelViewProjection; 13 | 14 | uniform mat4 inst; // transform of individual object instance 15 | uniform mat4 instNormal; // should be mat3 but Qt3D only supports mat4... 16 | 17 | void main() 18 | { 19 | // TODO: i think this is not entirely correct: the translation by "pos" works 20 | // like this only because we assume that "inst" matrix only does translation/scale/rotation 21 | // which all keep "w" set to 1. correctly we should use translation matrix... 22 | vec4 offsetPos = inst * vec4(vertexPosition, 1.0) + vec4(pos, 0.0); 23 | 24 | worldNormal = normalize(mat3(instNormal) * vertexNormal); 25 | worldPosition = vec3(offsetPos); 26 | 27 | gl_Position = modelViewProjection * offsetPos; 28 | } 29 | -------------------------------------------------------------------------------- /instanced/instancedgeometry.cpp: -------------------------------------------------------------------------------- 1 | #include "instancedgeometry.h" 2 | 3 | #include 4 | 5 | 6 | InstancedGeometry::InstancedGeometry( Qt3DCore::QNode *parent ) 7 | : Qt3DExtras::QSphereGeometry( parent ) 8 | , mPositionAttribute( new Qt3DRender::QAttribute( this ) ) 9 | , mInstanceBuffer( new Qt3DRender::QBuffer( Qt3DRender::QBuffer::VertexBuffer, this ) ) 10 | { 11 | 12 | mPositionAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute ); 13 | mPositionAttribute->setBuffer( mInstanceBuffer ); 14 | mPositionAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float ); 15 | mPositionAttribute->setVertexSize( 3 ); 16 | mPositionAttribute->setName( Qt3DRender::QAttribute::defaultPositionAttributeName() ); 17 | mPositionAttribute->setName( QStringLiteral( "pos" ) ); 18 | mPositionAttribute->setDivisor( 1 ); 19 | mPositionAttribute->setByteStride( 3 * sizeof( float ) ); 20 | 21 | addAttribute( mPositionAttribute ); 22 | setBoundingVolumePositionAttribute( mPositionAttribute ); 23 | } 24 | 25 | int InstancedGeometry::count() 26 | { 27 | return mInstanceCount; 28 | } 29 | 30 | QMatrix4x4 InstancedGeometry::normalMatrix(QMatrix4x4 mat) 31 | { 32 | QMatrix3x3 normalMatrix = mat.normalMatrix(); 33 | 34 | // QMatrix3x3 is not supported for passing to shaders, so we pass QMatrix4x4 35 | float *n = normalMatrix.data(); 36 | QMatrix4x4 normalMatrix4( 37 | n[0], n[3], n[6], 0, 38 | n[1], n[4], n[7], 0, 39 | n[2], n[5], n[8], 0, 40 | 0, 0, 0, 0 ); 41 | 42 | return normalMatrix4; 43 | } 44 | 45 | void InstancedGeometry::setPoints(const QVector &vertices) 46 | { 47 | QByteArray vertexBufferData; 48 | vertexBufferData.resize( vertices.size() * 3 * sizeof( float ) ); 49 | float *rawVertexArray = reinterpret_cast( vertexBufferData.data() ); 50 | int idx = 0; 51 | for ( const auto &v : vertices ) 52 | { 53 | rawVertexArray[idx++] = v.x(); 54 | rawVertexArray[idx++] = v.y(); 55 | rawVertexArray[idx++] = v.z(); 56 | } 57 | 58 | mInstanceCount = vertices.count(); 59 | mInstanceBuffer->setData( vertexBufferData ); 60 | 61 | mPositionAttribute->setCount( mInstanceCount ); 62 | 63 | emit countChanged(mInstanceCount); 64 | } 65 | -------------------------------------------------------------------------------- /instanced/instancedgeometry.h: -------------------------------------------------------------------------------- 1 | #ifndef INSTANCEDGEOMETRY_H 2 | #define INSTANCEDGEOMETRY_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | 11 | class InstancedGeometry : public Qt3DExtras::QSphereGeometry 12 | { 13 | Q_OBJECT 14 | 15 | Q_PROPERTY(int count READ count NOTIFY countChanged) 16 | 17 | public: 18 | InstancedGeometry( Qt3DCore::QNode *parent = nullptr ); 19 | 20 | void setPoints( const QVector &vertices ); 21 | 22 | int count(); 23 | 24 | Q_INVOKABLE static QMatrix4x4 normalMatrix(QMatrix4x4 mat); 25 | 26 | signals: 27 | void countChanged(int count); 28 | 29 | private: 30 | Qt3DRender::QAttribute *mPositionAttribute = nullptr; 31 | Qt3DRender::QBuffer *mInstanceBuffer = nullptr; 32 | int mInstanceCount = 0; 33 | }; 34 | 35 | #endif // INSTANCEDGEOMETRY_H 36 | -------------------------------------------------------------------------------- /instanced/light.inc.frag: -------------------------------------------------------------------------------- 1 | 2 | // copy of light.inc.frag from qt3d extras 3 | 4 | const int MAX_LIGHTS = 8; 5 | const int TYPE_POINT = 0; 6 | const int TYPE_DIRECTIONAL = 1; 7 | const int TYPE_SPOT = 2; 8 | struct Light { 9 | int type; 10 | vec3 position; 11 | vec3 color; 12 | float intensity; 13 | vec3 direction; 14 | float constantAttenuation; 15 | float linearAttenuation; 16 | float quadraticAttenuation; 17 | float cutOffAngle; 18 | }; 19 | uniform Light lights[MAX_LIGHTS]; 20 | uniform int lightCount; 21 | 22 | // Pre-convolved environment maps 23 | struct EnvironmentLight { 24 | samplerCube irradiance; // For diffuse contribution 25 | samplerCube specular; // For specular contribution 26 | }; 27 | uniform EnvironmentLight envLight; 28 | uniform int envLightCount = 0; 29 | 30 | void adsModelNormalMapped(const in vec3 worldPos, 31 | const in vec3 tsNormal, 32 | const in vec3 worldEye, 33 | const in float shininess, 34 | const in mat3 tangentMatrix, 35 | out vec3 diffuseColor, 36 | out vec3 specularColor) 37 | { 38 | diffuseColor = vec3(0.0); 39 | specularColor = vec3(0.0); 40 | 41 | // We perform all work in tangent space, so we convert quantities from world space 42 | vec3 tsPos = tangentMatrix * worldPos; 43 | vec3 n = normalize(tsNormal); 44 | vec3 v = normalize(tangentMatrix * (worldEye - worldPos)); 45 | vec3 s = vec3(0.0); 46 | 47 | for (int i = 0; i < lightCount; ++i) { 48 | float att = 1.0; 49 | float sDotN = 0.0; 50 | 51 | if (lights[i].type != TYPE_DIRECTIONAL) { 52 | // Point and Spot lights 53 | 54 | // Transform the light position from world to tangent space 55 | vec3 tsLightPos = tangentMatrix * lights[i].position; 56 | vec3 sUnnormalized = tsLightPos - tsPos; 57 | s = normalize(sUnnormalized); // Light direction in tangent space 58 | 59 | // Calculate the attenuation factor 60 | sDotN = dot(s, n); 61 | if (sDotN > 0.0) { 62 | if (lights[i].constantAttenuation != 0.0 63 | || lights[i].linearAttenuation != 0.0 64 | || lights[i].quadraticAttenuation != 0.0) { 65 | float dist = length(sUnnormalized); 66 | att = 1.0 / (lights[i].constantAttenuation + 67 | lights[i].linearAttenuation * dist + 68 | lights[i].quadraticAttenuation * dist * dist); 69 | } 70 | 71 | // The light direction is in world space, convert to tangent space 72 | if (lights[i].type == TYPE_SPOT) { 73 | // Check if fragment is inside or outside of the spot light cone 74 | vec3 tsLightDirection = tangentMatrix * lights[i].direction; 75 | if (degrees(acos(dot(-s, tsLightDirection))) > lights[i].cutOffAngle) 76 | sDotN = 0.0; 77 | } 78 | } 79 | } else { 80 | // Directional lights 81 | // The light direction is in world space, convert to tangent space 82 | s = normalize(tangentMatrix * -lights[i].direction); 83 | sDotN = dot(s, n); 84 | } 85 | 86 | // Calculate the diffuse factor 87 | float diffuse = max(sDotN, 0.0); 88 | 89 | // Calculate the specular factor 90 | float specular = 0.0; 91 | if (diffuse > 0.0 && shininess > 0.0) { 92 | float normFactor = (shininess + 2.0) / 2.0; 93 | vec3 r = reflect(-s, n); // Reflection direction in tangent space 94 | specular = normFactor * pow(max(dot(r, v), 0.0), shininess); 95 | } 96 | 97 | // Accumulate the diffuse and specular contributions 98 | diffuseColor += att * lights[i].intensity * diffuse * lights[i].color; 99 | specularColor += att * lights[i].intensity * specular * lights[i].color; 100 | } 101 | } 102 | 103 | void adsModel(const in vec3 worldPos, 104 | const in vec3 worldNormal, 105 | const in vec3 worldEye, 106 | const in float shininess, 107 | out vec3 diffuseColor, 108 | out vec3 specularColor) 109 | { 110 | diffuseColor = vec3(0.0); 111 | specularColor = vec3(0.0); 112 | 113 | // We perform all work in world space 114 | vec3 n = normalize(worldNormal); 115 | vec3 v = normalize(worldEye - worldPos); 116 | vec3 s = vec3(0.0); 117 | 118 | for (int i = 0; i < lightCount; ++i) { 119 | float att = 1.0; 120 | float sDotN = 0.0; 121 | 122 | if (lights[i].type != TYPE_DIRECTIONAL) { 123 | // Point and Spot lights 124 | 125 | // Light position is already in world space 126 | vec3 sUnnormalized = lights[i].position - worldPos; 127 | s = normalize(sUnnormalized); // Light direction 128 | 129 | // Calculate the attenuation factor 130 | sDotN = dot(s, n); 131 | if (sDotN > 0.0) { 132 | if (lights[i].constantAttenuation != 0.0 133 | || lights[i].linearAttenuation != 0.0 134 | || lights[i].quadraticAttenuation != 0.0) { 135 | float dist = length(sUnnormalized); 136 | att = 1.0 / (lights[i].constantAttenuation + 137 | lights[i].linearAttenuation * dist + 138 | lights[i].quadraticAttenuation * dist * dist); 139 | } 140 | 141 | // The light direction is in world space already 142 | if (lights[i].type == TYPE_SPOT) { 143 | // Check if fragment is inside or outside of the spot light cone 144 | if (degrees(acos(dot(-s, lights[i].direction))) > lights[i].cutOffAngle) 145 | sDotN = 0.0; 146 | } 147 | } 148 | } else { 149 | // Directional lights 150 | // The light direction is in world space already 151 | s = normalize(-lights[i].direction); 152 | sDotN = dot(s, n); 153 | } 154 | 155 | // Calculate the diffuse factor 156 | float diffuse = max(sDotN, 0.0); 157 | 158 | // Calculate the specular factor 159 | float specular = 0.0; 160 | if (diffuse > 0.0 && shininess > 0.0) { 161 | float normFactor = (shininess + 2.0) / 2.0; 162 | vec3 r = reflect(-s, n); // Reflection direction in world space 163 | specular = normFactor * pow(max(dot(r, v), 0.0), shininess); 164 | } 165 | 166 | // Accumulate the diffuse and specular contributions 167 | diffuseColor += att * lights[i].intensity * diffuse * lights[i].color; 168 | specularColor += att * lights[i].intensity * specular * lights[i].color; 169 | } 170 | } 171 | 172 | void adModel(const in vec3 worldPos, 173 | const in vec3 worldNormal, 174 | out vec3 diffuseColor) 175 | { 176 | diffuseColor = vec3(0.0); 177 | 178 | // We perform all work in world space 179 | vec3 n = normalize(worldNormal); 180 | vec3 s = vec3(0.0); 181 | 182 | for (int i = 0; i < lightCount; ++i) { 183 | float att = 1.0; 184 | float sDotN = 0.0; 185 | 186 | if (lights[i].type != TYPE_DIRECTIONAL) { 187 | // Point and Spot lights 188 | 189 | // Light position is already in world space 190 | vec3 sUnnormalized = lights[i].position - worldPos; 191 | s = normalize(sUnnormalized); // Light direction 192 | 193 | // Calculate the attenuation factor 194 | sDotN = dot(s, n); 195 | if (sDotN > 0.0) { 196 | if (lights[i].constantAttenuation != 0.0 197 | || lights[i].linearAttenuation != 0.0 198 | || lights[i].quadraticAttenuation != 0.0) { 199 | float dist = length(sUnnormalized); 200 | att = 1.0 / (lights[i].constantAttenuation + 201 | lights[i].linearAttenuation * dist + 202 | lights[i].quadraticAttenuation * dist * dist); 203 | } 204 | 205 | // The light direction is in world space already 206 | if (lights[i].type == TYPE_SPOT) { 207 | // Check if fragment is inside or outside of the spot light cone 208 | if (degrees(acos(dot(-s, lights[i].direction))) > lights[i].cutOffAngle) 209 | sDotN = 0.0; 210 | } 211 | } 212 | } else { 213 | // Directional lights 214 | // The light direction is in world space already 215 | s = normalize(-lights[i].direction); 216 | sDotN = dot(s, n); 217 | } 218 | 219 | // Calculate the diffuse factor 220 | float diffuse = max(sDotN, 0.0); 221 | 222 | // Accumulate the diffuse contributions 223 | diffuseColor += att * lights[i].intensity * diffuse * lights[i].color; 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /instanced/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "instancedgeometry.h" 8 | 9 | int main(int argc, char* argv[]) 10 | { 11 | QGuiApplication app(argc, argv); 12 | 13 | QVector pos; 14 | pos << QVector3D(1, 1, 0); 15 | pos << QVector3D(-1, 2, 8); 16 | pos << QVector3D(1, 1, 7); 17 | pos << QVector3D(0, 0, 4); 18 | pos << QVector3D(1, 5, 1); 19 | pos << QVector3D(-3, 3, 0); 20 | pos << QVector3D(2, 2, -2); 21 | 22 | InstancedGeometry instGeom; 23 | instGeom.setPoints(pos); 24 | 25 | Qt3DExtras::Quick::Qt3DQuickWindow view; 26 | view.setTitle("Instanced Rendering"); 27 | view.resize(1600, 800); 28 | view.engine()->qmlEngine()->rootContext()->setContextProperty("_window", &view); 29 | view.engine()->qmlEngine()->rootContext()->setContextProperty("_instg", &instGeom); 30 | view.setSource(QUrl("qrc:/main.qml")); 31 | view.show(); 32 | 33 | return app.exec(); 34 | } 35 | -------------------------------------------------------------------------------- /instanced/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.1 as QQ2 2 | import Qt3D.Core 2.0 3 | import Qt3D.Render 2.0 4 | import Qt3D.Input 2.0 5 | import Qt3D.Extras 2.0 6 | 7 | Entity { 8 | 9 | components: [ 10 | rendSettings, 11 | inputSettings 12 | ] 13 | 14 | InputSettings { id: inputSettings } 15 | 16 | RenderSettings { 17 | id: rendSettings 18 | activeFrameGraph: RenderSurfaceSelector { 19 | Viewport { 20 | normalizedRect: Qt.rect(0,0,1,1) 21 | CameraSelector { 22 | camera: camera 23 | ClearBuffers { 24 | buffers: ClearBuffers.ColorDepthBuffer 25 | } 26 | } 27 | } 28 | } 29 | 30 | } 31 | 32 | Camera { 33 | id: camera 34 | projectionType: CameraLens.PerspectiveProjection 35 | fieldOfView: 45 36 | aspectRatio: _window.width / _window.height 37 | nearPlane: 0.1 38 | farPlane: 100.0 39 | position: Qt.vector3d(0.0, 10.0, 20.0) 40 | viewCenter: Qt.vector3d(0.0, 0.0, 0.0) 41 | upVector: Qt.vector3d(0.0, 1.0, 0.0) 42 | } 43 | 44 | FirstPersonCameraController { camera: camera } 45 | 46 | Entity { 47 | PlaneMesh { 48 | id: pm 49 | width: 20 50 | height: 20 51 | } 52 | PhongMaterial { 53 | id: pmm 54 | ambient: Qt.rgba(0.0,0.0,0.7,1) 55 | } 56 | components: [ pm, pmm ] 57 | } 58 | 59 | 60 | Entity { 61 | 62 | id: myEntity 63 | property matrix4x4 instTransform: Qt.matrix4x4( // identity matrix 64 | 1,0,0,0, 65 | 0,1,0,0, 66 | 0,0,1,0, 67 | 0,0,0,1) 68 | 69 | GeometryRenderer { 70 | id: gr 71 | geometry: _instg 72 | instanceCount: _instg.count 73 | } 74 | 75 | Transform { 76 | id: trr 77 | translation: Qt.vector3d(0,1.5,0) 78 | } 79 | 80 | Material { 81 | id: grm 82 | 83 | parameters: [ 84 | Parameter { name: "ka"; value: Qt.rgba(0.05, 0.05, 0.05, 1.0) }, 85 | Parameter { name: "kd"; value: Qt.rgba(0.7, 0.7, 0.7, 1.0) }, 86 | Parameter { name: "ks"; value: Qt.rgba(0.01, 0.01, 0.01, 1.0) }, 87 | Parameter { name: "shininess"; value: 150. }, 88 | 89 | Parameter { name: "inst"; value: myEntity.instTransform }, 90 | Parameter { name: "instNormal"; value: _instg.normalMatrix( myEntity.instTransform ) } // normal matrix (actually just 3x3) 91 | ] 92 | 93 | effect: Effect { 94 | techniques: Technique { 95 | graphicsApiFilter { api: GraphicsApiFilter.OpenGL; profile: GraphicsApiFilter.CoreProfile; majorVersion: 3; minorVersion: 1 } 96 | renderPasses: [ 97 | RenderPass { 98 | shaderProgram: ShaderProgram { 99 | vertexShaderCode: loadSource("qrc:/shaders/instanced.vert") 100 | fragmentShaderCode: loadSource("qrc:/shaders/instanced.frag") 101 | } 102 | } 103 | ] 104 | } 105 | } 106 | } 107 | 108 | components: [ gr, grm, trr ] 109 | } 110 | 111 | 112 | // reference sphere (for shading) 113 | Entity { 114 | PhongMaterial { 115 | id: redMat 116 | ambient: "red" 117 | } 118 | SphereMesh { 119 | id: sphereMesh 120 | } 121 | Transform { 122 | id: sphereTransform 123 | translation: Qt.vector3d(0,5,0) 124 | } 125 | 126 | components: [ redMat, sphereMesh, sphereTransform ] 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /instanced/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | 5 | 6 | -------------------------------------------------------------------------------- /instanced/shaders.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | instanced.frag 4 | instanced.vert 5 | light.inc.frag 6 | 7 | 8 | -------------------------------------------------------------------------------- /lines/drawdata.cpp: -------------------------------------------------------------------------------- 1 | #include "drawdata.h" 2 | 3 | 4 | #include 5 | 6 | LineMeshGeometry::LineMeshGeometry( Qt3DCore::QNode *parent ) 7 | : Qt3DRender::QGeometry( parent ) 8 | , mPositionAttribute( new Qt3DRender::QAttribute( this ) ) 9 | , mIndexAttribute( new Qt3DRender::QAttribute( this ) ) 10 | , mVertexBuffer( new Qt3DRender::QBuffer( Qt3DRender::QBuffer::VertexBuffer, this ) ) 11 | , mIndexBuffer( new Qt3DRender::QBuffer( Qt3DRender::QBuffer::IndexBuffer, this ) ) 12 | { 13 | mPositionAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute ); 14 | mPositionAttribute->setBuffer( mVertexBuffer ); 15 | mPositionAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float ); 16 | mPositionAttribute->setVertexSize( 3 ); 17 | mPositionAttribute->setName( Qt3DRender::QAttribute::defaultPositionAttributeName() ); 18 | 19 | mIndexAttribute->setAttributeType( Qt3DRender::QAttribute::IndexAttribute ); 20 | mIndexAttribute->setVertexBaseType( Qt3DRender::QAttribute::UnsignedInt ); 21 | 22 | addAttribute( mPositionAttribute ); 23 | addAttribute( mIndexAttribute ); 24 | } 25 | 26 | int LineMeshGeometry::vertexCount() 27 | { 28 | return mIndexCount; 29 | } 30 | 31 | void LineMeshGeometry::setVertices( const QVector &vertices, const QVector &indices ) 32 | { 33 | QByteArray vertexBufferData; 34 | vertexBufferData.resize( vertices.size() * 3 * sizeof( float ) ); 35 | float *rawVertexArray = reinterpret_cast( vertexBufferData.data() ); 36 | int idx = 0; 37 | for ( const auto &v : vertices ) 38 | { 39 | rawVertexArray[idx++] = v.x(); 40 | rawVertexArray[idx++] = v.y(); 41 | rawVertexArray[idx++] = v.z(); 42 | } 43 | 44 | mVertexCount = vertices.count(); 45 | mVertexBuffer->setData( vertexBufferData ); 46 | 47 | 48 | mIndexCount = indices.count(); 49 | QByteArray indexBufferData; 50 | indexBufferData.resize( mIndexCount * sizeof(int) ); 51 | int *rawIndexArray = reinterpret_cast( indexBufferData.data() ); 52 | for (int i = 0; i < indices.count(); ++i) 53 | rawIndexArray[i] = indices[i]; 54 | mIndexBuffer->setData( indexBufferData ); 55 | 56 | mIndexAttribute->setBuffer( mIndexBuffer ); 57 | mIndexAttribute->setCount( mIndexCount ); 58 | 59 | emit countChanged(mVertexCount); 60 | } 61 | -------------------------------------------------------------------------------- /lines/drawdata.h: -------------------------------------------------------------------------------- 1 | #ifndef DRAWDATA_H 2 | #define DRAWDATA_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | 11 | class LineMeshGeometry : public Qt3DRender::QGeometry 12 | { 13 | Q_OBJECT 14 | 15 | Q_PROPERTY(int count READ vertexCount NOTIFY countChanged) 16 | 17 | public: 18 | LineMeshGeometry( Qt3DCore::QNode *parent = nullptr ); 19 | 20 | int vertexCount(); 21 | 22 | void setVertices( const QVector &vertices, const QVector &indices ); 23 | 24 | signals: 25 | void countChanged(int count); 26 | 27 | private: 28 | Qt3DRender::QAttribute *mPositionAttribute = nullptr; 29 | Qt3DRender::QAttribute *mIndexAttribute = nullptr; 30 | Qt3DRender::QBuffer *mVertexBuffer = nullptr; 31 | Qt3DRender::QBuffer *mIndexBuffer = nullptr; 32 | int mVertexCount = 0; 33 | int mIndexCount = 0; 34 | 35 | }; 36 | 37 | #endif // DRAWDATA_H 38 | -------------------------------------------------------------------------------- /lines/fun3d-lines.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | QT += 3dcore 3drender 3dinput 3dquick qml quick 3dquickextras 3dextras 3 | 4 | # The following define makes your compiler emit warnings if you use 5 | # any Qt feature that has been marked deprecated (the exact warnings 6 | # depend on your compiler). Refer to the documentation for the 7 | # deprecated API to know how to port your code away from it. 8 | DEFINES += QT_DEPRECATED_WARNINGS 9 | 10 | # You can also make your code fail to compile if it uses deprecated APIs. 11 | # In order to do so, uncomment the following line. 12 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 13 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 14 | 15 | SOURCES += \ 16 | main.cpp \ 17 | drawdata.cpp 18 | 19 | RESOURCES += qml.qrc \ 20 | shaders.qrc 21 | 22 | # Additional import path used to resolve QML modules in Qt Creator's code model 23 | QML_IMPORT_PATH = 24 | 25 | # Additional import path used to resolve QML modules just for Qt Quick Designer 26 | QML_DESIGNER_IMPORT_PATH = 27 | 28 | # Default rules for deployment. 29 | qnx: target.path = /tmp/$${TARGET}/bin 30 | else: unix:!android: target.path = /opt/$${TARGET}/bin 31 | !isEmpty(target.path): INSTALLS += target 32 | 33 | HEADERS += \ 34 | drawdata.h 35 | 36 | DISTFILES += \ 37 | lines.vert \ 38 | lines.frag \ 39 | lines.geom 40 | -------------------------------------------------------------------------------- /lines/line-texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wonder-sk/qt3d-experiments/eef89210f0cdee05443d62442a640c04978fc63c/lines/line-texture.png -------------------------------------------------------------------------------- /lines/lines.frag: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | uniform vec4 lineColor; 4 | uniform bool useTex; 5 | uniform sampler2D tex0; 6 | 7 | in VertexData{ 8 | vec2 mTexCoord; 9 | // vec3 mColor; 10 | } VertexIn; 11 | 12 | out vec4 oColor; 13 | 14 | void main(void) 15 | { 16 | if (!useTex) 17 | { 18 | // option 1: plain color 19 | oColor = lineColor; 20 | } 21 | else 22 | { 23 | // option 2: textured color 24 | oColor = texture(tex0, VertexIn.mTexCoord.xy ); 25 | } 26 | 27 | //vec4 clr = texture( tex0, VertexIn.mTexCoord.xy ); 28 | //oColor.rgb = VertexIn.mColor * clr.rgb; 29 | //oColor.a = clr.a; 30 | } 31 | -------------------------------------------------------------------------------- /lines/lines.geom: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | uniform float THICKNESS; // the thickness of the line in pixels 4 | uniform float MITER_LIMIT; // 1.0: always miter, -1.0: never miter, 0.75: default 5 | uniform vec2 WIN_SCALE; // the size of the viewport in pixels 6 | 7 | uniform mat4 modelViewProjection; 8 | 9 | uniform vec3 camNearPlanePoint; 10 | uniform vec3 camNearPlaneNormal; 11 | 12 | layout( lines_adjacency ) in; 13 | layout( triangle_strip, max_vertices = 7 ) out; 14 | 15 | 16 | in VertexData{ 17 | vec3 worldPosition; 18 | // vec3 mColor; 19 | } VertexIn[4]; 20 | 21 | out VertexData{ 22 | vec2 mTexCoord; 23 | // vec3 mColor; 24 | } VertexOut; 25 | 26 | vec2 toScreenSpace( vec4 vertex ) 27 | { 28 | return vec2( vertex.xy / vertex.w ) * WIN_SCALE; 29 | } 30 | 31 | vec4 clip_near_plane(vec4 pt1, vec4 pt2) 32 | { 33 | // Figure out intersection point of line pt1-pt2 and near plane in homogenous coordinates. 34 | // Near plane is z=-1 in NDC, that means in homogenous coordinates that's z/w=-1 35 | // Going from line equation P = P1 + u * (P2 - P1) we need to figure out "u" 36 | // In the above equation P, P1, P2 are vectors, so individual coordinate values are 37 | // x = x1 + u * (x2 - x1) and so on for y,z,w as well. Now combining near plane equation z/w=-1 38 | // with line equation gives us the following equation for "u" (it's easy to do the math on paper) 39 | 40 | float u = (-pt1.z - pt1.w) / ((pt2.z-pt1.z) + (pt2.w - pt1.w)); 41 | return pt1 + (pt2-pt1)*u; 42 | } 43 | 44 | void main( void ) 45 | { 46 | vec4 px0 = gl_in[0].gl_Position; 47 | vec4 px1 = gl_in[1].gl_Position; 48 | vec4 px2 = gl_in[2].gl_Position; 49 | vec4 px3 = gl_in[3].gl_Position; 50 | 51 | // This implements rejection of lines from Cohen-Sutherland line clipping algorithm. 52 | // Thanks to that we filter out majority of lines that may otherwise cause issues. 53 | // Lines that can't be trivially rejected, should be further clipped 54 | int px1c = int(px1.w+px1.x<0) << 0 | int(px1.w-px1.x<0) << 1 | int(px1.w+px1.y<0) << 2 | int(px1.w-px1.y<0) << 3 | int(px1.w+px1.z<0) << 4 | int(px1.w-px1.z<0) << 5; 55 | int px2c = int(px2.w+px2.x<0) << 0 | int(px2.w-px2.x<0) << 1 | int(px2.w+px2.y<0) << 2 | int(px2.w-px2.y<0) << 3 | int(px2.w+px2.z<0) << 4 | int(px2.w-px2.z<0) << 5; 56 | if ((px1c & px2c) != 0) 57 | return; // trivial reject 58 | 59 | // Perform line clipping with near plane if needed. We search for intersection between the line and the near plane. 60 | // In case the near plane intersects line between segment's endpoints, we need to adjust the line 61 | // otherwise we would use completely non-sense points when points get 'behind' the camera. 62 | // It seems we don't need to clip against other five planes - only the near plane is critical because 63 | // that turns the coordinates after perspective division in toScreenSpace() into a mess because of w < 1 64 | if ((px1c & 16) != 0) 65 | { 66 | // first point is in front of the near plane - need to clip it 67 | px1 = clip_near_plane(px1, px2); 68 | px0 = px1; 69 | } 70 | if ((px2c & 16) != 0) 71 | { 72 | // second point is in front of the near plane - need to clip it 73 | px2 = clip_near_plane(px1, px2); 74 | px3 = px2; 75 | } 76 | 77 | // get the four vertices passed to the shader: 78 | vec2 p0 = toScreenSpace( px0 ); // start of previous segment 79 | vec2 p1 = toScreenSpace( px1 ); // end of previous segment, start of current segment 80 | vec2 p2 = toScreenSpace( px2 ); // end of current segment, start of next segment 81 | vec2 p3 = toScreenSpace( px3 ); // end of next segment 82 | 83 | // these are already 'final' depths in range [0,1] so we don't need to further transform them 84 | float p1z = px1.z / px1.w; 85 | float p2z = px2.z / px2.w; 86 | 87 | // determine the direction of each of the 3 segments (previous, current, next) 88 | vec2 v0 = normalize( p1 - p0 ); 89 | vec2 v1 = normalize( p2 - p1 ); 90 | vec2 v2 = normalize( p3 - p2 ); 91 | 92 | // Martin's addition to fix flicker on starting ending point of lines 93 | if (p1 == p0) v0 = v1; 94 | if (p3 == p2) v2 = v1; 95 | 96 | // determine the normal of each of the 3 segments (previous, current, next) 97 | vec2 n0 = vec2( -v0.y, v0.x ); 98 | vec2 n1 = vec2( -v1.y, v1.x ); 99 | vec2 n2 = vec2( -v2.y, v2.x ); 100 | 101 | // determine miter lines by averaging the normals of the 2 segments 102 | vec2 miter_a = normalize( n0 + n1 ); // miter at start of current segment 103 | vec2 miter_b = normalize( n1 + n2 ); // miter at end of current segment 104 | 105 | // determine the length of the miter by projecting it onto normal and then inverse it 106 | float length_a = THICKNESS / dot( miter_a, n1 ); 107 | float length_b = THICKNESS / dot( miter_b, n1 ); 108 | 109 | // prevent excessively long miters at sharp corners 110 | if( dot( v0, v1 ) < -MITER_LIMIT ) { 111 | miter_a = n1; 112 | length_a = THICKNESS; 113 | 114 | // close the gap 115 | if( dot( v0, n1 ) > 0 ) { 116 | VertexOut.mTexCoord = vec2( 0, 0 ); 117 | //VertexOut.mColor = VertexIn[1].mColor; 118 | gl_Position = vec4( ( p1 + THICKNESS * n1 ) / WIN_SCALE, p1z, 1.0 ); 119 | EmitVertex(); 120 | 121 | VertexOut.mTexCoord = vec2( 0, 0 ); 122 | //VertexOut.mColor = VertexIn[1].mColor; 123 | gl_Position = vec4( ( p1 + THICKNESS * n0 ) / WIN_SCALE, p1z, 1.0 ); 124 | EmitVertex(); 125 | 126 | VertexOut.mTexCoord = vec2( 0, 0.5 ); 127 | //VertexOut.mColor = VertexIn[1].mColor; 128 | gl_Position = vec4( p1 / WIN_SCALE, p1z, 1.0 ); 129 | EmitVertex(); 130 | 131 | EndPrimitive(); 132 | } 133 | else { 134 | VertexOut.mTexCoord = vec2( 0, 1 ); 135 | //VertexOut.mColor = VertexIn[1].mColor; 136 | gl_Position = vec4( ( p1 - THICKNESS * n0 ) / WIN_SCALE, p1z, 1.0 ); 137 | EmitVertex(); 138 | 139 | VertexOut.mTexCoord = vec2( 0, 1 ); 140 | //VertexOut.mColor = VertexIn[1].mColor; 141 | gl_Position = vec4( ( p1 - THICKNESS * n1 ) / WIN_SCALE, p1z, 1.0 ); 142 | EmitVertex(); 143 | 144 | VertexOut.mTexCoord = vec2( 0, 0.5 ); 145 | //VertexOut.mColor = VertexIn[1].mColor; 146 | gl_Position = vec4( p1 / WIN_SCALE, p1z, 1.0 ); 147 | EmitVertex(); 148 | 149 | EndPrimitive(); 150 | } 151 | } 152 | 153 | if( dot( v1, v2 ) < -MITER_LIMIT ) { 154 | miter_b = n1; 155 | length_b = THICKNESS; 156 | } 157 | 158 | // generate the triangle strip 159 | VertexOut.mTexCoord = vec2( 0, 0 ); 160 | //VertexOut.mColor = VertexIn[1].mColor; 161 | gl_Position = vec4( ( p1 + length_a * miter_a ) / WIN_SCALE, p1z, 1.0 ); 162 | EmitVertex(); 163 | 164 | VertexOut.mTexCoord = vec2( 0, 1 ); 165 | //VertexOut.mColor = VertexIn[1].mColor; 166 | gl_Position = vec4( ( p1 - length_a * miter_a ) / WIN_SCALE, p1z, 1.0 ); 167 | EmitVertex(); 168 | 169 | VertexOut.mTexCoord = vec2( 0, 0 ); 170 | //VertexOut.mColor = VertexIn[2].mColor; 171 | gl_Position = vec4( ( p2 + length_b * miter_b ) / WIN_SCALE, p2z, 1.0 ); 172 | EmitVertex(); 173 | 174 | VertexOut.mTexCoord = vec2( 0, 1 ); 175 | //VertexOut.mColor = VertexIn[2].mColor; 176 | gl_Position = vec4( ( p2 - length_b * miter_b ) / WIN_SCALE, p2z, 1.0 ); 177 | EmitVertex(); 178 | 179 | EndPrimitive(); 180 | } 181 | -------------------------------------------------------------------------------- /lines/lines.vert: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | uniform mat4 modelViewProjection; 4 | 5 | in vec3 vertexPosition; 6 | //in vec3 ciColor; 7 | 8 | out VertexData{ 9 | // vec3 mColor; 10 | vec3 worldPosition; 11 | } VertexOut; 12 | 13 | 14 | void main(void) 15 | { 16 | //VertexOut.mColor = ciColor; 17 | gl_Position = modelViewProjection * vec4( vertexPosition, 1.0 ); 18 | VertexOut.worldPosition = vertexPosition; 19 | } 20 | -------------------------------------------------------------------------------- /lines/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "drawdata.h" 8 | 9 | 10 | int main(int argc, char* argv[]) 11 | { 12 | QGuiApplication app(argc, argv); 13 | 14 | QVector3D px(0,-1,0); // used as restart primitive (index zero) 15 | 16 | QVector3D p1(0,0,0); 17 | QVector3D p2(0,5,0); 18 | QVector3D p3(-5, 5, -5); 19 | QVector3D p4(5, 5, 5); 20 | QVector3D p5(-5, 5, 5); 21 | QVector3D p6(5, 5, -5); 22 | QVector3D p7(5, 1, -5); 23 | 24 | QVector pos; 25 | QVector indices; 26 | 27 | pos << px << p1 << p2 << p3 << p4 << p5 << p6 << p7; 28 | 29 | indices << 1 << 1 << 2 << 2 << 0; 30 | indices << 3 << 3 << 4 << 4 << 0; 31 | indices << 5 << 5 << 6 << 7 << 7 << 0; 32 | 33 | // make a cube for testing 34 | int i0 = 8; 35 | QVector3D o(2,2,5); 36 | pos << QVector3D(-1,-1,-1)+o << QVector3D(+1,-1,-1)+o << QVector3D(+1,+1,-1)+o << QVector3D(-1,+1,-1)+o; 37 | pos << QVector3D(-1,-1,+1)+o << QVector3D(+1,-1,+1)+o << QVector3D(+1,+1,+1)+o << QVector3D(-1,+1,+1)+o; 38 | indices << i0+3 << i0+0 << i0+1 << i0+2 << i0+3 << i0+0 << i0+1 << 0; 39 | indices << i0+7 << i0+4 << i0+5 << i0+6 << i0+7 << i0+4 << i0+5 << 0; 40 | indices << i0+3 << i0+0 << i0+4 << i0+7 << i0+3 << i0+0 << i0+4 << 0; 41 | indices << i0+2 << i0+1 << i0+5 << i0+6 << i0+2 << i0+1 << i0+5 << 0; 42 | 43 | // lines adjacency primitive: each line is given as (prev)-(p0)-(p1)-(next) 44 | 45 | LineMeshGeometry lmg; 46 | lmg.setVertices(pos, indices); 47 | 48 | Qt3DExtras::Quick::Qt3DQuickWindow view; 49 | view.setTitle("Lines"); 50 | view.resize(1600, 800); 51 | view.engine()->qmlEngine()->rootContext()->setContextProperty("_window", &view); 52 | view.engine()->qmlEngine()->rootContext()->setContextProperty("_lmg", &lmg); 53 | view.setSource(QUrl("qrc:/main.qml")); 54 | view.show(); 55 | 56 | return app.exec(); 57 | } 58 | -------------------------------------------------------------------------------- /lines/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.1 as QQ2 2 | import Qt3D.Core 2.0 3 | import Qt3D.Render 2.0 4 | import Qt3D.Input 2.0 5 | import Qt3D.Extras 2.0 6 | 7 | Entity { 8 | 9 | components: [ 10 | rendSettings, 11 | inputSettings 12 | ] 13 | 14 | InputSettings { id: inputSettings } 15 | 16 | RenderSettings { 17 | id: rendSettings 18 | activeFrameGraph: RenderSurfaceSelector { 19 | Viewport { 20 | normalizedRect: Qt.rect(0,0,1,1) 21 | CameraSelector { 22 | camera: camera 23 | ClearBuffers { 24 | buffers: ClearBuffers.ColorDepthBuffer 25 | } 26 | } 27 | } 28 | } 29 | 30 | } 31 | 32 | Camera { 33 | id: camera 34 | projectionType: CameraLens.PerspectiveProjection 35 | fieldOfView: 45 36 | aspectRatio: _window.width / _window.height 37 | nearPlane: 0.1 38 | farPlane: 100.0 39 | position: Qt.vector3d(0.0, 10.0, 20.0) 40 | viewCenter: Qt.vector3d(0.0, 0.0, 0.0) 41 | upVector: Qt.vector3d(0.0, 1.0, 0.0) 42 | 43 | // debugging of line clipping 44 | //position: Qt.vector3d(1.3532, 2.14093, 4.8184) 45 | //viewCenter: Qt.vector3d(23.6686, 0.746005, 4.53359) 46 | //upVector: Qt.vector3d(0.0623777, 0.998053, -0.000796974) 47 | 48 | function dumpCameraSetup() { 49 | console.log("position " + position) 50 | console.log("viewCenter" + viewCenter) 51 | console.log("upVector" + upVector) 52 | } 53 | 54 | function viewProjMatrix() { 55 | var viewMat = Qt.matrix4x4(); 56 | viewMat.lookAt(camera.position, camera.viewCenter, camera.upVector) 57 | var viewProjMat = camera.projectionMatrix.times( viewMat ) 58 | return viewProjMat; 59 | } 60 | 61 | function projectPoint(pt) { // expecting vector3d 62 | return viewProjMatrix().times(Qt.vector4d(pt.x, pt.y, pt.z, 1)) 63 | } 64 | 65 | function perspectiveDivide(pt) { // expecting vector4d 66 | return Qt.vector3d(pt.x/pt.w, pt.y/pt.w, pt.z/pt.w) 67 | } 68 | 69 | function regCode(pt) { 70 | console.log( (pt.w+pt.x<0) + "-" + (pt.w-pt.x<0) + "-" + (pt.w+pt.y<0) + "-" + (pt.w-pt.y<0) + "-" + (pt.w+pt.z<0) + "-" + (pt.w-pt.z<0) ) 71 | } 72 | 73 | function regCodeX(pt) { 74 | return (pt.w+pt.x<0) << 0 | 75 | (pt.w-pt.x<0) << 1 | 76 | (pt.w+pt.y<0) << 2 | 77 | (pt.w-pt.y<0) << 3 | 78 | (pt.w+pt.z<0) << 4 | 79 | (pt.w-pt.z<0) << 5 80 | } 81 | 82 | function tstLineClipping() { 83 | 84 | //dumpCameraSetup() 85 | 86 | // when trying c++ version of our material 87 | //_lineMaterial.setCameraParameters(camera.position, camera.viewVector, camera.nearPlane) 88 | //_lineMaterial.setViewportSize(Qt.size(_window.width, _window.height)) 89 | 90 | // this code does calculation of intersection between line and camera's near plane 91 | // ... used in geometry shader later to fix lines with points "behind" the camera 92 | var pt1in = Qt.vector3d(1,1,4) 93 | var pt2in = Qt.vector3d(3,1,4) 94 | //var pt1in = Qt.vector3d(-5,5,5) 95 | //var pt2in = Qt.vector3d(5,5,-5) 96 | var lineDir = pt2in.minus(pt1in) 97 | 98 | var pt1 = projectPoint(pt1in) 99 | var pt2 = projectPoint(pt2in) 100 | var pt1x = perspectiveDivide(pt1) 101 | var pt2x = perspectiveDivide(pt2) 102 | //console.log("projected " + pt1 + " -- " + pt2) 103 | console.log("pers " + pt1x + " -- " + pt2x ) 104 | 105 | regCode(pt1) 106 | regCode(pt2) 107 | var c1 = regCodeX(pt1) 108 | var c2 = regCodeX(pt2) 109 | if (!(c1|c2)) 110 | console.log("accept") 111 | else if (c1&c2) 112 | console.log("reject") 113 | else 114 | console.log("clip!") 115 | 116 | if ((c1 & 16) || (c2 & 16)) 117 | console.log("clip near!") 118 | 119 | var u = (-pt1.z - pt1.w) / ((pt2.z-pt1.z) + (pt2.w - pt1.w)); 120 | var px = pt1.plus(pt2.minus(pt1).times(u)) 121 | //var px = Qt.vector4d( pt1.x + u*(pt2.x-pt1.x), pt1.y + u*(pt2.y-pt1.y), pt1.z + u*(pt2.z-pt1.z), pt1.w + u*(pt2.w-pt1.w) ); 122 | var pxx = perspectiveDivide(px); 123 | console.log("int point " + pxx + " u " + u) 124 | 125 | var nearPlaneNormal = camera.viewVector 126 | var nearPlanePoint = camera.position.plus(nearPlaneNormal.times(camera.nearPlane)) 127 | var d = (nearPlaneNormal.dotProduct(nearPlanePoint.minus(pt1in)) / lineDir.dotProduct(nearPlaneNormal)) 128 | var intPt = pt1in.plus(lineDir.times(d)) 129 | 130 | console.log("int_p1" + "---" + " " + d + " " + intPt) 131 | 132 | //var lineDir2 = pt1in.minus(pt2in) 133 | //var d2 = (nearPlaneNormal.dotProduct(nearPlanePoint.minus(pt2in)) / lineDir2.dotProduct(nearPlaneNormal)) 134 | //var intPt2 = pt2in.plus(lineDir2.times(d)) 135 | 136 | //console.log("int_p2" + "---" + " " + d2 + " " + intPt2) 137 | 138 | } 139 | 140 | onPositionChanged: tstLineClipping() 141 | onViewCenterChanged: tstLineClipping() 142 | } 143 | 144 | FirstPersonCameraController { camera: camera } 145 | 146 | Entity { 147 | PlaneMesh { 148 | id: pm 149 | width: 20 150 | height: 20 151 | } 152 | PhongMaterial { 153 | id: pmm 154 | ambient: Qt.rgba(0.0,0.0,0.7,1) 155 | } 156 | components: [ pm, pmm ] 157 | } 158 | 159 | Entity { 160 | GeometryRenderer { 161 | id: gr 162 | primitiveType: GeometryRenderer.LineStripAdjacency 163 | primitiveRestartEnabled: true 164 | restartIndexValue: 0 165 | //primitiveType: GeometryRenderer.Lines 166 | vertexCount: _lmg.count // _posData.count 167 | geometry: _lmg 168 | 169 | // for some unknown reason this simply does not work 170 | // (anyway it's a pain that one can't put data into QBuffer from JS) 171 | // 172 | // geometry: Geometry { 173 | // attributes: [ 174 | // Attribute { 175 | // name: Attribute.defaultPositionAttributeName() 176 | // attributeType: Attribute.VertexAttribute 177 | // vertexBaseType: Attribute.Float 178 | // vertexSize: 3 179 | // byteOffset: 0 180 | // //byteStride: 4*3 181 | // buffer: _posData.buffer 182 | // count: _posData.count 183 | // } 184 | // ] 185 | // } 186 | } 187 | 188 | 189 | Material { 190 | id: grm 191 | 192 | parameters: [ 193 | Parameter { name: "THICKNESS"; value: 10 }, 194 | Parameter { name: "MITER_LIMIT"; value: -1 }, // 0.75 }, 195 | Parameter { name: "WIN_SCALE"; value: Qt.size(_window.width,_window.height) }, 196 | Parameter { name: "tex0"; value: txt }, 197 | Parameter { name: "lineColor"; value: Qt.rgba(0,1,0,1) }, 198 | Parameter { name: "useTex"; value: false }, 199 | 200 | Parameter { name: "camNearPlanePoint"; value: camera.position.plus(camera.viewVector.times(camera.nearPlane)) }, 201 | Parameter { name: "camNearPlaneNormal"; value: camera.viewVector } 202 | ] 203 | 204 | Texture2D { 205 | id : txt 206 | generateMipMaps : false 207 | magnificationFilter : Texture.Linear 208 | minificationFilter : Texture.Linear 209 | textureImages: [ 210 | TextureImage { 211 | source: "qrc:/line-texture.png" 212 | } 213 | ] 214 | } 215 | 216 | effect: Effect { 217 | techniques: Technique { 218 | graphicsApiFilter { api: GraphicsApiFilter.OpenGL; profile: GraphicsApiFilter.CoreProfile; majorVersion: 3; minorVersion: 1 } 219 | renderPasses: [ 220 | RenderPass { 221 | 222 | // enable alpha blending 223 | renderStates: [ 224 | BlendEquation { 225 | blendFunction: BlendEquation.Add 226 | }, 227 | BlendEquationArguments { 228 | sourceRgb: BlendEquationArguments.SourceAlpha 229 | destinationRgb: BlendEquationArguments.OneMinusSourceAlpha 230 | } 231 | ] 232 | 233 | shaderProgram: ShaderProgram { 234 | vertexShaderCode: loadSource("qrc:/shaders/lines.vert") 235 | geometryShaderCode: loadSource("qrc:/shaders/lines.geom") 236 | fragmentShaderCode: loadSource("qrc:/shaders/lines.frag") 237 | } 238 | } 239 | ] 240 | } 241 | } 242 | } 243 | 244 | components: [ gr, grm ] 245 | } 246 | 247 | Entity { 248 | PhongMaterial { 249 | id: redMat 250 | ambient: "red" 251 | } 252 | SphereMesh { 253 | id: sphereMesh 254 | } 255 | Transform { 256 | id: sphereTransform 257 | translation: Qt.vector3d(0,5,0) 258 | } 259 | 260 | components: [ redMat, sphereMesh, sphereTransform ] 261 | } 262 | 263 | Entity { 264 | PhongMaterial { 265 | id: grayMat 266 | ambient: "gray" 267 | } 268 | CuboidMesh { 269 | id: cubeMesh 270 | } 271 | Transform { 272 | id: cubeTransform 273 | translation: Qt.vector3d(2,2,5) 274 | scale: 2 275 | } 276 | 277 | components: [ grayMat, cubeMesh, cubeTransform ] 278 | //enabled: false 279 | } 280 | 281 | } 282 | -------------------------------------------------------------------------------- /lines/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | line-texture.png 5 | 6 | 7 | -------------------------------------------------------------------------------- /lines/shaders.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | lines.frag 4 | lines.geom 5 | lines.vert 6 | 7 | 8 | -------------------------------------------------------------------------------- /logdepth-bad.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wonder-sk/qt3d-experiments/eef89210f0cdee05443d62442a640c04978fc63c/logdepth-bad.mp4 -------------------------------------------------------------------------------- /logdepth-good.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wonder-sk/qt3d-experiments/eef89210f0cdee05443d62442a640c04978fc63c/logdepth-good.mp4 -------------------------------------------------------------------------------- /logdepth/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | project(logdepth VERSION 0.1 LANGUAGES CXX) 4 | 5 | set(CMAKE_AUTOUIC ON) 6 | set(CMAKE_AUTOMOC ON) 7 | set(CMAKE_AUTORCC ON) 8 | 9 | set(CMAKE_CXX_STANDARD 17) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets 3DCore 3DRender 3DExtras) 13 | find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets 3DCore 3DRender 3DExtras) 14 | 15 | set(PROJECT_SOURCES 16 | main.cpp 17 | resources.qrc 18 | ) 19 | 20 | add_executable(logdepth 21 | ${PROJECT_SOURCES} 22 | ) 23 | 24 | target_link_libraries(logdepth PRIVATE 25 | Qt${QT_VERSION_MAJOR}::Widgets 26 | Qt${QT_VERSION_MAJOR}::3DCore 27 | Qt${QT_VERSION_MAJOR}::3DRender 28 | Qt${QT_VERSION_MAJOR}::3DExtras 29 | ) 30 | 31 | install(TARGETS logdepth 32 | BUNDLE DESTINATION . 33 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 34 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 35 | ) 36 | -------------------------------------------------------------------------------- /logdepth/basic.frag: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | uniform vec3 color; 4 | 5 | uniform float farPlane; 6 | 7 | in float vFragDepth; 8 | 9 | out vec4 outColor; 10 | 11 | 12 | void main() { 13 | outColor = vec4(color, 1.0); 14 | 15 | // TODO: optimize like in three.js / outerra: log2, put the whole 1/log(1+farPlane) to a uniform 16 | gl_FragDepth = log( vFragDepth ) / log( 1.0 + farPlane ); 17 | } 18 | -------------------------------------------------------------------------------- /logdepth/basic.vert: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | uniform mat4 mvp; 4 | 5 | 6 | in vec3 vertexPosition; 7 | 8 | out float vFragDepth; 9 | 10 | void main() { 11 | gl_Position = mvp * vec4(vertexPosition, 1.0); 12 | 13 | vFragDepth = 1.0 + gl_Position.w; 14 | } 15 | -------------------------------------------------------------------------------- /logdepth/main.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This code demonstrates logarithmic depth buffer rendering technique. 3 | * It is a way to increase the precision of the depth buffer when using 4 | * large depth range (e.g. when creating a virtual globe). The idea is 5 | * that the fragment shader sets depth of fragments to make a better 6 | * use of the range [0..1] instead of keeping the depth value that came 7 | * out from the projection matrix. 8 | * 9 | * frag_depth = log(1 + depth) / log(1 + far_plane_depth) 10 | * 11 | * Nowadays, the issue with depth buffer precision is most commonly handled 12 | * with "Reverse Z" technique, which does not need any changes in shaders, 13 | * but unfortunately at this point, it can't be used in Qt3D with OpenGL 14 | * renderer, because that technique needs a call to glClipControl(), which 15 | * is currently not supported by Qt3D. 16 | * 17 | * The test scene is just a sphere and two planes partially intersecting the sphere, 18 | * with near and far planes set so that the depth buffer precision issue becomes 19 | * clearly visible. To see how things would behave without logarithmic depth buffer, 20 | * just comment out the line with "gl_FragDepth" in basic.frag. 21 | * 22 | * For more see: 23 | * https://virtualglobebook.com/ 24 | * https://outerra.blogspot.com/2013/07/logarithmic-depth-buffer-optimizations.html 25 | */ 26 | 27 | #include 28 | 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | 54 | 55 | QTimer *timer; 56 | int counter = 0; 57 | 58 | 59 | class MyEventFilter : public QObject { 60 | 61 | public: 62 | explicit MyEventFilter(QObject* parent = nullptr) : QObject(parent) {} 63 | 64 | protected: 65 | bool eventFilter(QObject* watched, QEvent* event) override { 66 | if (event->type() == QEvent::KeyPress) { 67 | QKeyEvent* keyEvent = static_cast(event); 68 | if (keyEvent->key() == Qt::Key_Space) { 69 | 70 | qDebug() << "SPACE!"; 71 | if ( timer->isActive() ) timer->stop(); else timer->start(); 72 | 73 | // You can optionally consume the event here 74 | // event->accept(); 75 | return true; 76 | } 77 | } 78 | // Pass the event to the parent or base class if not handled 79 | return QObject::eventFilter(watched, event); 80 | } 81 | }; 82 | 83 | 84 | 85 | 86 | Qt3DRender::QMaterial* basicMaterial( QColor color ) 87 | { 88 | Qt3DRender::QMaterial *material = new Qt3DRender::QMaterial(); 89 | 90 | Qt3DRender::QShaderProgram* shaderProgram = new Qt3DRender::QShaderProgram(); 91 | shaderProgram->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/basic.vert")))); 92 | shaderProgram->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/basic.frag")))); 93 | 94 | Qt3DRender::QRenderPass *renderPass = new Qt3DRender::QRenderPass(); 95 | renderPass->setShaderProgram(shaderProgram); 96 | 97 | Qt3DRender::QTechnique* technique = new Qt3DRender::QTechnique(); 98 | technique->addRenderPass(renderPass); 99 | 100 | technique->graphicsApiFilter()->setApi( Qt3DRender::QGraphicsApiFilter::OpenGL ); 101 | technique->graphicsApiFilter()->setProfile( Qt3DRender::QGraphicsApiFilter::CoreProfile ); 102 | technique->graphicsApiFilter()->setMajorVersion( 3 ); 103 | technique->graphicsApiFilter()->setMinorVersion( 3 ); 104 | Qt3DRender::QFilterKey *filterKey = new Qt3DRender::QFilterKey(); 105 | filterKey->setName( QStringLiteral( "renderingStyle" ) ); 106 | filterKey->setValue( QStringLiteral( "forward" ) ); 107 | technique->addFilterKey( filterKey ); 108 | 109 | technique->addParameter( new Qt3DRender::QParameter( QStringLiteral( "color" ), color ) ); 110 | 111 | technique->addParameter( new Qt3DRender::QParameter( QStringLiteral( "farPlane" ), 1000000.0 ) ); 112 | 113 | Qt3DRender::QEffect* effect = new Qt3DRender::QEffect(); 114 | effect->addTechnique(technique); 115 | material->setEffect(effect); 116 | return material; 117 | } 118 | 119 | 120 | 121 | int main(int argc, char *argv[]) 122 | { 123 | QApplication a(argc, argv); 124 | 125 | // Create the 3D window 126 | Qt3DExtras::Qt3DWindow *view = new Qt3DExtras::Qt3DWindow(); 127 | 128 | // geometries 129 | 130 | Qt3DExtras::QPlaneMesh *planeMeshA = new Qt3DExtras::QPlaneMesh; 131 | 132 | Qt3DExtras::QPlaneMesh *planeMeshB = new Qt3DExtras::QPlaneMesh; 133 | 134 | Qt3DExtras::QSphereMesh *sphereMesh = new Qt3DExtras::QSphereMesh; 135 | sphereMesh->setRadius(2.0f); 136 | 137 | // materials 138 | 139 | Qt3DRender::QMaterial *materialRed = basicMaterial(Qt::red); 140 | Qt3DRender::QMaterial *materialGreen = basicMaterial(Qt::green); 141 | Qt3DRender::QMaterial *materialBlue = basicMaterial(Qt::blue); 142 | 143 | // transforms 144 | 145 | Qt3DCore::QTransform *sphereTransform = new Qt3DCore::QTransform; 146 | sphereTransform->setTranslation(QVector3D(0.0f, 0.0f, 0.0f)); 147 | 148 | Qt3DCore::QTransform *planeATransform = new Qt3DCore::QTransform; 149 | planeATransform->setTranslation(QVector3D(-0.51f, 1.98f, 0.0f)); 150 | 151 | Qt3DCore::QTransform *planeBTransform = new Qt3DCore::QTransform; 152 | planeBTransform->setTranslation(QVector3D(+0.51f, 1.98f, 0.0f)); 153 | 154 | // scene 155 | 156 | Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity; 157 | 158 | Qt3DCore::QEntity *sphereEntity = new Qt3DCore::QEntity(rootEntity); 159 | sphereEntity->addComponent(sphereMesh); 160 | sphereEntity->addComponent(materialRed); 161 | sphereEntity->addComponent(sphereTransform); 162 | 163 | Qt3DCore::QEntity *planeAEntity = new Qt3DCore::QEntity(rootEntity); 164 | planeAEntity->addComponent(planeMeshA); 165 | planeAEntity->addComponent(materialGreen); 166 | planeAEntity->addComponent(planeATransform); 167 | 168 | Qt3DCore::QEntity *planeBEntity = new Qt3DCore::QEntity(rootEntity); 169 | planeBEntity->addComponent(planeMeshB); 170 | planeBEntity->addComponent(materialBlue); 171 | planeBEntity->addComponent(planeBTransform); 172 | 173 | // 174 | 175 | Qt3DExtras::QForwardRenderer *forwardRenderer = view->defaultFrameGraph(); 176 | 177 | QVector3D cameraBasePosition(1.0, 10.0, 0.0); 178 | QVector3D cameraBaseViewCenter(0.0, 0.0, 0.0); 179 | Qt3DRender::QCamera *camera = qobject_cast( forwardRenderer->camera() ); 180 | camera->setPosition( cameraBasePosition ); 181 | camera->setViewCenter( cameraBaseViewCenter ); 182 | 183 | camera->setNearPlane(0.001); 184 | camera->setFarPlane(1000000); 185 | 186 | qDebug() << "near/far" << camera->nearPlane() << camera->farPlane(); 187 | 188 | // Add root entity to scene 189 | view->setRootEntity(rootEntity); 190 | 191 | view->show(); 192 | 193 | timer = new QTimer(view); 194 | timer->setInterval(20); 195 | 196 | MyEventFilter *myEventFilter = new MyEventFilter(view); 197 | view->installEventFilter(myEventFilter); 198 | 199 | float animationRange = 0.1; 200 | 201 | QObject::connect( timer, &QTimer::timeout, [camera, animationRange, cameraBasePosition, cameraBaseViewCenter] { 202 | ++counter; 203 | double t = (double) (counter % 50) / 50.; 204 | QVector3D tOffset(sin(t*3.14159*2) * animationRange, 0, cos(t*3.14159*2) * animationRange); 205 | 206 | camera->setPosition( cameraBasePosition + tOffset ); 207 | camera->setViewCenter( cameraBaseViewCenter + tOffset ); 208 | 209 | }); 210 | 211 | 212 | return a.exec(); 213 | } 214 | -------------------------------------------------------------------------------- /logdepth/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | basic.frag 4 | basic.vert 5 | 6 | 7 | -------------------------------------------------------------------------------- /msaa-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wonder-sk/qt3d-experiments/eef89210f0cdee05443d62442a640c04978fc63c/msaa-off.png -------------------------------------------------------------------------------- /msaa-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wonder-sk/qt3d-experiments/eef89210f0cdee05443d62442a640c04978fc63c/msaa-on.png -------------------------------------------------------------------------------- /msaa/fun3d-msaa.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | QT += 3dcore 3drender 3dinput 3dquick qml quick 3dquickextras 3dextras 3 | 4 | # The following define makes your compiler emit warnings if you use 5 | # any Qt feature that has been marked deprecated (the exact warnings 6 | # depend on your compiler). Refer to the documentation for the 7 | # deprecated API to know how to port your code away from it. 8 | DEFINES += QT_DEPRECATED_WARNINGS 9 | 10 | # You can also make your code fail to compile if it uses deprecated APIs. 11 | # In order to do so, uncomment the following line. 12 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 13 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 14 | 15 | SOURCES += \ 16 | main.cpp 17 | 18 | RESOURCES += qml.qrc 19 | 20 | # Additional import path used to resolve QML modules in Qt Creator's code model 21 | QML_IMPORT_PATH = 22 | 23 | # Additional import path used to resolve QML modules just for Qt Quick Designer 24 | QML_DESIGNER_IMPORT_PATH = 25 | 26 | # Default rules for deployment. 27 | qnx: target.path = /tmp/$${TARGET}/bin 28 | else: unix:!android: target.path = /opt/$${TARGET}/bin 29 | !isEmpty(target.path): INSTALLS += target 30 | -------------------------------------------------------------------------------- /msaa/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | int main(int argc, char* argv[]) 9 | { 10 | QGuiApplication app(argc, argv); 11 | 12 | // TODO: not sure how to set surface format for Qt3DQuickWindow in order 13 | // to set up number of samples per pixel for multisample anti-aliasing 14 | // (calling view.setFormat(...) with changed number of samples caused OpenGL context creation failures) 15 | 16 | Qt3DExtras::Quick::Qt3DQuickWindow view; 17 | view.setTitle("Multisample anti-aliasing (MSAA)"); 18 | view.resize(1600, 800); 19 | view.engine()->qmlEngine()->rootContext()->setContextProperty("_window", &view); 20 | view.engine()->qmlEngine()->rootContext()->setContextProperty("_dpr", view.devicePixelRatio()); 21 | view.setSource(QUrl("qrc:/main.qml")); 22 | view.show(); 23 | 24 | return app.exec(); 25 | } 26 | -------------------------------------------------------------------------------- /msaa/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.1 as QQ2 2 | import Qt3D.Core 2.10 3 | import Qt3D.Render 2.10 4 | import Qt3D.Input 2.10 5 | import Qt3D.Extras 2.10 6 | 7 | Entity { 8 | 9 | property bool msaa: true 10 | 11 | components: [ 12 | rendSettings, 13 | inputSettings 14 | ] 15 | 16 | InputSettings { id: inputSettings } 17 | 18 | KeyboardDevice { id: keyboardDevice } 19 | 20 | KeyboardHandler { 21 | focus: true 22 | sourceDevice: keyboardDevice 23 | onSpacePressed: { 24 | parent.msaa = !parent.msaa 25 | console.log("msaa:" + parent.msaa) 26 | } 27 | } 28 | 29 | RenderSettings { 30 | id: rendSettings 31 | activeFrameGraph: parent.msaa ? msaaFrameGraph : basicFrameGraph 32 | } 33 | 34 | // framegraph used when rendering without anti-aliasing - simple forward renderer 35 | RenderSurfaceSelector { 36 | id: basicFrameGraph 37 | Viewport { 38 | normalizedRect: Qt.rect(0,0,1,1) 39 | CameraSelector { 40 | camera: camera 41 | ClearBuffers { 42 | buffers: ClearBuffers.ColorDepthBuffer 43 | } 44 | } 45 | } 46 | } 47 | 48 | // framegraph used when rendering with anti-aliasing 49 | // - first the usual forward renderer, but rendering to a render target 50 | // with multisample textures, and with MSAA render state enabled 51 | // - then resolve the multisample framebuffer by doing blit 52 | RenderSurfaceSelector { 53 | id: msaaFrameGraph 54 | RenderTargetSelector { 55 | target: msaaFramebuffer 56 | RenderStateSet { 57 | renderStates: [ 58 | CullFace { mode: CullFace.Back }, 59 | DepthTest { depthFunction: DepthTest.Less }, 60 | MultiSampleAntiAliasing {} 61 | ] 62 | Viewport { 63 | normalizedRect: Qt.rect(0,0,1,1) 64 | CameraSelector { 65 | camera: camera 66 | ClearBuffers { 67 | buffers: ClearBuffers.ColorDepthBuffer 68 | } 69 | } 70 | } 71 | } 72 | } 73 | BlitFramebuffer { 74 | source: msaaFramebuffer 75 | sourceRect: Qt.rect(0, 0, _window.width * _dpr, _window.height * _dpr) 76 | destinationRect: sourceRect 77 | NoDraw {} 78 | } 79 | } 80 | 81 | RenderTarget { 82 | id: msaaFramebuffer 83 | attachments: [ 84 | RenderTargetOutput { 85 | attachmentPoint : RenderTargetOutput.Color0 86 | texture: Texture2DMultisample { 87 | width : _window.width * _dpr 88 | height : _window.height * _dpr 89 | format : Texture.RGBA8_UNorm 90 | } 91 | }, 92 | RenderTargetOutput { 93 | attachmentPoint : RenderTargetOutput.Depth 94 | texture: Texture2DMultisample { 95 | width : _window.width * _dpr 96 | height : _window.height * _dpr 97 | format: Texture.DepthFormat 98 | } 99 | } 100 | ] 101 | } 102 | 103 | Camera { 104 | id: camera 105 | projectionType: CameraLens.PerspectiveProjection 106 | fieldOfView: 45 107 | aspectRatio: _window.width / _window.height 108 | nearPlane: 0.1 109 | farPlane: 100.0 110 | position: Qt.vector3d(-5.0, 5.0, 20.0) 111 | viewCenter: Qt.vector3d(0.0, 0.0, 0.0) 112 | upVector: Qt.vector3d(0.0, 1.0, 0.0) 113 | } 114 | 115 | FirstPersonCameraController { camera: camera } 116 | 117 | Entity { 118 | PlaneMesh { 119 | id: pm 120 | width: 20 121 | height: 20 122 | } 123 | PhongMaterial { 124 | id: pmm 125 | ambient: Qt.rgba(0.0,0.0,0.7,1) 126 | } 127 | components: [ pm, pmm ] 128 | } 129 | 130 | Entity { 131 | PhongMaterial { 132 | id: redMat 133 | ambient: "red" 134 | } 135 | SphereMesh { 136 | id: sphereMesh 137 | } 138 | Transform { 139 | id: sphereTransform 140 | translation: Qt.vector3d(0,3,0) 141 | } 142 | 143 | components: [ redMat, sphereMesh, sphereTransform ] 144 | } 145 | 146 | Entity { 147 | PhongMaterial { 148 | id: grayMat 149 | ambient: "gray" 150 | } 151 | CuboidMesh { 152 | id: cubeMesh 153 | } 154 | Transform { 155 | id: cubeTransform 156 | translation: Qt.vector3d(1,2,4) 157 | scale: 2 158 | } 159 | 160 | components: [ grayMat, cubeMesh, cubeTransform ] 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /msaa/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | 5 | 6 | -------------------------------------------------------------------------------- /qt3d-arrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wonder-sk/qt3d-experiments/eef89210f0cdee05443d62442a640c04978fc63c/qt3d-arrows.png -------------------------------------------------------------------------------- /qt3d-billboards.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wonder-sk/qt3d-experiments/eef89210f0cdee05443d62442a640c04978fc63c/qt3d-billboards.png -------------------------------------------------------------------------------- /qt3d-edge-detection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wonder-sk/qt3d-experiments/eef89210f0cdee05443d62442a640c04978fc63c/qt3d-edge-detection.png -------------------------------------------------------------------------------- /qt3d-instanced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wonder-sk/qt3d-experiments/eef89210f0cdee05443d62442a640c04978fc63c/qt3d-instanced.png -------------------------------------------------------------------------------- /qt3d-lines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wonder-sk/qt3d-experiments/eef89210f0cdee05443d62442a640c04978fc63c/qt3d-lines.png -------------------------------------------------------------------------------- /qt3d-ssao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wonder-sk/qt3d-experiments/eef89210f0cdee05443d62442a640c04978fc63c/qt3d-ssao.png -------------------------------------------------------------------------------- /rtc-bad.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wonder-sk/qt3d-experiments/eef89210f0cdee05443d62442a640c04978fc63c/rtc-bad.mp4 -------------------------------------------------------------------------------- /rtc-good.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wonder-sk/qt3d-experiments/eef89210f0cdee05443d62442a640c04978fc63c/rtc-good.mp4 -------------------------------------------------------------------------------- /rtc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | project(rtc VERSION 0.1 LANGUAGES CXX) 4 | 5 | set(CMAKE_AUTOUIC ON) 6 | set(CMAKE_AUTOMOC ON) 7 | set(CMAKE_AUTORCC ON) 8 | 9 | set(CMAKE_CXX_STANDARD 17) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets 3DCore 3DRender 3DExtras) 13 | find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets 3DCore 3DRender 3DExtras) 14 | 15 | set(PROJECT_SOURCES 16 | main.cpp 17 | resources.qrc 18 | matrix4x4.cpp 19 | matrix4x4.h 20 | vector3d.cpp 21 | vector3d.h 22 | ) 23 | 24 | add_executable(rtc 25 | ${PROJECT_SOURCES} 26 | ) 27 | 28 | target_link_libraries(rtc PRIVATE 29 | Qt${QT_VERSION_MAJOR}::Widgets 30 | Qt${QT_VERSION_MAJOR}::3DCore 31 | Qt${QT_VERSION_MAJOR}::3DRender 32 | Qt${QT_VERSION_MAJOR}::3DExtras 33 | ) 34 | 35 | 36 | install(TARGETS rtc 37 | BUNDLE DESTINATION . 38 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 39 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 40 | ) 41 | -------------------------------------------------------------------------------- /rtc/basic.frag: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | uniform vec3 color; 4 | 5 | out vec4 outColor; 6 | 7 | 8 | void main() { 9 | outColor = vec4(color, 1.0); 10 | } 11 | -------------------------------------------------------------------------------- /rtc/basic.vert: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | uniform mat4 mvp; 4 | 5 | uniform mat4 my_mvp; 6 | 7 | in vec3 vertexPosition; 8 | 9 | void main() { 10 | //gl_Position = mvp * vec4(vertexPosition, 1.0); 11 | gl_Position = my_mvp * vec4(vertexPosition, 1.0); 12 | } 13 | -------------------------------------------------------------------------------- /rtc/main.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This code demonstrates the "relative to center" rendering technique with Qt3D. 3 | * It is useful when working with large coordinates (e.g. when creating a virtual 4 | * globe) without getting numerical issues (which would make objects jump around 5 | * when zoomed in). The idea is that we make sure to do model-view-projection (MVP) 6 | * matrix calculation in double precision, and only then convert it to single 7 | * precision floats that will be used on the GPU. 8 | * 9 | * How it is done with Qt3D: 10 | * - we have our own transform implementation (MyTransform) with double coordinates 11 | * - we have our own camera implementation (MyCamera) with double coordinates 12 | * - all our materials have a "my_mvp" uniform, that contains MVP matrix we have 13 | * calculated ourselves using MyTransform and MyCamera 14 | * - on any change of camera's transform or model's transform, we need to update 15 | * my_mvp uniform in the materials accordingly 16 | * 17 | * The test scene is just a sphere and two planes partially intersecting the sphere, 18 | * all moved far away from the scene's origin (see "megaOffset" variable) to 19 | * demonstrate the issue with large coordinates, and how that creates jitter 20 | * (especially when the animation is started by pressing SPACE) 21 | * 22 | * Check out rtc.py in this directory if you are just after the math bits. 23 | * 24 | * For more see: 25 | * https://virtualglobebook.com/ 26 | * https://help.agi.com/AGIComponents/html/BlogPrecisionsPrecisions.htm 27 | */ 28 | 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | 57 | #include "vector3d.h" 58 | #include "matrix4x4.h" 59 | 60 | 61 | // comment out to see how things would behave with single precision math 62 | #define USE_DOUBLE 63 | 64 | 65 | QTimer *timer; 66 | int counter = 0; 67 | 68 | 69 | Qt3DRender::QMaterial* basicMaterial( QColor color, Qt3DRender::QParameter **pParamMvp ) 70 | { 71 | Qt3DRender::QMaterial *material = new Qt3DRender::QMaterial(); 72 | 73 | Qt3DRender::QShaderProgram* shaderProgram = new Qt3DRender::QShaderProgram(); 74 | shaderProgram->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/basic.vert")))); 75 | shaderProgram->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/basic.frag")))); 76 | 77 | Qt3DRender::QRenderPass *renderPass = new Qt3DRender::QRenderPass(); 78 | renderPass->setShaderProgram(shaderProgram); 79 | 80 | Qt3DRender::QTechnique* technique = new Qt3DRender::QTechnique(); 81 | technique->addRenderPass(renderPass); 82 | 83 | technique->graphicsApiFilter()->setApi( Qt3DRender::QGraphicsApiFilter::OpenGL ); 84 | technique->graphicsApiFilter()->setProfile( Qt3DRender::QGraphicsApiFilter::CoreProfile ); 85 | technique->graphicsApiFilter()->setMajorVersion( 3 ); 86 | technique->graphicsApiFilter()->setMinorVersion( 3 ); 87 | Qt3DRender::QFilterKey *filterKey = new Qt3DRender::QFilterKey(); 88 | filterKey->setName( QStringLiteral( "renderingStyle" ) ); 89 | filterKey->setValue( QStringLiteral( "forward" ) ); 90 | technique->addFilterKey( filterKey ); 91 | 92 | technique->addParameter( new Qt3DRender::QParameter( QStringLiteral( "color" ), color ) ); 93 | 94 | *pParamMvp = new Qt3DRender::QParameter( QStringLiteral( "my_mvp" ), QMatrix4x4() ); 95 | technique->addParameter( *pParamMvp ); 96 | 97 | Qt3DRender::QEffect* effect = new Qt3DRender::QEffect(); 98 | effect->addTechnique(technique); 99 | material->setEffect(effect); 100 | return material; 101 | } 102 | 103 | 104 | 105 | Matrix4x4 floatToDoubleMatrix( QMatrix4x4 m ) 106 | { 107 | Matrix4x4 out; 108 | double *outData = out.data(); 109 | const float *mData = m.constData(); 110 | for ( int i = 0; i < 16; ++i ) 111 | outData[i] = mData[i]; // conversion float->double 112 | return out; 113 | } 114 | 115 | 116 | QMatrix4x4 doubleToFloatMatrix( Matrix4x4 m ) 117 | { 118 | QMatrix4x4 out; 119 | float *outData = out.data(); 120 | const double *mData = m.constData(); 121 | for ( int i = 0; i < 16; ++i ) 122 | outData[i] = mData[i]; // conversion double->float 123 | return out; 124 | } 125 | 126 | 127 | 128 | // framegraph like from QForwardRenderer but without QCameraSelector 129 | Qt3DRender::QFrameGraphNode * myFrameGraph() 130 | { 131 | Qt3DRender::QTechniqueFilter *techniqueFilter = new Qt3DRender::QTechniqueFilter(); 132 | Qt3DRender::QFilterKey *forwardRenderingStyle = new Qt3DRender::QFilterKey(techniqueFilter); 133 | forwardRenderingStyle->setName(QStringLiteral("renderingStyle")); 134 | forwardRenderingStyle->setValue(QStringLiteral("forward")); 135 | techniqueFilter->addMatch(forwardRenderingStyle); 136 | 137 | Qt3DRender::QRenderSurfaceSelector *surfaceSelector = new Qt3DRender::QRenderSurfaceSelector( techniqueFilter ); 138 | 139 | Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport( surfaceSelector ); 140 | viewport->setNormalizedRect(QRectF(0.0, 0.0, 1.0, 1.0)); 141 | 142 | Qt3DRender::QClearBuffers *clearBuffer = new Qt3DRender::QClearBuffers( viewport ); 143 | clearBuffer->setClearColor(Qt::gray); 144 | clearBuffer->setBuffers(Qt3DRender::QClearBuffers::ColorDepthBuffer); 145 | 146 | //Qt3DRender::QDebugOverlay *debugOverlay = new Qt3DRender::QDebugOverlay( clearBuffer ); 147 | 148 | return techniqueFilter; 149 | } 150 | 151 | 152 | 153 | // Our double precision 4x4 transform (replaces QTransform from Qt3D) 154 | class MyTransform : public Qt3DCore::QComponent { 155 | 156 | public: 157 | void setTranslation( Vector3D t ) { 158 | m_matrix4x4.translate( t ); 159 | } 160 | 161 | Matrix4x4 matrix() const { return m_matrix4x4; } 162 | 163 | private: 164 | Matrix4x4 m_matrix4x4; 165 | }; 166 | 167 | 168 | // Our camera that replaces Qt3D's QCamera (does not even need to be a QEntity as a part of the scene) 169 | class MyCamera 170 | { 171 | public: 172 | MyCamera() { 173 | updateProjectionMatrix(); 174 | } 175 | 176 | void updateProjectionMatrix() { 177 | 178 | const float radians = (m_fovAngleVertical / 2.0f) * 3.14159 / 180; 179 | const float sine = std::sin(radians); 180 | if (sine == 0.0f) 181 | return; 182 | float cotan = std::cos(radians) / sine; 183 | float clip = m_far - m_near; 184 | 185 | m_projectionMatrix = Matrix4x4( 186 | cotan / m_aspectRatio, 0, 0, 0, 187 | 0, cotan, 0, 0, 188 | 0, 0, -(m_near + m_far) / clip, -(2.0f * m_near * m_far) / clip, 189 | 0, 0, -1, 0 190 | ); 191 | } 192 | 193 | void setDouble( Vector3D position, Vector3D viewCenter ) { 194 | 195 | Vector3D upVector(0.0, 0.0, -1.0); // we're looking at (X,Z) plane 196 | //Vector3D upVector(0.0, 1.0, 0.0); // default used in QCamera 197 | 198 | // TODO: do we need any of this??? 199 | /* 200 | const Vector3D viewDirection = (viewCenter - position).normalized(); 201 | 202 | Matrix4x4 transformMatrix; 203 | transformMatrix.translate(position); 204 | 205 | // default: m_upVector(0.0f, 1.0f, 0.0f) 206 | 207 | // Negative viewDirection because OpenGL convention is looking down -Z 208 | transformMatrix.rotate(QQuaternion::fromDirection(-viewDirection, m_upVector.normalized())); 209 | 210 | m_transform->setMatrix(transformMatrix); 211 | */ 212 | 213 | // this pretty much figures our rotation from the vectors + applies negative translation of "position" 214 | Matrix4x4 viewMatrix; 215 | viewMatrix.lookAt(position, viewCenter, upVector); 216 | m_viewMatrix4x4 = viewMatrix; 217 | } 218 | 219 | void setAspectRatio(float aspectRatio) { 220 | m_aspectRatio = aspectRatio; 221 | updateProjectionMatrix(); 222 | } 223 | 224 | Matrix4x4 projectionMatrix() const { 225 | return m_projectionMatrix; 226 | } 227 | 228 | Matrix4x4 viewMatrix() const { 229 | return m_viewMatrix4x4; 230 | } 231 | 232 | float m_near = 0.1; 233 | float m_far = 1024; 234 | float m_fovAngleVertical = 25; // in degrees 235 | float m_aspectRatio = 1.33333; 236 | 237 | Matrix4x4 m_projectionMatrix; 238 | Matrix4x4 m_viewMatrix4x4; 239 | }; 240 | 241 | 242 | 243 | void updateMvp( MyCamera *camera, MyTransform *transform, Qt3DRender::QParameter *paramMvp ) 244 | { 245 | #ifdef USE_DOUBLE 246 | 247 | // GOOD: using doubles 248 | 249 | Matrix4x4 P = camera->projectionMatrix(); 250 | Matrix4x4 V = camera->viewMatrix(); 251 | Matrix4x4 M = transform->matrix(); 252 | qDebug() << "P" << P; 253 | qDebug() << "M" << M; 254 | qDebug() << "V" << V; 255 | 256 | // approach A: simpler (in my opinion) 257 | Matrix4x4 MV = V * M; 258 | qDebug() << "MV" << MV; 259 | Matrix4x4 MVP = P * MV; 260 | paramMvp->setValue( doubleToFloatMatrix( MVP ) ); 261 | 262 | // approach B: as described in the virtual globes book 263 | //Vector3D centerWgs = Vector3D(1089205, 932789, 2009853); // TODO - center of this entity - could be our megaOffset 264 | //Vector3D centerEye = MV.map(centerWgs); 265 | //Matrix4x4 MV_RTC = MV; 266 | //MV_RTC.data()[12] = centerEye.x(); 267 | //MV_RTC.data()[13] = centerEye.y(); 268 | //MV_RTC.data()[14] = centerEye.z(); 269 | //qDebug() << "MV_RTC" << MV_RTC; 270 | //Matrix4x4 MVP_RTC = P * MV_RTC; 271 | //paramMvp->setValue( doubleToFloatMatrix( MVP_RTC ) ); 272 | 273 | // verification that both approaches A and B give the same result 274 | //Vector3D testPt(1089205, 932785, 2009853); 275 | //qDebug() << "final mvp pt:" << MV.map(testPt); 276 | //qDebug() << "final rtc pt:" << MV_RTC.map(testPt); 277 | 278 | #else 279 | 280 | // BAD: using floating point arithmetics 281 | 282 | QMatrix4x4 P = doubleToFloatMatrix( camera->projectionMatrix() ); 283 | QMatrix4x4 V = doubleToFloatMatrix( camera->viewMatrix() ); 284 | QMatrix4x4 M = doubleToFloatMatrix( transform->matrix() ); 285 | QMatrix4x4 MV = V * M; 286 | QMatrix4x4 MVP = P * MV; 287 | qDebug() << "MVP" << MVP; 288 | paramMvp->setValue( MVP ); 289 | #endif 290 | } 291 | 292 | 293 | // updates MVP matrix in the material of all entities 294 | void updateAllMvp( MyCamera *camera, QList< QPair< MyTransform*, Qt3DRender::QParameter *> > lst ) 295 | { 296 | for ( auto pair : lst ) 297 | { 298 | updateMvp(camera, pair.first, pair.second); 299 | } 300 | } 301 | 302 | 303 | 304 | // start/stop animation with a space 305 | class MyEventFilter : public QObject { 306 | 307 | public: 308 | explicit MyEventFilter(QObject* parent = nullptr) : QObject(parent) {} 309 | 310 | protected: 311 | bool eventFilter(QObject* watched, QEvent* event) override { 312 | if (event->type() == QEvent::KeyPress) { 313 | QKeyEvent* keyEvent = static_cast(event); 314 | if (keyEvent->key() == Qt::Key_Space) { 315 | 316 | qDebug() << "SPACE!"; 317 | if ( timer->isActive() ) timer->stop(); else timer->start(); 318 | 319 | // You can optionally consume the event here 320 | // event->accept(); 321 | return true; 322 | } 323 | } 324 | // Pass the event to the parent or base class if not handled 325 | return QObject::eventFilter(watched, event); 326 | } 327 | }; 328 | 329 | 330 | 331 | int main(int argc, char *argv[]) { 332 | QApplication app(argc, argv); 333 | 334 | // Create the 3D window 335 | Qt3DExtras::Qt3DWindow *view = new Qt3DExtras::Qt3DWindow(); 336 | 337 | timer = new QTimer(view); 338 | timer->setInterval(20); 339 | 340 | MyEventFilter *myEventFilter = new MyEventFilter(view); 341 | view->installEventFilter(myEventFilter); 342 | 343 | // geometries 344 | 345 | Qt3DExtras::QPlaneMesh *planeMeshA = new Qt3DExtras::QPlaneMesh; 346 | 347 | Qt3DExtras::QPlaneMesh *planeMeshB = new Qt3DExtras::QPlaneMesh; 348 | 349 | Qt3DExtras::QSphereMesh *sphereMesh = new Qt3DExtras::QSphereMesh; 350 | sphereMesh->setRadius(2.0f); 351 | 352 | // materials 353 | 354 | Qt3DRender::QParameter *paramMvpRed; 355 | Qt3DRender::QParameter *paramMvpGreen; 356 | Qt3DRender::QParameter *paramMvpBlue; 357 | Qt3DRender::QMaterial *materialRed = basicMaterial(Qt::red, ¶mMvpRed); 358 | Qt3DRender::QMaterial *materialGreen = basicMaterial(Qt::green, ¶mMvpGreen); 359 | Qt3DRender::QMaterial *materialBlue = basicMaterial(Qt::blue, ¶mMvpBlue); 360 | 361 | // transforms 362 | 363 | //Vector3D megaOffset(0, 0, 0); 364 | //Vector3D megaOffset(10, 10, 10); 365 | //Vector3D megaOffset(10000, 10000, 10000); 366 | Vector3D megaOffset(1089205, 932789, 2009853); 367 | 368 | float animationRange = 0.1; 369 | 370 | MyTransform *sphereTransform = new MyTransform; 371 | sphereTransform->setTranslation(Vector3D(0.0, 0.0, 0.0) + megaOffset); 372 | 373 | MyTransform *planeATransform = new MyTransform; 374 | planeATransform->setTranslation(Vector3D(-0.51, 1.98, 0.0) + megaOffset); 375 | 376 | MyTransform *planeBTransform = new MyTransform; 377 | planeBTransform->setTranslation(Vector3D(+0.51, 1.98, 0.0) + megaOffset); 378 | 379 | // scene 380 | 381 | Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity; 382 | 383 | Qt3DCore::QEntity *sphereEntity = new Qt3DCore::QEntity(rootEntity); 384 | sphereEntity->addComponent(sphereMesh); 385 | sphereEntity->addComponent(materialRed); 386 | sphereEntity->addComponent(sphereTransform); 387 | 388 | Qt3DCore::QEntity *planeAEntity = new Qt3DCore::QEntity(rootEntity); 389 | planeAEntity->addComponent(planeMeshA); 390 | planeAEntity->addComponent(materialGreen); 391 | planeAEntity->addComponent(planeATransform); 392 | 393 | Qt3DCore::QEntity *planeBEntity = new Qt3DCore::QEntity(rootEntity); 394 | planeBEntity->addComponent(planeMeshB); 395 | planeBEntity->addComponent(materialBlue); 396 | planeBEntity->addComponent(planeBTransform); 397 | 398 | // 399 | 400 | QList< QPair< MyTransform*, Qt3DRender::QParameter *> > allEntities; 401 | allEntities << qMakePair(sphereTransform, paramMvpRed); 402 | allEntities << qMakePair(planeATransform, paramMvpGreen); 403 | allEntities << qMakePair(planeBTransform, paramMvpBlue); 404 | 405 | // set up frame graph 406 | // (like a frame graph from QForwardRenderer, but without frustum culling + camera selector) 407 | 408 | view->setActiveFrameGraph( myFrameGraph() ); 409 | 410 | // set up our camera (that has view matrix in double coordinates) 411 | 412 | MyCamera *myCamera = new MyCamera; 413 | //myCamera->setParent(rootEntity); 414 | myCamera->setAspectRatio(float(view->width()) / std::max(1.f, static_cast(view->height()))); 415 | 416 | QObject::connect( view, &QWindow::widthChanged, [view, myCamera, allEntities] { 417 | myCamera->setAspectRatio(float(view->width()) / std::max(1.f, static_cast(view->height()))); 418 | updateAllMvp(myCamera, allEntities); 419 | }); 420 | QObject::connect( view, &QWindow::heightChanged, [view, myCamera, allEntities] { 421 | myCamera->setAspectRatio(float(view->width()) / std::max(1.f, static_cast(view->height()))); 422 | updateAllMvp(myCamera, allEntities); 423 | }); 424 | 425 | // Setup camera 426 | Vector3D cameraBasePosition(1.0, 10.0, 0.0); 427 | Vector3D cameraBaseViewCenter(0.0, 0.0, 0.0); 428 | cameraBasePosition = cameraBasePosition + megaOffset; 429 | cameraBaseViewCenter = cameraBaseViewCenter + megaOffset; 430 | myCamera->setDouble( cameraBasePosition, cameraBaseViewCenter ); 431 | 432 | updateAllMvp(myCamera, allEntities); 433 | 434 | // Add root entity to scene 435 | view->setRootEntity(rootEntity); 436 | 437 | view->show(); 438 | 439 | QObject::connect( timer, &QTimer::timeout, [myCamera, allEntities, animationRange, cameraBasePosition, cameraBaseViewCenter] { 440 | ++counter; 441 | double t = (double) (counter % 50) / 50.; 442 | Vector3D tOffset(sin(t*3.14159*2) * animationRange, 0, cos(t*3.14159*2) * animationRange); 443 | 444 | myCamera->setDouble( cameraBasePosition + tOffset, cameraBaseViewCenter + tOffset ); 445 | 446 | updateAllMvp(myCamera, allEntities); 447 | }); 448 | 449 | return app.exec(); 450 | } 451 | -------------------------------------------------------------------------------- /rtc/matrix4x4.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "matrix4x4.h" 3 | 4 | // the implementation is partially based on Qt's QMatrix4x4 (simplified) 5 | 6 | 7 | Matrix4x4::Matrix4x4( double m11, double m12, double m13, double m14, 8 | double m21, double m22, double m23, double m24, 9 | double m31, double m32, double m33, double m34, 10 | double m41, double m42, double m43, double m44 ) 11 | { 12 | m[0][0] = m11; m[0][1] = m21; m[0][2] = m31; m[0][3] = m41; 13 | m[1][0] = m12; m[1][1] = m22; m[1][2] = m32; m[1][3] = m42; 14 | m[2][0] = m13; m[2][1] = m23; m[2][2] = m33; m[2][3] = m43; 15 | m[3][0] = m14; m[3][1] = m24; m[3][2] = m34; m[3][3] = m44; 16 | } 17 | 18 | void Matrix4x4::translate( const Vector3D &vector ) 19 | { 20 | m[3][0] += m[0][0] * vector.x() + m[1][0] * vector.y() + m[2][0] * vector.z(); 21 | m[3][1] += m[0][1] * vector.x() + m[1][1] * vector.y() + m[2][1] * vector.z(); 22 | m[3][2] += m[0][2] * vector.x() + m[1][2] * vector.y() + m[2][2] * vector.z(); 23 | m[3][3] += m[0][3] * vector.x() + m[1][3] * vector.y() + m[2][3] * vector.z(); 24 | } 25 | 26 | #if 0 27 | QList< double > Matrix4x4::dataList() const 28 | { 29 | QList< double > res; 30 | res.reserve( 9 ); 31 | for ( int i = 0; i < 16; ++i ) 32 | { 33 | res.append( m[i / 4][i % 4] ); 34 | } 35 | return res; 36 | } 37 | #endif 38 | 39 | Vector3D operator*( const Matrix4x4 &matrix, const Vector3D &vector ) 40 | { 41 | double x, y, z, w; 42 | 43 | x = vector.x() * matrix.m[0][0] + 44 | vector.y() * matrix.m[1][0] + 45 | vector.z() * matrix.m[2][0] + 46 | matrix.m[3][0]; 47 | y = vector.x() * matrix.m[0][1] + 48 | vector.y() * matrix.m[1][1] + 49 | vector.z() * matrix.m[2][1] + 50 | matrix.m[3][1]; 51 | z = vector.x() * matrix.m[0][2] + 52 | vector.y() * matrix.m[1][2] + 53 | vector.z() * matrix.m[2][2] + 54 | matrix.m[3][2]; 55 | w = vector.x() * matrix.m[0][3] + 56 | vector.y() * matrix.m[1][3] + 57 | vector.z() * matrix.m[2][3] + 58 | matrix.m[3][3]; 59 | if ( w == 1.0f ) 60 | return Vector3D( x, y, z ); 61 | else 62 | return Vector3D( x / w, y / w, z / w ); 63 | } 64 | 65 | bool Matrix4x4::isIdentity() const 66 | { 67 | if ( m[0][0] != 1.0 || m[0][1] != 0.0 || m[0][2] != 0.0 ) 68 | return false; 69 | if ( m[0][3] != 0.0 || m[1][0] != 0.0 || m[1][1] != 1.0 ) 70 | return false; 71 | if ( m[1][2] != 0.0 || m[1][3] != 0.0 || m[2][0] != 0.0 ) 72 | return false; 73 | if ( m[2][1] != 0.0 || m[2][2] != 1.0 || m[2][3] != 0.0 ) 74 | return false; 75 | if ( m[3][0] != 0.0 || m[3][1] != 0.0 || m[3][2] != 0.0 ) 76 | return false; 77 | return ( m[3][3] == 1.0 ); 78 | } 79 | 80 | void Matrix4x4::setToIdentity() 81 | { 82 | m[0][0] = 1.0; 83 | m[0][1] = 0.0; 84 | m[0][2] = 0.0; 85 | m[0][3] = 0.0; 86 | m[1][0] = 0.0; 87 | m[1][1] = 1.0; 88 | m[1][2] = 0.0; 89 | m[1][3] = 0.0; 90 | m[2][0] = 0.0; 91 | m[2][1] = 0.0; 92 | m[2][2] = 1.0; 93 | m[2][3] = 0.0; 94 | m[3][0] = 0.0; 95 | m[3][1] = 0.0; 96 | m[3][2] = 0.0; 97 | m[3][3] = 1.0; 98 | } 99 | 100 | 101 | Matrix4x4 operator*( const Matrix4x4 &m1, const Matrix4x4 &m2 ) 102 | { 103 | Matrix4x4 m( 1 ); 104 | m.m[0][0] = m1.m[0][0] * m2.m[0][0] 105 | + m1.m[1][0] * m2.m[0][1] 106 | + m1.m[2][0] * m2.m[0][2] 107 | + m1.m[3][0] * m2.m[0][3]; 108 | m.m[0][1] = m1.m[0][1] * m2.m[0][0] 109 | + m1.m[1][1] * m2.m[0][1] 110 | + m1.m[2][1] * m2.m[0][2] 111 | + m1.m[3][1] * m2.m[0][3]; 112 | m.m[0][2] = m1.m[0][2] * m2.m[0][0] 113 | + m1.m[1][2] * m2.m[0][1] 114 | + m1.m[2][2] * m2.m[0][2] 115 | + m1.m[3][2] * m2.m[0][3]; 116 | m.m[0][3] = m1.m[0][3] * m2.m[0][0] 117 | + m1.m[1][3] * m2.m[0][1] 118 | + m1.m[2][3] * m2.m[0][2] 119 | + m1.m[3][3] * m2.m[0][3]; 120 | 121 | m.m[1][0] = m1.m[0][0] * m2.m[1][0] 122 | + m1.m[1][0] * m2.m[1][1] 123 | + m1.m[2][0] * m2.m[1][2] 124 | + m1.m[3][0] * m2.m[1][3]; 125 | m.m[1][1] = m1.m[0][1] * m2.m[1][0] 126 | + m1.m[1][1] * m2.m[1][1] 127 | + m1.m[2][1] * m2.m[1][2] 128 | + m1.m[3][1] * m2.m[1][3]; 129 | m.m[1][2] = m1.m[0][2] * m2.m[1][0] 130 | + m1.m[1][2] * m2.m[1][1] 131 | + m1.m[2][2] * m2.m[1][2] 132 | + m1.m[3][2] * m2.m[1][3]; 133 | m.m[1][3] = m1.m[0][3] * m2.m[1][0] 134 | + m1.m[1][3] * m2.m[1][1] 135 | + m1.m[2][3] * m2.m[1][2] 136 | + m1.m[3][3] * m2.m[1][3]; 137 | 138 | m.m[2][0] = m1.m[0][0] * m2.m[2][0] 139 | + m1.m[1][0] * m2.m[2][1] 140 | + m1.m[2][0] * m2.m[2][2] 141 | + m1.m[3][0] * m2.m[2][3]; 142 | m.m[2][1] = m1.m[0][1] * m2.m[2][0] 143 | + m1.m[1][1] * m2.m[2][1] 144 | + m1.m[2][1] * m2.m[2][2] 145 | + m1.m[3][1] * m2.m[2][3]; 146 | m.m[2][2] = m1.m[0][2] * m2.m[2][0] 147 | + m1.m[1][2] * m2.m[2][1] 148 | + m1.m[2][2] * m2.m[2][2] 149 | + m1.m[3][2] * m2.m[2][3]; 150 | m.m[2][3] = m1.m[0][3] * m2.m[2][0] 151 | + m1.m[1][3] * m2.m[2][1] 152 | + m1.m[2][3] * m2.m[2][2] 153 | + m1.m[3][3] * m2.m[2][3]; 154 | 155 | m.m[3][0] = m1.m[0][0] * m2.m[3][0] 156 | + m1.m[1][0] * m2.m[3][1] 157 | + m1.m[2][0] * m2.m[3][2] 158 | + m1.m[3][0] * m2.m[3][3]; 159 | m.m[3][1] = m1.m[0][1] * m2.m[3][0] 160 | + m1.m[1][1] * m2.m[3][1] 161 | + m1.m[2][1] * m2.m[3][2] 162 | + m1.m[3][1] * m2.m[3][3]; 163 | m.m[3][2] = m1.m[0][2] * m2.m[3][0] 164 | + m1.m[1][2] * m2.m[3][1] 165 | + m1.m[2][2] * m2.m[3][2] 166 | + m1.m[3][2] * m2.m[3][3]; 167 | m.m[3][3] = m1.m[0][3] * m2.m[3][0] 168 | + m1.m[1][3] * m2.m[3][1] 169 | + m1.m[2][3] * m2.m[3][2] 170 | + m1.m[3][3] * m2.m[3][3]; 171 | return m; 172 | } 173 | 174 | 175 | /*! 176 | Multiplies this matrix by a viewing matrix derived from an eye 177 | point. The \a center value indicates the center of the view that 178 | the \a eye is looking at. The \a up value indicates which direction 179 | should be considered up with respect to the \a eye. 180 | 181 | \note The \a up vector must not be parallel to the line of sight 182 | from \a eye to \a center. 183 | */ 184 | void Matrix4x4::lookAt(const Vector3D& eye, const Vector3D& center, const Vector3D& up) 185 | { 186 | Vector3D forward = center - eye; 187 | if (qFuzzyIsNull(forward.x()) && qFuzzyIsNull(forward.y()) && qFuzzyIsNull(forward.z())) 188 | return; 189 | 190 | forward.normalize(); 191 | Vector3D side = Vector3D::crossProduct(forward, up).normalized(); 192 | Vector3D upVector = Vector3D::crossProduct(side, forward); 193 | 194 | Matrix4x4 m; //(Qt::Uninitialized); 195 | m.m[0][0] = side.x(); 196 | m.m[1][0] = side.y(); 197 | m.m[2][0] = side.z(); 198 | m.m[3][0] = 0.0f; 199 | m.m[0][1] = upVector.x(); 200 | m.m[1][1] = upVector.y(); 201 | m.m[2][1] = upVector.z(); 202 | m.m[3][1] = 0.0f; 203 | m.m[0][2] = -forward.x(); 204 | m.m[1][2] = -forward.y(); 205 | m.m[2][2] = -forward.z(); 206 | m.m[3][2] = 0.0f; 207 | m.m[0][3] = 0.0f; 208 | m.m[1][3] = 0.0f; 209 | m.m[2][3] = 0.0f; 210 | m.m[3][3] = 1.0f; 211 | 212 | if ( !isIdentity() ) 213 | *this = *this * m; 214 | else 215 | *this = m; 216 | translate(-eye); 217 | } 218 | -------------------------------------------------------------------------------- /rtc/matrix4x4.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef MATRIX4X4_H 3 | #define MATRIX4X4_H 4 | 5 | 6 | #include "vector3d.h" 7 | 8 | class Matrix4x4 9 | { 10 | public: 11 | //! Initializes identity matrix 12 | Matrix4x4() { setToIdentity(); } 13 | //! Initializes matrix by setting all values in row-major order 14 | Matrix4x4( double m11, double m12, double m13, double m14, 15 | double m21, double m22, double m23, double m24, 16 | double m31, double m32, double m33, double m34, 17 | double m41, double m42, double m43, double m44 ); 18 | 19 | bool operator==( const Matrix4x4 &other ) const 20 | { 21 | const double *data = *m; 22 | const double *otherData = *( other.m ); 23 | for ( int i = 0; i < 16; ++i, data++, otherData++ ) 24 | { 25 | if ( !qgsDoubleNear( *data, *otherData ) ) 26 | return false; 27 | } 28 | return true; 29 | } 30 | 31 | bool operator!=( const Matrix4x4 &other ) const 32 | { 33 | return !( *this == other ); 34 | } 35 | 36 | //! Returns pointer to the matrix data (stored in column-major order) 37 | const double *constData() const { return *m; } 38 | //! Returns pointer to the matrix data (stored in column-major order) 39 | double *data() { return *m; } 40 | #if 0 41 | //! Returns matrix data (in column-major order) 42 | QList< double > dataList() const; 43 | #endif 44 | 45 | /** 46 | * Multiplies this matrix by another that translates coordinates by the components of a \a vector. 47 | */ 48 | void translate( const Vector3D &vector ); 49 | 50 | //! Matrix-vector multiplication (vector is converted to homogeneous coordinates [X,Y,Z,1] and back) 51 | Vector3D map( const Vector3D &vector ) const 52 | { 53 | return *this * vector; 54 | } 55 | 56 | //! Returns whether this matrix is an identity matrix 57 | bool isIdentity() const; 58 | //! Sets matrix to be identity matrix 59 | void setToIdentity(); 60 | 61 | friend Matrix4x4 operator*( const Matrix4x4 &m1, const Matrix4x4 &m2 ); 62 | friend Vector3D operator*( const Matrix4x4 &matrix, const Vector3D &vector ); 63 | 64 | void lookAt(const Vector3D& eye, const Vector3D& center, const Vector3D& up); 65 | 66 | const double& operator()(int aRow, int aColumn) const 67 | { 68 | Q_ASSERT(aRow >= 0 && aRow < 4 && aColumn >= 0 && aColumn < 4); 69 | return m[aColumn][aRow]; 70 | } 71 | 72 | private: 73 | // Matrix data - in column-major order 74 | double m[4][4]; 75 | 76 | //! Construct without initializing identity matrix. 77 | explicit Matrix4x4( int ) { } // cppcheck-suppress uninitMemberVarPrivate 78 | }; 79 | 80 | //! Matrix-matrix multiplication (useful to concatenate transforms) 81 | Vector3D operator*( const Matrix4x4 &matrix, const Vector3D &vector ); 82 | //! Matrix-vector multiplication (vector is converted to homogeneous coordinates [X,Y,Z,1] and back) 83 | Matrix4x4 operator*( const Matrix4x4 &m1, const Matrix4x4 &m2 ); 84 | 85 | 86 | #include 87 | 88 | inline QDebug operator<<(QDebug dbg, const Matrix4x4 &m) 89 | { 90 | QDebugStateSaver saver(dbg); 91 | 92 | // Output in row-major order because it is more human-readable. 93 | dbg.nospace() << "Matrix4x4(" << Qt::endl 94 | << qSetFieldWidth(10) 95 | << m(0, 0) << m(0, 1) << m(0, 2) << m(0, 3) << Qt::endl 96 | << m(1, 0) << m(1, 1) << m(1, 2) << m(1, 3) << Qt::endl 97 | << m(2, 0) << m(2, 1) << m(2, 2) << m(2, 3) << Qt::endl 98 | << m(3, 0) << m(3, 1) << m(3, 2) << m(3, 3) << Qt::endl 99 | << qSetFieldWidth(0) << ')'; 100 | return dbg; 101 | } 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /rtc/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | basic.frag 4 | basic.vert 5 | 6 | 7 | -------------------------------------------------------------------------------- /rtc/rtc.py: -------------------------------------------------------------------------------- 1 | """ 2 | A simple test script to apply model-view-projection matrix with and 3 | without relative-to-center technique to verify that the results are 4 | equivalent. 5 | """ 6 | 7 | import numpy as np 8 | 9 | # 10 | # Scene setup (page 161 of the book) 11 | # 12 | 13 | # model matrix 14 | M = np.identity(4, dtype=np.float64) 15 | 16 | # view matrix 17 | V = np.array([ 18 | [0.78, 0.63, 0.00, -4946218.10], 19 | [0.20, -0.25, 0.95, -1304368.35], 20 | [0.60, -0.73, -0.32, -3810548.19], 21 | [0.00, 0.00, 0.00, 1.00]], dtype=np.float64) 22 | 23 | P = np.array([ 24 | [2.80, 0.00, 0.00, 0.00], 25 | [0.00, 3.73, 0.00, 0.00], 26 | [0.00, 0.00, -1.00, -0.02], 27 | [0.00, 0.00, -1.00, 0.00]], dtype=np.float64) 28 | 29 | pt_wgs84 = np.array([[6378137.0], [0.0], [0.0], [1.0]], dtype=np.float64) 30 | 31 | center_wgs84 = pt_wgs84 32 | 33 | # 34 | # Standard MVP (using 64-bit floats) 35 | # 36 | 37 | MVP = P @ V @ M 38 | 39 | pt_x = MVP @ pt_wgs84 40 | pt_x /= pt_x[3] 41 | print("Basline:\n", pt_x) 42 | 43 | # 44 | # MVP with relative-to-center (RTC) 45 | # (pages 164-167) 46 | # 47 | 48 | pt_center = pt_wgs84 - center_wgs84 49 | pt_center[3,0] = 1.0 # keep homogenous coordinates 50 | 51 | # step 1: calculate center_eye using original MV matrix 52 | MV = V @ M 53 | center_eye = MV @ center_wgs84 54 | 55 | # step 2: update fourth column in MV matrix 56 | MV_RTC = MV.copy() 57 | MV_RTC[0, 3] = center_eye[0,0] 58 | MV_RTC[1, 3] = center_eye[1,0] 59 | MV_RTC[2, 3] = center_eye[2,0] 60 | 61 | # step 3: calculate final MVP matrix 62 | MVP_RTC = P @ MV_RTC 63 | 64 | pt_y = MVP_RTC @ pt_center 65 | pt_y /= pt_y[3] 66 | print("RTC:\n", pt_y) 67 | 68 | 69 | # 70 | # MVP with RTC - "simplified" 71 | # 72 | 73 | # step 1: add translation to the model matrix 74 | M_tr = M.copy() 75 | M_tr[0, 3] = center_wgs84[0,0] 76 | M_tr[1, 3] = center_wgs84[1,0] 77 | M_tr[2, 3] = center_wgs84[2,0] 78 | 79 | # step 2: calculate final MVP matrix 80 | MVP_RTC_2 = P @ V @ M_tr 81 | 82 | pt_z = MVP_RTC_2 @ pt_center 83 | pt_z /= pt_z[3] 84 | print("RTC simplified:\n", pt_z) 85 | -------------------------------------------------------------------------------- /rtc/vector3d.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "vector3d.h" 3 | -------------------------------------------------------------------------------- /rtc/vector3d.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef VECTOR3D_H 3 | #define VECTOR3D_H 4 | 5 | #include 6 | 7 | #include 8 | 9 | 10 | template 11 | inline bool qgsNumberNear( T a, T b, T epsilon = std::numeric_limits::epsilon() * 4 ) 12 | { 13 | const bool aIsNan = std::isnan( a ); 14 | const bool bIsNan = std::isnan( b ); 15 | if ( aIsNan || bIsNan ) 16 | return aIsNan && bIsNan; 17 | 18 | const T diff = a - b; 19 | return diff >= -epsilon && diff <= epsilon; 20 | } 21 | 22 | inline bool qgsDoubleNear( double a, double b, double epsilon = 4 * std::numeric_limits::epsilon() ) 23 | { 24 | return qgsNumberNear( a, b, epsilon ); 25 | } 26 | 27 | class Vector3D 28 | { 29 | public: 30 | //! Constructs a null vector 31 | Vector3D() = default; 32 | 33 | //! Constructs a vector from given coordinates 34 | Vector3D( double x, double y, double z ) 35 | : mX( x ), mY( y ), mZ( z ) {} 36 | 37 | //! Constructs a vector from single-precision QVector3D 38 | Vector3D( const QVector3D &v ) 39 | : mX( v.x() ), mY( v.y() ), mZ( v.z() ) {} 40 | 41 | //! Returns TRUE if all three coordinates are zero 42 | bool isNull() const { return mX == 0 && mY == 0 && mZ == 0; } 43 | 44 | //! Returns X coordinate 45 | double x() const { return mX; } 46 | //! Returns Y coordinate 47 | double y() const { return mY; } 48 | //! Returns Z coordinate 49 | double z() const { return mZ; } 50 | 51 | /** 52 | * Sets X coordinate 53 | * \since QGIS 3.34 54 | */ 55 | void setX( double x ) { mX = x; } 56 | 57 | /** 58 | * Sets Y coordinate 59 | * \since QGIS 3.34 60 | */ 61 | void setY( double y ) { mY = y; } 62 | 63 | /** 64 | * Sets Z coordinate 65 | * \since QGIS 3.34 66 | */ 67 | void setZ( double z ) { mZ = z; } 68 | 69 | //! Sets vector coordinates 70 | void set( double x, double y, double z ) 71 | { 72 | mX = x; 73 | mY = y; 74 | mZ = z; 75 | } 76 | 77 | // TODO c++20 - replace with = default 78 | bool operator==( const Vector3D &other ) const 79 | { 80 | return mX == other.mX && mY == other.mY && mZ == other.mZ; 81 | } 82 | bool operator!=( const Vector3D &other ) const 83 | { 84 | return !operator==( other ); 85 | } 86 | 87 | //! Returns sum of two vectors 88 | Vector3D operator+( const Vector3D &other ) const 89 | { 90 | return Vector3D( mX + other.mX, mY + other.mY, mZ + other.mZ ); 91 | } 92 | 93 | //! Returns difference of two vectors 94 | Vector3D operator-( const Vector3D &other ) const 95 | { 96 | return Vector3D( mX - other.mX, mY - other.mY, mZ - other.mZ ); 97 | } 98 | 99 | //! Returns a new vector multiplied by scalar 100 | Vector3D operator *( const double factor ) const 101 | { 102 | 103 | return Vector3D( mX * factor, mY * factor, mZ * factor ); 104 | } 105 | 106 | //! Returns a new vector divided by scalar 107 | Vector3D operator /( const double factor ) const 108 | { 109 | return Vector3D( mX / factor, mY / factor, mZ / factor ); 110 | } 111 | 112 | //! Returns the dot product of two vectors 113 | static double dotProduct( const Vector3D &v1, const Vector3D &v2 ) 114 | { 115 | return v1.x() * v2.x() + v1.y() * v2.y() + v1.z() * v2.z(); 116 | } 117 | 118 | //! Returns the cross product of two vectors 119 | static Vector3D crossProduct( const Vector3D &v1, const Vector3D &v2 ) 120 | { 121 | return Vector3D( v1.y() * v2.z() - v1.z() * v2.y(), 122 | v1.z() * v2.x() - v1.x() * v2.z(), 123 | v1.x() * v2.y() - v1.y() * v2.x() ); 124 | } 125 | 126 | //! Returns the length of the vector 127 | double length() const 128 | { 129 | return std::sqrt( mX * mX + mY * mY + mZ * mZ ); 130 | } 131 | 132 | //! Normalizes the current vector in place. 133 | void normalize() 134 | { 135 | const double len = length(); 136 | if ( !qgsDoubleNear( len, 0.0 ) ) 137 | { 138 | mX /= len; 139 | mY /= len; 140 | mZ /= len; 141 | } 142 | } 143 | 144 | Vector3D normalized() const 145 | { 146 | const float len = length(); 147 | return qFuzzyIsNull(len - 1.0f) ? *this : qFuzzyIsNull(len) ? Vector3D() 148 | : Vector3D(mX / len, mY / len, mZ / len); 149 | } 150 | 151 | //! Returns the distance with the \a other Vector3D 152 | double distance( const Vector3D &other ) const 153 | { 154 | return std::sqrt( ( mX - other.x() ) * ( mX - other.x() ) + 155 | ( mY - other.y() ) * ( mY - other.y() ) + 156 | ( mZ - other.z() ) * ( mZ - other.z() ) ); 157 | } 158 | 159 | //! Returns the perpendicular point of vector \a vp from [\a v1 - \a v2] 160 | static Vector3D perpendicularPoint( const Vector3D &v1, const Vector3D &v2, const Vector3D &vp ) 161 | { 162 | const Vector3D d = ( v2 - v1 ) / v2.distance( v1 ); 163 | const Vector3D v = vp - v2; 164 | const double t = dotProduct( v, d ); 165 | Vector3D P = v2 + ( d * t ); 166 | return P; 167 | } 168 | 169 | #if 0 170 | /** 171 | * Returns a string representation of the 3D vector. 172 | * Members will be truncated to the specified \a precision. 173 | */ 174 | QString toString( int precision = 17 ) const 175 | { 176 | QString str = "Vector3D ("; 177 | str += qgsDoubleToString( mX, precision ); 178 | str += ", "; 179 | str += qgsDoubleToString( mY, precision ); 180 | str += ", "; 181 | str += qgsDoubleToString( mZ, precision ); 182 | str += ')'; 183 | return str; 184 | } 185 | #endif 186 | 187 | /** 188 | * Converts the current object to QVector3D 189 | * \warning the conversion may decrease the accuracy (double to float values conversion) 190 | * \since QGIS 3.24 191 | */ 192 | QVector3D toVector3D() const { return QVector3D( static_cast< float >( mX ), static_cast< float >( mY ), static_cast< float >( mZ ) ); } 193 | 194 | friend inline Vector3D operator-(Vector3D vector) 195 | { 196 | return Vector3D(-vector.mX, -vector.mY, -vector.mZ); 197 | } 198 | 199 | private: 200 | double mX = 0, mY = 0, mZ = 0; 201 | }; 202 | 203 | #include 204 | 205 | inline QDebug operator<<(QDebug dbg, Vector3D vector) 206 | { 207 | QDebugStateSaver saver(dbg); 208 | dbg.nospace() << "Vector3D(" 209 | << vector.x() << ", " << vector.y() << ", " << vector.z() << ')'; 210 | return dbg; 211 | } 212 | 213 | #endif // VECTOR3D_H 214 | -------------------------------------------------------------------------------- /ssao/FinalMaterial.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.1 as QQ2 2 | import Qt3D.Core 2.0 3 | import Qt3D.Render 2.0 4 | import Qt3D.Input 2.0 5 | import Qt3D.Extras 2.0 6 | 7 | 8 | Material { 9 | 10 | property Texture2D textureColor 11 | property Texture2D textureSsao 12 | 13 | parameters: [ 14 | Parameter { name: "col"; value: textureColor }, 15 | Parameter { name: "ssao"; value: textureSsao } 16 | ] 17 | effect: Effect { 18 | techniques: Technique { 19 | graphicsApiFilter { api: GraphicsApiFilter.OpenGL; profile: GraphicsApiFilter.CoreProfile; majorVersion: 3; minorVersion: 1 } 20 | renderPasses: [ 21 | RenderPass { 22 | shaderProgram: ShaderProgram { 23 | id: sp 24 | vertexShaderCode: " 25 | #version 420 core 26 | 27 | in vec3 vertexPosition; 28 | 29 | uniform mat4 modelViewProjection; 30 | 31 | void main() 32 | { 33 | gl_Position = modelViewProjection * vec4( vertexPosition, 1.0 ); 34 | }" 35 | 36 | 37 | fragmentShaderCode: " 38 | #version 420 core 39 | 40 | uniform sampler2D col; 41 | uniform sampler2D ssao; 42 | 43 | out vec4 fragColor; 44 | 45 | void main() 46 | { 47 | float ssao = texelFetch(ssao, ivec2(gl_FragCoord), 0 ).r; 48 | fragColor = vec4(texelFetch(col, ivec2(gl_FragCoord), 0 ).rgb * ssao, 1.0); 49 | } 50 | " 51 | 52 | onLogChanged: { 53 | console.warn("status", sp.status) 54 | console.log(sp.log) 55 | } 56 | } 57 | } 58 | ] 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ssao/MyScene.qml: -------------------------------------------------------------------------------- 1 | import Qt3D.Core 2.0 2 | import Qt3D.Render 2.0 3 | import Qt3D.Input 2.0 4 | import Qt3D.Extras 2.0 5 | 6 | Entity { 7 | id: sceneRoot 8 | 9 | Entity { 10 | PlaneMesh { 11 | id: pm 12 | width: 20 13 | height: 20 14 | } 15 | PhongMaterial { 16 | id: pmm 17 | ambient: Qt.rgba(0.3,0.3,0.3,1) 18 | } 19 | components: [ pm, pmm ] 20 | } 21 | 22 | NodeInstantiator { 23 | 24 | model: 200 25 | 26 | delegate: Entity { 27 | property alias tform: tform 28 | property color color: "green" 29 | CuboidMesh { id: mesh } 30 | PhongMaterial { id: material; ambient: color } 31 | Transform { id: tform; } 32 | components: [ mesh, material, tform ] 33 | } 34 | 35 | onObjectAdded: { 36 | object.tform.translation = Qt.vector3d(Math.random()*10-5, Math.random()*10-5, Math.random()*10-5) 37 | object.color = ["green", "red", "yellow", "blue"][index % 4] 38 | } 39 | } 40 | 41 | } 42 | 43 | -------------------------------------------------------------------------------- /ssao/SsaoBlurMaterial.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.1 as QQ2 2 | import Qt3D.Core 2.0 3 | import Qt3D.Render 2.0 4 | import Qt3D.Input 2.0 5 | import Qt3D.Extras 2.0 6 | 7 | 8 | Material { 9 | 10 | property Texture2D textureSsao 11 | 12 | parameters: [ 13 | Parameter { name: "ssaoInput"; value: textureSsao } 14 | ] 15 | effect: Effect { 16 | techniques: Technique { 17 | graphicsApiFilter { api: GraphicsApiFilter.OpenGL; profile: GraphicsApiFilter.CoreProfile; majorVersion: 3; minorVersion: 1 } 18 | renderPasses: [ 19 | RenderPass { 20 | shaderProgram: ShaderProgram { 21 | id: sp 22 | vertexShaderCode: " 23 | #version 420 core 24 | 25 | in vec3 vertexPosition; 26 | 27 | uniform mat4 modelViewProjection; 28 | 29 | void main() 30 | { 31 | gl_Position = modelViewProjection * vec4( vertexPosition, 1.0 ); 32 | }" 33 | 34 | 35 | fragmentShaderCode: " 36 | #version 330 core 37 | 38 | out float FragColor; 39 | 40 | uniform sampler2D ssaoInput; 41 | 42 | void main() 43 | { 44 | float result = 0.0; 45 | for (int x = -2; x < 2; ++x) 46 | { 47 | for (int y = -2; y < 2; ++y) 48 | { 49 | result += texelFetch(ssaoInput, ivec2(gl_FragCoord) + ivec2(x,y), 0).r; 50 | } 51 | } 52 | FragColor = result / (4.0 * 4.0); 53 | } " 54 | 55 | onLogChanged: { 56 | console.warn("status", sp.status) 57 | console.log(sp.log) 58 | } 59 | } 60 | } 61 | ] 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /ssao/SsaoMaterial.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.1 as QQ2 2 | import Qt3D.Core 2.0 3 | import Qt3D.Render 2.0 4 | import Qt3D.Input 2.0 5 | import Qt3D.Extras 2.0 6 | 7 | 8 | Material { 9 | 10 | property Texture2D textureColor 11 | property Texture2D textureDepth 12 | property real cameraZNear 13 | property real cameraZFar 14 | property real cameraFov 15 | property real cameraAr 16 | property matrix4x4 cameraProjMatrix 17 | 18 | // create a list of randomly generated 3D vectors inside a unit sphere for sampling neighbors 19 | function generate_kernel() { 20 | var lst = [] 21 | var kernelSize = 64; 22 | for (var i = 0; i < kernelSize; ++i) { 23 | var scale = i / kernelSize 24 | scale = 0.1 + 0.9 * scale * scale 25 | var vct = Qt.vector3d(Math.random()*2-1, Math.random()*2-1, Math.random()*2-1).normalized() 26 | vct = vct.times(scale) 27 | lst.push(vct) 28 | } 29 | return lst 30 | } 31 | 32 | // 4x4 array of random rotation vectors 33 | function generate_random_texture() { 34 | var lst = [] 35 | for (var i = 0; i < 16; ++i) { 36 | var vct = Qt.vector3d(Math.random() /**2-1*/, Math.random() /**2-1*/, 0.0) 37 | lst.push(vct) 38 | } 39 | return lst; 40 | } 41 | 42 | parameters: [ 43 | Parameter { name: "col"; value: textureColor }, 44 | Parameter { name: "dep"; value: textureDepth }, 45 | Parameter { name: "zNear"; value: cameraZNear }, 46 | Parameter { name: "zFar"; value: cameraZFar }, 47 | Parameter { name: "uKernelOffsets[0]"; value: generate_kernel() }, 48 | Parameter { name: "uNoiseTexture[0]"; value: generate_random_texture() }, 49 | Parameter { name: "origProjMatrix"; value: cameraProjMatrix }, 50 | Parameter { name: "uTanHalfFov"; value: Math.tan( cameraFov/2 * Math.PI/180) }, 51 | Parameter { name: "uAspectRatio"; value: cameraAr } 52 | ] 53 | effect: Effect { 54 | techniques: Technique { 55 | graphicsApiFilter { api: GraphicsApiFilter.OpenGL; profile: GraphicsApiFilter.CoreProfile; majorVersion: 3; minorVersion: 1 } 56 | renderPasses: [ 57 | RenderPass { 58 | shaderProgram: ShaderProgram { 59 | id: sp 60 | vertexShaderCode: " 61 | #version 420 core 62 | 63 | in vec3 vertexPosition; 64 | 65 | uniform mat4 modelViewProjection; 66 | 67 | // view frustum parameters: 68 | uniform float uTanHalfFov; 69 | uniform float uAspectRatio; 70 | 71 | noperspective out vec3 vViewRay; // ray to far plane 72 | 73 | void main() 74 | { 75 | // math of the view ray is covered here: https://ogldev.org/www/tutorial46/tutorial46.html 76 | // 77 | // our quad is in X-Z plane. 78 | // and its coordinates are in range [-0.5,0.5] that's why we multiply by 2 79 | vViewRay = vec3( 80 | -vertexPosition.x * uTanHalfFov * uAspectRatio * 2, 81 | vertexPosition.z * uTanHalfFov * 2, 82 | 1.0 // since we'll be multiplying by linear depth, leave z as 1 83 | ); 84 | 85 | gl_Position = modelViewProjection * vec4( vertexPosition, 1.0 ); 86 | }" 87 | 88 | 89 | fragmentShaderCode: " 90 | #version 420 core 91 | 92 | // name must be equal to parameter of the Material 93 | uniform sampler2D col; 94 | uniform sampler2D dep; 95 | 96 | uniform float zNear; 97 | uniform float zFar; 98 | 99 | uniform vec3 uKernelOffsets[64]; // unit sphere with random vectors in it 100 | uniform vec3 uNoiseTexture[256]; // 101 | uniform mat4 origProjMatrix; // perspective projection matrix used for forward rendering 102 | 103 | noperspective in vec3 vViewRay; // ray to far plane 104 | 105 | out vec4 fragColor; 106 | 107 | 108 | // converts depth from depth texture (in range [0,1]) to 'linear' depth based on camera settings 109 | float depthSampleToDepth(float depthSample) 110 | { 111 | depthSample = 2.0 * depthSample - 1.0; 112 | float zLinear = 2.0 * zNear * zFar / (zFar + zNear - depthSample * (zFar - zNear)); 113 | return zLinear; 114 | } 115 | 116 | 117 | vec3 rotate_x(vec3 vct, float angle) 118 | { 119 | return vec3(vct.x*cos(angle)-vct.y*sin(angle), vct.x*sin(angle)+vct.y*cos(angle), vct.z); 120 | } 121 | vec3 rotate_y(vec3 vct, float angle) 122 | { 123 | return vec3(vct.x*cos(angle)+vct.z*sin(angle), vct.y, -vct.x*sin(angle)+vct.z*cos(angle)); 124 | } 125 | 126 | 127 | // based on the code from John Chapman 128 | float ssao(vec3 originPos, float radius, vec3 noise) 129 | { 130 | float occlusion = 0.0; 131 | for (int i = 0; i < 64; ++i) 132 | { 133 | // get sample position: 134 | vec3 samplePos = rotate_y(rotate_x(uKernelOffsets[i], noise.x), noise.y); 135 | samplePos = samplePos + originPos; 136 | 137 | // project sample position: 138 | vec4 offset = origProjMatrix * vec4(samplePos, 1.0); 139 | offset.xy /= offset.w; // only need xy (range [-1,1]) 140 | offset.xy = offset.xy * 0.5 + 0.5; // scale/bias to texcoords (range [0,1]) 141 | 142 | // get sample depth: 143 | float sampleDepth = texture(dep, offset.xy).r; 144 | sampleDepth = depthSampleToDepth(sampleDepth); 145 | 146 | float rangeCheck = smoothstep(0.0, 1.0, radius / abs(originPos.z - sampleDepth)); 147 | occlusion += rangeCheck * step(sampleDepth, samplePos.z); 148 | } 149 | 150 | return 1.0 - (occlusion / float(64)); 151 | } 152 | 153 | 154 | void main() 155 | { 156 | // for debugging 157 | //fragColor = vec4(texelFetch(col, ivec2(gl_FragCoord), 0 ).rgb, 1.0); 158 | //float shade = exp(-edlFactor(ivec2(gl_FragCoord)) * 4000); 159 | //fragColor = vec4(fragColor.rgb * shade, fragColor.a); 160 | //fragColor = vec4(vViewRay.x, vViewRay.y, 0.0,1.0); 161 | 162 | // calculate view-space 3D coordinates of this pixel 163 | vec2 texelSize = 1.0 / vec2(textureSize(dep, 0)); 164 | vec2 screenTexCoords = gl_FragCoord.xy * texelSize; 165 | float originDepth = depthSampleToDepth(texture(dep, screenTexCoords).r); 166 | vec3 originPos = vViewRay * originDepth; 167 | 168 | vec4 originColor = vec4(texture(col, screenTexCoords).rgb, 1.0); 169 | 170 | int a_idx = int(gl_FragCoord.x) % 4 + 4 * (int(gl_FragCoord.y) % 4); 171 | vec3 noise = uNoiseTexture[a_idx] * 2*3.14; 172 | 173 | float ssao_res = ssao(originPos, 0.5, noise); 174 | //fragColor = originColor * pow(ssao_res, 1.0); 175 | 176 | // more debugging 177 | //fragColor = vec4(vec3(originDepth/100),1.0); 178 | //fragColor = vec4(originPos.x, originPos.y, 0.0, 1.0); 179 | fragColor = vec4(pow(ssao_res, 1.0)); 180 | } 181 | " 182 | 183 | onLogChanged: { 184 | console.warn("status", sp.status) 185 | console.log(sp.log) 186 | } 187 | } 188 | } 189 | ] 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /ssao/fun3d-ssao.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | QT += 3dcore 3drender 3dinput 3dquick qml quick 3dquickextras 3dextras 3 | 4 | #CONFIG += c++11 5 | 6 | # The following define makes your compiler emit warnings if you use 7 | # any Qt feature that has been marked deprecated (the exact warnings 8 | # depend on your compiler). Refer to the documentation for the 9 | # deprecated API to know how to port your code away from it. 10 | #DEFINES += QT_DEPRECATED_WARNINGS 11 | 12 | # You can also make your code fail to compile if it uses deprecated APIs. 13 | # In order to do so, uncomment the following line. 14 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 15 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 16 | 17 | SOURCES += \ 18 | main.cpp 19 | 20 | RESOURCES += qml.qrc 21 | 22 | OTHER_FILES += \ 23 | main.qml 24 | 25 | # Additional import path used to resolve QML modules in Qt Creator's code model 26 | #QML_IMPORT_PATH = 27 | 28 | # Additional import path used to resolve QML modules just for Qt Quick Designer 29 | #QML_DESIGNER_IMPORT_PATH = 30 | 31 | # Default rules for deployment. 32 | #qnx: target.path = /tmp/$${TARGET}/bin 33 | #else: unix:!android: target.path = /opt/$${TARGET}/bin 34 | #!isEmpty(target.path): INSTALLS += target 35 | 36 | INCLUDEPATH += /home/martin/inst/qt3d-everywhere-src-5.12.8/include 37 | -------------------------------------------------------------------------------- /ssao/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char* argv[]) 9 | { 10 | QGuiApplication app(argc, argv); 11 | 12 | Qt3DExtras::Quick::Qt3DQuickWindow view; 13 | view.setTitle("Screen Space Ambient Occlusion"); 14 | view.resize(800, 800); 15 | view.engine()->qmlEngine()->rootContext()->setContextProperty("_window", &view); 16 | view.setSource(QUrl("qrc:/main.qml")); 17 | view.show(); 18 | 19 | return app.exec(); 20 | } 21 | -------------------------------------------------------------------------------- /ssao/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.1 as QQ2 2 | import Qt3D.Core 2.0 3 | import Qt3D.Render 2.0 4 | import Qt3D.Input 2.0 5 | import Qt3D.Extras 2.0 6 | 7 | Entity { 8 | 9 | components: [ 10 | rendSettings, 11 | inputSettings 12 | ] 13 | 14 | InputSettings { id: inputSettings } 15 | 16 | RenderSettings { 17 | id: rendSettings 18 | activeFrameGraph: RenderSurfaceSelector { 19 | Viewport { 20 | normalizedRect: Qt.rect(0,0,1,1) 21 | 22 | // "ordinary" pass to render scene with shading to a color+depth texture 23 | CameraSelector { 24 | camera: camera 25 | ClearBuffers { 26 | buffers: ClearBuffers.ColorDepthBuffer 27 | clearColor: Qt.rgba(0.3,0.3,0.3,1) 28 | LayerFilter { 29 | layers: [layerSsao, layerSsaoBlur, layerFinal] 30 | filterMode: LayerFilter.DiscardAnyMatchingLayers 31 | RenderTargetSelector { 32 | target: RenderTarget { 33 | attachments: [ 34 | RenderTargetOutput { attachmentPoint : RenderTargetOutput.Color0; texture: colorTexture }, 35 | RenderTargetOutput { attachmentPoint : RenderTargetOutput.Depth; texture: depthTexture } 36 | ] 37 | } 38 | } 39 | } 40 | } 41 | } 42 | 43 | // SSAO pass - using depth to produce SSAO texture 44 | CameraSelector { 45 | camera: ortoCamera 46 | RenderStateSet { 47 | // disable depth tests (no need to clear buffers) 48 | renderStates: [ DepthTest { depthFunction: DepthTest.Always } ] 49 | LayerFilter { 50 | layers: [layerSsao] 51 | RenderTargetSelector { 52 | target: RenderTarget { 53 | attachments: [ 54 | RenderTargetOutput { attachmentPoint : RenderTargetOutput.Color0; texture: ssaoTexture } 55 | ] 56 | } 57 | } 58 | } 59 | } 60 | } 61 | 62 | // SSAO blur pass - blurs SSAO results to remove noise 63 | CameraSelector { 64 | camera: ortoCamera 65 | RenderStateSet { 66 | // disable depth tests (no need to clear buffers) 67 | renderStates: [ DepthTest { depthFunction: DepthTest.Always } ] 68 | LayerFilter { 69 | layers: [layerSsaoBlur] 70 | RenderTargetSelector { 71 | target: RenderTarget { 72 | attachments: [ 73 | RenderTargetOutput { attachmentPoint : RenderTargetOutput.Color0; texture: ssaoBlurTexture } 74 | ] 75 | } 76 | } 77 | } 78 | } 79 | } 80 | 81 | // final pass - combine color texture and SSAO to the final result 82 | CameraSelector { 83 | camera: ortoCamera 84 | RenderStateSet { 85 | // disable depth tests (no need to clear buffers) 86 | renderStates: [ DepthTest { depthFunction: DepthTest.Always } ] 87 | LayerFilter { 88 | layers: [layerFinal] 89 | } 90 | } 91 | } 92 | 93 | } 94 | } 95 | 96 | } 97 | 98 | Texture2D { 99 | id : colorTexture 100 | width : _window.width 101 | height : _window.height 102 | format : Texture.RGB16F 103 | generateMipMaps : false 104 | magnificationFilter : Texture.Linear 105 | minificationFilter : Texture.Linear 106 | wrapMode { 107 | x: WrapMode.ClampToEdge 108 | y: WrapMode.ClampToEdge 109 | } 110 | } 111 | 112 | Texture2D { 113 | id : depthTexture 114 | width : _window.width 115 | height : _window.height 116 | 117 | format: Texture.DepthFormat 118 | 119 | generateMipMaps : false 120 | magnificationFilter : Texture.Linear 121 | minificationFilter : Texture.Linear 122 | wrapMode { 123 | x: WrapMode.ClampToEdge 124 | y: WrapMode.ClampToEdge 125 | } 126 | } 127 | 128 | Texture2D { 129 | id : ssaoTexture 130 | width : _window.width 131 | height : _window.height 132 | format : Texture.R16F 133 | generateMipMaps : false 134 | magnificationFilter : Texture.Linear 135 | minificationFilter : Texture.Linear 136 | wrapMode { 137 | x: WrapMode.ClampToEdge 138 | y: WrapMode.ClampToEdge 139 | } 140 | } 141 | 142 | Texture2D { 143 | id : ssaoBlurTexture 144 | width : _window.width 145 | height : _window.height 146 | format : Texture.R16F 147 | generateMipMaps : false 148 | magnificationFilter : Texture.Linear 149 | minificationFilter : Texture.Linear 150 | wrapMode { 151 | x: WrapMode.ClampToEdge 152 | y: WrapMode.ClampToEdge 153 | } 154 | } 155 | 156 | Camera { 157 | id: ortoCamera 158 | projectionType: CameraLens.OrthographicProjection 159 | aspectRatio: _window.width / _window.height 160 | nearPlane: 1 161 | farPlane: 100.0 162 | position: Qt.vector3d(0.0, 10.0, 0.0) 163 | viewCenter: Qt.vector3d(0.0, 0.0, 0.0) 164 | upVector: Qt.vector3d(0.0, 1.0, 0.0) 165 | } 166 | 167 | 168 | 169 | Camera { 170 | id: camera 171 | projectionType: CameraLens.PerspectiveProjection 172 | fieldOfView: 45 173 | aspectRatio: _window.width / _window.height 174 | nearPlane: 0.1 175 | farPlane: 1000.0 176 | position: Qt.vector3d(0.0, 10.0, 20.0) 177 | viewCenter: Qt.vector3d(0.0, 0.0, 0.0) 178 | upVector: Qt.vector3d(0.0, 1.0, 0.0) 179 | } 180 | 181 | FirstPersonCameraController { camera: camera } 182 | 183 | Layer { id: layerSsao } // quad used for SSAO 184 | Layer { id: layerSsaoBlur } // quad used for SSAO blur 185 | Layer { id: layerFinal } // quad used for post-processing 186 | 187 | MyScene { 188 | id: sceneRoot 189 | } 190 | 191 | ///// 192 | ///// 193 | ///// 194 | 195 | 196 | Entity { 197 | PlaneMesh { 198 | id: ssaoQuadMesh 199 | width: 1 200 | height: 1 201 | } 202 | Transform { 203 | id: ssaoQuadTransform 204 | translation: Qt.vector3d(0, 2, 0) 205 | } 206 | SsaoMaterial { 207 | id: ssaoMaterial 208 | 209 | textureColor: colorTexture 210 | textureDepth: depthTexture 211 | cameraZNear: camera.nearPlane 212 | cameraZFar: camera.farPlane 213 | cameraFov: camera.fieldOfView 214 | cameraAr: camera.aspectRatio 215 | cameraProjMatrix: camera.projectionMatrix 216 | } 217 | 218 | components: [ ssaoQuadMesh, ssaoMaterial, ssaoQuadTransform, layerSsao ] 219 | } 220 | 221 | ///// 222 | 223 | 224 | Entity { 225 | PlaneMesh { 226 | id: ssaoQuadBlurMesh 227 | width: 1 228 | height: 1 229 | } 230 | Transform { 231 | id: ssaoQuadBlurTransform 232 | translation: Qt.vector3d(0, 2, 0) 233 | } 234 | SsaoBlurMaterial { 235 | id: ssaoBlurMaterial 236 | 237 | textureSsao: ssaoTexture 238 | } 239 | 240 | components: [ ssaoQuadBlurMesh, ssaoBlurMaterial, ssaoQuadBlurTransform, layerSsaoBlur ] 241 | } 242 | ///// 243 | 244 | Entity { 245 | PlaneMesh { 246 | id: finalQuadMesh 247 | width: 1 248 | height: 1 249 | } 250 | Transform { 251 | id: finalQuadTransform 252 | translation: Qt.vector3d(0, 2, 0) 253 | } 254 | FinalMaterial { 255 | id: finalMaterial 256 | 257 | textureColor: colorTexture 258 | textureSsao: ssaoBlurTexture 259 | } 260 | 261 | components: [ finalQuadMesh, finalMaterial, finalQuadTransform, layerFinal ] 262 | } 263 | 264 | 265 | } 266 | -------------------------------------------------------------------------------- /ssao/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | MyScene.qml 5 | SsaoMaterial.qml 6 | FinalMaterial.qml 7 | SsaoBlurMaterial.qml 8 | 9 | 10 | --------------------------------------------------------------------------------