Qt3d. Нарисуйте прозрачный QSphereMesh над треугольниками - PullRequest
2 голосов
/ 05 марта 2019

У меня есть функция, которая рисует треугольники через OpenGL

Я рисую два треугольника нажатием кнопки (функция on_drawMapPushButton_clicked ()).

Затем я рисую сферу, которая находится над этими треугольниками.И теперь я вижу, что сфера нарисована правильно по первому треугольнику, но второй треугольник нарисован по сфере, а не наоборот.

Если я нажму кнопку второй раз, то сфера будет правильно нарисована по первому и второму треугольникам..

Когда я нажимаю кнопку третий раз, второй треугольник снова рисуется над сферой.

Когда я нажимаю кнопку в четвертый раз, тогда сфер правильно рисуется над первым и вторым треугольниками и т. Д.

Если я использую вphereMesh QPhongMaterial вместо QPhongAlphaMaterial, тогда сфер правильно рисуетсяпервый и второй треугольники всегда.Как и должно быть.

Я не могу понять, что я делаю неправильно, чтобы моя сфера всегда рисовалась поверх треугольников.

Код, который рисует прозрачную сферу:

selectModel_ = new Qt3DExtras::QSphereMesh(selectEntity_);
selectModel_->setRadius(75);
selectModel_->setSlices(150);

selectMaterial_ = new Qt3DExtras::QPhongAlphaMaterial(selectEntity_);
selectMaterial_->setAmbient(QColor(28, 61, 136));
selectMaterial_->setDiffuse(QColor(11, 56, 159));
selectMaterial_->setSpecular(QColor(10, 67, 199));
selectMaterial_->setShininess(0.8f);

selectEntity_->addComponent(selectModel_);
selectEntity_->addComponent(selectMaterial_);

Функция drawTriangles:

void drawTriangles(QPolygonF triangles, QColor color){
    int numOfVertices = triangles.size();

    // Create and fill vertex buffer
    QByteArray bufferBytes;
    bufferBytes.resize(3 * numOfVertices * static_cast<int>(sizeof(float)));
    float *positions = reinterpret_cast<float*>(bufferBytes.data());

    for(auto point : triangles){
        *positions++ = static_cast<float>(point.x());
        *positions++ = 0.0f; //We need to drow only on the surface
        *positions++ = static_cast<float>(point.y());
    }

    geometry_ = new Qt3DRender::QGeometry(mapEntity_);
    auto *buf = new Qt3DRender::QBuffer(geometry_);
    buf->setData(bufferBytes);

    positionAttribute_ = new Qt3DRender::QAttribute(mapEntity_);
    positionAttribute_->setName(Qt3DRender::QAttribute::defaultPositionAttributeName());
    positionAttribute_->setVertexBaseType(Qt3DRender::QAttribute::Float); //In our buffer we will have only floats
    positionAttribute_->setVertexSize(3); // Size of a vertex
    positionAttribute_->setAttributeType(Qt3DRender::QAttribute::VertexAttribute); // Attribute type
    positionAttribute_->setByteStride(3 * sizeof(float));
    positionAttribute_->setBuffer(buf); 
    geometry_->addAttribute(positionAttribute_); // Add attribute to ours  Qt3DRender::QGeometry

    // Create and fill an index buffer
    QByteArray indexBytes;
    indexBytes.resize(numOfVertices * static_cast<int>(sizeof(unsigned int))); // start to end
    unsigned int *indices = reinterpret_cast<unsigned int*>(indexBytes.data());

    for(unsigned int i = 0; i < static_cast<unsigned int>(numOfVertices); ++i) {
        *indices++ = i;
    }

    auto *indexBuffer = new Qt3DRender::QBuffer(geometry_);
    indexBuffer->setData(indexBytes);

    indexAttribute_ = new Qt3DRender::QAttribute(geometry_);
    indexAttribute_->setVertexBaseType(Qt3DRender::QAttribute::UnsignedInt); //In our buffer we will have only unsigned ints
    indexAttribute_->setAttributeType(Qt3DRender::QAttribute::IndexAttribute); // Attribute type
    indexAttribute_->setBuffer(indexBuffer);
    indexAttribute_->setCount(static_cast<unsigned int>(numOfVertices)); // Set count of our vertices
    geometry_->addAttribute(indexAttribute_); // Add the attribute to ours Qt3DRender::QGeometry

    shape_ = new Qt3DRender::QGeometryRenderer(mapEntity_);
    shape_->setPrimitiveType(Qt3DRender::QGeometryRenderer::Triangles);
    shape_->setGeometry(geometry_); 

    //Create material
    material_ = new Qt3DExtras::QPhongMaterial(mapEntity_);
    material_->setAmbient(color);

    trianglesEntity_ = new Qt3DCore::QEntity(mapEntity_);
    trianglesEntity_->addComponent(shape_); 
    trianglesEntity_->addComponent(material_);
}

Обработчик нажатия кнопки on_drawMapPushButton_clicked ():

void on_drawMapPushButton_clicked()
{
    clearMap(); //Implementation is above
    QPolygonF triangle1;
    triangle1 << QPointF( 0 ,-1000) << QPointF(0 ,1000) << QPointF(1000, -1000);
    drawTriangles(triangle1, Qt::black);

    QPolygonF triangle2;
    triangle2 << QPointF(-1000,-1000) << QPointF(-100,1000) << QPointF(-100,-1000);
    drawTriangles(triangle2, Qt::red);
}

Функция очистки карты clearMap ():

void clearMap()
{
    if(mapEntity_){
        delete mapEntity_;
        mapEntity_ = nullptr;
        mapEntity_ = new Qt3DCore::QEntity(view3dRootEntity_);
    }
}

Ответы [ 3 ]

4 голосов
/ 07 марта 2019

Хорошо, здесь идет расширенный ответ.

Причина, по которой это иногда случается, а иногда не зависит от порядка ваших сущностей.Если вы экспериментируете с двумя простыми сферами, одна прозрачная, а другая нет, вы увидите, что когда прозрачная сфера будет добавлена ​​позже, она будет нарисована над непрозрачным объектом - так, как вы этого хотите.

Это происходит потому, что непрозрачный объект будет нарисован первым (он появляется первым в графе сцены), а затем прозрачный объект, который даст вам желаемый результат.В другом случае, когда прозрачный объект рисуется первым, непрозрачный объект рисуется выше, потому что QPhongAlphaMaterial имеет состояние визуализации QNoDepthMask, которое говорит ему не записывать вбуфер глубины.Таким образом, непрозрачный объект всегда проходит тест глубины, на котором прозрачный объект фактически уже нарисован.Вам нужно еще немного поработать, чтобы правильно нарисовать прозрачные объекты для произвольных графиков сцены и положения камеры.

График рендеринга Qt3D

Чтобы понять, что вам нужно сделать, вы должны понять, как рендеринг Qt3Dграфик выложен.Если вы уже знаете это, вы можете пропустить эту часть.

enter image description here

Курсивные слова ссылки на элементы в графическом изображении в следующем тексте.

Если вы используете Qt3DWindow, вы не сможете получить доступ к корневому узлу графика рендеринга .Поддерживается окном.Вы можете получить доступ к корневому узлу QRenderSettings и вашего фрейма графа через функции activeFramegraph() и renderSettings(), которые вы оба можете вызвать в окне.Вы также можете установить корневой узел графа сцены с помощью функции setRootEntity() Qt3DWindow.Внутренне окно имеет QAspectEngine, где оно устанавливает корневой узел всего графа, который является корневым узлом графа рендеринга на изображении графа выше.

Если вы хотите вставить узел фрейма графа в существующий фреймграф 3D-окна, вы должны добавить его в качестве родителя активного фрейма графа, который я объясню в следующем разделе.Если у вас есть свой собственный фреймграф, который вы установили в окне через setActiveFramegraph(), просто добавьте его в конец, этого должно быть достаточно.

Использование QSortPolicy

Как вы уже узналив соответствии с другими вашими вопросами, вы можете использовать QSortPolicy в вашем фрейме для сортировки объектов по расстоянию до камеры.Вы можете добавить политику сортировки следующим образом (при условии, что view является вашим Qt3DWindow, а scene является вашим корневым объектом графа сцены, хотя я не понимаю, почему это должно быть):

Qt3DRender::QFrameGraphNode *framegraph = view.activeFrameGraph();
Qt3DRender::QSortPolicy *sortPolicy = new Qt3DRender::QSortPolicy(scene);
framegraph->setParent(sortPolicy);
QVector<Qt3DRender::QSortPolicy::SortType> sortTypes = 
      QVector<Qt3DRender::QSortPolicy::SortType>() << Qt3DRender::QSortPolicy::BackToFront;
sortPolicy->setSortTypes(sortTypes);
view.setActiveFrameGraph(framegraph);

Проблема с этим кодом заключается в том, что эта политика сортировки сортирует объекты по расстоянию между их центрами до камеры.Если один из непрозрачных объектов находится ближе к камере, чем прозрачный объект, он все равно рисуется позже и перекрывает прозрачный объект.См. Изображения ниже для графического объяснения.

Красная и черная сферы находятся дальше от камеры, чем тор, поэтому они рисуются первыми и не закрывают тор. no occlusion

Нет, центр красной сферы ближе к камере, чем центр тора.Он обрабатывается позже тора и закрывает его. occlusion

Использование двух ветвей фрейма графа

Вы можете решить проблему выше, если используете два фрейма графаветви.Тот, который рисует все непрозрачные объекты, и тот, который рисует все прозрачные.Чтобы достичь этого, вы должны использовать QLayer и QLayerFilter.Вы можете прикрепить слои к объектам, а затем добавить фильтры слоев в свой фреймграф.Таким образом, вы можете исключить объекты из определенной ветки вашего фрейма.

Допустим, вы создали два слоя, один для непрозрачных объектов и один для прозрачных:

Qt3DRender::QLayer *transparentLayer = new Qt3DRender::QLayer;
Qt3DRender::QLayer *opaqueLayer = new Qt3DRender::QLayer;

Вы должныприкрепите прозрачный слой к каждому прозрачному объекту, а непрозрачный слой к каждому непрозрачному объекту как компоненту (используя addComponent()).

К сожалению, вам нужно специальное дерево фрейма графа, чтобы включить два соответствующих фильтра слоя (опять же, при условии, что Qt3DWindow - это ваш *1090*):

Qt3DRender::QRenderSurfaceSelector *renderSurfaceSelector
        = new Qt3DRender::QRenderSurfaceSelector();
renderSurfaceSelector->setSurface(&view);
Qt3DRender::QClearBuffers *clearBuffers 
        = new Qt3DRender::QClearBuffers(renderSurfaceSelector);
clearBuffers->setBuffers(Qt3DRender::QClearBuffers::AllBuffers);
clearBuffers->setClearColor(Qt::white);

Это первая ветвь, которая очищаетбуферы.Теперь добавьте следующий код:

Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport(renderSurfaceSelector);
Qt3DRender::QCameraSelector *cameraSelector = new Qt3DRender::QCameraSelector(viewport);
Qt3DRender::QCamera *camera = new Qt3DRender::QCamera(cameraSelector);
// set your camera parameters here
cameraSelector->setCamera(camera);

Поскольку вы создаете QViewport как дочерний элемент QRenderSurfaceSelector, теперь он является родным братом в вашем фреймграфе относительно QClearBuffers.Вы можете увидеть иллюстрацию примера framegraphs здесь .

Теперь вам нужно создать два конечных узла, которые содержат фильтры слоев.Движок Qt3D всегда выполняет целую ветвь, когда достигает листа.Это означает, что сначала рисуются непрозрачные объекты, а затем прозрачные.

// not entirely sure why transparent filter has to go first
// I would have expected the reversed order of the filters but this works...

Qt3DRender::QLayerFilter *transparentFilter = new Qt3DRender::QLayerFilter(camera);
transparentFilter->addLayer(transparentLayer);

Qt3DRender::QLayerFilter *opaqueFilter = new Qt3DRender::QLayerFilter(camera);
opaqueFilter->addLayer(opaqueLayer);

Два фильтра слоя теперь являются конечными узлами в вашей ветви фрейма графа, и Qt3D сначала будет рисовать непрозрачные объекты, а затем впоследствии, так как ониспользует тот же видовой экран и все, нарисует прозрачные объекты выше.Он будет рисовать их правильно (т. Е. Не перед частями непрозрачных объектов, за которыми фактически скрывается прозрачный объект, поскольку мы не очистили буферы глубины снова -> Разделение фрейма графа происходит только на узле камеры).

Теперь установите новый framegaph на Qt3DWindow:

view.setActiveFrameGraph(renderSurfaceSelector);

Результат:

enter image description here

0 голосов
/ 18 марта 2019

Если вы используете Qt3d с QML и хотите управлять рисованием элементов порядка, вы можете управлять им по порядку слоев в вашем файле QML.

Что-то вроде:

{
  objectName: "firstLayer"
  id : firstLayer
}
Layer {
  objectName: "secondLayer"
  id: secondLayer
}

Порядок, в котором вы добавляете их к фильтрам слоев, будет управлять тем, который рисуется первым:

RenderSurfaceSelector {
   CameraSelector {
     id : cameraSelector
     camera: mainCamera
     FrustumCulling {
       ClearBuffers {
       buffers : ClearBuffers.AllBuffers
       clearColor: "#04151c"
       NoDraw {}
    }
    LayerFilter
    {
       objectName: "firstLayerFilter"
       id: firstLayerFilter
       layers: [firstLayer]
    }

    LayerFilter
    {
      id: secondLayerFilter
      objectName: "secondLayerFilter"
      layers: [secondLayer]
    }

Тогда все, что вы добавляете во второй слой, будет нарисовано поверх первого слоя.Я использовал это, чтобы текст всегда отображался перед фигурами, но его можно использовать и с прозрачными пленками.

0 голосов
/ 11 марта 2019

Моя ошибка заключалась в том, что я сделал неправильный порядок создания и удаления сущностей Треугольники и Сфера.

В псевдокоде правильный порядок выглядит следующим образом:

clearTriangles();
clearSphere();       
drawTriangles();
drawSphere();
...