├── .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 | 
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 | |  |  |
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 | 
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 | 
42 |
43 | # Instanced Rendering
44 |
45 | Using instancing to render a single geometry at multiple different positions.
46 |
47 | 
48 |
49 | # Lines
50 |
51 | Rendering of lines in 3D space with constant screen space thickness. Supports flat and miter joins.
52 |
53 | 
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 | 
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 |
--------------------------------------------------------------------------------