Disable all light sources in Qt 3d - c++

In my company there is a shift from an old 3D engine to Qt3d. One goal of this undertaking is to compare the rendered view of the old 3D engine with the Qt3d rendering.
For this I wrote a small example application, where I can compare old and new rendering. There are still plenty differences. My first idea was to switch of all light sources in both engines and compare the silhouettes of both renderings.
Now, there is some thing, that I really don't understand and this has to do with the Qt3d lighting model.
In my small example app I define a simple sphere mesh and a camera and a check box, which can disable a point light source. The sphere is lighted by the phong reflection model.
No, if I switch out mylight, I'm expecting a simple black sphere in my viewer, as there is really no light. Instead there is still some lighting (from a different source). I think, that there is some other light source, that is activated by default.
How, can I disable all light sources in Qt3d? As a side question I'm also wondering why meshMaterial->setAmbient(QColor(255, 0, 0)); has no visible effect on the material after all. It really doesn't matter what you will enter here.
#include <QApplication>
#include <QWidget>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QFrame>
#include <Qt3DCore/QTransform.h>
#include <Qt3DRender/QCamera.h>
#include <Qt3DRender/QRenderSettings.h>
#include <Qt3DRender/QPointLight.h>
#include <Qt3DExtras/QSphereMesh>
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DExtras/qforwardrenderer.h>
#include <Qt3DExtras/Qt3DWindow.h>
#include <Qt3DExtras/QFirstPersonCameraController.h>
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
auto view = new Qt3DExtras::Qt3DWindow();
view->defaultFrameGraph()->setClearColor(QColor(255,255,255));
auto rootEntity = new Qt3DCore::QEntity();
view->setRootEntity(rootEntity);
auto cameraEntity = view->camera();
cameraEntity->lens()->setPerspectiveProjection(45.0f, 1., 0.1f, 10000.0f);
cameraEntity->setPosition(QVector3D(5, 5, 5));
cameraEntity->setUpVector(QVector3D(0, 1, 0));
cameraEntity->setViewCenter(QVector3D(0, 0, 0));
auto lightEntity = new Qt3DCore::QEntity(rootEntity);
auto light = new Qt3DRender::QPointLight(lightEntity);
light->setColor("white");
light->setIntensity(1);
lightEntity->addComponent(light);
auto lightTransform = new Qt3DCore::QTransform(lightEntity);
lightTransform->setTranslation(cameraEntity->position());
lightEntity->addComponent(lightTransform);
lightEntity->setEnabled(false);
// For camera controls
auto camController = new Qt3DExtras::QFirstPersonCameraController(rootEntity);
camController->setCamera(cameraEntity);
auto mesh = new Qt3DExtras::QSphereMesh();
mesh->setRadius(1.);
auto meshMaterial = new Qt3DExtras::QPhongMaterial();
meshMaterial->setDiffuse(QColor(0, 255, 0));
meshMaterial->setAmbient(QColor(255, 0, 0));
meshMaterial->setSpecular(QColor(0,0,255));
meshMaterial->setShininess(23);
auto meshEntity = new Qt3DCore::QEntity(rootEntity);
meshEntity->addComponent(mesh);
meshEntity->addComponent(meshMaterial);
meshEntity->setEnabled(true);
auto disableLight = new QCheckBox();
auto container = QWidget::createWindowContainer(view);
QFrame frame;
frame.setLayout(new QVBoxLayout);
frame.layout()->addWidget(container);
frame.layout()->addWidget(disableLight);
QObject::connect(disableLight, &QCheckBox::stateChanged, [lightEntity](auto state) {
lightEntity->setEnabled(state == Qt::CheckState::Checked);
});
frame.setFixedSize(500, 500);
frame.show();
return a.exec();
}

If no light entities are created in your Qt3D scene Qt3D will add one for you. This is to prevent users not seeing anything without light.
Once you add a light yourself the default one is omitted
You can workaround this default behaviour by adding a light with intensity set to zero:
DirectionalLight {
worldDirection: Qt.vector3d(-1, 1, -1)
intensity: 0.0
}
This will give you the following effect:
test with cuboid and sphere mesh with PhongMaterial:
So tinkering with the intensity property of the light might give what you want.

Related

Problem creating multiple viewports in Qt3D using C++

I am trying to setup a Qt3DWindow with multiple viewports using C++. According to the documentation and the provided QML example, all I need to do is create a framegraph, where a main QViewport object branches into several RenderViews. The first RenderView contains a QClearBuffers object, and the remaining ones contain the tiled viewports and their corresponding camera selectors. Therefore, if I want N viewports, I need to create N+1 RenderViews.
However, if I follow that procedure, the main viewport displays some sort of "default" view, which appears on top of the window, spanning all the viewports. I don't know where this view comes from, since it doesn't correspond to any camera. Here is the output from the code posted below.
I found a solution, but I am not comfortable with it since it feels like some kind of hack: instead of making all the RenderViews branch from the main viewport, I attach one of the child viewports to the ClearBuffers object itself. Thus, for N viewports, I have N RenderViews instead of N+1. I don't quite understand the internals of the framegraph, so I would like to know if this solution is only wrong from an OCD perspective, or it can actually backfire at some point.
Here is a minimal example with two viewports sharing the default camera. If I make either viewPort1 or viewPort2 branch from clearBuffers instead of mainViewPort, everything works as expected:
#include <QGuiApplication>
#include <Qt3DCore/QTransform>
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DExtras/QTorusMesh>
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QRenderSurfaceSelector>
#include <Qt3DRender/QViewport>
#include <Qt3DRender/QClearBuffers>
#include <Qt3DRender/QCameraSelector>
Qt3DCore::QEntity *createScene()
{
// Root entity
auto rootEntity = new Qt3DCore::QEntity;
// Torus
auto torusEntity = new Qt3DCore::QEntity(rootEntity);
auto torusMesh = new Qt3DExtras::QTorusMesh;
torusMesh->setRadius(0.5f);
torusMesh->setMinorRadius(0.1f);
torusMesh->setRings(100);
torusMesh->setSlices(20);
auto torusTransform = new Qt3DCore::QTransform;
torusTransform->setScale3D(QVector3D(1.2f, 1.f, 0.8f));
torusTransform->setRotation(QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), 45.0f));
torusEntity->addComponent(torusMesh);
torusEntity->addComponent(torusTransform);
torusEntity->addComponent(new Qt3DExtras::QPhongMaterial(rootEntity));
return rootEntity;
}
int main(int argc, char* argv[])
{
QGuiApplication app(argc, argv);
Qt3DExtras::Qt3DWindow view;
// Set camera transform
view.camera()->setPosition(QVector3D(0, 4.0f, 0));
view.camera()->setViewCenter(QVector3D(0, 0, 0));
// Framegraph root node
auto surfaceSelector = new Qt3DRender::QRenderSurfaceSelector();
auto mainViewPort = new Qt3DRender::QViewport(surfaceSelector);
// First RenderView: clear buffers
auto clearBuffers = new Qt3DRender::QClearBuffers(mainViewPort);
clearBuffers->setBuffers(Qt3DRender::QClearBuffers::ColorDepthBuffer);
clearBuffers->setClearColor(Qt::white);
// Second RenderView: left viewport
auto viewPort1 = new Qt3DRender::QViewport(mainViewPort);
viewPort1->setNormalizedRect(QRectF(0.0f, 0.0f, 0.5f, 1.0f));
auto cameraSelector1 = new Qt3DRender::QCameraSelector(viewPort1);
cameraSelector1->setCamera(view.camera());
// Third RenderView: right viewport
auto viewPort2 = new Qt3DRender::QViewport(mainViewPort);
viewPort2->setNormalizedRect(QRectF(0.5f, 0.0f, 0.5f, 1.0f));
auto cameraSelector2= new Qt3DRender::QCameraSelector(viewPort2);
cameraSelector2->setCamera(view.camera());
// Add framegraph and scenegraph to viewer
view.setActiveFrameGraph(surfaceSelector);
view.setRootEntity(createScene());
view.show();
return app.exec();
}
I still think it's kind of a bug that it draws ontop of the viewports but you can put a QNoDraw node in your framegraph as a child of the QClearBuffers:
auto noDraw = new Qt3DRender::QNoDraw(clearBuffers);
That solves the rendering issue.

Qt3d: Artifacts of displaying when applied Qt3DRender::QLayerFilter

I am trying to use layer filtering as shown in this answer. For this I wrote a simple test (see below). This is a continuation of the question.
At a certain position of the red sphere, an artifact appears, which looks like display from the another camera in coordinates (0.0, 0.0, 0.0).
See screen:
In my example, the red sphere can be moved with the WSAD buttons.
See (-7, 0, -14) red sphere position. How to remove these artifacts?
The full test project can be viewed here.
main.cpp
int main(int argc, char *argv[])
{
QGuiApplication application(argc, argv);
My3DWindow window;
auto sphere1 = new Qt3DCore::QEntity(window.Scene());
auto sphere2 = new Qt3DCore::QEntity(window.Scene());
// material, transform, mesh initialisation
sphere1->addComponent(material1);
sphere1->addComponent(spheremesh1);
sphere1->addComponent(transform1);
sphere1->addComponent(window.OpaqueLayer());
sphere2->addComponent(material2);
sphere2->addComponent(spheremesh2);
sphere2->addComponent(transform2);
sphere2->addComponent(window.TransparentLayer());
window.show();
return application.exec();
}
My3DWindow class:
My3DWindow::My3DWindow(QScreen *screen):
Qt3DExtras::Qt3DWindow(screen)
{
m_Scene = new Qt3DCore::QEntity;
setRootEntity(m_Scene);
auto renderSurfaceSelector = new Qt3DRender::QRenderSurfaceSelector(m_Scene);
renderSurfaceSelector->setSurface(this);
auto clearBuffers = new Qt3DRender::QClearBuffers(renderSurfaceSelector);
clearBuffers->setBuffers(Qt3DRender::QClearBuffers::AllBuffers);
clearBuffers->setClearColor(Qt::gray);
auto viewport = new Qt3DRender::QViewport(renderSurfaceSelector);
auto cameraSelector = new Qt3DRender::QCameraSelector(viewport);
m_Camera = new Qt3DRender::QCamera(cameraSelector);
m_Camera->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);
m_Camera->setPosition(QVector3D(0.0f, 0.0f, 100.0f));
m_Camera->setViewCenter(QVector3D(0.0f, 0.0f, 0.0f));
cameraSelector->setCamera(m_Camera);
auto cameraController = new Qt3DExtras::QFirstPersonCameraController(m_Scene);
cameraController->setCamera(m_Camera);
m_OpaqueLayer = new Qt3DRender::QLayer;
auto opaqueFilter = new Qt3DRender::QLayerFilter(m_Camera);
opaqueFilter->addLayer(m_OpaqueLayer);
m_TransparentLayer = new Qt3DRender::QLayer;
auto transparentFilter = new Qt3DRender::QLayerFilter(m_Camera);
transparentFilter->addLayer(m_TransparentLayer);
setActiveFrameGraph(renderSurfaceSelector);
}
You can fix that by adding a QNoDraw node as a child of clearBuffers, as shown in this answer. The "artifact" is not caused by the layer filters, it is a problem of QClearBuffers itself.
Making clearBuffers a child of cameraSelector may seem to work on the surface, but what's actually happening is that everything is being rendered twice, so the transparent sphere will appear darker. You can verify this by commenting out either one of the filters: the objects in the corresponding layer will get rendered anyway.
By leaving clearBuffers as a child of renderSurfaceSelector and adding the QNoDraw, you don't get undesired stuff drawn on top of your viewport, and the filters behave as expected.
Fixed. In the original example, an error found. I don't fully understand why it's the right thing to do:
auto clearBuffers = new Qt3DRender::QClearBuffers(cameraSelector);
insted
auto clearBuffers = new Qt3DRender::QClearBuffers(renderSurfaceSelector);

Per axis grid properties (color), how it works?

From this source code:
#include "mainwindow.h"
#include <Q3DScatter>
using namespace QtDataVisualization;
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
Q3DScatter *graph = new Q3DScatter;
QWidget *widget = QWidget::createWindowContainer(graph);
setCentralWidget(widget);
}
MainWindow::~MainWindow() {}
I get:
See that wall directly in front, its grayed (can I describe it this way?) if compared to the other walls. How can I tweak grid lines that way? I mean just one wall.
Basically, you can change the color of the grid-line with the following code.
#include <QApplication>
#include <QtDataVisualization/Q3DScatter>
#include <QtDataVisualization/Q3DLight>
#include <QtDataVisualization/Q3DTheme>
#include <QDebug>
#include <QTimer>
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
auto scatter = new QtDataVisualization::Q3DScatter;
auto widget=QWidget::createWindowContainer(scatter);
widget->show();
scatter->activeTheme()->setType(QtDataVisualization::Q3DTheme::ThemeQt);
scatter->activeTheme()->setGridLineColor(QColor("red"));
auto light = scatter->scene()->activeLight();
auto camera = scatter->scene()->activeCamera();
int counter = 0;
QTimer timer;
timer.start(1 / 60);
QObject::connect(&timer, &QTimer::timeout, [&]() {
camera->setXRotation(counter++/60);
});
return a.exec();
}
Still, there seems to be no effect on how the grid-lines are rendered. I think this is do to the lighting of the 3D scene and of course of the camera position.
My small example program rotates the camera of the scene and therefore also the lighting of the grid-lines changes.
I think, there is little you can do here, as the interfaces doesn't allow you to change the shader code of the grid-lines.
Solution might be to add a custom 3d item, set a mesh and texture color. Resize the item to look like a grid axis and set the position. Repeat the idea for as many axis lines it is required.
Wondering if there is a (better) alternative approach.

How to add rendersetting component to my root entity in qt3d?

I am working on some codes about qt3d. I create a Qt3DWindow(named view).
Try to add a root entity(named rootEntity) in it.
Put a cube entity like this:
m_cubeEntity = new Qt3DCore::QEntity;
...
Qt3DExtras::QCuboidMesh *cubeMesh = new Qt3DExtras::QCuboidMesh;
Qt3DRender::QMaterial *cubeMaterial = new Qt3DRender::QMaterial;
Qt3DCore::QTransform *cubeTransform = new Qt3DCore::QTransform;
Qt3DRender::QObjectPicker *objectPicker = new Qt3DRender::QObjectPicker;
...
m_cubeEntity.addComponent(cubeMesh);
m_cubeEntity.addComponent(cubeMaterial);
m_cubeEntity.addComponent(cubeTransform);
m_cubeEntity.adComponent(objectPicker);
m_cubeEntity.setParent(m_rootEntity);
Everything works fine.
And then I find using PickingSettings.BoundingVolumePicking to pick my cube entity is inaccuracy. I want the PickingSettings.TrianglePicking.
====================================================================/
So then i do it like this:
m_renderSettings = new Qt3DRender::QRenderSettings();
m_renderSettings->pickingSettings()->setPickMethod(Qt3DRender::QPickingSettings::TrianglePicking);
m_renderSettings->pickingSettings()->setPickResultMode(Qt3DRender::QPickingSettings::AllPicks);
m_renderer = new Qt3DExtras::QForwardRenderer();
m_renderer->setClearColor(Qt::lightGray);
m_renderSettings->setActiveFrameGraph(m_renderer);
m_rootEntity->addComponent(m_renderSettings);
But now nothing is rendered. If i remove "m_renderSettings" from rootEntity, everything returns correct.
How to set rendersetting correctly for the root entity?
Most likely you just set a bad framegraph. You can easily display the standard framegraph using dumpObjectTree(), which tends to be a very useful function in debugging the framegraph of Qt3D.
For your simple use case it suffices to just use the renderSettings already contained in the activeFrameGraph() of the Qt3DWindow.
Just try the following simple app, that contains your desired QObjectPicker.
#include <QApplication>
#include <Qt3DCore/QTransform>
#include <Qt3DRender/QPickEvent>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QFrameGraphNode>
#include <Qt3DRender/QObjectPicker>
#include <Qt3DExtras/QDiffuseSpecularMaterial>
#include <Qt3DExtras/QCuboidMesh>
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DRender/QRenderSettings>
#include <QDebug>
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
auto view = new Qt3DExtras::Qt3DWindow();
auto rootEntity = new Qt3DCore::QEntity();
view->setRootEntity(rootEntity);
// Shows your framegraph! Simple forward renderer!
view->activeFrameGraph()->dumpObjectTree();
auto rendersettings=view->renderSettings();
rendersettings->pickingSettings()->setPickMethod(Qt3DRender::QPickingSettings::TrianglePicking);
rendersettings->pickingSettings()->setPickResultMode(Qt3DRender::QPickingSettings::AllPicks);
auto cameraEntity = view->camera();
cameraEntity->lens()->setPerspectiveProjection(45.0f, 1., 0.1f, 10000.0f);
cameraEntity->setPosition(QVector3D(0, 2, 0));
cameraEntity->setUpVector(QVector3D(0, 1, 0));
cameraEntity->setViewCenter(QVector3D(0, 0, 0));
auto cubeEntity = new Qt3DCore::QEntity(rootEntity);
auto cubeMesh = new Qt3DExtras::QCuboidMesh;
cubeMesh->setXExtent(1.);
cubeMesh->setYExtent(1.);
auto cubeMaterial = new Qt3DExtras::QDiffuseSpecularMaterial;
auto objectPicker = new Qt3DRender::QObjectPicker;
QObject::connect(objectPicker, &Qt3DRender::QObjectPicker::clicked, [](Qt3DRender::QPickEvent* pick) {
qDebug() << pick;
});
cubeEntity->addComponent(cubeMesh);
cubeEntity->addComponent(cubeMaterial);
cubeEntity->addComponent(objectPicker);
view->show();
return a.exec();
}

How to exclude empty QEntity from bounding volume computation

My company uses Qt3D to display its CAD models. Wee tried to use the function QCamera::viewEntity(Qt3DCore::QEntity *entity) in order to compute the bounding sphere of a given entity and also to fit the entity to the screen.
Now, we stumbled across an unsolvable problem in case of empty QEntity nodes. I'll call a node empty, if it does not contain any vertex/point after all. In this case I expected, that it should be neglected in the computation of bounding volume. Instead it seems, that it will be treated, as it has a bounding sphere with center (0.,0.,0.) and radius 0.
The following code illustrates the issue:
main.cpp
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QFrame>
#include <Qt3DRender/QRenderSettings>
#include <Qt3DCore/QTransform>
#include <Qt3DRender/QCamera>
#include <Qt3DExtras/QSphereMesh>
#include <Qt3DExtras/QDiffuseSpecularMaterial>
#include <Qt3DExtras/QForwardRenderer>
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QCameraLens>
#include <QPushButton>
Qt3DCore::QEntity* createSphereMesh()
{
auto sphereMat = new Qt3DExtras::QDiffuseSpecularMaterial;
sphereMat->setDiffuse(QColor(Qt::blue));
auto mesh = new Qt3DExtras::QSphereMesh();
mesh->setRadius(0.5);
auto meshEntity = new Qt3DCore::QEntity;
meshEntity->addComponent(mesh);
meshEntity->addComponent(sphereMat);
return meshEntity;
}
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
auto view = new Qt3DExtras::Qt3DWindow();
view->defaultFrameGraph()->setClearColor(QColor(127, 127, 127));
auto settings = view->renderSettings();
settings->setActiveFrameGraph(view->activeFrameGraph());
auto rootEntity = new Qt3DCore::QEntity();
view->setRootEntity(rootEntity);
auto sphere1 = createSphereMesh();
sphere1->setParent(rootEntity);
auto trafo1 = new Qt3DCore::QTransform;
trafo1->setTranslation(QVector3D(20, 10, 0));
sphere1->addComponent(trafo1);
auto sphere2 = createSphereMesh();
sphere2->setParent(rootEntity);
auto trafo2 = new Qt3DCore::QTransform;
trafo2->setTranslation(QVector3D(20, -10, 0));
sphere2->addComponent(trafo2);
QObject::connect(view->camera()->lens(), &Qt3DRender::QCameraLens::viewSphere, [&](const QVector3D& center, float radius) {
qDebug() << "Bounding Sphere:" << center << radius;
auto boundingSphereEntity = new Qt3DCore::QEntity;
auto sphereMat = new Qt3DExtras::QDiffuseSpecularMaterial;
sphereMat->setAlphaBlendingEnabled(true);
sphereMat->setDiffuse(QColor(255,255,255,80));
auto mesh = new Qt3DExtras::QSphereMesh();
mesh->setRadius(radius);
boundingSphereEntity->addComponent(sphereMat);
boundingSphereEntity->addComponent(mesh);
auto trafoAll = new Qt3DCore::QTransform;
trafoAll->setTranslation(center);
boundingSphereEntity->addComponent(trafoAll);
boundingSphereEntity->addComponent(mesh);
boundingSphereEntity->addComponent(sphereMat);
boundingSphereEntity->setParent(rootEntity);
});
auto container = QWidget::createWindowContainer(view);
auto viewAllBtn = new QPushButton("View All");
QObject::connect(viewAllBtn, &QPushButton::clicked, [&]() {
view->camera()->viewAll();
});
QFrame frame;
frame.setFixedSize(500, 500);
frame.setLayout(new QVBoxLayout);
frame.layout()->addWidget(container);
frame.layout()->addWidget(viewAllBtn);
frame.show();
return a.exec();
}
I have two blue spheres with radius 0.5 being at distance 20 from each other. I'm expecting a bounding sphere with center (20,0,0) and radius (10.5).
Instead the program prints:
Bounding Sphere: QVector3D(12.1432, 2.14455, -1.32699e-08) 14.9644
It seems that the value 2.14455 comes really from nowhere and that my add bounding volume sphere is somewhat unpredictable.
If I will replace the translation QVector3D(20, 10, 0) by QVector3D(0,10,0) the result will be like expected.
How, can I exclude the root entity, from my bounding volume computation?