Qt3D geometry shader working in QML but not in C++

混江龙づ霸主 提交于 2020-08-20 06:11:07

问题


Update

The OpenGL version seems to be 4.3 at least according to the following code

QSurfaceFormat format = view.format();
int major = format.majorVersion();
int minor = format.minorVersion();

so geometry shaders should work and the issue seems to be something else.



Original Question

While trying to answer this question related to how to create billboards in Qt3D I encountered an issue I found no solution for.

I used the code from this GitHub repository which contains C++ and QML. It works perfectly and showcases how billboards can be implemented in Qt3D - at least when using QML. This is what a screenshot of the code looks like:


Now, the person asking the question I mentioned needs it to be in C++ so I tried to translate it since every QML class has a corresponding C++ class. I only succeeded to some extend. The original code uses a geometry shader to create the billboards. When I don't include the geometry part of the shader I manage to get the individual points drawn on-screen with a pre-defined color like this (I circled the points so you can see them better):

But as soon as I include the geometry shader all the points vanish. But this exact shader has worked under QML.

I boiled it down to the geometry shader being the issue because when I comment it out I get the white points but when I add it the points are not shown anymore (and obviously the billboards aren't either):

billboardShaderProgram->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl("qrc:/shaders/billboards.vert")));
//billboardShaderProgram->setGeometryShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl("qrc:/shaders/billboards.geom")));
billboardShaderProgram->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl("qrc:/shaders/billboards.frag")));

After having checked the OpenGL version (which seems to be 4.3) the issue must be something else in how I create the objects in C++.



Code


You can find the project here on GitHub.


Alternatively, I'll add the relevant classes here and try to keep them to a minimum.

main.cpp:

// Includes for framegraph
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DRender/QFrameGraphNode>
#include <Qt3DRender/QRenderSurfaceSelector>
#include <Qt3DRender/QViewport>
#include <Qt3DRender/QCameraSelector>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QCameraLens>
#include <Qt3DRender/QClearBuffers>
#include <Qt3DExtras/QForwardRenderer>

#include <Qt3DExtras/QFirstPersonCameraController>
#include <Qt3DInput/QInputSettings>

#include <Qt3DCore/QEntity>
#include <Qt3DExtras/QPlaneMesh>
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DRender/QGeometryRenderer>
#include <Qt3DCore/QTransform>
#include <Qt3DRender/QMaterial>
#include <Qt3DRender/QParameter>
#include <Qt3DRender/QTexture>
#include <Qt3DRender/QTextureImage>
#include <Qt3DRender/QEffect>
#include <Qt3DRender/QTechnique>
#include <Qt3DRender/QRenderPass>
#include <Qt3DRender/QShaderProgram>
#include <Qt3DExtras/QSphereMesh>
#include <Qt3DExtras/QCuboidMesh>
#include <Qt3DRender/QGraphicsApiFilter>
#include <Qt3DExtras/QTextureMaterial>

#include <QSurfaceFormat>

#include <QVector3D>
#include <QColor>

#include <QGuiApplication>

#include "billboardgeometry.h"

#include <QOpenGLContext>

Qt3DExtras::QFirstPersonCameraController * cameraController;

int windowWidth = 1600;
int windowHeight = 800;

Qt3DCore::QEntity *createScene() {
    Qt3DCore::QEntity *root = new Qt3DCore::QEntity();

    cameraController = new Qt3DExtras::QFirstPersonCameraController(root);

    // Add plane
    Qt3DCore::QEntity *planeEntity = new Qt3DCore::QEntity(root);
    Qt3DExtras::QPlaneMesh *planeMesh = new Qt3DExtras::QPlaneMesh(planeEntity);
    planeMesh->setWidth(20);
    planeMesh->setHeight(20);
    Qt3DExtras::QPhongMaterial *planeMaterial = new Qt3DExtras::QPhongMaterial(planeEntity);
    planeMaterial->setAmbient(QColor(0, 0, 0.7 * 255, 0.1 * 255));
    planeEntity->addComponent(planeMesh);
    planeEntity->addComponent(planeMaterial);

    // Add sphere
    Qt3DCore::QEntity *sphereEntity = new Qt3DCore::QEntity(root);
    Qt3DExtras::QSphereMesh *sphereMesh = new Qt3DExtras::QSphereMesh(sphereEntity);
    Qt3DExtras::QPhongMaterial *sphereMaterial = new Qt3DExtras::QPhongMaterial(sphereEntity);
    sphereMaterial->setAmbient(Qt::red);
    Qt3DCore::QTransform *sphereTransform = new Qt3DCore::QTransform(sphereEntity);
    sphereTransform->setTranslation(QVector3D(0., 5., 0.));
    sphereEntity->addComponent(sphereMesh);
    sphereEntity->addComponent(sphereMaterial);
    sphereEntity->addComponent(sphereTransform);

    // Add cube
    Qt3DCore::QEntity *cubeEntity = new Qt3DCore::QEntity(root);
    Qt3DExtras::QCuboidMesh *cubeMesh = new Qt3DExtras::QCuboidMesh(cubeEntity);
    Qt3DExtras::QPhongMaterial *cubeMaterial = new Qt3DExtras::QPhongMaterial(cubeEntity);
    cubeMaterial->setAmbient(Qt::gray);
    Qt3DCore::QTransform *cubeTransform = new Qt3DCore::QTransform();
    cubeTransform->setTranslation(QVector3D(2., 2., 5.));
    cubeEntity->addComponent(cubeMesh);
    cubeEntity->addComponent(cubeMaterial);
    cubeEntity->addComponent(cubeTransform);

    // Add Billboard
    Qt3DCore::QEntity *billboardEntity = new Qt3DCore::QEntity(root);

    // Create billboard geometry
    QVector<QVector3D> pos;
    pos << QVector3D(1, 1, 0);
    pos << QVector3D(-1, 2, 8);
    pos << QVector3D(1, 1, 7);
    pos << QVector3D(0, 0, 4);
    BillboardGeometry *billboardGeometry = new BillboardGeometry(billboardEntity);
    billboardGeometry->setPoints(pos);
    Qt3DRender::QGeometryRenderer *billboardRenderer = new Qt3DRender::QGeometryRenderer(billboardEntity);
    billboardRenderer->setPrimitiveType(Qt3DRender::QGeometryRenderer::Points);
    billboardRenderer->setGeometry(billboardGeometry);
    billboardRenderer->setVertexCount(billboardGeometry->count());

    Qt3DCore::QTransform *billboardTransform = new Qt3DCore::QTransform(billboardEntity);
    billboardTransform->setTranslation(QVector3D(0., 1.5, 0.));

    // Billboard material

    // Image of billboard material
    Qt3DRender::QMaterial *billboardMaterial = new Qt3DRender::QMaterial(billboardEntity);
    Qt3DRender::QTexture2D* texture = new Qt3DRender::QTexture2D();
    Qt3DRender::QTextureImage* textureImage = new Qt3DRender::QTextureImage(texture);
    textureImage->setSource(QUrl(QStringLiteral("qrc:/success-kid.png")));
    texture->addTextureImage(textureImage);

    // Parameters of billboard material
    Qt3DRender::QParameter* billboardParam1 = new Qt3DRender::QParameter(QStringLiteral("tex0"), texture);
    Qt3DRender::QParameter* billboardParam2 = new Qt3DRender::QParameter(QStringLiteral("WIN_SCALE"), QSize(1600, 800));
    Qt3DRender::QParameter* billboardParam3 = new Qt3DRender::QParameter(QStringLiteral("BB_SIZE"), QSize(100, 100));
    billboardMaterial->addParameter(billboardParam1);
    billboardMaterial->addParameter(billboardParam2);
    billboardMaterial->addParameter(billboardParam3);

    // Effect of material
    Qt3DRender::QEffect* billboardEffect = new Qt3DRender::QEffect();
    Qt3DRender::QTechnique* billboardTechnique = new Qt3DRender::QTechnique();
    billboardTechnique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGL);
    billboardTechnique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::CoreProfile);
    billboardTechnique->graphicsApiFilter()->setMajorVersion(3);
    billboardTechnique->graphicsApiFilter()->setMinorVersion(1);
    // You need the filter key because the QForwardRenderer employed as the default framegraph by the Qt3DWindow
    // extends QTechniqueFilter and filters for this key exactly. Without it, the material gets discarded.
    Qt3DRender::QFilterKey* filterKey = new Qt3DRender::QFilterKey(billboardMaterial);
    filterKey->setName(QStringLiteral("renderingStyle"));
    filterKey->setValue(QStringLiteral("forward"));
    billboardTechnique->addFilterKey(filterKey);
    Qt3DRender::QRenderPass* billboardRenderPass = new Qt3DRender::QRenderPass();
    Qt3DRender::QShaderProgram* billboardShaderProgram = new Qt3DRender::QShaderProgram();
    billboardShaderProgram->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl("qrc:/shaders/billboards.vert")));
    //billboardShaderProgram->setGeometryShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl("qrc:/shaders/billboards.geom")));
    billboardShaderProgram->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl("qrc:/shaders/billboards.frag")));
    billboardRenderPass->setShaderProgram(billboardShaderProgram);
    billboardTechnique->addRenderPass(billboardRenderPass);
    billboardEffect->addTechnique(billboardTechnique);
    billboardMaterial->setEffect(billboardEffect);

    billboardEntity->addComponent(billboardRenderer);
    billboardEntity->addComponent(billboardMaterial);
    billboardEntity->addComponent(billboardTransform);

    return root;
}

int main(int argc, char* argv[])
{
    QGuiApplication app(argc, argv);

    Qt3DExtras::Qt3DWindow view;
    view.resize(windowWidth, windowHeight);
    Qt3DExtras::QForwardRenderer *renderer = (Qt3DExtras::QForwardRenderer *)view.activeFrameGraph();
    renderer->setClearColor("black");

    Qt3DRender::QCamera *camera = view.camera();
    camera->setProjectionType(Qt3DRender::QCameraLens::PerspectiveProjection);
    camera->setFieldOfView(45);
    // Cast to float to ensure float division
    camera->setAspectRatio(windowWidth / (float) windowHeight);
    camera->setNearPlane(0.1f);
    camera->setFarPlane(100.f);
    camera->setPosition(QVector3D(0., 10., 20.));
    camera->setViewCenter(QVector3D(0., 0., 0.));
    camera->setUpVector(QVector3D(0., 1., 0.));

    Qt3DCore::QEntity *root = createScene();
    view.setRootEntity(root);
    cameraController->setCamera(camera);

    view.setTitle("Billboards");
    view.show();

    return app.exec();
}

billboardgeometry.h:

#ifndef BILLBOARDGEOMETRY_H
#define BILLBOARDGEOMETRY_H

#include <Qt3DRender/QGeometry>
#include <Qt3DRender/QBuffer>

#include <QVector3D>


class BillboardGeometry : public Qt3DRender::QGeometry
{
  Q_OBJECT

  Q_PROPERTY(int count READ count NOTIFY countChanged)

public:
  BillboardGeometry( Qt3DCore::QNode *parent = nullptr );

  void setPoints( const QVector<QVector3D> &vertices );

  int count();

signals:
    void countChanged(int count);

private:
  Qt3DRender::QAttribute *mPositionAttribute = nullptr;
  Qt3DRender::QBuffer *mVertexBuffer = nullptr;
  int mVertexCount = 0;
};

#endif // BILLBOARDGEOMETRY_H

billboardgeometry.cpp:

#include "billboardgeometry.h"

#include <Qt3DRender/QAttribute>


BillboardGeometry::BillboardGeometry( Qt3DCore::QNode *parent )
  : Qt3DRender::QGeometry( parent )
  , mPositionAttribute( new Qt3DRender::QAttribute( this ) )
  , mVertexBuffer( new Qt3DRender::QBuffer( Qt3DRender::QBuffer::VertexBuffer, this ) )
{

  mPositionAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute );
  mPositionAttribute->setBuffer( mVertexBuffer );
  mPositionAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float );
  mPositionAttribute->setVertexSize( 3 );
  mPositionAttribute->setName( Qt3DRender::QAttribute::defaultPositionAttributeName() );

  addAttribute( mPositionAttribute );

}

int BillboardGeometry::count()
{
  return mVertexCount;
}

void BillboardGeometry::setPoints(const QVector<QVector3D> &vertices)
{
  QByteArray vertexBufferData;
  vertexBufferData.resize( vertices.size() * 3 * sizeof( float ) );
  float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
  int idx = 0;
  for ( const auto &v : vertices )
  {
    rawVertexArray[idx++] = v.x();
    rawVertexArray[idx++] = v.y();
    rawVertexArray[idx++] = v.z();
  }

  mVertexCount = vertices.count();
  mVertexBuffer->setData( vertexBufferData );

  emit countChanged(mVertexCount);
}

billboard.vert:

#version 150

uniform mat4 modelViewProjection;

in vec3 vertexPosition;

void main(void)
{
    gl_Position = modelViewProjection * vec4(vertexPosition, 1);
}

billboard.geom:

#version 150

layout (points) in;
layout (triangle_strip, max_vertices = 4) out;

uniform mat4 modelViewProjection;

uniform vec2 BB_SIZE;    // billboard size in pixels
uniform vec2 WIN_SCALE;  // the size of the viewport in pixels

out vec2 UV;

void main (void)
{

    vec4 P = gl_in[0].gl_Position;
    P /= P.w;
    
    //vec2 size = vec2(0.5,0.5);
    vec2 size = BB_SIZE / WIN_SCALE;
    
    gl_Position = P;
    gl_Position.xy += vec2(-0.5,-0.5) * size;
    UV = vec2(0,0);
    EmitVertex();
    
    gl_Position = P;
    gl_Position.xy += vec2(0.5,-0.5) * size;
    UV = vec2(1,0);
    EmitVertex();
    
    gl_Position = P;
    gl_Position.xy += vec2(-0.5,+0.5) * size;
    UV = vec2(0,1);
    EmitVertex();
    
    gl_Position = P;
    gl_Position.xy += vec2(+0.5,+0.5) * size;
    UV = vec2(1,1);
    EmitVertex();
    
    EndPrimitive();
}

billboard.frag:

#version 150

uniform sampler2D tex0;

in vec2 UV;

void main(void)
{
    //gl_FragColor = texture(tex0, UV);
    gl_FragColor = vec4(1, 1, 1, 1);
}

shaders.qrc:

<RCC>
    <qresource prefix="/shaders">
        <file>billboards.frag</file>
        <file>billboards.vert</file>
        <file>billboards.geom</file>
    </qresource>
</RCC>

billboards.pro:

TEMPLATE = app
QT += 3dcore 3drender 3dinput 3dquick qml quick 3dquickextras 3dextras

SOURCES += \
        main.cpp \
    billboardgeometry.cpp

RESOURCES += qml.qrc \
    shaders.qrc


# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

HEADERS += \
    billboardgeometry.h

success-kid.jpg


回答1:


GitHub users wonder-sk and ismailsunni solved this issue for me by pointing to an error in the code:

Qt3DRender::QParameter* billboardParam2 = new Qt3DRender::QParameter(QStringLiteral("WIN_SCALE"), QSize(1600, 800));
Qt3DRender::QParameter* billboardParam3 = new Qt3DRender::QParameter(QStringLiteral("BB_SIZE"), QSize(100, 100));

In these two lines it needs to be QSizeF and not QSize - voila the shader is working!

Alternatively, there already exists a C++ port here.



来源:https://stackoverflow.com/questions/63079722/qt3d-geometry-shader-working-in-qml-but-not-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!